An antivirus (AV) checks which DLLs or functions our binary is calling. These calls can serve as indicators to determine whether our binary is malicious or not.
Function call obfuscation is a technique used to conceal the DLLs or external functions that will be called during runtime. This can be achieved using standard Windows API functions.
handle = GetModuleHandle("library.dll");
GetProcAddress(handle, "MyFunction");
The following is an example of a program with the VirtualAlloc
function:
#include <windows.h>
#include <stdio.h>
int main() {
void * valloc;
valloc = VirtualAlloc(0, 1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
printf("valloc -> 0x%p", valloc);
return 0;
}
If we check the imports we can see that VirtualAlloc is on the Import Address Table (IAT).
c:\Tools\DropIsec\test>dumpbin /imports test.exe
Microsoft (R) COFF/PE Dumper Version 9.00.30729.01
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file test.exe
File Type: EXECUTABLE IMAGE
Section contains the following imports:
KERNEL32.dll
14000A000 Import Address Table
14000BCD8 Import Name Table
0 time date stamp
0 Index of first forwarder reference
4F8 VirtualAlloc
18C GetCommandLineA
...
88 CreateFileA
Summary
4000 .data
1000 .pdata
3000 .rdata
9000 .text
The idea of Function Call Obfuscation is to call the function without being on the IAT.
We can search the function pointer with GetModuleHandle
and GetProcAddress
.
Find the decorator
First we need to find the sintax of the WinAPI on microsoft documentation.
Note: Search for
<winapi> msdns
.
LPVOID VirtualAlloc(
[in, optional] LPVOID lpAddress,
[in] SIZE_T dwSize,
[in] DWORD flAllocationType,
[in] DWORD flProtect
);
And check in requirements section in which dll is implemented, in that case in kernell.dll
.
Obfuscate
Create a pointer global variable which will store the address of the WINAPI and avoid using the literal string of the WINAPI (encrypt or obfuscate it).
typedef LPVOID (WINAPI * t_VirtualAlloc)(
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect
);
We need to find the address and use the new pointer.
t_VirtualAlloc pVirtualAlloc;
pVirtualAlloc = (t_VirtualAlloc)GetProcAddress(GetModuleHandle("kernel32.dll"), "VirtualAlloc");
valloc = pVirtualAlloc(0, 1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
Now VirtualAlloc
is not present in the IAT but yes as a hardcoded string.
c:\Tools\DropIsec\test>c:\Tools\Sysinternals\strings.exe -n 8 test.exe | findstr /i "virtual"
VirtualAlloc
pVirtualAlloc -> 0x%p
- pure virtual function call
RtlVirtualUnwind
We can encrypt or obfuscate it:
char sVirtualAlloc[] = {'V','i','r','t','u','a','l','A','l','l','o','c', 0x0};
pVirtualAlloc = (t_VirtualAlloc)GetProcAddress(GetModuleHandle("kernel32.dll"), sVirtualAlloc);
Final code:
#include <windows.h>
#include <stdio.h>
typedef LPVOID (WINAPI * t_VirtualAlloc)(
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect
);
int main() {
void * valloc;
t_VirtualAlloc pVirtualAlloc;
char sVirtualAlloc[] = {'V','i','r','t','u','a','l','A','l','l','o','c'};
pVirtualAlloc = (t_VirtualAlloc)GetProcAddress(GetModuleHandle("kernel32.dll"), sVirtualAlloc);
valloc = pVirtualAlloc(0, 1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
printf("valloc -> 0x%p\n", valloc);
printf("pVirtualAlloc -> 0x%p\n", pVirtualAlloc);
return 0;
}
Clean results:
c:\Tools\test>dumpbin /imports test.exe | findstr /i "virtual"
426 RtlVirtualUnwind
c:\Tools\test>strings.exe -n 8 test.exe | findstr /i "virtual"
pVirtualAlloc -> 0x%p
- pure virtual function call
RtlVirtualUnwind