shellcode编写要点:
因为shellcode作为一段能独立运行的代码,对于一些使用的函数、变量等有一定的要求。
1、不能使用全局变量
2、不能使用常量字符串
3、编译时使用release编译
4、需要指定出程序入口点
5、对于一些需要接收外部输入或是创建某些进程或线程时,需要手动创建内存进行保存
简单例子,实现弹窗:
动态加载api,通过实现getprocaddress对指定dll的函数进行加载,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | FARPROC getProcAddress(HMODULE hModuleBase){ PIMAGE_DOS_HEADER lpDosHeader = (PIMAGE_DOS_HEADER)hModuleBase; PIMAGE_NT_HEADERS32 lpNtHeader = (PIMAGE_NT_HEADERS)((DWORD)hModuleBase + lpDosHeader->e_lfanew); if (!lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size) { return NULL; } if (!lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress) { return NULL; } PIMAGE_EXPORT_DIRECTORY lpExports = (PIMAGE_EXPORT_DIRECTORY)((DWORD)hModuleBase + (DWORD)lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); PDWORD lpdwFunName = (PDWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfNames); PWORD lpword = (PWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfNameOrdinals); PDWORD lpdwFunAddr = (PDWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfFunctions); DWORD dwLoop = 0; FARPROC pRet = NULL; for (; dwLoop <= lpExports->NumberOfNames - 1; dwLoop++) { char* pFunName = (char*)(lpdwFunName[dwLoop] + (DWORD)hModuleBase); if (pFunName[0] == 'G' && pFunName[1] == 'e' && pFunName[2] == 't' && pFunName[3] == 'P' && pFunName[4] == 'r' && pFunName[5] == 'o' && pFunName[6] == 'c' && pFunName[7] == 'A' && pFunName[8] == 'd' && pFunName[9] == 'd' && pFunName[10] == 'r' && pFunName[11] == 'e' && pFunName[12] == 's' && pFunName[13] == 's') { pRet = (FARPROC)(lpdwFunAddr[lpword[dwLoop]] + (DWORD)hModuleBase); break; } } return pRet;} |
查找kernel32基地址:



1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // 根据反编译几次来查找kernel32地址__declspec(naked) DWORD getKernel32(){ __asm { mov eax, fs: [30h] mov eax, [eax + 0ch] mov eax, [eax + 14h] mov eax, [eax] mov eax, [eax] mov eax, [eax + 10h] ret }} |
去掉全局变量
1 2 3 | typedef HMODULE(WINAPI* FN_LoadLibraryW)(LPCWSTR lpLibFileName); char xyLoadLibraryW[] = { 'L','o','a','d','L','i','b','r','a','r','y','W',0 }; FN_LoadLibraryW fn_LoadLibraryW = (FN_LoadLibraryW)fn_GetProcAddress((HMODULE)getKernel32(), xyLoadLibraryW); |
指定入口点(如果不指定编译器会加一些别的代码进来,导致shellcode非常大)

生成shellcode:
release模式编译好exe后,以16进制打开,找到.text节数据

.text节从0x00000400开始,那么现在就找一段连续的数据,不一定要到最大地址。

