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  }