WIndows ShellCode开发 第四章 动态API调用
文章目录WIndows ShellCode开发 第四章 动态API调用消息框汇编程序WIndows ShellCode开发 第四章 动态API调用不检验直接跳到这一章最好是将第一章命令仔细看完然后再来这一章因为已经写过一遍的缘故其中代码可以只会将重要部分进行注释并且逻辑讲解也会少很多消息框汇编程序BITS 64 SECTION .text global main main: sub rsp,0x28 and rsp,0xFFFFFFFFFFFFFFF0 xor rcx,rcx mov rax,[gs:rcx0x60] ; PEB mov rax,[rax0x18] ; PEB-LDR mov rsi,[rax0x10] ; PEB-LDR-InLoadOrderModuleList mov rsi,[rsi] mov rsi,[rsi] mov rbx,[rsi0x30] ; kernel32.dll base mov r8,rbx ; kernel32.dll base -- r8 mov eax,[rbx0x3C] ; 获取 PE Header 的偏移 (e_lfanew) add rax,r8 ; RAX PE Header 的实际地址 mov edx,[rax0x88] ; 获取导出表 (Export Directory) 的 RVA add rdx,r8 ; RDX 导出表的实际内存地址 (Export Directory VA) mov r10d,[rdx0x14] ; 函数总数 xor r11,r11 mov r11d,[rdx0x20] ; AddressOfNames RVA add r11,r8 mov rcx,r10 ; 计量器的总数 mov rax,0x9090737365726464 ; ddress shl rax,0x10 shr rax,0x10 ; 前面两个0x90是占位符避免ShellCode中出现空字节(0x00) push rax mov rax,0x41636F7250746547 ; GetProcA push rax mov rax,rsp kernel32findfunction: jecxz FunctionNameNotFound ; 计量总数到零跳转到FunctionNameNotFound xor ebx,ebx mov ebx,[r11rcx*4] add rbx,r8 dec rcx mov r9,qword[rax] ; qword[rax] 表示读取8字节内容为GetProcA cmp [rbx],r9 ; [rbx]读取与r9相对应的字节数进行匹配 jnz kernel32findfunction ; 第一次粗滤匹配 mov r9d,dword[rax8] ; 读取rax8地址(GetProcA后面那几个字节ddress)的四字节数据读取四个字节并进行比较是因为核心字节已经读取到了可以拿来进行匹配并避免了读取到零字符。 cmp [rbx8],r9d ; r9d 是32位的所以[rbx8]自动读取4字节数据与r9d内进行比较 jz FunctionNameFound jnz kernel32findfunction FunctionNameNotFound: int3 FunctionNameFound: push rcx pop r15 inc r15 xor r11,r11 mov r11d,[rdx0x1c] ; AddressOfFunctions RVA add r11,r8 mov eax,[r11r15*4] add rax,r8 push rax pop r15 mov r12,r15 ; 将GetProcAddress地址存一下后续需要用到r15 mov rdi,r8 mov rcx,r8 ; 给 GetProcAddress 准备第一个参数 mov rax,0x41797261 ; 构造LoadLibraryA字符串后4字节(aryA)先压栈 push rax mov rax,0x7262694C64616F4C ; LoadLibr push rax mov rdx,rsp ; 给 GetProcAddress 准备第二个参数 sub rsp,0x30 call r15 add rsp,0x30 mov r15,rax ; GetProcAddress 函数执行完毕后会将返回值即目标函数的地址写入 rax 寄存器因此此时的RAX的值成为 LoadLibraryA 函数的地址然后存入r15 mov r14,r12 ; 将 GetProcAddress地址 存到r14 mov rcx,rdi ; 将 kernel32.dll 基址作为第一参数基地址DLL句柄 mov rax,0x90737365 ; 0x90作为占用符避免空字节 shl eax,0x8 shr eax,0x8 push rax mov rax,0x636F725074697845 ; ExitProc push rax mov rdx,rsp ; 第二个参数存入rdx sub rsp,0x30 call r14 add rsp,0x30 mov r14,rax ; 将RAX中ExitProcess函数地址传给R14 mov rax, 0x90906C6C ; 构造 ll 字符串前两个字节直接用nop填充 shl eax,0x10 shr eax,0x10 push rax mov rax, 0x642E323372657375 ; 构造 user32.d 字符串 push rax mov rcx,rsp ; 将构造好的字符串传给rcx作为第一参数 sub rsp,0x30 call r15 ; 调用LoadLibraryA 加载 user32.dll mov rdi,rax ; 将user32.dll基地址保存到rdi mov rcx,rdi mov rax,0x9041786F ; oxA字符串 shl eax,0x8 shr eax,0x8 push rax mov rax,0x426567617373654D ; MessageB字符串 push rax mov rdx,rsp ; 将MessageBoxA作为第二参数 sub rsp,0x30 call r12 ; 调用GetProcAddress mov r15,rax ; 将 MessageBoxA 函数地址传入r15 xor rcx,rcx mov rax,0x90909073 shl eax,24 shr eax,24 push rax mov rax,0x6B6E616C62656E6F push rax mov rdx,rsp ; 第二参数lpText mov r8,rsp ; 第三参数lpCaption xor r9d,r9d sub rsp,0x30 call r15 add rsp, 0x30 xor ecx,ecx call r14直接编译链接nasm -f win64 mesg.asm -o mesg.obj gcc -m64 mesg.obj -o mesg.exe -lkernel32 -nostartfiles双击mesg.exe成功弹窗接下来将这些编译为ShellCode在加载器执行nasm.exe -f win64 mesg.asm -o mesg.o$shellcode ; (objdump -D mesg.o | Select-String ^ ).Line | ForEach-Object { $_.Split(t)[1] -split | Where-Object { $_ -match ^[0-9a-f]{2}$ } | ForEach-Object { $shellcode \x $_ } }; $shellcode简单加载器C语言#include windows.h #include stdio.h #include signal.h unsigned char shellcode[] \x48\x83\xec\x28\x48\x83\xe4\xf0\x48\x31\xc9\x65\x48\x8b\x41\x60\x48\x8b\x40\x18\x48\x8b\x70\x10\x48\x8b\x36\x48\x8b\x36\x48\x8b\x5e\x30\x49\x89\xd8\x8b\x43\x3c\x4c\x01\xc0\x8b\x90\x88\x00\x00\x00\x4c\x01\xc2\x44\x8b\x52\x14\x4d\x31\xdb\x44\x8b\x5a\x20\x4d\x01\xc3\x4c\x89\xd1\x48\xb8\x64\x64\x72\x65\x73\x73\x90\x90\x48\xc1\xe0\x10\x48\xc1\xe8\x10\x50\x48\xb8\x47\x65\x74\x50\x72\x6f\x63\x41\x50\x48\x89\xe0\x67\xe3\x20\x31\xdb\x41\x8b\x1c\x8b\x4c\x01\xc3\x48\xff\xc9\x4c\x8b\x08\x4c\x39\x0b\x75\xe9\x44\x8b\x48\x08\x44\x39\x4b\x08\x74\x03\x75\xdd\xcc\x51\x41\x5f\x49\xff\xc7\x4d\x31\xdb\x44\x8b\x5a\x1c\x4d\x01\xc3\x43\x8b\x04\xbb\x4c\x01\xc0\x50\x41\x5f\x4d\x89\xfc\x4c\x89\xc7\x4c\x89\xc1\xb8\x61\x72\x79\x41\x50\x48\xb8\x4c\x6f\x61\x64\x4c\x69\x62\x72\x50\x48\x89\xe2\x48\x83\xec\x30\x41\xff\xd7\x48\x83\xc4\x30\x49\x89\xc7\x4d\x89\xe6\x48\x89\xf9\xb8\x65\x73\x73\x90\xc1\xe0\x08\xc1\xe8\x08\x50\x48\xb8\x45\x78\x69\x74\x50\x72\x6f\x63\x50\x48\x89\xe2\x48\x83\xec\x30\x41\xff\xd6\x48\x83\xc4\x30\x49\x89\xc6\xb8\x6c\x6c\x90\x90\xc1\xe0\x10\xc1\xe8\x10\x50\x48\xb8\x75\x73\x65\x72\x33\x32\x2e\x64\x50\x48\x89\xe1\x48\x83\xec\x30\x41\xff\xd7\x48\x89\xc7\x48\x89\xf9\xb8\x6f\x78\x41\x90\xc1\xe0\x08\xc1\xe8\x08\x50\x48\xb8\x4d\x65\x73\x73\x61\x67\x65\x42\x50\x48\x89\xe2\x48\x83\xec\x30\x41\xff\xd4\x49\x89\xc7\x48\x31\xc9\xb8\x73\x90\x90\x90\xc1\xe0\x18\xc1\xe8\x18\x50\x48\xb8\x6f\x6e\x65\x62\x6c\x61\x6e\x6b\x50\x48\x89\xe2\x49\x89\xe0\x45\x31\xc9\x48\x83\xec\x30\x41\xff\xd7\x48\x83\xc4\x30\x31\xc9\x41\xff\xd6; // 异常处理函数 void handler(int sig) { printf(Exception occurred! (signal %d)\n, sig); exit(1); } int main() { printf(Loading Shellcode...\n); printf(Shellcode size: %d bytes\n, sizeof(shellcode)); // 注册异常处理 signal(SIGSEGV, handler); signal(SIGILL, handler); // 申请可执行内存 void* exec VirtualAlloc(0, sizeof(shellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (exec NULL) { printf(VirtualAlloc failed: %d\n, GetLastError()); return 1; } memcpy(exec, shellcode, sizeof(shellcode)); printf(Shellcode at: 0x%p\n, exec); printf(Press ENTER to execute...); getchar(); printf(Executing...\n); // 执行shellcode ((void(*)())exec)(); printf(Shellcode returned (should not happen)\n); VirtualFree(exec, 0, MEM_RELEASE); return 0; }gcc -o loader.exe loader.c -m64