选中后以C格式化方式复制下载,放到加载器中。
1 2 3 4 5 6 7 8 9 10 11 12 | #include <windows.h>unsigned char a[] = { 0x55, 0x8B, 0xEC, 0x83, 0xEC, 0x5C, 0x53, 0x56, 0x57, 0xE8, 0x72, 0x01, 0x00, 0x00, 0x8B, 0xD0, 0x33, 0xDB, 0x8B, 0x42, 0x3C, 0x39, 0x5C, 0x10, 0x7C, 0x0F, 0x84, 0x9F, 0x00, 0x00, 0x00, 0x8B, 0x74, 0x10, 0x78, 0x85, 0xF6, 0x0F, 0x84, 0x93, 0x00, 0x00, 0x00, 0x8B, 0x44, 0x16, 0x24, 0x33, 0xC9, 0x8B, 0x7C, 0x16, 0x20, 0x03, 0xC2, 0x89, 0x45, 0xFC, 0x03, 0xFA, 0x8B, 0x44, 0x16, 0x1C, 0x8B, 0x74, 0x16, 0x18, 0x03, 0xC2, 0x89, 0x45, 0xF8, 0x4E, 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00, 0x8B, 0x04, 0x8F, 0x03, 0xC2, 0x80, 0x38, 0x47, 0x75, 0x4E, 0x80, 0x78, 0x01, 0x65, 0x75, 0x48, 0x80, 0x78, 0x02, 0x74, 0x75, 0x42, 0x80, 0x78, 0x03, 0x50, 0x75, 0x3C, 0x80, 0x78, 0x04, 0x72, 0x75, 0x36, 0x80, 0x78, 0x05, 0x6F, 0x75, 0x30, 0x80, 0x78, 0x06, 0x63, 0x75, 0x2A, 0x80, 0x78, 0x07, 0x41, 0x75, 0x24, 0x80, 0x78, 0x08, 0x64, 0x75, 0x1E, 0x80, 0x78, 0x09, 0x64, 0x75, 0x18, 0x80, 0x78, 0x0A, 0x72, 0x75, 0x12, 0x80, 0x78, 0x0B, 0x65, 0x75, 0x0C, 0x80, 0x78, 0x0C, 0x73, 0x75, 0x06, 0x80, 0x78, 0x0D, 0x73, 0x74, 0x07, 0x41, 0x3B, 0xCE, 0x76, 0xA3, 0xEB, 0x0F, 0x8B, 0x45, 0xFC, 0x8B, 0x5D, 0xF8, 0x0F, 0xB7, 0x04, 0x48, 0x8B, 0x1C, 0x83, 0x03, 0xDA, 0x8D, 0x45, 0xD0, 0xC7, 0x45, 0xD0, 0x4C, 0x6F, 0x61, 0x64, 0x50, 0xC7, 0x45, 0xD4, 0x4C, 0x69, 0x62, 0x72, 0xC7, 0x45, 0xD8, 0x61, 0x72, 0x79, 0x57, 0xC6, 0x45, 0xDC, 0x00, 0xE8, 0xA0, 0x00, 0x00, 0x00, 0x50, 0xFF, 0xD3, 0x33, 0xC9, 0xC7, 0x45, 0xA4, 0x75, 0x00, 0x73, 0x00, 0x66, 0x89, 0x4D, 0xB8, 0x8D, 0x4D, 0xE0, 0x51, 0x8D, 0x4D, 0xA4, 0xC7, 0x45, 0xA8, 0x65, 0x00, 0x72, 0x00, 0x51, 0xC7, 0x45, 0xAC, 0x33, 0x00, 0x32, 0x00, 0xC7, 0x45, 0xB0, 0x2E, 0x00, 0x64, 0x00, 0xC7, 0x45, 0xB4, 0x6C, 0x00, 0x6C, 0x00, 0xC7, 0x45, 0xE0, 0x4D, 0x65, 0x73, 0x73, 0xC7, 0x45, 0xE4, 0x61, 0x67, 0x65, 0x42, 0xC7, 0x45, 0xE8, 0x6F, 0x78, 0x57, 0x00, 0xFF, 0xD0, 0x50, 0xFF, 0xD3, 0x33, 0xC9, 0xC7, 0x45, 0xBC, 0x53, 0x00, 0x68, 0x00, 0x51, 0x66, 0x89, 0x4D, 0xCC, 0x66, 0x89, 0x4D, 0xF4, 0x8D, 0x4D, 0xEC, 0x51, 0x8D, 0x4D, 0xBC, 0xC7, 0x45, 0xC0, 0x65, 0x00, 0x6C, 0x00, 0x51, 0x6A, 0x00, 0xC7, 0x45, 0xC4, 0x6C, 0x00, 0x63, 0x00, 0xC7, 0x45, 0xC8, 0x6F, 0x00, 0x64, 0x00, 0xC7, 0x45, 0xEC, 0x54, 0x00, 0x45, 0x00, 0xC7, 0x45, 0xF0, 0x53, 0x00, 0x54, 0x00, 0xFF, 0xD0, 0x5F, 0x5E, 0x33, 0xC0, 0x5B, 0x8B, 0xE5, 0x5D, 0xC3, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x64, 0xA1, 0x30, 0x00, 0x00, 0x00, 0x8B, 0x40, 0x0C, 0x8B, 0x40, 0x14, 0x8B, 0x00, 0x8B, 0x00, 0x8B, 0x40, 0x10, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };int EntryMain() { DWORD temp; //创建内存 VirtualProtect(a, sizeof(a), PAGE_EXECUTE, &temp); //执行shellcode ((void(*)())a)();} |
特别注意:
如果你需要实现的是例如一个通信shellcode,那么你应该对接收数据手动的创建一块内存进行存储,如果只是简单的动态加载了api,但是你现在是以shellcode的方式进行执行,会引发内存异常,这是因为程序在执行这块数据的时候没有找到。
1 2 3 4 5 6 7 8 | //部分代码//接收数据if (Myrecv(sock, (char*)&payloadLen, sizeof(payloadLen), 0) != sizeof(payloadLen)){ goto Fail; }//划分内存,用于存储接收数据char* orig_buffer = (char*)MyVirtualAlloc(NULL, payloadLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE); |