|
| 1 | +# Golang (Windows) Shellcode Runner |
| 2 | + |
| 3 | +A small project I put together to help me understand the concepts behind process injection, hollowing and running lots of win32 API calls directly from Go using just the syscall library. |
| 4 | + |
| 5 | +Hopefully the code is easy to understand, even though its all this low level nonsense with uintptr etc. |
| 6 | + |
| 7 | +## Specifying Shellcode |
| 8 | + |
| 9 | +Shell code to run can be specified one of three ways: directly on the args via `-i`, from a file via `-f`, or from a url with `-u`. This last option is useful as the shellcode will never be written to disk, possibly bypassing anti-virus. |
| 10 | + |
| 11 | +> Shellcode this has been tested with on Windows 11 was created with msfvenom: `msfvenom -p windows/x64/shell_reverse_tcp LHOST=127.0.0.1 PORT=4444 EXITFUNC=thread -f raw -o msfvenom_reversetcp_4444.txt` and then it was run via `-f msfvenom_reversetcp_4444.txt`. |
| 12 | +
|
| 13 | +## Modes of Operation |
| 14 | + |
| 15 | +The shellcode can be run in one of three ways: directly, via process injection, or via process hollowing |
| 16 | + |
| 17 | +### Direct Execution |
| 18 | + |
| 19 | +This is the default option if `-p` or `-e` are not specified. The following APIs will be invoked: |
| 20 | + |
| 21 | +- **VirtualAlloc** to create some memory space in the runner process |
| 22 | +- **RtlMoveMemory** to copy the shellcode into this space |
| 23 | +- **CreateThread** to start a thread to run the shellcode |
| 24 | +- **WaitForSingleObject** to keep the process alive until the thread exits |
| 25 | + |
| 26 | +This mode can be useful for debugging shellcode; add an `int3` to your assembly, attach to the runner process and go! |
| 27 | + |
| 28 | +## Process Injection |
| 29 | + |
| 30 | +Provide a process ID with `-p`. The process must be one you either created or otherwise have the writes to mess with. I tested with notepad, grabbing its PID from task manager. |
| 31 | + |
| 32 | +The following APIs are invoked to run the shellcode inside the target process: |
| 33 | + |
| 34 | +- **OpenProcess** to get the process information |
| 35 | +- **VirtualAllocEx** to allocate some memory inside the process for the shellcode |
| 36 | +- **WriteProcessMemory** to write the shellcode inside this space |
| 37 | +- **CreateRemoteThread** to tell the process to run the shellcode with a new thread |
| 38 | + |
| 39 | +The shellcode runner (this go project) doesn't need to be kept alive in this mode, and so will exit. |
| 40 | + |
| 41 | +## Process Hollowing |
| 42 | + |
| 43 | +Provide an executable path with `-e` - I tested using svchost, with the path `c:\\windows\\system32\\svchost.exe`. svchost in particular, if not run as system, will immediately exit when run. But process hollowing launches it suspended and then replaces its code with the shellcode, like a parasite :D |
| 44 | + |
| 45 | +The following APIs are invoked in this mode: |
| 46 | + |
| 47 | +- **CreateProcessA** to start a new process. The flag 0x4 is passed to start it suspended, just before it would run its code. |
| 48 | +- **ZwQueryInformationProcess** to get the address of the process's PEB (process environment block) |
| 49 | +- **ReadProcessMemory** to query the PEB for the image base address |
| 50 | +- **ReadProcessMemory** again to read from the image base address (loading in the PE header for example) |
| 51 | +- **WriteProcessMemory** to overwrite the memory from the code base address with shellcode |
| 52 | +- **ResumeThread** to restart the suspended process, triggering the shellcode. |
| 53 | + |
| 54 | +As with process injection, the go shellcode runner will exit once the thread is resumed. |
0 commit comments