github.com/iDigitalFlame/xmt@v0.5.4/device/winapi/dll.go (about)

     1  //go:build windows || (!windows && !implant)
     2  // +build windows !windows,!implant
     3  
     4  // Copyright (C) 2020 - 2023 iDigitalFlame
     5  //
     6  // This program is free software: you can redistribute it and/or modify
     7  // it under the terms of the GNU General Public License as published by
     8  // the Free Software Foundation, either version 3 of the License, or
     9  // any later version.
    10  //
    11  // This program is distributed in the hope that it will be useful,
    12  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  // GNU General Public License for more details.
    15  //
    16  // You should have received a copy of the GNU General Public License
    17  // along with this program.  If not, see <https://www.gnu.org/licenses/>.
    18  //
    19  
    20  package winapi
    21  
    22  import (
    23  	"io"
    24  	"strings"
    25  	"unsafe"
    26  
    27  	"github.com/iDigitalFlame/xmt/data"
    28  	"github.com/iDigitalFlame/xmt/util/xerr"
    29  )
    30  
    31  const sectionSize = unsafe.Sizeof(imageSectionHeader{})
    32  
    33  type imageNtHeader struct {
    34  	Signature uint32
    35  	File      imageFileHeader
    36  }
    37  type imageExportDir struct {
    38  	_, _                  uint32
    39  	_, _                  uint16
    40  	Name                  uint32
    41  	Base                  uint32
    42  	NumberOfFunctions     uint32
    43  	NumberOfNames         uint32
    44  	AddressOfFunctions    uint32
    45  	AddressOfNames        uint32
    46  	AddressOfNameOrdinals uint32
    47  }
    48  type imageDosHeader struct {
    49  	magic uint16
    50  	_     [56]byte
    51  	pos   int32
    52  }
    53  type imageFileHeader struct {
    54  	Machine              uint16
    55  	NumberOfSections     uint16
    56  	_, _, _              uint32
    57  	SizeOfOptionalHeader uint16
    58  	Characteristics      uint16
    59  }
    60  type imageSectionHeader struct {
    61  	Name             [8]uint8
    62  	VirtualSize      uint32
    63  	VirtualAddress   uint32
    64  	SizeOfRawData    uint32
    65  	PointerToRawData uint32
    66  	_, _             uint32
    67  	_, _             uint16
    68  	Characteristics  uint32
    69  }
    70  type imageDataDirectory struct {
    71  	VirtualAddress uint32
    72  	Size           uint32
    73  }
    74  type imageOptionalHeader32 struct {
    75  	_                   [92]byte
    76  	NumberOfRvaAndSizes uint32
    77  	Directory           [16]imageDataDirectory
    78  }
    79  type imageOptionalHeader64 struct {
    80  	_                   [108]byte
    81  	NumberOfRvaAndSizes uint32
    82  	Directory           [16]imageDataDirectory
    83  }
    84  
    85  func byteString(b [256]byte) string {
    86  	var n int
    87  	for i := range b {
    88  		if b[i] == 0 {
    89  			break
    90  		}
    91  		n++
    92  	}
    93  	return string(b[:n])
    94  }
    95  
    96  // ExtractDLLBase will extract the '.text' (executable) section of the supplied
    97  // DLL file path or basename (Windows-only) and return the '.text' base address
    98  // and raw bytes to be used in calls to 'winapi.Patch*' or 'winapi.Check*'
    99  //
   100  // This function returns any errors that may occur during reading.
   101  //
   102  // Non-Windows devices may use this function to extract DLL data.
   103  func ExtractDLLBase(dll string) (uint32, []byte, error) {
   104  	b, err := data.ReadFile(fullPath(dll))
   105  	if err != nil {
   106  		return 0, nil, err
   107  	}
   108  	return ExtractDLLBaseRaw(b)
   109  }
   110  
   111  // ExtractDLLBaseRaw will extract the '.text' (executable) section of the supplied
   112  // DLL raw bytes and return the '.text' base address and raw bytes to be used in
   113  // calls to 'winapi.Patch*' or 'winapi.Check*'
   114  //
   115  // This function returns any errors that may occur during reading.
   116  //
   117  // Non-Windows devices may use this function to extract DLL data.
   118  func ExtractDLLBaseRaw(v []byte) (uint32, []byte, error) {
   119  	_, s, _, b, err := extractDLLBase(v)
   120  	return s.VirtualAddress, b[s.PointerToRawData:s.SizeOfRawData], err
   121  }
   122  
   123  // ExtractDLLFunction will extract 'count' bytes from the supplied DLL file path
   124  // or basename (Windows-only) at the base of the supplied function name.
   125  //
   126  // If 'count' is zero, this defaults to 16 bytes.
   127  //
   128  // This function returns any errors that may occur during reading. Forwarded
   129  // functions also return an error that indicates where the forward points to.
   130  //
   131  // Non-Windows devices may use this function to extract DLL data.
   132  func ExtractDLLFunction(dll string, name string, count uint32) ([]byte, error) {
   133  	b, err := data.ReadFile(fullPath(dll))
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  	return ExtractDLLFunctionRaw(b, name, count)
   138  }
   139  
   140  // ExtractDLLFunctionRaw will extract 'count' bytes from the supplied DLL raw bytes
   141  // at the base of the supplied function name.
   142  //
   143  // If 'count' is zero, this defaults to 16 bytes.
   144  //
   145  // This function returns any errors that may occur during reading. Forwarded
   146  // functions also return an error that indicates where the forward points to.
   147  //
   148  // Non-Windows devices may use this function to extract DLL data.
   149  func ExtractDLLFunctionRaw(v []byte, name string, count uint32) ([]byte, error) {
   150  	a, q, e, b, err := extractDLLBase(v)
   151  	if err != nil {
   152  		return nil, err
   153  	}
   154  	u := uint32(len(b))
   155  	if u < a {
   156  		return nil, xerr.Sub("cannot find data section", 0x1D)
   157  	}
   158  	if u < e.PointerToRawData+a+e.VirtualSize+40 {
   159  		return nil, io.ErrUnexpectedEOF
   160  	}
   161  	if count == 0 {
   162  		count = 16
   163  	}
   164  	var (
   165  		i = (*imageExportDir)(unsafe.Pointer(&b[e.PointerToRawData+a]))
   166  		h = e.PointerToRawData - e.VirtualAddress
   167  		f = h + i.AddressOfFunctions
   168  		s = h + i.AddressOfNames
   169  		o = h + i.AddressOfNameOrdinals
   170  		m = q.VirtualAddress + q.VirtualSize
   171  		r = make([]byte, 0, count)
   172  	)
   173  	if u < f || u < s || u < o || u < m {
   174  		return nil, io.ErrUnexpectedEOF
   175  	}
   176  	for x, k, c := uint32(0), "", uint32(0); x < i.NumberOfNames; x++ {
   177  		k = byteString(*(*[256]byte)(unsafe.Pointer(
   178  			&b[h+*(*uint32)(unsafe.Pointer(&b[s+(x*4)]))],
   179  		)))
   180  		if !strings.EqualFold(k, name) {
   181  			continue
   182  		}
   183  		// Grab ASM from '.text' section
   184  		c = (q.PointerToRawData - q.VirtualAddress) + *(*uint32)(unsafe.Pointer(
   185  			&b[f+uint32(*(*uint16)(unsafe.Pointer(&b[o+(x*2)]))*4)],
   186  		))
   187  		if c < m && c > f {
   188  			if xerr.ExtendedInfo {
   189  				return nil, xerr.Sub(`function is a forward to "`+byteString(*(*[256]byte)(unsafe.Pointer(&b[c])))+`"`, 0x70)
   190  			}
   191  			return nil, xerr.Sub("function is a forward", 0x70)
   192  		}
   193  		for z := uint32(0); z < count; z++ {
   194  			r = append(r, b[c+z])
   195  		}
   196  	}
   197  	if len(r) == 0 {
   198  		return nil, xerr.Sub("cannot find function", 0x6F)
   199  	}
   200  	return r, nil
   201  }
   202  func extractDLLBase(b []byte) (uint32, *imageSectionHeader, *imageSectionHeader, []byte, error) {
   203  	if len(b) < 62 {
   204  		return 0, nil, nil, nil, xerr.Sub("base is not a valid DOS header", 0x19)
   205  	}
   206  	var (
   207  		u = int32(len(b))
   208  		d = (*imageDosHeader)(unsafe.Pointer(&b[0]))
   209  	)
   210  	if d.magic != 0x5A4D {
   211  		return 0, nil, nil, nil, xerr.Sub("base is not a valid DOS header", 0x19)
   212  	}
   213  	if u < d.pos+24 {
   214  		return 0, nil, nil, nil, xerr.Sub("offset base is not a valid NT header", 0x1A)
   215  	}
   216  	n := *(*imageNtHeader)(unsafe.Pointer(&b[d.pos]))
   217  	if n.Signature != 0x00004550 {
   218  		return 0, nil, nil, nil, xerr.Sub("offset base is not a valid NT header", 0x1A)
   219  	}
   220  	if n.File.Characteristics&0x2000 == 0 {
   221  		return 0, nil, nil, nil, xerr.Sub("header does not represent a DLL", 0x1B)
   222  	}
   223  	switch n.File.Machine {
   224  	case 0, 0x14C, 0x1C4, 0xAA64, 0x8664:
   225  	default:
   226  		return 0, nil, nil, nil, xerr.Sub("header does not represent a DLL", 0x1B)
   227  	}
   228  	var (
   229  		p = d.pos + int32(unsafe.Sizeof(n))
   230  		v [16]imageDataDirectory
   231  	)
   232  	if u < p+104 {
   233  		return 0, nil, nil, nil, io.ErrUnexpectedEOF
   234  	}
   235  	if *(*uint16)(unsafe.Pointer(&b[p])) == 0x20B {
   236  		v = (*imageOptionalHeader64)(unsafe.Pointer(&b[p])).Directory
   237  	} else {
   238  		v = (*imageOptionalHeader32)(unsafe.Pointer(&b[p])).Directory
   239  	}
   240  	if p = d.pos + int32(unsafe.Sizeof(n.File)) + int32(n.File.SizeOfOptionalHeader) + 4; u < p {
   241  		return 0, nil, nil, nil, io.ErrUnexpectedEOF
   242  	}
   243  	// NOTE(dij): For clarity 's' is our '.text' section, it CAN be our entry
   244  	//            points section, but it might not. 'e' will store the entry
   245  	//            points section.
   246  	var s, e *imageSectionHeader
   247  	for i := uint16(0); i < n.File.NumberOfSections; i++ {
   248  		k := p + (int32(sectionSize) * int32(i))
   249  		if u < k+40 {
   250  			return 0, nil, nil, nil, io.ErrUnexpectedEOF
   251  		}
   252  		x := (*imageSectionHeader)(unsafe.Pointer(&b[k]))
   253  		// Find the '.text' section
   254  		if x.Name[0] == 0x2E && x.Name[1] == 0x74 && x.Name[3] == 0x78 {
   255  			s = x
   256  		}
   257  		// Find the entry point table
   258  		if x.VirtualAddress < v[0].VirtualAddress && v[0].VirtualAddress < (x.VirtualAddress+x.VirtualSize) {
   259  			e = x
   260  		}
   261  		if e != nil && s != nil {
   262  			break
   263  		}
   264  	}
   265  	if s == nil || len(b) < int(s.PointerToRawData) {
   266  		return 0, nil, nil, nil, xerr.Sub("cannot find data section", 0x1D)
   267  	}
   268  	if e == nil {
   269  		e = s // Make sure 'e' is never nil
   270  	}
   271  	return v[0].VirtualAddress - e.VirtualAddress, s, e, b, nil
   272  }