写在前面的话:
ShellCode是一门艺术,就像围棋手门追求的“神之一手”,今天就来初探一下这让人疯狂的艺术;
零、代码0
相信手写opcode,目前很少有人干了,其实,也确实已经没有这个必要了,毕竟,汇编引擎帮我们干了不少事;
如果直接贴出来一堆二进制出来,相信不少人都会迷惑,因此,我们还是从汇编入手了;
先说下代码的功能,就是简单的谈了个框,但是简单的事情背后,也是有内涵的;
0.代码中用到的API,能随便用吗?考虑到ShellCode的Context,如果目标没有导入对应的DLL怎么办;
1.没有导入,不会自己加载吗?那么问题就来了,加载DLL不也需要函数吗,这个API哪里来;
2.ShellCode里,用到的变量的地址,怎么去拿;怎么使用变量;
带着这些问题,看下面的代码吧,相信你会找到答案(附注:代码中用到的知识,可以参考前面的两篇帖子,这里直接贴代码了)
_asm { sub esp, 0x70; jmp Palyload; _asm _emit(0x4C) _asm _emit(0x6F) _asm _emit(0x61) _asm _emit(0x64) _asm _emit(0x4C) _asm _emit(0x69); _asm _emit(0x62) _asm _emit(0x72) _asm _emit(0x61) _asm _emit(0x72) _asm _emit(0x79) _asm _emit(0x41) _asm _emit(0x00);// LoadLibraryA _asm _emit(0x75) _asm _emit(0x73) _asm _emit(0x65) _asm _emit(0x72) _asm _emit(0x33) _asm _emit(0x32); _asm _emit(0x2E) _asm _emit(0x64) _asm _emit(0x6C) _asm _emit(0x6C) _asm _emit(0x00);// user32.dll _asm _emit(0xB0) _asm _emit(0xF8) _asm _emit(0x9D) _asm _emit(0x74); // MessageBoxA _asm _emit(0xC0) _asm _emit(0x3B) _asm _emit(0x2F) _asm _emit(0x75); // ExitProcess _asm _emit(0x48) _asm _emit(0x65) _asm _emit(0x6C) _asm _emit(0x6C) _asm _emit(0x6F) _asm _emit(0x20); // Hello _asm _emit(0x57) _asm _emit(0x6F) _asm _emit(0x72) _asm _emit(0x6C) _asm _emit(0x64) _asm _emit(0x00); // World Palyload: call CodeStart; CodeStart: pop edx; // GetPC push eax; push ebx; push ecx; push esi; push edi; mov eax, fs:[0x30]; // PEB mov eax, [eax + 0xC]; // LDR mov eax, [eax + 0xC]; // InLoadOrderModuleList, exe mov eax, [eax]; // nt.dll mov eax, [eax]; // kernel32.dll mov eax, dword ptr ds : [eax + 0x18]; // BaseAddr; push eax; // ESP + C add eax, [eax + 0x168]; // ExportStart_VA mov ebx, eax; add ebx, 0x28; push ebx; // EAT->ESP + 8 mov ebx, eax; add ebx, 0x1914; push ebx; // ENT->ESP + 4 mov ebx, eax; add ebx, 0x3200; push ebx; // EOT->ESP xor ebx, ebx; mov eax, 0x63B; cld; _ENT_FIND: mov ecx, 13; mov esi, [esp + 4]; mov esi, [esi + 4 * ebx]; // ENT RVA add esi, [esp + 0xC]; lea edi, [edx - 0x31]; // LoadLibraryA repe cmpsb; je _ENT_OK; inc ebx; dec eax; cmp eax, 0; jg _ENT_FIND; jmp _ENT_END; _ENT_OK: mov ecx, [esp]; // EOT Number mov ecx, [ecx + 2 * ebx]; and ecx, 0xFFFF; mov esi, [esp + 8]; mov esi, [esi + 4 * ecx]; // EAT Address RVA add esi, [esp + 0xC]; // EAT Address VA add esp, 0x10; lea eax, [edx - 0x24]; // user32.dll push edx; push eax; call esi; pop edx; mov eax, [edx - 0x19]; // MessageBoxA lea ebx, [edx - 0x11]; // Hello World push edx; push 0; push 0; push ebx; push 0; call eax; pop edx; mov eax, [edx - 0x15]; // ExitProcess xor ebx, ebx; push ebx; call eax; _ENT_END: pop edi; pop esi; pop ecx; pop ebx; pop eax; }
一、抠出OpCode,进行测试,代码1
char cShellCode[] = "x83xECx70xEBx2Cx4Cx6Fx61x64x4Cx69x62x72" "x61x72x79x41x00x75x73x65x72x33x32x2Ex64" "x6Cx6Cx00xB0xF8x9Dx74xC0x3Bx2Fx75x48x65" "x6Cx6Cx6Fx20x57x6Fx72x6Cx64x00xE8x00x00" "x00x00x5Ax50x53x51x56x57x64xA1x30x00x00" "x00x8Bx40x0Cx8Bx40x0Cx8Bx00x8Bx00x3Ex8B" "x40x18x50x03x80x68x01x00x00x8BxD8x83xC3" "x28x53x8BxD8x81xC3x14x19x00x00x53x8BxD8" "x81xC3x00x32x00x00x53x33xDBxB8x3Bx06x00" "x00xFCxB9x0Dx00x00x00x8Bx74x24x04x8Bx34" "x9Ex03x74x24x0Cx8Dx7AxCFxF3xA6x74x09x43" "x48x83xF8x00x7FxE2xEBx3Bx8Bx0Cx24x8Bx0C" "x59x81xE1xFFxFFx00x00x8Bx74x24x08x8Bx34" "x8Ex03x74x24x0Cx83xC4x10x8Dx42xDCx52x50" "xFFxD6x5Ax8Bx42xE7x8Dx5AxEFx52x6Ax00x6A" "x00x53x6Ax00xFFxD0x5Ax8Bx42xEBx33xDBx53" "xFFxD0x5Fx5Ex59x5Bx58"; __asm { LEA EAX, cShellCode; PUSH EAX; RET; }
可以参考代码中相关注释,另外,对于文章开头提出的疑问,有不解的,可以参考前面两篇帖子;