├── README.md ├── go.mod ├── go.sum ├── main.go └── pkg ├── peloader └── peloader.go └── winapi └── kernel32.go /README.md: -------------------------------------------------------------------------------- 1 | # GoPELoader 2 | 3 | # Credits 4 | * https://github.com/fancycode/MemoryModule -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/latortuga71/GoPeLoader 2 | 3 | go 1.18 4 | 5 | require golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 h1:Sx/u41w+OwrInGdEckYmEuU5gHoGSL4QbDz3S9s6j4U= 2 | golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 3 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | 7 | "github.com/latortuga71/GoPeLoader/pkg/peloader" 8 | ) 9 | 10 | func main() { 11 | pePath := `` 12 | data, err := os.ReadFile(pePath) 13 | if err != nil { 14 | log.Fatal(err) 15 | } 16 | rawPeFile := peloader.NewRawPE(peloader.Dll, false, data) 17 | err = rawPeFile.LoadPEFromMemory() 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | err = rawPeFile.FreePeFromMemory() 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /pkg/peloader/peloader.go: -------------------------------------------------------------------------------- 1 | package peloader 2 | 3 | import ( 4 | "bytes" 5 | "debug/pe" 6 | "errors" 7 | "fmt" 8 | "log" 9 | "os" 10 | "runtime" 11 | "syscall" 12 | "unsafe" 13 | 14 | "github.com/latortuga71/GoPeLoader/pkg/winapi" 15 | "golang.org/x/sys/windows" 16 | ) 17 | 18 | // Section characteristics flags. 19 | const ( 20 | IMAGE_SCN_CNT_CODE = 0x00000020 21 | IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040 22 | IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080 23 | IMAGE_SCN_LNK_OTHER = 0x00000100 24 | IMAGE_SCN_LNK_INFO = 0x00000200 25 | IMAGE_SCN_LNK_REMOVE = 0x00000800 26 | IMAGE_SCN_LNK_COMDAT = 0x00001000 27 | IMAGE_SCN_GPREL = 0x00008000 28 | IMAGE_SCN_MEM_PURGEABLE = 0x00020000 29 | IMAGE_SCN_MEM_16BIT = 0x00020000 30 | IMAGE_SCN_MEM_LOCKED = 0x00040000 31 | IMAGE_SCN_MEM_PRELOAD = 0x00080000 32 | IMAGE_SCN_ALIGN_1BYTES = 0x00100000 33 | IMAGE_SCN_ALIGN_2BYTES = 0x00200000 34 | IMAGE_SCN_ALIGN_4BYTES = 0x00300000 35 | IMAGE_SCN_ALIGN_8BYTES = 0x00400000 36 | IMAGE_SCN_ALIGN_16BYTES = 0x00500000 37 | IMAGE_SCN_ALIGN_32BYTES = 0x00600000 38 | IMAGE_SCN_ALIGN_64BYTES = 0x00700000 39 | IMAGE_SCN_ALIGN_128BYTES = 0x00800000 40 | IMAGE_SCN_ALIGN_256BYTES = 0x00900000 41 | IMAGE_SCN_ALIGN_512BYTES = 0x00A00000 42 | IMAGE_SCN_ALIGN_1024BYTES = 0x00B00000 43 | IMAGE_SCN_ALIGN_2048BYTES = 0x00C00000 44 | IMAGE_SCN_ALIGN_4096BYTES = 0x00D00000 45 | IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000 46 | IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000 47 | IMAGE_SCN_MEM_DISCARDABLE = 0x02000000 48 | IMAGE_SCN_MEM_NOT_CACHED = 0x04000000 49 | IMAGE_SCN_MEM_NOT_PAGED = 0x08000000 50 | IMAGE_SCN_MEM_SHARED = 0x10000000 51 | IMAGE_SCN_MEM_EXECUTE = 0x20000000 52 | IMAGE_SCN_MEM_READ = 0x40000000 53 | IMAGE_SCN_MEM_WRITE = 0x80000000 54 | ) 55 | const ( 56 | IMAGE_DOS_SIGNATURE = 0x5A4D 57 | IMAGE_NT_SIGNATURE = 0x00004550 // PE00 58 | ) 59 | 60 | var ( 61 | PAGE_SIZE = os.Getpagesize() 62 | ) 63 | 64 | type MemorySection struct { 65 | Name string 66 | PeSection pe.Section 67 | MemoryAddress uintptr 68 | AlignedAddress uintptr 69 | Size uint32 70 | } 71 | 72 | type ProtFlags [2][2][2]uint32 73 | 74 | var ProtectionFlags = ProtFlags{ 75 | { 76 | // not executable 77 | {winapi.PAGE_NOACCESS, winapi.PAGE_WRITECOPY}, 78 | {winapi.PAGE_READONLY, winapi.PAGE_READWRITE}, 79 | }, 80 | { 81 | // executable 82 | {winapi.PAGE_EXECUTE, winapi.PAGE_EXECUTE_WRITECOPY}, 83 | {winapi.PAGE_EXECUTE_READ, winapi.PAGE_EXECUTE_READWRITE}, 84 | }, 85 | } 86 | 87 | const ( 88 | IMAGE_ORDINAL_FLAG64 = 0x8000000000000000 89 | IMAGE_ORDINAL_FLAG32 = 0x80000000 90 | ) 91 | 92 | type IMAGE_BASE_RELOCATION struct { 93 | VirtualAddress uint32 94 | SizeOfBlock uint32 95 | } 96 | 97 | const ( 98 | IMAGE_DIRECTORY_ENTRY_BASERELOC = 5 99 | ) 100 | 101 | const ( 102 | IMAGE_REL_BASED_ABSOLUTE = 0 103 | IMAGE_REL_BASED_HIGHLOW = 3 104 | IMAGE_REL_BASED_DIR64 = 10 105 | ) 106 | 107 | type BASE_RELOCATION_ENTRY struct { 108 | Offset uint8 //: 12; 109 | Type uint8 //: 4; 110 | } 111 | 112 | type IMAGE_IMPORT_BY_NAME struct { 113 | Hint [2]byte 114 | Name [10]byte 115 | } 116 | 117 | // ImageDOSHeader represents the DOS stub of a PE. 118 | type ImageDOSHeader struct { 119 | // Magic number. 120 | Magic uint16 121 | 122 | // Bytes on last page of file. 123 | BytesOnLastPageOfFile uint16 124 | 125 | // Pages in file. 126 | PagesInFile uint16 127 | 128 | // Relocations. 129 | Relocations uint16 130 | 131 | // Size of header in paragraphs. 132 | SizeOfHeader uint16 133 | 134 | // Minimum extra paragraphs needed. 135 | MinExtraParagraphsNeeded uint16 136 | 137 | // Maximum extra paragraphs needed. 138 | MaxExtraParagraphsNeeded uint16 139 | 140 | // Initial (relative) SS value. 141 | InitialSS uint16 142 | 143 | // Initial SP value. 144 | InitialSP uint16 145 | 146 | // Checksum. 147 | Checksum uint16 148 | 149 | // Initial IP value. 150 | InitialIP uint16 151 | 152 | // Initial (relative) CS value. 153 | InitialCS uint16 154 | 155 | // File address of relocation table. 156 | AddressOfRelocationTable uint16 157 | 158 | // Overlay number. 159 | OverlayNumber uint16 160 | 161 | // Reserved words. 162 | ReservedWords1 [4]uint16 163 | 164 | // OEM identifier. 165 | OEMIdentifier uint16 166 | 167 | // OEM information. 168 | OEMInformation uint16 169 | 170 | // Reserved words. 171 | ReservedWords2 [10]uint16 172 | 173 | // File address of new exe header (Elfanew). 174 | AddressOfNewEXEHeader uint32 175 | } 176 | 177 | func ImageOrdinal64(ordinal uint64) uint64 { 178 | return ordinal & 0xffff 179 | } 180 | 181 | func ImageOrdinal32(ordinal uint32) uint32 { 182 | return ordinal & 0xffff 183 | } 184 | 185 | func ImageSnapByOridinal32(ordinal uint32) bool { 186 | return ((ordinal & IMAGE_ORDINAL_FLAG32) != 0) 187 | } 188 | 189 | func ImageSnapByOridinal64(ordinal uint64) bool { 190 | return ((ordinal & IMAGE_ORDINAL_FLAG64) != 0) 191 | } 192 | 193 | func OffsetPointer(start uintptr, offset uintptr) uintptr { 194 | return start + offset 195 | } 196 | 197 | ///https://github.com/fancycode/MemoryModule/blob/master/MemoryModule.c 198 | func ReadAsciiFromMemory(startPtr uintptr, memoryStart uintptr) []byte { 199 | var asciiArray []byte 200 | for x := 0; ; x++ { 201 | byteValue := *(*byte)(unsafe.Pointer(uintptr(startPtr+uintptr(x)) + memoryStart)) 202 | if byteValue == 0 { 203 | break 204 | } 205 | asciiArray = append(asciiArray, byteValue) 206 | } 207 | return asciiArray 208 | } 209 | 210 | func ReadAsciiFromMemoryNoBase(startPtr uintptr) []byte { 211 | var asciiArray []byte 212 | for x := 0; ; x++ { 213 | byteValue := *(*byte)(unsafe.Pointer(uintptr(startPtr + uintptr(x)))) 214 | if byteValue == 0 { 215 | break 216 | } 217 | asciiArray = append(asciiArray, byteValue) 218 | } 219 | return asciiArray 220 | } 221 | 222 | func AlignAddressDown(address, alignment uintptr) uintptr { 223 | return address & ^(alignment - 1) 224 | } 225 | 226 | func AlignValueUp(value, alignment uint32) uint32 { 227 | not := ^(alignment - 1) 228 | return (value + alignment - 1) & not 229 | } 230 | 231 | func CheckSize(size uint32, expectedSz uint32) bool { 232 | if size < expectedSz { 233 | return false 234 | } 235 | return true 236 | } 237 | 238 | func GetRealSectionSize(peHeader *pe.OptionalHeader64, section *pe.Section) uint32 { 239 | if section.Size != 0 { 240 | return section.Size 241 | } 242 | if section.Characteristics&IMAGE_SCN_CNT_INITIALIZED_DATA > 0 { 243 | return peHeader.SizeOfInitializedData 244 | } 245 | if section.Characteristics&IMAGE_SCN_CNT_UNINITIALIZED_DATA > 0 { 246 | return peHeader.SizeOfUninitializedData 247 | } 248 | return 0 249 | } 250 | 251 | func CreateImportAddressTable(peHeader *pe.OptionalHeader64, memoryStart uintptr) error { 252 | dataDirectory := peHeader.DataDirectory 253 | if len(dataDirectory) == 0 { 254 | return errors.New("[+] Data Directory Empty") 255 | } 256 | directory := dataDirectory[1] 257 | importDirectorySize := unsafe.Sizeof(pe.ImportDirectory{}) 258 | importDescriptionPtr := unsafe.Pointer(memoryStart + uintptr(directory.VirtualAddress)) 259 | for winapi.IsBadReadPtr(uintptr(importDescriptionPtr), importDirectorySize) && (*pe.ImportDirectory)(importDescriptionPtr).Name != 0 { 260 | importDescriptor := (*pe.ImportDirectory)(importDescriptionPtr) 261 | namePtr := importDescriptor.Name 262 | nameAscii := ReadAsciiFromMemory(uintptr(namePtr), memoryStart) 263 | libraryHandle, err := windows.LoadLibrary(string(nameAscii)) 264 | if err != nil { 265 | return errors.New(fmt.Sprintf("Failed to load required libary %v", err)) 266 | } 267 | var thunkRef unsafe.Pointer 268 | var funcRef unsafe.Pointer 269 | if importDescriptor.OriginalFirstThunk == 0 { 270 | thunkRef = unsafe.Pointer(uintptr(importDescriptor.OriginalFirstThunk) + memoryStart) 271 | funcRef = unsafe.Pointer(uintptr(importDescriptor.FirstThunk) + memoryStart) 272 | } else { 273 | thunkRef = unsafe.Pointer(uintptr(importDescriptor.FirstThunk) + memoryStart) 274 | funcRef = unsafe.Pointer(uintptr(importDescriptor.FirstThunk) + memoryStart) 275 | } 276 | for { 277 | if *(*uintptr)(thunkRef) == 0 { 278 | break 279 | } 280 | if ImageSnapByOridinal64(*(*uint64)(thunkRef)) { 281 | funcPtr, err := windows.GetProcAddressByOrdinal(libraryHandle, uintptr(ImageOrdinal64(*(*uint64)(thunkRef)))) 282 | if err != nil { 283 | return errors.New(fmt.Sprintf("Failed to get proc address by ordinal %v", err)) 284 | } 285 | *(*uintptr)(funcRef) = funcPtr 286 | } else { 287 | thunkData := memoryStart + *(*uintptr)(thunkRef) 288 | funcName := string(ReadAsciiFromMemoryNoBase(thunkData + 2)) 289 | funcPtr, err := windows.GetProcAddress(libraryHandle, funcName) 290 | if err != nil { 291 | return errors.New(fmt.Sprintf("Failed to get proc addess by name %v", err)) 292 | } 293 | *(*uintptr)(funcRef) = uintptr(unsafe.Pointer(funcPtr)) 294 | } 295 | if *(*uint64)(funcRef) == 0 { 296 | return errors.New("Failed to get function pointer") 297 | } 298 | sizeOfPtr := unsafe.Sizeof(uintptr(thunkRef)) 299 | thunkRef = unsafe.Pointer(uintptr(thunkRef) + sizeOfPtr) 300 | funcRef = unsafe.Pointer(uintptr(funcRef) + sizeOfPtr) 301 | } 302 | importDescriptionPtr = unsafe.Pointer((uintptr(importDescriptionPtr) + unsafe.Sizeof(pe.ImportDirectory{})/2)) 303 | } 304 | return nil 305 | } 306 | 307 | func FinalizeSections(dll *pe.File, peHeaderOptionalHeader64 *pe.OptionalHeader64, baseAddress uintptr, memorySections []MemorySection) error { 308 | for _, s := range memorySections { 309 | s.AlignedAddress = AlignAddressDown(s.MemoryAddress, uintptr(PAGE_SIZE)) 310 | s.Size = GetRealSectionSize(peHeaderOptionalHeader64, &s.PeSection) 311 | } 312 | for _, s := range memorySections { 313 | if s.Size == 0 { 314 | continue 315 | } 316 | if s.PeSection.Characteristics&IMAGE_SCN_MEM_DISCARDABLE != 0 { 317 | err := windows.VirtualFree(s.MemoryAddress, uintptr(s.Size), windows.MEM_DECOMMIT) 318 | if err != nil { 319 | return errors.New(fmt.Sprintf("Failed to free discarded section %v", err)) 320 | } 321 | continue 322 | } 323 | //var protFlags uint32 324 | executable := (s.PeSection.Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0 325 | readable := (s.PeSection.Characteristics & IMAGE_SCN_MEM_READ) != 0 326 | writable := (s.PeSection.Characteristics & IMAGE_SCN_MEM_WRITE) != 0 327 | var e, r, w uint32 328 | if executable { 329 | e = 1 330 | } 331 | if readable { 332 | r = 1 333 | } 334 | if writable { 335 | w = 1 336 | } 337 | e, r, w = 1, 1, 1 338 | protFlags := ProtectionFlags[e][r][w] 339 | var oldFlags uint32 340 | if s.PeSection.Characteristics&IMAGE_SCN_MEM_NOT_CACHED != 0 { 341 | protFlags |= winapi.PAGE_NOCACHE 342 | } 343 | err := windows.VirtualProtect(s.MemoryAddress, uintptr(s.Size), protFlags, &oldFlags) 344 | if err != nil { 345 | return errors.New(fmt.Sprintf("Failed to change memory protections for section %s %v", s.Name, err)) 346 | } 347 | } 348 | return nil 349 | } 350 | 351 | func CopySectionsToMemory(dll *pe.File, peHeaderOptionalHeader64 *pe.OptionalHeader64, baseAddress uintptr) ([]MemorySection, error) { 352 | memSections := make([]MemorySection, 0) 353 | for _, section := range dll.Sections { 354 | memSection := MemorySection{} 355 | memSection.Name = section.Name 356 | data, err := section.Data() 357 | if err != nil { 358 | return nil, errors.New(fmt.Sprintf("Failed to get section data %v", err)) 359 | } 360 | if len(data) == 0 { 361 | sectionSz := peHeaderOptionalHeader64.SectionAlignment 362 | if sectionSz > 0 { 363 | dest, _ := winapi.VirtualAlloc(baseAddress+uintptr(section.VirtualAddress), sectionSz, winapi.MEM_COMMIT, winapi.PAGE_READWRITE) 364 | if dest == 0 { 365 | return nil, errors.New(fmt.Sprintf("Failed to allocate memory for section, %v", err)) 366 | } 367 | memSection.MemoryAddress = dest 368 | memSection.PeSection = *section 369 | memSection.Size = sectionSz 370 | memSections = append(memSections, memSection) 371 | } 372 | continue 373 | } 374 | dest, _ := winapi.VirtualAlloc(baseAddress+uintptr(section.VirtualAddress), section.Size, winapi.MEM_COMMIT, winapi.PAGE_READWRITE) 375 | if dest == 0 { 376 | return nil, errors.New(fmt.Sprintf("Failed to allocate memory for section, %v", err)) 377 | } 378 | dest = baseAddress + uintptr(section.VirtualAddress) 379 | var wrote uint32 380 | result, err := winapi.WriteProcessMemory(syscall.Handle(winapi.GetCurrentProcess()), dest, uintptr(unsafe.Pointer(&data[0])), section.Size, &wrote) 381 | if !result { 382 | return nil, errors.New(fmt.Sprintf("Failed to write section to memory%v", err)) 383 | } 384 | memSection.MemoryAddress = dest 385 | memSection.Size = section.Size 386 | memSection.PeSection = *section 387 | memSections = append(memSections, memSection) 388 | } 389 | return memSections, nil 390 | } 391 | 392 | func BaseRelocate(addressDiff uint64, baseAddress uintptr, peHeader pe.OptionalHeader64) error { 393 | dataDirectory := peHeader.DataDirectory 394 | if len(dataDirectory) == 0 { 395 | return errors.New("Data Directory Empty") 396 | } 397 | directory := dataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC] 398 | baseRelocDirectoryPtr := unsafe.Pointer(baseAddress + uintptr(directory.VirtualAddress)) 399 | relocate := (*IMAGE_BASE_RELOCATION)(baseRelocDirectoryPtr) 400 | if directory.Size == 0 { 401 | return errors.New("Something went wrong, if directory size is zero we shouldnt need to relocate.") 402 | } 403 | for relocate.VirtualAddress > 0 { 404 | destinationAddress := baseAddress + uintptr(relocate.VirtualAddress) 405 | relocationInfoPtr := unsafe.Pointer(OffsetPointer(uintptr(unsafe.Pointer(relocate)), unsafe.Sizeof(IMAGE_BASE_RELOCATION{}))) 406 | relocationInfo := (*uint16)(relocationInfoPtr) 407 | var i uint32 408 | for i = 0; i < ((relocate.SizeOfBlock - 8) / 2); i++ { 409 | relocType := *relocationInfo >> 12 410 | offset := *relocationInfo & 0xfff 411 | switch relocType { 412 | case IMAGE_REL_BASED_ABSOLUTE: 413 | break 414 | case IMAGE_REL_BASED_HIGHLOW: 415 | patchAddressHl := (*uint32)(unsafe.Pointer(destinationAddress + uintptr(offset))) 416 | *patchAddressHl += uint32(addressDiff) 417 | break 418 | case IMAGE_REL_BASED_DIR64: 419 | patchAddress64 := (*uint64)(unsafe.Pointer(destinationAddress + uintptr(offset))) 420 | *patchAddress64 += uint64(addressDiff) 421 | break 422 | default: 423 | break 424 | } 425 | relocationInfo = (*uint16)(unsafe.Pointer(uintptr(unsafe.Pointer(relocationInfo)) + 2)) 426 | } 427 | if relocate.VirtualAddress < 1 { 428 | break 429 | } 430 | relocate = (*IMAGE_BASE_RELOCATION)(unsafe.Pointer(uintptr(unsafe.Pointer(relocate)) + uintptr(relocate.SizeOfBlock))) //32 431 | 432 | } 433 | return nil 434 | } 435 | 436 | type PeType int 437 | 438 | const ( 439 | Dll PeType = iota 440 | Exe 441 | ) 442 | 443 | type RawPe struct { 444 | peType PeType 445 | rawData []byte 446 | peStruct *pe.File 447 | peHeaders *pe.OptionalHeader64 448 | alignedImageSize uint32 449 | removeHeader bool 450 | // other stuff here in the future like exports 451 | // delete header flags etcs 452 | } 453 | 454 | func NewRawPE(peT PeType, removeDOSHeaders bool, data []byte) *RawPe { 455 | return &RawPe{ 456 | peType: peT, 457 | rawData: data, 458 | peStruct: nil, 459 | } 460 | } 461 | 462 | func (r *RawPe) LoadPEFromMemory() error { 463 | buffer := bytes.NewBuffer(r.rawData) 464 | peFile, err := pe.NewFile(bytes.NewReader(buffer.Bytes())) 465 | if err != nil { 466 | log.Fatalf("Failed to load pe file %v", err) 467 | } 468 | r.peStruct = peFile 469 | if !DosHeaderCheck(r.rawData) { 470 | return errors.New("Dos header check failed.") 471 | } 472 | // only support 64 bit. 473 | r.peHeaders = r.peStruct.OptionalHeader.(*pe.OptionalHeader64) 474 | if (r.peHeaders.SectionAlignment & 1) != 0 { 475 | return (errors.New("Unknown Alignment error.")) 476 | } 477 | //alignedImgSize := AlignValueUp(r.peHeaders.SizeOfImage, uint32(PAGE_SIZE)) 478 | r.alignedImageSize = AlignValueUp(r.peHeaders.SizeOfImage, uint32(PAGE_SIZE)) 479 | if r.alignedImageSize != AlignValueUp(r.peStruct.Sections[r.peStruct.NumberOfSections-1].VirtualAddress+r.peStruct.Sections[r.peStruct.NumberOfSections-1].Size, uint32(PAGE_SIZE)) { 480 | return errors.New("Failed to align image.") 481 | } 482 | // allocating memory chunk for image. 483 | var baseAddressOfMemoryAlloc uintptr 484 | prefBaseAddr := uintptr(r.peHeaders.ImageBase) 485 | baseAddressOfMemoryAlloc, err = winapi.VirtualAlloc(uintptr(r.peHeaders.ImageBase), r.alignedImageSize, winapi.MEM_RESERVE|winapi.MEM_COMMIT, winapi.PAGE_READWRITE) 486 | if baseAddressOfMemoryAlloc == 0 { 487 | log.Println("Failed to allocate at preffered base address...Attempting to allocate anywhere else.") 488 | baseAddressOfMemoryAlloc, err = winapi.VirtualAlloc(uintptr(0), r.alignedImageSize, winapi.MEM_RESERVE|winapi.MEM_COMMIT, winapi.PAGE_READWRITE) 489 | if err != nil { 490 | return errors.New(fmt.Sprintf("Failed to allocate memory at random location %v", err)) 491 | } 492 | } 493 | // base memory chunk allocated. 494 | peHead, err := winapi.VirtualAlloc(baseAddressOfMemoryAlloc, r.peHeaders.SizeOfHeaders, winapi.MEM_COMMIT, winapi.PAGE_READWRITE) 495 | if peHead == 0 { 496 | return errors.New(fmt.Sprintf("Failed to commit memory for pe headers %v", err)) 497 | } 498 | // committed memory for pe headers. 499 | var wrote uint32 500 | if ok, err := winapi.WriteProcessMemory(syscall.Handle(winapi.GetCurrentProcess()), peHead, uintptr(unsafe.Pointer(&r.rawData[0])), r.peHeaders.SizeOfHeaders, &wrote); !ok { 501 | return errors.New(fmt.Sprintf("Failed to write pe headers to memory %v", err)) 502 | } 503 | // wrote pe headers to memory 504 | r.peHeaders.ImageBase = uint64(baseAddressOfMemoryAlloc) 505 | // updating pe header to reflect base address (just incase it changed) 506 | // now you commit sections in the memory block and copy the sections to the proper locations 507 | memSections, err := CopySectionsToMemory(r.peStruct, r.peHeaders, baseAddressOfMemoryAlloc) 508 | if err != nil { 509 | return err 510 | } 511 | //base relocations if preferred base address is doesnt match where we allocated memory 512 | baseAddressDiff := uint64(baseAddressOfMemoryAlloc - prefBaseAddr) 513 | if baseAddressDiff != 0 { 514 | if err := BaseRelocate(baseAddressDiff, baseAddressOfMemoryAlloc, *r.peHeaders); err != nil { 515 | return errors.New(fmt.Sprintf("Failed to base relocate %v", err)) 516 | } 517 | } 518 | err = CreateImportAddressTable(r.peHeaders, baseAddressOfMemoryAlloc) 519 | if err != nil { 520 | return err 521 | } 522 | err = FinalizeSections(r.peStruct, r.peHeaders, baseAddressOfMemoryAlloc, memSections) 523 | if err != nil { 524 | return err 525 | } 526 | //ExecuteTLSCallbacks TODO 527 | entryPointPtr := unsafe.Pointer(uintptr(r.peHeaders.AddressOfEntryPoint) + baseAddressOfMemoryAlloc) 528 | runtime.LockOSThread() 529 | switch r.peType { 530 | case Dll: 531 | // calling dll entry point 532 | syscall.Syscall(uintptr(entryPointPtr), 3, baseAddressOfMemoryAlloc, 1, 0) 533 | break 534 | case Exe: 535 | // calling exe entry point no args 536 | // we are not patching exitThread so when exes exit they will crash process 537 | // exe needs to call exitThread before exiting and needs to be run in seperate thread 538 | hThread, err := winapi.CreateThread(0, 0, uintptr(entryPointPtr), 0, 0, nil) 539 | if err != nil { 540 | log.Fatal(err) 541 | } 542 | windows.WaitForSingleObject(windows.Handle(hThread), windows.INFINITE) 543 | break 544 | default: 545 | return errors.New("Provided Invalid PE Type") 546 | } 547 | runtime.UnlockOSThread() 548 | return nil 549 | } 550 | 551 | func (r *RawPe) FreePeFromMemory() error { 552 | err := windows.VirtualFree(uintptr(r.peHeaders.ImageBase), uintptr(r.alignedImageSize), winapi.MEM_DECOMMIT) 553 | if err != nil { 554 | return errors.New(fmt.Sprintf("Failed to free PE memory allocation %v", err)) 555 | } 556 | return err 557 | } 558 | 559 | func DosHeaderCheck(rawPeFileData []byte) bool { 560 | dosHeaderStruct := (*ImageDOSHeader)(unsafe.Pointer(&rawPeFileData[0])) 561 | if dosHeaderStruct.Magic != IMAGE_DOS_SIGNATURE { 562 | return false 563 | } 564 | return true 565 | } 566 | -------------------------------------------------------------------------------- /pkg/winapi/kernel32.go: -------------------------------------------------------------------------------- 1 | package winapi 2 | 3 | import ( 4 | "fmt" 5 | "syscall" 6 | "unsafe" 7 | 8 | "golang.org/x/sys/windows" 9 | ) 10 | 11 | const ( 12 | HEAP_ZERO_MEMORY = 0x00000008 13 | HEAP_CREATE_ENABLE_EXECUTE = 0x00040000 14 | MEM_COMMIT = 0x00001000 15 | MEM_RESERVE = 0x00002000 16 | MEM_DECOMMIT = 0x00004000 17 | MEM_RELEASE = 0x00008000 18 | MEM_RESET = 0x00080000 19 | MEM_TOP_DOWN = 0x00100000 20 | MEM_WRITE_WATCH = 0x00200000 21 | MEM_PHYSICAL = 0x00400000 22 | MEM_RESET_UNDO = 0x01000000 23 | MEM_LARGE_PAGES = 0x20000000 24 | 25 | PAGE_NOACCESS = 0x00000001 26 | PAGE_READONLY = 0x00000002 27 | PAGE_READWRITE = 0x00000004 28 | PAGE_WRITECOPY = 0x00000008 29 | PAGE_EXECUTE = 0x00000010 30 | PAGE_EXECUTE_READ = 0x00000020 31 | PAGE_EXECUTE_READWRITE = 0x00000040 32 | PAGE_EXECUTE_WRITECOPY = 0x00000080 33 | PAGE_GUARD = 0x00000100 34 | PAGE_NOCACHE = 0x00000200 35 | PAGE_WRITECOMBINE = 0x00000400 36 | PAGE_TARGETS_INVALID = 0x40000000 37 | PAGE_TARGETS_NO_UPDATE = 0x40000000 38 | 39 | QUOTA_LIMITS_HARDWS_MIN_DISABLE = 0x00000002 40 | QUOTA_LIMITS_HARDWS_MIN_ENABLE = 0x00000001 41 | QUOTA_LIMITS_HARDWS_MAX_DISABLE = 0x00000008 42 | QUOTA_LIMITS_HARDWS_MAX_ENABLE = 0x00000004 43 | ) 44 | 45 | var ( 46 | pModKernel32 = syscall.NewLazyDLL("kernel32.dll") 47 | pGetModuleHandleW = pModKernel32.NewProc("GetModuleHandleW") 48 | pGetCurrentProcess = pModKernel32.NewProc("GetCurrentProcess") 49 | pOpenProcess = pModKernel32.NewProc("OpenProcess") 50 | pGetProcessHeap = pModKernel32.NewProc("GetProcessHeap") 51 | pHeapCreate = pModKernel32.NewProc("HeapCreate") 52 | pCreateProcess = pModKernel32.NewProc("CreateProcess") 53 | pGetExitCodeThread = pModKernel32.NewProc("GetExitCodeThread") 54 | pVirtualProtect = pModKernel32.NewProc("VirtualProtect") 55 | pVirtualProtectEx = pModKernel32.NewProc("VirtualProtectEx") 56 | pReadFile = pModKernel32.NewProc("ReadFile") 57 | pHeapAlloc = pModKernel32.NewProc("HeapAlloc") 58 | pHeapFree = pModKernel32.NewProc("HeapFree") 59 | pVirtualAlloc = pModKernel32.NewProc("VirtualAlloc") 60 | pVirtualAllocEx = pModKernel32.NewProc("VirtualAllocEx") 61 | pWriteProcessMemory = pModKernel32.NewProc("WriteProcessMemory") 62 | pReadProcessMemory = pModKernel32.NewProc("ReadProcessMemory") 63 | pCreateThread = pModKernel32.NewProc("CreateThread") 64 | pCreateRemoteThread = pModKernel32.NewProc("CreateRemoteThread") 65 | pWriteFile = pModKernel32.NewProc("WriteFile") 66 | pWaitNamedPipe = pModKernel32.NewProc("WaitNamedPipeW") 67 | pCreateFile = pModKernel32.NewProc("CreateFileW") 68 | pFlushFileBuffers = pModKernel32.NewProc("FlushFileBuffers") 69 | PGlobalLock = pModKernel32.NewProc("GlobalLock") 70 | PGlobalUnlock = pModKernel32.NewProc("GlobalUnlock") 71 | pIsBadReadPtr = pModKernel32.NewProc("IsBadReadPtr") 72 | pCreatePipe = pModKernel32.NewProc("CreatePipe") 73 | pSetStdHandle = pModKernel32.NewProc("SetStdHandle") 74 | ) 75 | 76 | func SetStdHandle(nStdHandle uint32, nHandle windows.Handle) error { 77 | r, _, err := pSetStdHandle.Call(uintptr(nStdHandle), uintptr(nHandle)) 78 | if r == 0 { 79 | return err 80 | } 81 | fmt.Println(err) 82 | return nil 83 | } 84 | 85 | func CreatePipe(hReadPipe uintptr, hWritePipe uintptr, lpPipeAttributes uintptr, nSize uint32) error { 86 | r, _, err := pCreateFile.Call(hReadPipe, hWritePipe, lpPipeAttributes, uintptr(nSize)) 87 | if r == 0 { 88 | return err 89 | } 90 | fmt.Println(err) 91 | return nil 92 | } 93 | 94 | func IsBadReadPtr(startAddr uintptr, blockSz uintptr) bool { 95 | res, _, _ := pIsBadReadPtr.Call(startAddr, blockSz) 96 | if res == 0 { 97 | return true 98 | } 99 | return false 100 | } 101 | 102 | func GetCurrentProcess() windows.Handle { 103 | hCurrentProc, _, _ := pGetCurrentProcess.Call() 104 | return windows.Handle(hCurrentProc) 105 | } 106 | 107 | func GetExitCodeThread(hThread syscall.Handle, lpExitCode *uint32) (bool, error) { 108 | res, _, err := pGetExitCodeThread.Call(uintptr(hThread), uintptr(unsafe.Pointer(lpExitCode))) 109 | if res == 0 { 110 | return false, err 111 | } 112 | return true, nil 113 | } 114 | 115 | func OpenProcess(desiredAccess uint32, inheritHandle uint32, processId uint32) (syscall.Handle, error) { 116 | procHandle, _, err := pOpenProcess.Call(uintptr(desiredAccess), uintptr(inheritHandle), uintptr(processId)) 117 | if procHandle == 0 { 118 | return 0, err 119 | } 120 | return syscall.Handle(procHandle), nil 121 | } 122 | 123 | func CreateRemoteThread(hProcess syscall.Handle, lpThreadAttributes uintptr, dwStackSz uint32, lpStartAddress uintptr, lpParameteter uintptr, dwCreationFlags uint32, lpThreadId *uint32) (syscall.Handle, error) { 124 | thread, _, err := pCreateRemoteThread.Call( 125 | uintptr(hProcess), 126 | lpThreadAttributes, 127 | uintptr(dwStackSz), 128 | lpStartAddress, 129 | lpParameteter, 130 | uintptr(dwCreationFlags), 131 | uintptr(unsafe.Pointer(lpThreadId))) 132 | if thread == 0 { 133 | return 0, err 134 | } 135 | return syscall.Handle(thread), nil 136 | } 137 | 138 | func CreateThread(lpThreadAttributes uintptr, dwStackSz uint32, lpStartAddress uintptr, lpParameteter uintptr, dwCreationFlags uint32, lpThreadId *uint32) (syscall.Handle, error) { 139 | thread, _, err := pCreateThread.Call( 140 | lpThreadAttributes, 141 | uintptr(dwStackSz), 142 | lpStartAddress, 143 | lpParameteter, 144 | uintptr(dwCreationFlags), 145 | uintptr(unsafe.Pointer(lpThreadId))) 146 | if thread == 0 { 147 | return 0, err 148 | } 149 | return syscall.Handle(thread), nil 150 | } 151 | 152 | func WriteProcessMemory(hProcess syscall.Handle, lpAddresss uintptr, lpBuffer uintptr, nSize uint32, lpNumberOfBytesWritten *uint32) (bool, error) { 153 | writeMem, _, err := pWriteProcessMemory.Call( 154 | uintptr(hProcess), 155 | lpAddresss, 156 | lpBuffer, 157 | uintptr(nSize), 158 | uintptr(unsafe.Pointer(lpNumberOfBytesWritten))) 159 | if writeMem == 0 { 160 | return false, err 161 | } 162 | return true, nil 163 | } 164 | 165 | func VirtualAlloc(lpAddress uintptr, dwSize uint32, allocationType uint32, flProtect uint32) (uintptr, error) { 166 | lpBaseAddress, _, err := pVirtualAlloc.Call( 167 | lpAddress, 168 | uintptr(dwSize), 169 | uintptr(allocationType), 170 | uintptr(flProtect)) 171 | if lpBaseAddress == 0 { 172 | return 0, err 173 | } 174 | return lpBaseAddress, nil 175 | } 176 | 177 | func VirtualAllocEx(hProcess syscall.Handle, lpAddress uintptr, dwSize uint32, allocationType uint32, flProtect uint32) (uintptr, error) { 178 | lpBaseAddress, _, err := pVirtualAllocEx.Call( 179 | uintptr(hProcess), 180 | lpAddress, 181 | uintptr(dwSize), 182 | uintptr(allocationType), 183 | uintptr(flProtect)) 184 | if lpBaseAddress == 0 { 185 | return 0, err 186 | } 187 | return lpBaseAddress, nil 188 | } 189 | 190 | func VirtualProtectEx(hProcess syscall.Handle, lpAddress uintptr, dwSize uint32, flNewProtect uint32, lpflOldProtect *uint32) (bool, error) { 191 | res, _, err := pVirtualProtectEx.Call(uintptr(hProcess), lpAddress, uintptr(dwSize), uintptr(flNewProtect), uintptr(unsafe.Pointer(lpflOldProtect))) 192 | if res == 0 { 193 | return false, err 194 | } 195 | return true, nil 196 | } 197 | 198 | func HeapCreate(flOptions uint32, dwInitialSz uint32, dwMaximumSz uint32) (syscall.Handle, error) { 199 | heap, _, err := pHeapCreate.Call(uintptr(flOptions), uintptr(dwInitialSz), 0) 200 | if heap == 0 { 201 | return 0, err 202 | } 203 | return syscall.Handle(heap), nil 204 | } 205 | 206 | func HeapAlloc(hHeap syscall.Handle, dwFlags uint32, dwBytes uint32) (uintptr, error) { 207 | lpAddr, _, err := pHeapAlloc.Call(uintptr(hHeap), uintptr(dwFlags), uintptr(dwBytes)) 208 | if lpAddr == 0 { 209 | return 0, err 210 | } 211 | return lpAddr, nil 212 | } 213 | 214 | func ReadProcessMemory(hProcess syscall.Handle, lpBaseAddress uintptr, lpBuffer uintptr, nSize uint32, lpNumberOfBytesRead *uint32) (bool, error) { 215 | ok, _, err := pReadProcessMemory.Call( 216 | uintptr(hProcess), 217 | lpBaseAddress, 218 | lpBuffer, 219 | uintptr(nSize), 220 | uintptr(unsafe.Pointer(lpNumberOfBytesRead))) 221 | if ok == 0 { 222 | return false, err 223 | } 224 | return true, nil 225 | } 226 | 227 | func ReadFile(handle syscall.Handle, lpBuffer uintptr, bytesToRead uint32, numberOfBytesRead *uint32, lpOverlapped uintptr) bool { 228 | result, _, _ := pWriteFile.Call(uintptr(handle), lpBuffer, uintptr(bytesToRead), uintptr(unsafe.Pointer(numberOfBytesRead)), lpOverlapped) 229 | if result == 0 { 230 | return false 231 | } 232 | return true 233 | } 234 | 235 | func WriteFile(handle syscall.Handle, lpBuffer uintptr, bytesToWrite uint32, numberOfBytesWritten *uint32, lpOverlapped uintptr) bool { 236 | result, _, _ := pWriteFile.Call(uintptr(handle), lpBuffer, uintptr(bytesToWrite), uintptr(unsafe.Pointer(numberOfBytesWritten)), lpOverlapped) 237 | if result == 0 { 238 | return false 239 | } 240 | return true 241 | } 242 | 243 | func CreateFile(lpFileName string, desiredAccess uint32, dwShareMode uint32, lpSecuityAttributes uintptr, dwCreationDisposition uint32, dwFlags uint32, hTemplateFile uintptr) uintptr { 244 | lpFileNamePtr, err := syscall.UTF16PtrFromString(lpFileName) 245 | if err != nil { 246 | return 0 247 | } 248 | handle, _, _ := pCreateFile.Call(uintptr(unsafe.Pointer(lpFileNamePtr)), uintptr(desiredAccess), uintptr(dwShareMode), lpSecuityAttributes, uintptr(dwCreationDisposition), uintptr(dwFlags), hTemplateFile) 249 | if handle == 0 { 250 | return 0 251 | } 252 | return handle 253 | } 254 | 255 | func WaitNamedPipe(pipeName string, timout uint32) int { 256 | ptr, err := syscall.UTF16PtrFromString(pipeName) 257 | if err != nil { 258 | return 0 259 | } 260 | _, _, _ = pWaitNamedPipe.Call(uintptr(unsafe.Pointer(ptr)), uintptr(timout)) 261 | return 1 262 | } 263 | 264 | func FlushFileBuffers(handle syscall.Handle) bool { 265 | res, _, _ := pFlushFileBuffers.Call(uintptr(handle)) 266 | return res != 0 267 | } 268 | --------------------------------------------------------------------------------