[RedDev Series #2] Thoughts on Writing PE Shellcode

GhouLSec
5 min readFeb 7, 2020

--

I had written some windows PE shellcode since last year. However, when i try to fit the shellcode into my main windows code it will crash sometimes (the shellcode runs smoothly but it exits with error). Picture below shows the issue that i mentioned above.

Shellcode crashed :(
Shellcode without crash :)

After the investigation by using the debugger, i found out there is an esp repositioning issue when the program starts to exit. This cause the program returns to a unknown memory space.

Exit with error (Cause Analysis)

First, the program is push all the registers (eax, ebx, ecx, edx, esi, edi, ebp) into the stack, actually push ebp itself is enough. You can see the register’s value at the bottom right of the picture from 0x79fb30 til 0x79fb18. Just keep an eye on these value.

Pushing all registers into stack

After the program ready to return to the main code. It will adjust (by add esp, 64) the esp value to ensure the eip later will points to the return to the correct address.

The ret instruction means eip = esp
(You may refer to the Wiki link below for more details)

Adjusting the esp value

After series of pop instruction and reaches the retinstruction, the esp is now pointed to some unknown address/inaccessible memory address which will crashed later.

Return value, eip = esp

In order to fix it, we need to subtract the correct value to the esp , so eip later will points to the correct memory address.

Exit With no Crash (Analysis)

The difference here is just the substraction value for the esp . This time, the code will return to the main code and exit gracefully (Shown in 3rd picture below).

Pushing all registers into stack
Adjusting the esp value

From here you will see the ret instruction later will reutrn to the main code (cppplayground_v1._main), with add esp, 44. The program then exit with no error :D

Return value, eip = esp

Another easier way to exit the program without crash is using exitProcess() from Windows API. It’s kind of force exit somehow.

Exit Program with ExitProcess() Windows API

Call the ExitProcess() by pushing the string into the stack and then get the function call address. You can find more details in the references link below.

push    0x737365 
push 0x636f7250
push 0x74697845

Another way to push value into stack in assembly

Using jmp + call + constant value . It means that the program will jump to the function and call it. As you know, the constant appears before the function will push in the stack and access by the function e.g.
push ebp; call <func_name>.
By utilising this method, you can make a trick on it to push a constant into a stack.

truncated ...   
push edx ; LPPROCESS_INFORMATION lpProcessInformation
push edx ; LPSTARTUPINFO lpStartupInfo
push ebx ; LPCTSTR lpCurrentDirectory
push ebx ; LPVOID lpEnvironment
push ebx ; DWORD dwCreationFlags
push ebx ; BOOL bInheritHandles
push ebx ; LPSECURITY_ATTRIBUTES lpThreadAttributes
push ebx ; LPSECURITY_ATTRIBUTES lpProcessAttributes
jmp variable
lol: ; LPTSTR lpCommandLine after call will push args into stacks
push ebx ; LPCTSTR lpApplicationName
call eax
add esp, 0xEC
truncated ...
variable:
call lol
command db "cmd /c notepad",0 ; cmd args[1] of CreateProcessA
Before function call (ESP)
After function call (The constant pushed into ESP)

As for how to write basic a windows PE shellcode, you may refer to the link below for details. Cheers!

All the shellcode in this story is written using NASM. As how to compile it into windows PE file, I’m using the command below.
(Install NASM for nasm and GNU for ld)

nasm -f win32 <file.asm> -o <file.o> && ld <file.o> -o <file.exe>

How to detect a windows shellcode in a PE file

Basically, I will find these instruction in the assembly code to determine whether it is a windows based shellcode or not. Original source here.

mov ebx, fs:0x30	; Get pointer to PEB
mov ebx, [ebx + 0x0C] ; Get pointer to PEB_LDR_DATA
mov ebx, [ebx + 0x14] ; Get pointer to first entry in InMemoryOrderModuleList
mov ebx, [ebx] ; Get pointer to second (ntdll.dll) entry in InMemoryOrderModuleList
mov ebx, [ebx] ; Get pointer to third (kernel32.dll) entry in InMemoryOrderModuleList
mov ebx, [ebx + 0x10] ; Get kernel32.dll base address

Hope it helps as there are some windows malware uses this kind of shellcode too.

References

--

--

GhouLSec
GhouLSec

No responses yet