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 }