Shellcode - Module Unloader

  • Posted on: 3 August 2014
  • By: siteadm

Have you ever dealt with malwares that inject their DLLs into other processes? Sometimes they inject their DLL into some critical processes like csrss.exe (like recent Soraya malware), you are in middle of a hundred breakpoint placed Ollydbg, several IDAs loaded and you are deep in analysis, you just can't restart computer and you can't let malware running in csrss.exe. So I decided to write a basic shellcode to unload any given DLL (module), so I can inject this shellcode into infected process to unload malware or any DLL.

I wrote this basic shellcode, then I wrote a code to inject shellcodes and execute them in any given process. Now I can easily disinfect any process. It will be useful for different purposes, but I have my own uses.

In this post I'll mostly talk about shellcode part, later I'll publish and explain ProcModUnload app I wrote.

I did my best to make this shellcode as portable as possible, so I can use it in Win 7 and XP. Also keep in mind, as this shellcode is written for ProcModUnload app, it does have some unnecessary paddings.

I personally use nasm for compiling shellcodes, so you can download it from here.
Also for first part of shellcode I used some code from here with a little modification.

At very beginning of our shellcode, we need to tell to compiler we are going to compile a 32 bit code, so we add this lines at very beginning of our asm file:

[SECTION .text]

As first step we need to find kernel32.dll module address, then we have to resolve addresses of APIs we need to call:

xor ecx,ecx
mov eax,[fs:ecx+0x30]
mov eax,[eax+0xc]
mov esi,[eax+0x14]
xchg eax,esi
mov ebx,[eax+0x10]
mov edx,[ebx+0x3c]
add edx,ebx
mov edx,[edx+0x78]
add edx,ebx
mov esi,[edx+0x20]
add esi,ebx
xor ecx,ecx
xor ecx,ecx
mov eax,[fs:ecx+0x30]
mov eax,[eax+0xc] ; EAX = PEB->Ldr
mov esi,[eax+0x14] ; ESI = PEB->Ldr.InMemOrder
lodsd ; EAX = Second module
xchg eax,esi ; EAX = ESI, ESI = EAX
lodsd ; EAX = Third (kernel32)
mov ebx,[eax+0x10] ; EBX = Base address
mov edx,[ebx+0x3c] ; EDX = DOS->e_lfanew
add edx,ebx ; EDX = PE Header
mov edx,[edx+0x78] ; EDX = Offset export table
add edx,ebx ; EDX = Export table
mov esi,[edx+0x20] ; ESI = Offset names table
add esi,ebx ; ESI = Names table
xor ecx,ecx ; EXC = 0

As you can see this code will find export table and names table of kernel32, it's generic way and widely used.
Now we need to loop through names table to find GetProcAddress function:

inc ecx ; Loop for each function
add eax,ebx ; Loop untill function name
cmp dword [eax],0x50746547 ; GetP
jnz tryagain
cmp dword [eax+0x4],0x41636f72 ; rocA
jnz tryagain
cmp dword [eax+0x8],0x65726464 ; ddre
jnz tryagain
mov esi,[edx+0x24] ; ESI = Offset ordinals
add esi,ebx ; ESI = Ordinals table
mov cx,[esi+ecx*2] ; CX = Number of function
dec ecx
mov esi,[edx+0x1c] ; ESI = Offset address table
add esi,ebx ; ESI = Address table
mov edx,[esi+ecx*4] ; EDX = Pointer(offset)
add edx,ebx ; EDX = GetProcAddress

Now we have GetProcAddress in EDX register. Without changing it's value, we should call it with parameter of first API we need. As first step we'll resolve GetModuleHandleA API address:

xor ecx,ecx ; ECX = 0
push ebx ; Kernel32 base address
push edx ; GetProcAddress
push ecx ; 0
push dword 0x41656C64 ; dleA
push dword 0x6E614865 ; eHan
push dword 0x6C75646F ; odul
push dword 0x4D746547 ; Getm
push esp ; "GetModuleHandleA"
push ebx ; Kernel32 base address
call edx ; GetProcAddress(LL)

As you can see we have to insert GetModuleHandleA string into stack to pass it as argument. The method is simple, we split string into 4 bytes, then we reverse them, then we push each 4 byte into stack. So for example, for GetModuleHandleA we split it to 4 bytes: GetM odul eHan dleA then we reverse each part, which will become MteG ludo naHe dleA. Finally we push them in order into stack, so we do: PUSH Mteg and PUSH ludo ....

Important point 1: First char to push should be 0, it will be sign of end of string.

Important point 2: We can't call API after PUSHes. They are in stack, but we need to pass address of this string in stack to API, so we call PUSH ESP to push address of stack pointer in stack itself, now we can call the API

Now we have GetModuleHandleA function's address in EAX register, so we do


to store it.
Next step is resolving FreeLibrary function address in kernel32:

PUSH 0x00000000 ; 0
PUSH 0x00797261 ; ary
PUSH 0x7262694C ; Libr
PUSH 0x65657246 ; Free
PUSH ESP ; "FreeLibrary"
MOV EAX,[ESP+0x30] ; Kernel32 base address
CALL [ESP+0x30] ; GetProcAddress

Again we push "FreeLibrary" string into stack, then we push kernel32 base address into stack and finally we call GetProcAddress. C equivalent of this would be
GetProcAddress(hKernelBase, "FreeLibrary");

Now we got FreeLibrary API address in EAX register, we push it again into stack using PUSH EAX and as final step, we'll try to call FreeLibrary on malware dll:

PUSH 0x00000000 ; place holder
PUSH 0x00000000
PUSH 0x00000000
PUSH 0x00000000
PUSH 0x00000000
PUSH 0x00000000
PUSH 0x00000000
PUSH 0x00000000
PUSH 0x00000000
PUSH 0x00000000
PUSH 0x00000000
PUSH 0x00000000
PUSH 0x00000000
PUSH 0x00000000
PUSH 0x00000000
PUSH 0x00000000
PUSH 0x00000000
PUSH 0x00000000
PUSH 0x00000000
PUSH 0x00000000
PUSH 0x00000000
PUSH 0x00000000
PUSH 0x0000006C
PUSH 0x6C642E32
PUSH 0x336D6D69
PUSH ESP ; imm32.dll, it is here just for testing
CALL [ESP+0x7C] ; GetModuleHandleA
PUSH EAX ; Handle to library to be unloaded
CALL [ESP+0x68] ; FreeLibrary

So I know proper syntax is PUSH 0, instead of PUSH 0x00000000, I just wanted to keep 4 bytes format for my own uses. Also imm32.dll string is just for testing, it's a legit windows DLL, but just for testing I unload this innocent DLL. So we push its address into stack, we call GetModuleHandleA to get handle to this DLL loaded in memory and finally we call FreeLibrary and imm32.dll shouldn't be in memory anymore.

So I put whole shellcode together as one piece here.

You can compile it with:

nasm -f bin shellcode.asm -o shellcodebin

In next post, I'll share automation tool I created using this shellcode to unload any dll from any process.

Add new comment

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.
Enter the characters shown in the image.