wintun: load wintun.dll from RCDATA resource
Signed-off-by: Simon Rozman <simon@rozman.si> Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
parent
3e08b8aee0
commit
65e03a9182
2
go.mod
2
go.mod
|
@ -5,6 +5,6 @@ go 1.13
|
||||||
require (
|
require (
|
||||||
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79
|
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79
|
||||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5
|
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5
|
||||||
golang.org/x/sys v0.0.0-20201027130517-9d1ec526b7bf
|
golang.org/x/sys v0.0.0-20201107080550-4d91cf3a1aaf
|
||||||
golang.org/x/text v0.3.2
|
golang.org/x/text v0.3.2
|
||||||
)
|
)
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -9,6 +9,8 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201027130517-9d1ec526b7bf h1:HmHgHRpqpvB74D7bjXkue6kkHJfOrKyYJtW6Sv4jpI4=
|
golang.org/x/sys v0.0.0-20201027130517-9d1ec526b7bf h1:HmHgHRpqpvB74D7bjXkue6kkHJfOrKyYJtW6Sv4jpI4=
|
||||||
golang.org/x/sys v0.0.0-20201027130517-9d1ec526b7bf/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201027130517-9d1ec526b7bf/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201107080550-4d91cf3a1aaf h1:kt3wY1Lu5MJAnKTfoMR52Cu4gwvna4VTzNOiT8tY73s=
|
||||||
|
golang.org/x/sys v0.0.0-20201107080550-4d91cf3a1aaf/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
|
97
tun/wintun/dll_windows.go
Normal file
97
tun/wintun/dll_windows.go
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package wintun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
"golang.zx2c4.com/wireguard/tun/wintun/memmod"
|
||||||
|
"golang.zx2c4.com/wireguard/tun/wintun/resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
type lazyDLL struct {
|
||||||
|
Name string
|
||||||
|
mu sync.Mutex
|
||||||
|
module *memmod.Module
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLazyDLL(name string) *lazyDLL {
|
||||||
|
return &lazyDLL{Name: name}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *lazyDLL) Load() error {
|
||||||
|
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.module))) != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
d.mu.Lock()
|
||||||
|
defer d.mu.Unlock()
|
||||||
|
if d.module != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const ourModule windows.Handle = 0
|
||||||
|
resInfo, err := resource.FindByName(ourModule, d.Name, resource.RT_RCDATA)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to find \"%v\" RCDATA resource: %v", d.Name, err)
|
||||||
|
}
|
||||||
|
data, err := resource.Load(ourModule, resInfo)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to load resource: %v", err)
|
||||||
|
}
|
||||||
|
module, err := memmod.LoadLibrary(data)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to load library: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.module)), unsafe.Pointer(module))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *lazyDLL) NewProc(name string) *lazyProc {
|
||||||
|
return &lazyProc{dll: d, Name: name}
|
||||||
|
}
|
||||||
|
|
||||||
|
type lazyProc struct {
|
||||||
|
Name string
|
||||||
|
mu sync.Mutex
|
||||||
|
dll *lazyDLL
|
||||||
|
addr uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *lazyProc) Find() error {
|
||||||
|
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.addr))) != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
if p.addr != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := p.dll.Load()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error loading %v DLL: %v", p.dll.Name, err)
|
||||||
|
}
|
||||||
|
addr, err := p.dll.module.ProcAddressByName(p.Name)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error getting %v address: %v", p.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.addr)), unsafe.Pointer(addr))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *lazyProc) Addr() uintptr {
|
||||||
|
err := p.Find()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return p.addr
|
||||||
|
}
|
620
tun/wintun/memmod/memmod_windows.go
Normal file
620
tun/wintun/memmod/memmod_windows.go
Normal file
|
@ -0,0 +1,620 @@
|
||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package memmod
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
type addressList struct {
|
||||||
|
next *addressList
|
||||||
|
address uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (head *addressList) free() {
|
||||||
|
for node := head; node != nil; node = node.next {
|
||||||
|
windows.VirtualFree(node.address, 0, windows.MEM_RELEASE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Module struct {
|
||||||
|
headers *IMAGE_NT_HEADERS
|
||||||
|
codeBase uintptr
|
||||||
|
modules []windows.Handle
|
||||||
|
initialized bool
|
||||||
|
isDLL bool
|
||||||
|
isRelocated bool
|
||||||
|
nameExports map[string]uint16
|
||||||
|
entry uintptr
|
||||||
|
blockedMemory *addressList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *Module) headerDirectory(idx int) *IMAGE_DATA_DIRECTORY {
|
||||||
|
return &module.headers.OptionalHeader.DataDirectory[idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *Module) copySections(address uintptr, size uintptr, old_headers *IMAGE_NT_HEADERS) error {
|
||||||
|
sections := module.headers.Sections()
|
||||||
|
for i := range sections {
|
||||||
|
if sections[i].SizeOfRawData == 0 {
|
||||||
|
// Section doesn't contain data in the dll itself, but may define uninitialized data.
|
||||||
|
sectionSize := old_headers.OptionalHeader.SectionAlignment
|
||||||
|
if sectionSize == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dest, err := windows.VirtualAlloc(module.codeBase+uintptr(sections[i].VirtualAddress),
|
||||||
|
uintptr(sectionSize),
|
||||||
|
windows.MEM_COMMIT,
|
||||||
|
windows.PAGE_READWRITE)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error allocating section: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always use position from file to support alignments smaller than page size (allocation above will align to page size).
|
||||||
|
dest = module.codeBase + uintptr(sections[i].VirtualAddress)
|
||||||
|
// NOTE: On 64bit systems we truncate to 32bit here but expand again later when "PhysicalAddress" is used.
|
||||||
|
sections[i].SetPhysicalAddress((uint32)(dest & 0xffffffff))
|
||||||
|
var dst []byte
|
||||||
|
unsafeSlice(unsafe.Pointer(&dst), a2p(dest), int(sectionSize))
|
||||||
|
for j := range dst {
|
||||||
|
dst[j] = 0
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if size < uintptr(sections[i].PointerToRawData+sections[i].SizeOfRawData) {
|
||||||
|
return errors.New("Incomplete section")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit memory block and copy data from dll.
|
||||||
|
dest, err := windows.VirtualAlloc(module.codeBase+uintptr(sections[i].VirtualAddress),
|
||||||
|
uintptr(sections[i].SizeOfRawData),
|
||||||
|
windows.MEM_COMMIT,
|
||||||
|
windows.PAGE_READWRITE)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error allocating memory block: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always use position from file to support alignments smaller than page size (allocation above will align to page size).
|
||||||
|
memcpy(
|
||||||
|
module.codeBase+uintptr(sections[i].VirtualAddress),
|
||||||
|
address+uintptr(sections[i].PointerToRawData),
|
||||||
|
uintptr(sections[i].SizeOfRawData))
|
||||||
|
// NOTE: On 64bit systems we truncate to 32bit here but expand again later when "PhysicalAddress" is used.
|
||||||
|
sections[i].SetPhysicalAddress((uint32)(dest & 0xffffffff))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *Module) realSectionSize(section *IMAGE_SECTION_HEADER) uintptr {
|
||||||
|
size := section.SizeOfRawData
|
||||||
|
if size != 0 {
|
||||||
|
return uintptr(size)
|
||||||
|
}
|
||||||
|
if (section.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) != 0 {
|
||||||
|
return uintptr(module.headers.OptionalHeader.SizeOfInitializedData)
|
||||||
|
}
|
||||||
|
if (section.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) != 0 {
|
||||||
|
return uintptr(module.headers.OptionalHeader.SizeOfUninitializedData)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type sectionFinalizeData struct {
|
||||||
|
address uintptr
|
||||||
|
alignedAddress uintptr
|
||||||
|
size uintptr
|
||||||
|
characteristics uint32
|
||||||
|
last bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *Module) finalizeSection(sectionData *sectionFinalizeData) error {
|
||||||
|
if sectionData.size == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sectionData.characteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0 {
|
||||||
|
// Section is not needed any more and can safely be freed.
|
||||||
|
if sectionData.address == sectionData.alignedAddress &&
|
||||||
|
(sectionData.last ||
|
||||||
|
(sectionData.size%uintptr(module.headers.OptionalHeader.SectionAlignment)) == 0) {
|
||||||
|
// Only allowed to decommit whole pages.
|
||||||
|
windows.VirtualFree(sectionData.address, sectionData.size, windows.MEM_DECOMMIT)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine protection flags based on characteristics
|
||||||
|
var ProtectionFlags = [8]uint32{
|
||||||
|
windows.PAGE_NOACCESS, // not writeable, not readable, not executable
|
||||||
|
windows.PAGE_EXECUTE, // not writeable, not readable, executable
|
||||||
|
windows.PAGE_READONLY, // not writeable, readable, not executable
|
||||||
|
windows.PAGE_EXECUTE_READ, // not writeable, readable, executable
|
||||||
|
windows.PAGE_WRITECOPY, // writeable, not readable, not executable
|
||||||
|
windows.PAGE_EXECUTE_WRITECOPY, // writeable, not readable, executable
|
||||||
|
windows.PAGE_READWRITE, // writeable, readable, not executable
|
||||||
|
windows.PAGE_EXECUTE_READWRITE, // writeable, readable, executable
|
||||||
|
}
|
||||||
|
protect := ProtectionFlags[sectionData.characteristics>>29]
|
||||||
|
if (sectionData.characteristics & IMAGE_SCN_MEM_NOT_CACHED) != 0 {
|
||||||
|
protect |= windows.PAGE_NOCACHE
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change memory access flags.
|
||||||
|
var oldProtect uint32
|
||||||
|
err := windows.VirtualProtect(sectionData.address, sectionData.size, protect, &oldProtect)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error protecting memory page: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *Module) finalizeSections() error {
|
||||||
|
sections := module.headers.Sections()
|
||||||
|
imageOffset := module.headers.OptionalHeader.imageOffset()
|
||||||
|
sectionData := sectionFinalizeData{}
|
||||||
|
sectionData.address = uintptr(sections[0].PhysicalAddress()) | imageOffset
|
||||||
|
sectionData.alignedAddress = alignDown(sectionData.address, uintptr(module.headers.OptionalHeader.SectionAlignment))
|
||||||
|
sectionData.size = module.realSectionSize(§ions[0])
|
||||||
|
sectionData.characteristics = sections[0].Characteristics
|
||||||
|
|
||||||
|
// Loop through all sections and change access flags.
|
||||||
|
for i := uint16(1); i < module.headers.FileHeader.NumberOfSections; i++ {
|
||||||
|
sectionAddress := uintptr(sections[i].PhysicalAddress()) | imageOffset
|
||||||
|
alignedAddress := alignDown(sectionAddress, uintptr(module.headers.OptionalHeader.SectionAlignment))
|
||||||
|
sectionSize := module.realSectionSize(§ions[i])
|
||||||
|
// Combine access flags of all sections that share a page.
|
||||||
|
// TODO: We currently share flags of a trailing large section with the page of a first small section. This should be optimized.
|
||||||
|
if sectionData.alignedAddress == alignedAddress || sectionData.address+sectionData.size > alignedAddress {
|
||||||
|
// Section shares page with previous.
|
||||||
|
if (sections[i].Characteristics&IMAGE_SCN_MEM_DISCARDABLE) == 0 || (sectionData.characteristics&IMAGE_SCN_MEM_DISCARDABLE) == 0 {
|
||||||
|
sectionData.characteristics = (sectionData.characteristics | sections[i].Characteristics) &^ IMAGE_SCN_MEM_DISCARDABLE
|
||||||
|
} else {
|
||||||
|
sectionData.characteristics |= sections[i].Characteristics
|
||||||
|
}
|
||||||
|
sectionData.size = sectionAddress + sectionSize - sectionData.address
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err := module.finalizeSection(§ionData)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error finalizing section: %v", err)
|
||||||
|
}
|
||||||
|
sectionData.address = sectionAddress
|
||||||
|
sectionData.alignedAddress = alignedAddress
|
||||||
|
sectionData.size = sectionSize
|
||||||
|
sectionData.characteristics = sections[i].Characteristics
|
||||||
|
}
|
||||||
|
sectionData.last = true
|
||||||
|
err := module.finalizeSection(§ionData)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error finalizing section: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *Module) executeTLS() {
|
||||||
|
directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_TLS)
|
||||||
|
if directory.VirtualAddress == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tls := (*IMAGE_TLS_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
|
||||||
|
callback := tls.AddressOfCallbacks
|
||||||
|
if callback != 0 {
|
||||||
|
for {
|
||||||
|
f := *(*uintptr)(a2p(callback))
|
||||||
|
if f == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
syscall.Syscall(f, 3, module.codeBase, uintptr(DLL_PROCESS_ATTACH), uintptr(0))
|
||||||
|
callback += unsafe.Sizeof(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *Module) performBaseRelocation(delta uintptr) (relocated bool, err error) {
|
||||||
|
directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_BASERELOC)
|
||||||
|
if directory.Size == 0 {
|
||||||
|
return delta == 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
relocationHdr := (*IMAGE_BASE_RELOCATION)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
|
||||||
|
for relocationHdr.VirtualAddress > 0 {
|
||||||
|
dest := module.codeBase + uintptr(relocationHdr.VirtualAddress)
|
||||||
|
|
||||||
|
var relInfos []uint16
|
||||||
|
unsafeSlice(
|
||||||
|
unsafe.Pointer(&relInfos),
|
||||||
|
a2p(uintptr(unsafe.Pointer(relocationHdr))+unsafe.Sizeof(*relocationHdr)),
|
||||||
|
int((uintptr(relocationHdr.SizeOfBlock)-unsafe.Sizeof(*relocationHdr))/unsafe.Sizeof(relInfos[0])))
|
||||||
|
for _, relInfo := range relInfos {
|
||||||
|
// The upper 4 bits define the type of relocation.
|
||||||
|
relType := relInfo >> 12
|
||||||
|
// The lower 12 bits define the offset.
|
||||||
|
relOffset := uintptr(relInfo & 0xfff)
|
||||||
|
|
||||||
|
switch relType {
|
||||||
|
case IMAGE_REL_BASED_ABSOLUTE:
|
||||||
|
// Skip relocation.
|
||||||
|
|
||||||
|
case IMAGE_REL_BASED_LOW:
|
||||||
|
*(*uint16)(a2p(dest + relOffset)) += uint16(delta & 0xffff)
|
||||||
|
break
|
||||||
|
|
||||||
|
case IMAGE_REL_BASED_HIGH:
|
||||||
|
*(*uint16)(a2p(dest + relOffset)) += uint16(uint32(delta) >> 16)
|
||||||
|
break
|
||||||
|
|
||||||
|
case IMAGE_REL_BASED_HIGHLOW:
|
||||||
|
*(*uint32)(a2p(dest + relOffset)) += uint32(delta)
|
||||||
|
|
||||||
|
case IMAGE_REL_BASED_DIR64:
|
||||||
|
*(*uint64)(a2p(dest + relOffset)) += uint64(delta)
|
||||||
|
|
||||||
|
case IMAGE_REL_BASED_THUMB_MOV32:
|
||||||
|
inst := *(*uint32)(a2p(dest + relOffset))
|
||||||
|
imm16 := ((inst << 1) & 0x0800) + ((inst << 12) & 0xf000) +
|
||||||
|
((inst >> 20) & 0x0700) + ((inst >> 16) & 0x00ff)
|
||||||
|
if (inst & 0x8000fbf0) != 0x0000f240 {
|
||||||
|
return false, fmt.Errorf("Wrong Thumb2 instruction %08x, expected MOVW", inst)
|
||||||
|
}
|
||||||
|
imm16 += uint32(delta) & 0xffff
|
||||||
|
hiDelta := (uint32(delta&0xffff0000) >> 16) + ((imm16 & 0xffff0000) >> 16)
|
||||||
|
*(*uint32)(a2p(dest + relOffset)) = (inst & 0x8f00fbf0) + ((imm16 >> 1) & 0x0400) +
|
||||||
|
((imm16 >> 12) & 0x000f) +
|
||||||
|
((imm16 << 20) & 0x70000000) +
|
||||||
|
((imm16 << 16) & 0xff0000)
|
||||||
|
if hiDelta != 0 {
|
||||||
|
inst = *(*uint32)(a2p(dest + relOffset + 4))
|
||||||
|
imm16 = ((inst << 1) & 0x0800) + ((inst << 12) & 0xf000) +
|
||||||
|
((inst >> 20) & 0x0700) + ((inst >> 16) & 0x00ff)
|
||||||
|
if (inst & 0x8000fbf0) != 0x0000f2c0 {
|
||||||
|
return false, fmt.Errorf("Wrong Thumb2 instruction %08x, expected MOVT", inst)
|
||||||
|
}
|
||||||
|
imm16 += hiDelta
|
||||||
|
if imm16 > 0xffff {
|
||||||
|
return false, fmt.Errorf("Resulting immediate value won't fit: %08x", imm16)
|
||||||
|
}
|
||||||
|
*(*uint32)(a2p(dest + relOffset + 4)) = (inst & 0x8f00fbf0) +
|
||||||
|
((imm16 >> 1) & 0x0400) +
|
||||||
|
((imm16 >> 12) & 0x000f) +
|
||||||
|
((imm16 << 20) & 0x70000000) +
|
||||||
|
((imm16 << 16) & 0xff0000)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false, fmt.Errorf("Unsupported relocation: %v", relType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance to next relocation block.
|
||||||
|
relocationHdr = (*IMAGE_BASE_RELOCATION)(a2p(uintptr(unsafe.Pointer(relocationHdr)) + uintptr(relocationHdr.SizeOfBlock)))
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *Module) buildImportTable() error {
|
||||||
|
directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_IMPORT)
|
||||||
|
if directory.Size == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
module.modules = make([]windows.Handle, 0, 16)
|
||||||
|
importDesc := (*IMAGE_IMPORT_DESCRIPTOR)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
|
||||||
|
for !isBadReadPtr(uintptr(unsafe.Pointer(importDesc)), unsafe.Sizeof(*importDesc)) && importDesc.Name != 0 {
|
||||||
|
handle, err := loadLibraryA((*byte)(a2p(module.codeBase + uintptr(importDesc.Name))))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error loading module: %v", err)
|
||||||
|
}
|
||||||
|
var thunkRef, funcRef *uintptr
|
||||||
|
if importDesc.OriginalFirstThunk() != 0 {
|
||||||
|
thunkRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.OriginalFirstThunk())))
|
||||||
|
funcRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.FirstThunk)))
|
||||||
|
} else {
|
||||||
|
// No hint table.
|
||||||
|
thunkRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.FirstThunk)))
|
||||||
|
funcRef = (*uintptr)(a2p(module.codeBase + uintptr(importDesc.FirstThunk)))
|
||||||
|
}
|
||||||
|
for *thunkRef != 0 {
|
||||||
|
if IMAGE_SNAP_BY_ORDINAL(*thunkRef) {
|
||||||
|
*funcRef, err = getProcAddress(handle, (*byte)(a2p(IMAGE_ORDINAL(*thunkRef))))
|
||||||
|
} else {
|
||||||
|
thunkData := (*IMAGE_IMPORT_BY_NAME)(a2p(module.codeBase + *thunkRef))
|
||||||
|
*funcRef, err = getProcAddress(handle, &thunkData.Name[0])
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
windows.FreeLibrary(handle)
|
||||||
|
return fmt.Errorf("Error getting function address: %v", err)
|
||||||
|
}
|
||||||
|
thunkRef = (*uintptr)(a2p(uintptr(unsafe.Pointer(thunkRef)) + unsafe.Sizeof(*thunkRef)))
|
||||||
|
funcRef = (*uintptr)(a2p(uintptr(unsafe.Pointer(funcRef)) + unsafe.Sizeof(*funcRef)))
|
||||||
|
}
|
||||||
|
module.modules = append(module.modules, handle)
|
||||||
|
importDesc = (*IMAGE_IMPORT_DESCRIPTOR)(a2p(uintptr(unsafe.Pointer(importDesc)) + unsafe.Sizeof(*importDesc)))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *Module) buildNameExports() error {
|
||||||
|
directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT)
|
||||||
|
if directory.Size == 0 {
|
||||||
|
return errors.New("No export table found")
|
||||||
|
}
|
||||||
|
exports := (*IMAGE_EXPORT_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
|
||||||
|
if exports.NumberOfNames == 0 || exports.NumberOfFunctions == 0 {
|
||||||
|
return errors.New("No functions exported")
|
||||||
|
}
|
||||||
|
if exports.NumberOfNames == 0 {
|
||||||
|
return errors.New("No functions exported by name")
|
||||||
|
}
|
||||||
|
var nameRefs []uint32
|
||||||
|
unsafeSlice(unsafe.Pointer(&nameRefs), a2p(module.codeBase+uintptr(exports.AddressOfNames)), int(exports.NumberOfNames))
|
||||||
|
var ordinals []uint16
|
||||||
|
unsafeSlice(unsafe.Pointer(&ordinals), a2p(module.codeBase+uintptr(exports.AddressOfNameOrdinals)), int(exports.NumberOfNames))
|
||||||
|
module.nameExports = make(map[string]uint16)
|
||||||
|
for i := range nameRefs {
|
||||||
|
nameArray := windows.BytePtrToString((*byte)(a2p(module.codeBase + uintptr(nameRefs[i]))))
|
||||||
|
module.nameExports[nameArray] = ordinals[i]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadLibrary loads module image to memory.
|
||||||
|
func LoadLibrary(data []byte) (module *Module, err error) {
|
||||||
|
addr := uintptr(unsafe.Pointer(&data[0]))
|
||||||
|
size := uintptr(len(data))
|
||||||
|
if size < unsafe.Sizeof(IMAGE_DOS_HEADER{}) {
|
||||||
|
return nil, errors.New("Incomplete IMAGE_DOS_HEADER")
|
||||||
|
}
|
||||||
|
dosHeader := (*IMAGE_DOS_HEADER)(a2p(addr))
|
||||||
|
if dosHeader.E_magic != IMAGE_DOS_SIGNATURE {
|
||||||
|
return nil, fmt.Errorf("Not an MS-DOS binary (provided: %x, expected: %x)", dosHeader.E_magic, IMAGE_DOS_SIGNATURE)
|
||||||
|
}
|
||||||
|
if (size < uintptr(dosHeader.E_lfanew)+unsafe.Sizeof(IMAGE_NT_HEADERS{})) {
|
||||||
|
return nil, errors.New("Incomplete IMAGE_NT_HEADERS")
|
||||||
|
}
|
||||||
|
oldHeader := (*IMAGE_NT_HEADERS)(a2p(addr + uintptr(dosHeader.E_lfanew)))
|
||||||
|
if oldHeader.Signature != IMAGE_NT_SIGNATURE {
|
||||||
|
return nil, fmt.Errorf("Not an NT binary (provided: %x, expected: %x)", oldHeader.Signature, IMAGE_NT_SIGNATURE)
|
||||||
|
}
|
||||||
|
if oldHeader.FileHeader.Machine != imageFileProcess {
|
||||||
|
return nil, fmt.Errorf("Foreign platform (provided: %x, expected: %x)", oldHeader.FileHeader.Machine, imageFileProcess)
|
||||||
|
}
|
||||||
|
if (oldHeader.OptionalHeader.SectionAlignment & 1) != 0 {
|
||||||
|
return nil, errors.New("Unaligned section")
|
||||||
|
}
|
||||||
|
lastSectionEnd := uintptr(0)
|
||||||
|
sections := oldHeader.Sections()
|
||||||
|
optionalSectionSize := oldHeader.OptionalHeader.SectionAlignment
|
||||||
|
for i := range sections {
|
||||||
|
var endOfSection uintptr
|
||||||
|
if sections[i].SizeOfRawData == 0 {
|
||||||
|
// Section without data in the DLL
|
||||||
|
endOfSection = uintptr(sections[i].VirtualAddress) + uintptr(optionalSectionSize)
|
||||||
|
} else {
|
||||||
|
endOfSection = uintptr(sections[i].VirtualAddress) + uintptr(sections[i].SizeOfRawData)
|
||||||
|
}
|
||||||
|
if endOfSection > lastSectionEnd {
|
||||||
|
lastSectionEnd = endOfSection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
alignedImageSize := alignUp(uintptr(oldHeader.OptionalHeader.SizeOfImage), uintptr(oldHeader.OptionalHeader.SectionAlignment))
|
||||||
|
if alignedImageSize != alignUp(lastSectionEnd, uintptr(oldHeader.OptionalHeader.SectionAlignment)) {
|
||||||
|
return nil, errors.New("Section is not page-aligned")
|
||||||
|
}
|
||||||
|
|
||||||
|
module = &Module{isDLL: (oldHeader.FileHeader.Characteristics & IMAGE_FILE_DLL) != 0}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
module.Free()
|
||||||
|
module = nil
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Reserve memory for image of library.
|
||||||
|
// TODO: Is it correct to commit the complete memory region at once? Calling DllEntry raises an exception if we don't.
|
||||||
|
module.codeBase, err = windows.VirtualAlloc(oldHeader.OptionalHeader.ImageBase,
|
||||||
|
alignedImageSize,
|
||||||
|
windows.MEM_RESERVE|windows.MEM_COMMIT,
|
||||||
|
windows.PAGE_READWRITE)
|
||||||
|
if err != nil {
|
||||||
|
// Try to allocate memory at arbitrary position.
|
||||||
|
module.codeBase, err = windows.VirtualAlloc(0,
|
||||||
|
alignedImageSize,
|
||||||
|
windows.MEM_RESERVE|windows.MEM_COMMIT,
|
||||||
|
windows.PAGE_READWRITE)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error allocating code: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = module.check4GBBoundaries(alignedImageSize)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error reallocating code: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if size < uintptr(oldHeader.OptionalHeader.SizeOfHeaders) {
|
||||||
|
err = errors.New("Incomplete headers")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Commit memory for headers.
|
||||||
|
headers, err := windows.VirtualAlloc(module.codeBase,
|
||||||
|
uintptr(oldHeader.OptionalHeader.SizeOfHeaders),
|
||||||
|
windows.MEM_COMMIT,
|
||||||
|
windows.PAGE_READWRITE)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error allocating headers: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Copy PE header to code.
|
||||||
|
memcpy(headers, addr, uintptr(oldHeader.OptionalHeader.SizeOfHeaders))
|
||||||
|
module.headers = (*IMAGE_NT_HEADERS)(a2p(headers + uintptr(dosHeader.E_lfanew)))
|
||||||
|
|
||||||
|
// Update position.
|
||||||
|
module.headers.OptionalHeader.ImageBase = module.codeBase
|
||||||
|
|
||||||
|
// Copy sections from DLL file block to new memory location.
|
||||||
|
err = module.copySections(addr, size, oldHeader)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error copying sections: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust base address of imported data.
|
||||||
|
locationDelta := module.headers.OptionalHeader.ImageBase - oldHeader.OptionalHeader.ImageBase
|
||||||
|
if locationDelta != 0 {
|
||||||
|
module.isRelocated, err = module.performBaseRelocation(locationDelta)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error relocating module: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
module.isRelocated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load required dlls and adjust function table of imports.
|
||||||
|
err = module.buildImportTable()
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error building import table: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark memory pages depending on section headers and release sections that are marked as "discardable".
|
||||||
|
err = module.finalizeSections()
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error finalizing sections: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLS callbacks are executed BEFORE the main loading.
|
||||||
|
module.executeTLS()
|
||||||
|
|
||||||
|
// Get entry point of loaded module.
|
||||||
|
if module.headers.OptionalHeader.AddressOfEntryPoint != 0 {
|
||||||
|
module.entry = module.codeBase + uintptr(module.headers.OptionalHeader.AddressOfEntryPoint)
|
||||||
|
if module.isDLL {
|
||||||
|
// Notify library about attaching to process.
|
||||||
|
r0, _, _ := syscall.Syscall(module.entry, 3, module.codeBase, uintptr(DLL_PROCESS_ATTACH), 0)
|
||||||
|
successful := r0 != 0
|
||||||
|
if !successful {
|
||||||
|
err = windows.ERROR_DLL_INIT_FAILED
|
||||||
|
return
|
||||||
|
}
|
||||||
|
module.initialized = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.buildNameExports()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free releases module resources and unloads it.
|
||||||
|
func (module *Module) Free() {
|
||||||
|
if module.initialized {
|
||||||
|
// Notify library about detaching from process.
|
||||||
|
syscall.Syscall(module.entry, 3, module.codeBase, uintptr(DLL_PROCESS_DETACH), 0)
|
||||||
|
module.initialized = false
|
||||||
|
}
|
||||||
|
if module.modules != nil {
|
||||||
|
// Free previously opened libraries.
|
||||||
|
for _, handle := range module.modules {
|
||||||
|
windows.FreeLibrary(handle)
|
||||||
|
}
|
||||||
|
module.modules = nil
|
||||||
|
}
|
||||||
|
if module.codeBase != 0 {
|
||||||
|
windows.VirtualFree(module.codeBase, 0, windows.MEM_RELEASE)
|
||||||
|
module.codeBase = 0
|
||||||
|
}
|
||||||
|
if module.blockedMemory != nil {
|
||||||
|
module.blockedMemory.free()
|
||||||
|
module.blockedMemory = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcAddressByName returns function address by exported name.
|
||||||
|
func (module *Module) ProcAddressByName(name string) (uintptr, error) {
|
||||||
|
directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT)
|
||||||
|
if directory.Size == 0 {
|
||||||
|
return 0, errors.New("No export table found")
|
||||||
|
}
|
||||||
|
exports := (*IMAGE_EXPORT_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
|
||||||
|
if module.nameExports == nil {
|
||||||
|
return 0, errors.New("No functions exported by name")
|
||||||
|
}
|
||||||
|
if idx, ok := module.nameExports[name]; ok {
|
||||||
|
if uint32(idx) > exports.NumberOfFunctions {
|
||||||
|
return 0, errors.New("Ordinal number too high")
|
||||||
|
}
|
||||||
|
// AddressOfFunctions contains the RVAs to the "real" functions.
|
||||||
|
return module.codeBase + uintptr(*(*uint32)(a2p(module.codeBase + uintptr(exports.AddressOfFunctions) + uintptr(idx)*4))), nil
|
||||||
|
}
|
||||||
|
return 0, errors.New("Function not found by name")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcAddressByOrdinal returns function address by exported ordinal.
|
||||||
|
func (module *Module) ProcAddressByOrdinal(ordinal uint16) (uintptr, error) {
|
||||||
|
directory := module.headerDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT)
|
||||||
|
if directory.Size == 0 {
|
||||||
|
return 0, errors.New("No export table found")
|
||||||
|
}
|
||||||
|
exports := (*IMAGE_EXPORT_DIRECTORY)(a2p(module.codeBase + uintptr(directory.VirtualAddress)))
|
||||||
|
if uint32(ordinal) < exports.Base {
|
||||||
|
return 0, errors.New("Ordinal number too low")
|
||||||
|
}
|
||||||
|
idx := ordinal - uint16(exports.Base)
|
||||||
|
if uint32(idx) > exports.NumberOfFunctions {
|
||||||
|
return 0, errors.New("Ordinal number too high")
|
||||||
|
}
|
||||||
|
// AddressOfFunctions contains the RVAs to the "real" functions.
|
||||||
|
return module.codeBase + uintptr(*(*uint32)(a2p(module.codeBase + uintptr(exports.AddressOfFunctions) + uintptr(idx)*4))), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func alignDown(value, alignment uintptr) uintptr {
|
||||||
|
return value & ^(alignment - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func alignUp(value, alignment uintptr) uintptr {
|
||||||
|
return (value + alignment - 1) & ^(alignment - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func a2p(addr uintptr) unsafe.Pointer {
|
||||||
|
return unsafe.Pointer(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func memcpy(dst, src, size uintptr) {
|
||||||
|
var d, s []byte
|
||||||
|
unsafeSlice(unsafe.Pointer(&d), a2p(dst), int(size))
|
||||||
|
unsafeSlice(unsafe.Pointer(&s), a2p(src), int(size))
|
||||||
|
copy(d, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsafeSlice updates the slice slicePtr to be a slice
|
||||||
|
// referencing the provided data with its length & capacity set to
|
||||||
|
// lenCap.
|
||||||
|
//
|
||||||
|
// TODO: when Go 1.16 or Go 1.17 is the minimum supported version,
|
||||||
|
// update callers to use unsafe.Slice instead of this.
|
||||||
|
func unsafeSlice(slicePtr, data unsafe.Pointer, lenCap int) {
|
||||||
|
type sliceHeader struct {
|
||||||
|
Data unsafe.Pointer
|
||||||
|
Len int
|
||||||
|
Cap int
|
||||||
|
}
|
||||||
|
h := (*sliceHeader)(slicePtr)
|
||||||
|
h.Data = data
|
||||||
|
h.Len = lenCap
|
||||||
|
h.Cap = lenCap
|
||||||
|
}
|
16
tun/wintun/memmod/memmod_windows_32.go
Normal file
16
tun/wintun/memmod/memmod_windows_32.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// +build 386 arm
|
||||||
|
|
||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package memmod
|
||||||
|
|
||||||
|
func (opthdr *IMAGE_OPTIONAL_HEADER) imageOffset() uintptr {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *Module) check4GBBoundaries(alignedImageSize uintptr) (err error) {
|
||||||
|
return
|
||||||
|
}
|
8
tun/wintun/memmod/memmod_windows_386.go
Normal file
8
tun/wintun/memmod/memmod_windows_386.go
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package memmod
|
||||||
|
|
||||||
|
const imageFileProcess = IMAGE_FILE_MACHINE_I386
|
36
tun/wintun/memmod/memmod_windows_64.go
Normal file
36
tun/wintun/memmod/memmod_windows_64.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
// +build amd64 arm64
|
||||||
|
|
||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package memmod
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (opthdr *IMAGE_OPTIONAL_HEADER) imageOffset() uintptr {
|
||||||
|
return uintptr(opthdr.ImageBase & 0xffffffff00000000)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (module *Module) check4GBBoundaries(alignedImageSize uintptr) (err error) {
|
||||||
|
for (module.codeBase >> 32) < ((module.codeBase + alignedImageSize) >> 32) {
|
||||||
|
node := &addressList{
|
||||||
|
next: module.blockedMemory,
|
||||||
|
address: module.codeBase,
|
||||||
|
}
|
||||||
|
module.blockedMemory = node
|
||||||
|
module.codeBase, err = windows.VirtualAlloc(0,
|
||||||
|
alignedImageSize,
|
||||||
|
windows.MEM_RESERVE|windows.MEM_COMMIT,
|
||||||
|
windows.PAGE_READWRITE)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error allocating memory block: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
8
tun/wintun/memmod/memmod_windows_amd64.go
Normal file
8
tun/wintun/memmod/memmod_windows_amd64.go
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package memmod
|
||||||
|
|
||||||
|
const imageFileProcess = IMAGE_FILE_MACHINE_AMD64
|
8
tun/wintun/memmod/memmod_windows_arm.go
Normal file
8
tun/wintun/memmod/memmod_windows_arm.go
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package memmod
|
||||||
|
|
||||||
|
const imageFileProcess = IMAGE_FILE_MACHINE_ARMNT
|
8
tun/wintun/memmod/memmod_windows_arm64.go
Normal file
8
tun/wintun/memmod/memmod_windows_arm64.go
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package memmod
|
||||||
|
|
||||||
|
const imageFileProcess = IMAGE_FILE_MACHINE_ARM64
|
8
tun/wintun/memmod/mksyscall.go
Normal file
8
tun/wintun/memmod/mksyscall.go
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package memmod
|
||||||
|
|
||||||
|
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go syscall_windows.go
|
343
tun/wintun/memmod/syscall_windows.go
Normal file
343
tun/wintun/memmod/syscall_windows.go
Normal file
|
@ -0,0 +1,343 @@
|
||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package memmod
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
const (
|
||||||
|
IMAGE_DOS_SIGNATURE = 0x5A4D // MZ
|
||||||
|
IMAGE_OS2_SIGNATURE = 0x454E // NE
|
||||||
|
IMAGE_OS2_SIGNATURE_LE = 0x454C // LE
|
||||||
|
IMAGE_VXD_SIGNATURE = 0x454C // LE
|
||||||
|
IMAGE_NT_SIGNATURE = 0x00004550 // PE00
|
||||||
|
)
|
||||||
|
|
||||||
|
// DOS .EXE header
|
||||||
|
type IMAGE_DOS_HEADER struct {
|
||||||
|
E_magic uint16 // Magic number
|
||||||
|
E_cblp uint16 // Bytes on last page of file
|
||||||
|
E_cp uint16 // Pages in file
|
||||||
|
E_crlc uint16 // Relocations
|
||||||
|
E_cparhdr uint16 // Size of header in paragraphs
|
||||||
|
E_minalloc uint16 // Minimum extra paragraphs needed
|
||||||
|
E_maxalloc uint16 // Maximum extra paragraphs needed
|
||||||
|
E_ss uint16 // Initial (relative) SS value
|
||||||
|
E_sp uint16 // Initial SP value
|
||||||
|
E_csum uint16 // Checksum
|
||||||
|
E_ip uint16 // Initial IP value
|
||||||
|
E_cs uint16 // Initial (relative) CS value
|
||||||
|
E_lfarlc uint16 // File address of relocation table
|
||||||
|
E_ovno uint16 // Overlay number
|
||||||
|
E_res [4]uint16 // Reserved words
|
||||||
|
E_oemid uint16 // OEM identifier (for e_oeminfo)
|
||||||
|
E_oeminfo uint16 // OEM information; e_oemid specific
|
||||||
|
E_res2 [10]uint16 // Reserved words
|
||||||
|
E_lfanew int32 // File address of new exe header
|
||||||
|
}
|
||||||
|
|
||||||
|
// File header format
|
||||||
|
type IMAGE_FILE_HEADER struct {
|
||||||
|
Machine uint16
|
||||||
|
NumberOfSections uint16
|
||||||
|
TimeDateStamp uint32
|
||||||
|
PointerToSymbolTable uint32
|
||||||
|
NumberOfSymbols uint32
|
||||||
|
SizeOfOptionalHeader uint16
|
||||||
|
Characteristics uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
IMAGE_SIZEOF_FILE_HEADER = 20
|
||||||
|
|
||||||
|
IMAGE_FILE_RELOCS_STRIPPED = 0x0001 // Relocation info stripped from file.
|
||||||
|
IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002 // File is executable (i.e. no unresolved external references).
|
||||||
|
IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004 // Line nunbers stripped from file.
|
||||||
|
IMAGE_FILE_LOCAL_SYMS_STRIPPED = 0x0008 // Local symbols stripped from file.
|
||||||
|
IMAGE_FILE_AGGRESIVE_WS_TRIM = 0x0010 // Aggressively trim working set
|
||||||
|
IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020 // App can handle >2gb addresses
|
||||||
|
IMAGE_FILE_BYTES_REVERSED_LO = 0x0080 // Bytes of machine word are reversed.
|
||||||
|
IMAGE_FILE_32BIT_MACHINE = 0x0100 // 32 bit word machine.
|
||||||
|
IMAGE_FILE_DEBUG_STRIPPED = 0x0200 // Debugging info stripped from file in .DBG file
|
||||||
|
IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = 0x0400 // If Image is on removable media, copy and run from the swap file.
|
||||||
|
IMAGE_FILE_NET_RUN_FROM_SWAP = 0x0800 // If Image is on Net, copy and run from the swap file.
|
||||||
|
IMAGE_FILE_SYSTEM = 0x1000 // System File.
|
||||||
|
IMAGE_FILE_DLL = 0x2000 // File is a DLL.
|
||||||
|
IMAGE_FILE_UP_SYSTEM_ONLY = 0x4000 // File should only be run on a UP machine
|
||||||
|
IMAGE_FILE_BYTES_REVERSED_HI = 0x8000 // Bytes of machine word are reversed.
|
||||||
|
|
||||||
|
IMAGE_FILE_MACHINE_UNKNOWN = 0
|
||||||
|
IMAGE_FILE_MACHINE_TARGET_HOST = 0x0001 // Useful for indicating we want to interact with the host and not a WoW guest.
|
||||||
|
IMAGE_FILE_MACHINE_I386 = 0x014c // Intel 386.
|
||||||
|
IMAGE_FILE_MACHINE_R3000 = 0x0162 // MIPS little-endian, 0x160 big-endian
|
||||||
|
IMAGE_FILE_MACHINE_R4000 = 0x0166 // MIPS little-endian
|
||||||
|
IMAGE_FILE_MACHINE_R10000 = 0x0168 // MIPS little-endian
|
||||||
|
IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x0169 // MIPS little-endian WCE v2
|
||||||
|
IMAGE_FILE_MACHINE_ALPHA = 0x0184 // Alpha_AXP
|
||||||
|
IMAGE_FILE_MACHINE_SH3 = 0x01a2 // SH3 little-endian
|
||||||
|
IMAGE_FILE_MACHINE_SH3DSP = 0x01a3
|
||||||
|
IMAGE_FILE_MACHINE_SH3E = 0x01a4 // SH3E little-endian
|
||||||
|
IMAGE_FILE_MACHINE_SH4 = 0x01a6 // SH4 little-endian
|
||||||
|
IMAGE_FILE_MACHINE_SH5 = 0x01a8 // SH5
|
||||||
|
IMAGE_FILE_MACHINE_ARM = 0x01c0 // ARM Little-Endian
|
||||||
|
IMAGE_FILE_MACHINE_THUMB = 0x01c2 // ARM Thumb/Thumb-2 Little-Endian
|
||||||
|
IMAGE_FILE_MACHINE_ARMNT = 0x01c4 // ARM Thumb-2 Little-Endian
|
||||||
|
IMAGE_FILE_MACHINE_AM33 = 0x01d3
|
||||||
|
IMAGE_FILE_MACHINE_POWERPC = 0x01F0 // IBM PowerPC Little-Endian
|
||||||
|
IMAGE_FILE_MACHINE_POWERPCFP = 0x01f1
|
||||||
|
IMAGE_FILE_MACHINE_IA64 = 0x0200 // Intel 64
|
||||||
|
IMAGE_FILE_MACHINE_MIPS16 = 0x0266 // MIPS
|
||||||
|
IMAGE_FILE_MACHINE_ALPHA64 = 0x0284 // ALPHA64
|
||||||
|
IMAGE_FILE_MACHINE_MIPSFPU = 0x0366 // MIPS
|
||||||
|
IMAGE_FILE_MACHINE_MIPSFPU16 = 0x0466 // MIPS
|
||||||
|
IMAGE_FILE_MACHINE_AXP64 = IMAGE_FILE_MACHINE_ALPHA64
|
||||||
|
IMAGE_FILE_MACHINE_TRICORE = 0x0520 // Infineon
|
||||||
|
IMAGE_FILE_MACHINE_CEF = 0x0CEF
|
||||||
|
IMAGE_FILE_MACHINE_EBC = 0x0EBC // EFI Byte Code
|
||||||
|
IMAGE_FILE_MACHINE_AMD64 = 0x8664 // AMD64 (K8)
|
||||||
|
IMAGE_FILE_MACHINE_M32R = 0x9041 // M32R little-endian
|
||||||
|
IMAGE_FILE_MACHINE_ARM64 = 0xAA64 // ARM64 Little-Endian
|
||||||
|
IMAGE_FILE_MACHINE_CEE = 0xC0EE
|
||||||
|
)
|
||||||
|
|
||||||
|
// Directory format
|
||||||
|
type IMAGE_DATA_DIRECTORY struct {
|
||||||
|
VirtualAddress uint32
|
||||||
|
Size uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
const IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16
|
||||||
|
|
||||||
|
type IMAGE_NT_HEADERS struct {
|
||||||
|
Signature uint32
|
||||||
|
FileHeader IMAGE_FILE_HEADER
|
||||||
|
OptionalHeader IMAGE_OPTIONAL_HEADER
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ntheader *IMAGE_NT_HEADERS) Sections() []IMAGE_SECTION_HEADER {
|
||||||
|
return (*[0xffff]IMAGE_SECTION_HEADER)(unsafe.Pointer(
|
||||||
|
(uintptr)(unsafe.Pointer(ntheader)) +
|
||||||
|
unsafe.Offsetof(ntheader.OptionalHeader) +
|
||||||
|
uintptr(ntheader.FileHeader.SizeOfOptionalHeader)))[:ntheader.FileHeader.NumberOfSections]
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
IMAGE_DIRECTORY_ENTRY_EXPORT = 0 // Export Directory
|
||||||
|
IMAGE_DIRECTORY_ENTRY_IMPORT = 1 // Import Directory
|
||||||
|
IMAGE_DIRECTORY_ENTRY_RESOURCE = 2 // Resource Directory
|
||||||
|
IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3 // Exception Directory
|
||||||
|
IMAGE_DIRECTORY_ENTRY_SECURITY = 4 // Security Directory
|
||||||
|
IMAGE_DIRECTORY_ENTRY_BASERELOC = 5 // Base Relocation Table
|
||||||
|
IMAGE_DIRECTORY_ENTRY_DEBUG = 6 // Debug Directory
|
||||||
|
IMAGE_DIRECTORY_ENTRY_COPYRIGHT = 7 // (X86 usage)
|
||||||
|
IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7 // Architecture Specific Data
|
||||||
|
IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8 // RVA of GP
|
||||||
|
IMAGE_DIRECTORY_ENTRY_TLS = 9 // TLS Directory
|
||||||
|
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10 // Load Configuration Directory
|
||||||
|
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11 // Bound Import Directory in headers
|
||||||
|
IMAGE_DIRECTORY_ENTRY_IAT = 12 // Import Address Table
|
||||||
|
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13 // Delay Load Import Descriptors
|
||||||
|
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14 // COM Runtime descriptor
|
||||||
|
)
|
||||||
|
|
||||||
|
const IMAGE_SIZEOF_SHORT_NAME = 8
|
||||||
|
|
||||||
|
// Section header format
|
||||||
|
type IMAGE_SECTION_HEADER struct {
|
||||||
|
Name [IMAGE_SIZEOF_SHORT_NAME]byte
|
||||||
|
physicalAddressOrVirtualSize uint32
|
||||||
|
VirtualAddress uint32
|
||||||
|
SizeOfRawData uint32
|
||||||
|
PointerToRawData uint32
|
||||||
|
PointerToRelocations uint32
|
||||||
|
PointerToLinenumbers uint32
|
||||||
|
NumberOfRelocations uint16
|
||||||
|
NumberOfLinenumbers uint16
|
||||||
|
Characteristics uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ishdr *IMAGE_SECTION_HEADER) PhysicalAddress() uint32 {
|
||||||
|
return ishdr.physicalAddressOrVirtualSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ishdr *IMAGE_SECTION_HEADER) SetPhysicalAddress(addr uint32) {
|
||||||
|
ishdr.physicalAddressOrVirtualSize = addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ishdr *IMAGE_SECTION_HEADER) VirtualSize() uint32 {
|
||||||
|
return ishdr.physicalAddressOrVirtualSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ishdr *IMAGE_SECTION_HEADER) SetVirtualSize(addr uint32) {
|
||||||
|
ishdr.physicalAddressOrVirtualSize = addr
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Section characteristics.
|
||||||
|
IMAGE_SCN_TYPE_REG = 0x00000000 // Reserved.
|
||||||
|
IMAGE_SCN_TYPE_DSECT = 0x00000001 // Reserved.
|
||||||
|
IMAGE_SCN_TYPE_NOLOAD = 0x00000002 // Reserved.
|
||||||
|
IMAGE_SCN_TYPE_GROUP = 0x00000004 // Reserved.
|
||||||
|
IMAGE_SCN_TYPE_NO_PAD = 0x00000008 // Reserved.
|
||||||
|
IMAGE_SCN_TYPE_COPY = 0x00000010 // Reserved.
|
||||||
|
|
||||||
|
IMAGE_SCN_CNT_CODE = 0x00000020 // Section contains code.
|
||||||
|
IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 // Section contains initialized data.
|
||||||
|
IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 // Section contains uninitialized data.
|
||||||
|
|
||||||
|
IMAGE_SCN_LNK_OTHER = 0x00000100 // Reserved.
|
||||||
|
IMAGE_SCN_LNK_INFO = 0x00000200 // Section contains comments or some other type of information.
|
||||||
|
IMAGE_SCN_TYPE_OVER = 0x00000400 // Reserved.
|
||||||
|
IMAGE_SCN_LNK_REMOVE = 0x00000800 // Section contents will not become part of image.
|
||||||
|
IMAGE_SCN_LNK_COMDAT = 0x00001000 // Section contents comdat.
|
||||||
|
IMAGE_SCN_MEM_PROTECTED = 0x00004000 // Obsolete.
|
||||||
|
IMAGE_SCN_NO_DEFER_SPEC_EXC = 0x00004000 // Reset speculative exceptions handling bits in the TLB entries for this section.
|
||||||
|
IMAGE_SCN_GPREL = 0x00008000 // Section content can be accessed relative to GP
|
||||||
|
IMAGE_SCN_MEM_FARDATA = 0x00008000
|
||||||
|
IMAGE_SCN_MEM_SYSHEAP = 0x00010000 // Obsolete.
|
||||||
|
IMAGE_SCN_MEM_PURGEABLE = 0x00020000
|
||||||
|
IMAGE_SCN_MEM_16BIT = 0x00020000
|
||||||
|
IMAGE_SCN_MEM_LOCKED = 0x00040000
|
||||||
|
IMAGE_SCN_MEM_PRELOAD = 0x00080000
|
||||||
|
|
||||||
|
IMAGE_SCN_ALIGN_1BYTES = 0x00100000 //
|
||||||
|
IMAGE_SCN_ALIGN_2BYTES = 0x00200000 //
|
||||||
|
IMAGE_SCN_ALIGN_4BYTES = 0x00300000 //
|
||||||
|
IMAGE_SCN_ALIGN_8BYTES = 0x00400000 //
|
||||||
|
IMAGE_SCN_ALIGN_16BYTES = 0x00500000 // Default alignment if no others are specified.
|
||||||
|
IMAGE_SCN_ALIGN_32BYTES = 0x00600000 //
|
||||||
|
IMAGE_SCN_ALIGN_64BYTES = 0x00700000 //
|
||||||
|
IMAGE_SCN_ALIGN_128BYTES = 0x00800000 //
|
||||||
|
IMAGE_SCN_ALIGN_256BYTES = 0x00900000 //
|
||||||
|
IMAGE_SCN_ALIGN_512BYTES = 0x00A00000 //
|
||||||
|
IMAGE_SCN_ALIGN_1024BYTES = 0x00B00000 //
|
||||||
|
IMAGE_SCN_ALIGN_2048BYTES = 0x00C00000 //
|
||||||
|
IMAGE_SCN_ALIGN_4096BYTES = 0x00D00000 //
|
||||||
|
IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000 //
|
||||||
|
IMAGE_SCN_ALIGN_MASK = 0x00F00000
|
||||||
|
|
||||||
|
IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000 // Section contains extended relocations.
|
||||||
|
IMAGE_SCN_MEM_DISCARDABLE = 0x02000000 // Section can be discarded.
|
||||||
|
IMAGE_SCN_MEM_NOT_CACHED = 0x04000000 // Section is not cachable.
|
||||||
|
IMAGE_SCN_MEM_NOT_PAGED = 0x08000000 // Section is not pageable.
|
||||||
|
IMAGE_SCN_MEM_SHARED = 0x10000000 // Section is shareable.
|
||||||
|
IMAGE_SCN_MEM_EXECUTE = 0x20000000 // Section is executable.
|
||||||
|
IMAGE_SCN_MEM_READ = 0x40000000 // Section is readable.
|
||||||
|
IMAGE_SCN_MEM_WRITE = 0x80000000 // Section is writeable.
|
||||||
|
|
||||||
|
// TLS Characteristic Flags
|
||||||
|
IMAGE_SCN_SCALE_INDEX = 0x00000001 // Tls index is scaled.
|
||||||
|
)
|
||||||
|
|
||||||
|
// Based relocation format
|
||||||
|
type IMAGE_BASE_RELOCATION struct {
|
||||||
|
VirtualAddress uint32
|
||||||
|
SizeOfBlock uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
IMAGE_REL_BASED_ABSOLUTE = 0
|
||||||
|
IMAGE_REL_BASED_HIGH = 1
|
||||||
|
IMAGE_REL_BASED_LOW = 2
|
||||||
|
IMAGE_REL_BASED_HIGHLOW = 3
|
||||||
|
IMAGE_REL_BASED_HIGHADJ = 4
|
||||||
|
IMAGE_REL_BASED_MACHINE_SPECIFIC_5 = 5
|
||||||
|
IMAGE_REL_BASED_RESERVED = 6
|
||||||
|
IMAGE_REL_BASED_MACHINE_SPECIFIC_7 = 7
|
||||||
|
IMAGE_REL_BASED_MACHINE_SPECIFIC_8 = 8
|
||||||
|
IMAGE_REL_BASED_MACHINE_SPECIFIC_9 = 9
|
||||||
|
IMAGE_REL_BASED_DIR64 = 10
|
||||||
|
|
||||||
|
IMAGE_REL_BASED_IA64_IMM64 = 9
|
||||||
|
|
||||||
|
IMAGE_REL_BASED_MIPS_JMPADDR = 5
|
||||||
|
IMAGE_REL_BASED_MIPS_JMPADDR16 = 9
|
||||||
|
|
||||||
|
IMAGE_REL_BASED_ARM_MOV32 = 5
|
||||||
|
IMAGE_REL_BASED_THUMB_MOV32 = 7
|
||||||
|
)
|
||||||
|
|
||||||
|
// Export Format
|
||||||
|
type IMAGE_EXPORT_DIRECTORY struct {
|
||||||
|
Characteristics uint32
|
||||||
|
TimeDateStamp uint32
|
||||||
|
MajorVersion uint16
|
||||||
|
MinorVersion uint16
|
||||||
|
Name uint32
|
||||||
|
Base uint32
|
||||||
|
NumberOfFunctions uint32
|
||||||
|
NumberOfNames uint32
|
||||||
|
AddressOfFunctions uint32 // RVA from base of image
|
||||||
|
AddressOfNames uint32 // RVA from base of image
|
||||||
|
AddressOfNameOrdinals uint32 // RVA from base of image
|
||||||
|
}
|
||||||
|
|
||||||
|
type IMAGE_IMPORT_BY_NAME struct {
|
||||||
|
Hint uint16
|
||||||
|
Name [1]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func IMAGE_ORDINAL(ordinal uintptr) uintptr {
|
||||||
|
return ordinal & 0xffff
|
||||||
|
}
|
||||||
|
|
||||||
|
func IMAGE_SNAP_BY_ORDINAL(ordinal uintptr) bool {
|
||||||
|
return (ordinal & IMAGE_ORDINAL_FLAG) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thread Local Storage
|
||||||
|
type IMAGE_TLS_DIRECTORY struct {
|
||||||
|
StartAddressOfRawData uintptr
|
||||||
|
EndAddressOfRawData uintptr
|
||||||
|
AddressOfIndex uintptr // PDWORD
|
||||||
|
AddressOfCallbacks uintptr // PIMAGE_TLS_CALLBACK *;
|
||||||
|
SizeOfZeroFill uint32
|
||||||
|
Characteristics uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type IMAGE_IMPORT_DESCRIPTOR struct {
|
||||||
|
characteristicsOrOriginalFirstThunk uint32 // 0 for terminating null import descriptor
|
||||||
|
// RVA to original unbound IAT (PIMAGE_THUNK_DATA)
|
||||||
|
TimeDateStamp uint32 // 0 if not bound,
|
||||||
|
// -1 if bound, and real date\time stamp
|
||||||
|
// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
|
||||||
|
// O.W. date/time stamp of DLL bound to (Old BIND)
|
||||||
|
ForwarderChain uint32 // -1 if no forwarders
|
||||||
|
Name uint32
|
||||||
|
FirstThunk uint32 // RVA to IAT (if bound this IAT has actual addresses)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (imgimpdesc *IMAGE_IMPORT_DESCRIPTOR) Characteristics() uint32 {
|
||||||
|
return imgimpdesc.characteristicsOrOriginalFirstThunk
|
||||||
|
}
|
||||||
|
|
||||||
|
func (imgimpdesc *IMAGE_IMPORT_DESCRIPTOR) OriginalFirstThunk() uint32 {
|
||||||
|
return imgimpdesc.characteristicsOrOriginalFirstThunk
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
DLL_PROCESS_ATTACH = 1
|
||||||
|
DLL_THREAD_ATTACH = 2
|
||||||
|
DLL_THREAD_DETACH = 3
|
||||||
|
DLL_PROCESS_DETACH = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys loadLibraryA(libFileName *byte) (module windows.Handle, err error) = kernel32.LoadLibraryA
|
||||||
|
//sys getProcAddress(module windows.Handle, procName *byte) (addr uintptr, err error) = kernel32.GetProcAddress
|
||||||
|
//sys isBadReadPtr(addr uintptr, ucb uintptr) (ret bool) = kernel32.IsBadReadPtr
|
||||||
|
|
||||||
|
type SYSTEM_INFO struct {
|
||||||
|
ProcessorArchitecture uint16
|
||||||
|
Reserved uint16
|
||||||
|
PageSize uint32
|
||||||
|
MinimumApplicationAddress uintptr
|
||||||
|
MaximumApplicationAddress uintptr
|
||||||
|
ActiveProcessorMask uintptr
|
||||||
|
NumberOfProcessors uint32
|
||||||
|
ProcessorType uint32
|
||||||
|
AllocationGranularity uint32
|
||||||
|
ProcessorLevel uint16
|
||||||
|
ProcessorRevision uint16
|
||||||
|
}
|
45
tun/wintun/memmod/syscall_windows_32.go
Normal file
45
tun/wintun/memmod/syscall_windows_32.go
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
// +build 386 arm
|
||||||
|
|
||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package memmod
|
||||||
|
|
||||||
|
// Optional header format
|
||||||
|
type IMAGE_OPTIONAL_HEADER struct {
|
||||||
|
Magic uint16
|
||||||
|
MajorLinkerVersion uint8
|
||||||
|
MinorLinkerVersion uint8
|
||||||
|
SizeOfCode uint32
|
||||||
|
SizeOfInitializedData uint32
|
||||||
|
SizeOfUninitializedData uint32
|
||||||
|
AddressOfEntryPoint uint32
|
||||||
|
BaseOfCode uint32
|
||||||
|
BaseOfData uint32
|
||||||
|
ImageBase uintptr
|
||||||
|
SectionAlignment uint32
|
||||||
|
FileAlignment uint32
|
||||||
|
MajorOperatingSystemVersion uint16
|
||||||
|
MinorOperatingSystemVersion uint16
|
||||||
|
MajorImageVersion uint16
|
||||||
|
MinorImageVersion uint16
|
||||||
|
MajorSubsystemVersion uint16
|
||||||
|
MinorSubsystemVersion uint16
|
||||||
|
Win32VersionValue uint32
|
||||||
|
SizeOfImage uint32
|
||||||
|
SizeOfHeaders uint32
|
||||||
|
CheckSum uint32
|
||||||
|
Subsystem uint16
|
||||||
|
DllCharacteristics uint16
|
||||||
|
SizeOfStackReserve uintptr
|
||||||
|
SizeOfStackCommit uintptr
|
||||||
|
SizeOfHeapReserve uintptr
|
||||||
|
SizeOfHeapCommit uintptr
|
||||||
|
LoaderFlags uint32
|
||||||
|
NumberOfRvaAndSizes uint32
|
||||||
|
DataDirectory [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]IMAGE_DATA_DIRECTORY
|
||||||
|
}
|
||||||
|
|
||||||
|
const IMAGE_ORDINAL_FLAG uintptr = 0x80000000
|
44
tun/wintun/memmod/syscall_windows_64.go
Normal file
44
tun/wintun/memmod/syscall_windows_64.go
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
// +build amd64 arm64
|
||||||
|
|
||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package memmod
|
||||||
|
|
||||||
|
// Optional header format
|
||||||
|
type IMAGE_OPTIONAL_HEADER struct {
|
||||||
|
Magic uint16
|
||||||
|
MajorLinkerVersion uint8
|
||||||
|
MinorLinkerVersion uint8
|
||||||
|
SizeOfCode uint32
|
||||||
|
SizeOfInitializedData uint32
|
||||||
|
SizeOfUninitializedData uint32
|
||||||
|
AddressOfEntryPoint uint32
|
||||||
|
BaseOfCode uint32
|
||||||
|
ImageBase uintptr
|
||||||
|
SectionAlignment uint32
|
||||||
|
FileAlignment uint32
|
||||||
|
MajorOperatingSystemVersion uint16
|
||||||
|
MinorOperatingSystemVersion uint16
|
||||||
|
MajorImageVersion uint16
|
||||||
|
MinorImageVersion uint16
|
||||||
|
MajorSubsystemVersion uint16
|
||||||
|
MinorSubsystemVersion uint16
|
||||||
|
Win32VersionValue uint32
|
||||||
|
SizeOfImage uint32
|
||||||
|
SizeOfHeaders uint32
|
||||||
|
CheckSum uint32
|
||||||
|
Subsystem uint16
|
||||||
|
DllCharacteristics uint16
|
||||||
|
SizeOfStackReserve uintptr
|
||||||
|
SizeOfStackCommit uintptr
|
||||||
|
SizeOfHeapReserve uintptr
|
||||||
|
SizeOfHeapCommit uintptr
|
||||||
|
LoaderFlags uint32
|
||||||
|
NumberOfRvaAndSizes uint32
|
||||||
|
DataDirectory [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]IMAGE_DATA_DIRECTORY
|
||||||
|
}
|
||||||
|
|
||||||
|
const IMAGE_ORDINAL_FLAG uintptr = 0x8000000000000000
|
70
tun/wintun/memmod/zsyscall_windows.go
Normal file
70
tun/wintun/memmod/zsyscall_windows.go
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
// Code generated by 'go generate'; DO NOT EDIT.
|
||||||
|
|
||||||
|
package memmod
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
// Do the interface allocations only once for common
|
||||||
|
// Errno values.
|
||||||
|
const (
|
||||||
|
errnoERROR_IO_PENDING = 997
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||||
|
errERROR_EINVAL error = syscall.EINVAL
|
||||||
|
)
|
||||||
|
|
||||||
|
// errnoErr returns common boxed Errno values, to prevent
|
||||||
|
// allocations at runtime.
|
||||||
|
func errnoErr(e syscall.Errno) error {
|
||||||
|
switch e {
|
||||||
|
case 0:
|
||||||
|
return errERROR_EINVAL
|
||||||
|
case errnoERROR_IO_PENDING:
|
||||||
|
return errERROR_IO_PENDING
|
||||||
|
}
|
||||||
|
// TODO: add more here, after collecting data on the common
|
||||||
|
// error values see on Windows. (perhaps when running
|
||||||
|
// all.bat?)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||||
|
|
||||||
|
procGetProcAddress = modkernel32.NewProc("GetProcAddress")
|
||||||
|
procIsBadReadPtr = modkernel32.NewProc("IsBadReadPtr")
|
||||||
|
procLoadLibraryA = modkernel32.NewProc("LoadLibraryA")
|
||||||
|
)
|
||||||
|
|
||||||
|
func getProcAddress(module windows.Handle, procName *byte) (addr uintptr, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall(procGetProcAddress.Addr(), 2, uintptr(module), uintptr(unsafe.Pointer(procName)), 0)
|
||||||
|
addr = uintptr(r0)
|
||||||
|
if addr == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func isBadReadPtr(addr uintptr, ucb uintptr) (ret bool) {
|
||||||
|
r0, _, _ := syscall.Syscall(procIsBadReadPtr.Addr(), 2, uintptr(addr), uintptr(ucb), 0)
|
||||||
|
ret = r0 != 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadLibraryA(libFileName *byte) (module windows.Handle, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall(procLoadLibraryA.Addr(), 1, uintptr(unsafe.Pointer(libFileName)), 0, 0)
|
||||||
|
module = windows.Handle(r0)
|
||||||
|
if module == 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
8
tun/wintun/resource/mksyscall.go
Normal file
8
tun/wintun/resource/mksyscall.go
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package resource
|
||||||
|
|
||||||
|
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go resource_windows.go
|
143
tun/wintun/resource/resource_windows.go
Normal file
143
tun/wintun/resource/resource_windows.go
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
/* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 WireGuard LLC. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package resource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MAKEINTRESOURCE(i uint16) *uint16 {
|
||||||
|
return (*uint16)(unsafe.Pointer(uintptr(i)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Predefined Resource Types
|
||||||
|
var (
|
||||||
|
VS_VERSION_INFO uint16 = 1
|
||||||
|
|
||||||
|
RT_CURSOR = MAKEINTRESOURCE(1)
|
||||||
|
RT_BITMAP = MAKEINTRESOURCE(2)
|
||||||
|
RT_ICON = MAKEINTRESOURCE(3)
|
||||||
|
RT_MENU = MAKEINTRESOURCE(4)
|
||||||
|
RT_DIALOG = MAKEINTRESOURCE(5)
|
||||||
|
RT_STRING = MAKEINTRESOURCE(6)
|
||||||
|
RT_FONTDIR = MAKEINTRESOURCE(7)
|
||||||
|
RT_FONT = MAKEINTRESOURCE(8)
|
||||||
|
RT_ACCELERATOR = MAKEINTRESOURCE(9)
|
||||||
|
RT_RCDATA = MAKEINTRESOURCE(10)
|
||||||
|
RT_MESSAGETABLE = MAKEINTRESOURCE(11)
|
||||||
|
RT_GROUP_CURSOR = MAKEINTRESOURCE(12)
|
||||||
|
RT_GROUP_ICON = MAKEINTRESOURCE(14)
|
||||||
|
RT_VERSION = MAKEINTRESOURCE(16)
|
||||||
|
RT_DLGINCLUDE = MAKEINTRESOURCE(17)
|
||||||
|
RT_PLUGPLAY = MAKEINTRESOURCE(19)
|
||||||
|
RT_VXD = MAKEINTRESOURCE(20)
|
||||||
|
RT_ANICURSOR = MAKEINTRESOURCE(21)
|
||||||
|
RT_ANIICON = MAKEINTRESOURCE(22)
|
||||||
|
RT_HTML = MAKEINTRESOURCE(23)
|
||||||
|
RT_MANIFEST = MAKEINTRESOURCE(24)
|
||||||
|
CREATEPROCESS_MANIFEST_RESOURCE_ID = MAKEINTRESOURCE(1)
|
||||||
|
ISOLATIONAWARE_MANIFEST_RESOURCE_ID = MAKEINTRESOURCE(2)
|
||||||
|
ISOLATIONAWARE_NOSTATICIMPORT_MANIFEST_RESOURCE_ID = MAKEINTRESOURCE(3)
|
||||||
|
ISOLATIONPOLICY_MANIFEST_RESOURCE_ID = MAKEINTRESOURCE(4)
|
||||||
|
ISOLATIONPOLICY_BROWSER_MANIFEST_RESOURCE_ID = MAKEINTRESOURCE(5)
|
||||||
|
MINIMUM_RESERVED_MANIFEST_RESOURCE_ID = MAKEINTRESOURCE(1 /*inclusive*/)
|
||||||
|
MAXIMUM_RESERVED_MANIFEST_RESOURCE_ID = MAKEINTRESOURCE(16 /*inclusive*/)
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys findResource(module windows.Handle, name *uint16, resType *uint16) (resInfo windows.Handle, err error) = kernel32.FindResourceW
|
||||||
|
|
||||||
|
func FindByID(module windows.Handle, id uint16, resType *uint16) (resInfo windows.Handle, err error) {
|
||||||
|
return findResource(module, MAKEINTRESOURCE(id), resType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindByName(module windows.Handle, name string, resType *uint16) (resInfo windows.Handle, err error) {
|
||||||
|
var name16 *uint16
|
||||||
|
name16, err = windows.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resInfo, err = findResource(module, name16, resType)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//sys sizeofResource(module windows.Handle, resInfo windows.Handle) (size uint32, err error) = kernel32.SizeofResource
|
||||||
|
//sys loadResource(module windows.Handle, resInfo windows.Handle) (resData windows.Handle, err error) = kernel32.LoadResource
|
||||||
|
//sys lockResource(resData windows.Handle) (addr uintptr, err error) = kernel32.LockResource
|
||||||
|
|
||||||
|
func Load(module, resInfo windows.Handle) (data []byte, err error) {
|
||||||
|
size, err := sizeofResource(module, resInfo)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Unable to size resource: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resData, err := loadResource(module, resInfo)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Unable to load resource: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ptr, err := lockResource(resData)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Unable to lock resource: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
unsafeSlice(unsafe.Pointer(&data), unsafe.Pointer(ptr), int(size))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type VS_FIXEDFILEINFO struct {
|
||||||
|
Signature uint32
|
||||||
|
StrucVersion uint32
|
||||||
|
FileVersionMS uint32
|
||||||
|
FileVersionLS uint32
|
||||||
|
ProductVersionMS uint32
|
||||||
|
ProductVersionLS uint32
|
||||||
|
FileFlagsMask uint32
|
||||||
|
FileFlags uint32
|
||||||
|
FileOS uint32
|
||||||
|
FileType uint32
|
||||||
|
FileSubtype uint32
|
||||||
|
FileDateMS uint32
|
||||||
|
FileDateLS uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
//sys verQueryValue(block *byte, section *uint16, value **byte, size *uint32) (err error) = version.VerQueryValueW
|
||||||
|
|
||||||
|
func VerQueryRootValue(block []byte) (ffi *VS_FIXEDFILEINFO, err error) {
|
||||||
|
var data *byte
|
||||||
|
var size uint32
|
||||||
|
err = verQueryValue(&block[0], windows.StringToUTF16Ptr("\\"), &data, &size)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if uintptr(size) < unsafe.Sizeof(VS_FIXEDFILEINFO{}) {
|
||||||
|
err = errors.New("Incomplete VS_FIXEDFILEINFO")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ffi = (*VS_FIXEDFILEINFO)(unsafe.Pointer(data))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsafeSlice updates the slice slicePtr to be a slice
|
||||||
|
// referencing the provided data with its length & capacity set to
|
||||||
|
// lenCap.
|
||||||
|
//
|
||||||
|
// TODO: when Go 1.16 or Go 1.17 is the minimum supported version,
|
||||||
|
// update callers to use unsafe.Slice instead of this.
|
||||||
|
func unsafeSlice(slicePtr, data unsafe.Pointer, lenCap int) {
|
||||||
|
type sliceHeader struct {
|
||||||
|
Data unsafe.Pointer
|
||||||
|
Len int
|
||||||
|
Cap int
|
||||||
|
}
|
||||||
|
h := (*sliceHeader)(slicePtr)
|
||||||
|
h.Data = data
|
||||||
|
h.Len = lenCap
|
||||||
|
h.Cap = lenCap
|
||||||
|
}
|
112
tun/wintun/resource/zsyscall_windows.go
Normal file
112
tun/wintun/resource/zsyscall_windows.go
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
// Code generated by 'go generate'; DO NOT EDIT.
|
||||||
|
|
||||||
|
package resource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
// Do the interface allocations only once for common
|
||||||
|
// Errno values.
|
||||||
|
const (
|
||||||
|
errnoERROR_IO_PENDING = 997
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||||
|
)
|
||||||
|
|
||||||
|
// errnoErr returns common boxed Errno values, to prevent
|
||||||
|
// allocations at runtime.
|
||||||
|
func errnoErr(e syscall.Errno) error {
|
||||||
|
switch e {
|
||||||
|
case 0:
|
||||||
|
return nil
|
||||||
|
case errnoERROR_IO_PENDING:
|
||||||
|
return errERROR_IO_PENDING
|
||||||
|
}
|
||||||
|
// TODO: add more here, after collecting data on the common
|
||||||
|
// error values see on Windows. (perhaps when running
|
||||||
|
// all.bat?)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||||
|
modversion = windows.NewLazySystemDLL("version.dll")
|
||||||
|
|
||||||
|
procFindResourceW = modkernel32.NewProc("FindResourceW")
|
||||||
|
procSizeofResource = modkernel32.NewProc("SizeofResource")
|
||||||
|
procLoadResource = modkernel32.NewProc("LoadResource")
|
||||||
|
procLockResource = modkernel32.NewProc("LockResource")
|
||||||
|
procVerQueryValueW = modversion.NewProc("VerQueryValueW")
|
||||||
|
)
|
||||||
|
|
||||||
|
func findResource(module windows.Handle, name *uint16, resType *uint16) (resInfo windows.Handle, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall(procFindResourceW.Addr(), 3, uintptr(module), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(resType)))
|
||||||
|
resInfo = windows.Handle(r0)
|
||||||
|
if resInfo == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func sizeofResource(module windows.Handle, resInfo windows.Handle) (size uint32, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall(procSizeofResource.Addr(), 2, uintptr(module), uintptr(resInfo), 0)
|
||||||
|
size = uint32(r0)
|
||||||
|
if size == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadResource(module windows.Handle, resInfo windows.Handle) (resData windows.Handle, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall(procLoadResource.Addr(), 2, uintptr(module), uintptr(resInfo), 0)
|
||||||
|
resData = windows.Handle(r0)
|
||||||
|
if resData == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func lockResource(resData windows.Handle) (addr uintptr, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall(procLockResource.Addr(), 1, uintptr(resData), 0, 0)
|
||||||
|
addr = uintptr(r0)
|
||||||
|
if addr == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func verQueryValue(block *byte, section *uint16, value **byte, size *uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procVerQueryValueW.Addr(), 4, uintptr(unsafe.Pointer(block)), uintptr(unsafe.Pointer(section)), uintptr(unsafe.Pointer(value)), uintptr(unsafe.Pointer(size)), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
|
@ -34,7 +34,7 @@ type Adapter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
modwintun = windows.NewLazyDLL("wintun.dll")
|
modwintun = newLazyDLL("wintun.dll")
|
||||||
|
|
||||||
procWintunCreateAdapter = modwintun.NewProc("WintunCreateAdapter")
|
procWintunCreateAdapter = modwintun.NewProc("WintunCreateAdapter")
|
||||||
procWintunDeleteAdapter = modwintun.NewProc("WintunDeleteAdapter")
|
procWintunDeleteAdapter = modwintun.NewProc("WintunDeleteAdapter")
|
||||||
|
|
Loading…
Reference in a new issue