github.com/iDigitalFlame/xmt@v0.5.4/device/winapi/y_fnv_loader.go (about) 1 //go:build windows && (altload || crypt) 2 // +build windows 3 // +build altload crypt 4 5 // Copyright (C) 2020 - 2023 iDigitalFlame 6 // 7 // This program is free software: you can redistribute it and/or modify 8 // it under the terms of the GNU General Public License as published by 9 // the Free Software Foundation, either version 3 of the License, or 10 // any later version. 11 // 12 // This program is distributed in the hope that it will be useful, 13 // but WITHOUT ANY WARRANTY; without even the implied warranty of 14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 // GNU General Public License for more details. 16 // 17 // You should have received a copy of the GNU General Public License 18 // along with this program. If not, see <https://www.gnu.org/licenses/>. 19 // 20 21 package winapi 22 23 import ( 24 "strings" 25 "sync" 26 "sync/atomic" 27 "syscall" 28 "unsafe" 29 30 "github.com/iDigitalFlame/xmt/util/bugtrack" 31 "github.com/iDigitalFlame/xmt/util/xerr" 32 ) 33 34 type lazyDLL struct { 35 _ [0]func() 36 sync.Mutex 37 funcs map[uint32]*lazyProc 38 name string 39 addr uintptr 40 } 41 type lazyProc struct { 42 _ [0]func() 43 dll *lazyDLL 44 addr uintptr 45 } 46 47 func (d *lazyDLL) load() error { 48 if atomic.LoadUintptr(&d.addr) > 0 { 49 return nil 50 } 51 d.Lock() 52 var ( 53 h uintptr 54 err error 55 ) 56 if (len(d.name) == 12 || len(d.name) == 14) && d.name[0] == 'k' && d.name[2] == 'r' && d.name[3] == 'n' { 57 if h, err = loadDLL(d.name); fallbackLoad { 58 if h == 0 && len(d.name) == 14 { 59 // NOTE(dij): The "kernelbase.dll" file was not avaliable before 60 // Windows 7 so we'll redirect all KernelBase calls to 61 // Kernel32. We can tell this is "kernelbase.dll" fails 62 // to load. 63 d.name = dllKernel32.name 64 h, err = loadDLL(dllKernel32.name) 65 } 66 } 67 } else { 68 h, err = loadLibraryEx(d.name) 69 } 70 if h == 0 { 71 d.Unlock() 72 return err 73 } 74 atomic.StoreUintptr(&d.addr, h) 75 err = d.initFunctions(h) 76 d.Unlock() 77 return err 78 } 79 func (d *lazyDLL) free() error { 80 if d.addr == 0 { 81 return nil 82 } 83 d.Lock() 84 err := syscall.FreeLibrary(syscall.Handle(d.addr)) 85 atomic.StoreUintptr(&d.addr, 0) 86 d.Unlock() 87 return err 88 } 89 func (p *lazyProc) find() error { 90 if atomic.LoadUintptr(&p.addr) > 0 { 91 return nil 92 } 93 if err := p.dll.load(); err != nil { 94 return err 95 } 96 if atomic.LoadUintptr(&p.addr) > 0 { 97 return nil 98 } 99 return xerr.Sub("cannot load DLL function", 0x18) 100 } 101 func fnvHash(b [256]byte) uint32 { 102 h := uint32(2166136261) 103 for i := range b { 104 if b[i] == 0 { 105 break 106 } 107 h *= 16777619 108 h ^= uint32(b[i]) 109 } 110 return h 111 } 112 func (d *lazyDLL) proc(h uint32) *lazyProc { 113 if d.funcs == nil { 114 d.funcs = make(map[uint32]*lazyProc) 115 } 116 p := &lazyProc{dll: d} 117 d.funcs[h] = p 118 return p 119 } 120 func (d *lazyDLL) sysProc(h uint32) *lazyProc { 121 if len(d.name) != 9 && d.name[0] != 'n' && d.name[1] != 't' { 122 return d.proc(h) 123 } 124 p := d.proc(h) 125 registerSyscall(p, "", h) 126 return p 127 } 128 func (d *lazyDLL) initFunctions(h uintptr) error { 129 b := (*imageDosHeader)(unsafe.Pointer(h)) 130 if b.magic != 0x5A4D { 131 return xerr.Sub("base is not a valid DOS header", 0x19) 132 } 133 n := *(*imageNtHeader)(unsafe.Pointer(h + uintptr(b.pos))) 134 if n.Signature != 0x00004550 { 135 return xerr.Sub("offset base is not a valid NT header", 0x1A) 136 } 137 if n.File.Characteristics&0x2000 == 0 { 138 return xerr.Sub("header does not represent a DLL", 0x1B) 139 } 140 switch n.File.Machine { 141 case 0, 0x14C, 0x1C4, 0xAA64, 0x8664: 142 default: 143 return xerr.Sub("header does not represent a DLL", 0x1B) 144 } 145 var ( 146 p = b.pos + int32(unsafe.Sizeof(n)) 147 v [16]imageDataDirectory 148 ) 149 if *(*uint16)(unsafe.Pointer(h + uintptr(p))) == 0x20B { 150 v = (*imageOptionalHeader64)(unsafe.Pointer(h + uintptr(p))).Directory 151 } else { 152 v = (*imageOptionalHeader32)(unsafe.Pointer(h + uintptr(p))).Directory 153 } 154 if v[0].Size == 0 || v[0].VirtualAddress == 0 { 155 return xerr.Sub("header has an invalid first entry point", 0x1C) 156 } 157 var ( 158 i = (*imageExportDir)(unsafe.Pointer(h + uintptr(v[0].VirtualAddress))) 159 f = h + uintptr(i.AddressOfFunctions) 160 s = h + uintptr(i.AddressOfNames) 161 o = h + uintptr(i.AddressOfNameOrdinals) 162 m = h + uintptr(v[0].VirtualAddress) + uintptr(v[0].Size) 163 ) 164 for x, k, a := uint32(0), uint32(0), uintptr(0); x < i.NumberOfNames; x++ { 165 k = fnvHash(*(*[256]byte)(unsafe.Pointer( 166 h + uintptr(*(*uint32)(unsafe.Pointer(s + uintptr(x*4)))), 167 ))) 168 a = h + uintptr( 169 *(*uint32)(unsafe.Pointer(f + uintptr( 170 *(*uint16)(unsafe.Pointer(o + uintptr(x*2))), 171 )*4)), 172 ) 173 p, ok := d.funcs[k] 174 if !ok { 175 continue 176 } 177 if a < m && a > f { 178 var err error 179 if p.addr, err = loadForwardFunc((*[256]byte)(unsafe.Pointer(a))); err != nil { 180 return err 181 } 182 } else { 183 p.addr = a 184 } 185 delete(d.funcs, k) 186 } 187 d.funcs = nil 188 return nil 189 } 190 func loadForwardFunc(b *[256]byte) (uintptr, error) { 191 var n int 192 for n < 256 { 193 if (*b)[n] == 0 { 194 break 195 } 196 n++ 197 } 198 var ( 199 v = string((*b)[:n]) 200 i = strings.LastIndexByte(v, '.') 201 ) 202 if i == -1 { 203 return 0, syscall.EINVAL 204 } 205 d, f := v[0:i], v[i+1:] 206 if i < 5 || v[i-4] != '.' { 207 d = d + dllExt 208 } 209 if bugtrack.Enabled { 210 bugtrack.Track(`winapi.loadForwardFunc(): Loading forwarded function "%s" from "%s".`, f, d) 211 } 212 x, err := loadDLL(d) 213 if err != nil { 214 return 0, err 215 } 216 return findProc(x, f, d) 217 }