文章首发于先知社区从0到1编写shellcode
摘要
在c语言中我们使用的MessageBoxA
函数,其实存放在User32.dll
动态链接库中,它是一个windows API
函数
当c语言在链接时,它会将源文件中的代码、库函数等链接在一起,生成最终的可执行代码,形成可执行程序
当程序运行时,操作系统会加载程序所需的动态链接库到内存中,而User32.dll中的MessageBoxA函数也被放在了内存中的一个位置,供给程序使用
硬编码shellcode
硬编码shellcode
是直接通过地址调用相应API函数的一段16进制机器代码
例如:User32.dll中的MessageBoxA函数的内存地址我们知道,直接使用,这就是硬编码
先使用C语言编写一段弹窗程序,并修改visual stdio配置,能够让程序可以在xp的电脑上运行
c语言弹窗
#include <Windows.h>
int main() { MessageBoxA(NULL, "ming", "test", MB_OK | MB_ICONINFORMATION); return 0; }
|
visual stdio设置
当在xp环境中执行的时候,发现可以弹窗,程序正常执行
ollydbg定位MessageBoxA函数内存地址
通过查找字符串功能选项寻找对应的汇编语言命令位置
通过call
命令可以发现MessageBoxA函数地址为0x77D5050B
也就是说内存中0x77D5050B的位置存放着windows操作系统User32.dll文件中的MessageBoxA函数
编写内联汇编代码
#include<windows.h> void main() { LoadLibrary(L"user32.dll"); __asm { xor ebx, ebx push ebx push 0x74736574; test mov esi, esp push ebx push 0x676e696d; ming mov ecx, esp push ebx push esi push ecx push ebx mov esi, 0x77D5050B call esi } }
|
编译为可执行程序之后放入到xp中运行弹窗
生成shellcode
IDA反汇编
这些就是对应的shellcode
33DB5368746573748BC453686D696E678BCC53505153BE0B05D577FFD6
|
通过反汇编网站转换一下格式
https://defuse.ca/online-x86-assembler.htm
|
通过shellcode加载器执行shellcode
#include<windows.h> unsigned char shellcode[] = "\x33\xDB\x53\x68\x74\x65\x73\x74\x8B\xC4\x53\x68\x6D\x69\x6E\x67\x8B\xCC\x53\x50\x51\x53\xBE\x0B\x05\xD5\x77\xFF\xD6";
void main(int arc,char **argv) { LoadLibrary(L"user32.dll"); __asm { lea eax,shellcode push eax ret } }
|
可是不同的操作系统对应的api函数位置不同
以下是在windows10操作系统下使用x32dbg
调试得到的MessageBoxA函数地址,与xp下的0x77D5050B不同
修改代码重新编译才可以执行
所以,因为不同操作系统下api函数加载的内存位置不同导致了硬编码shellcode没有可移植性
,在xp下使用,但是不能在windows10上使用。
函数动态获取的shellcode
编写逻辑
所有的windows32程序都会加载kerner32.dll
基础的动态链接库,而GetProcAddress
函数就在kernel32.dll中
GetProcAddress这个函数的作用是从指定的动态链接库 (DLL) 检索导出函数 (也称为过程) 或变量的地址
使用GetProcAddress获取到kernel32.dll中LoadLibraryA
的地址,在通过LoadLibraryA加载User32.dll动态链接库
也就能找到MessageBoxA函数,也就能实现弹窗
定位kernel32.dll地址
#include "windows.h" #include "stdio.h"
int main() { int kernel32 = 0; __asm { mov ebx, fs: [0x30] mov ebx, [ebx + 0xc] mov ebx, [ebx + 0x14] mov ebx, [ebx]; ntdll mov ebx, [ebx]; kernel mov ebx, [ebx + 0x10]; DllBase mov kernel32, ebx } printf("kernel32=0x%x", kernel32); return 0; }
|
使用windbg查询kernel32.dll的基址
为了验证内联汇编程序得到的kerenl32.dll的基址位置正确,我们可以使用windbg
运行一个32位程序查看kerenl32.dll的基址
dt _PEB_LDR_DATA 0x777ddb00
|
dt _LIST_ENTRY 0x777ddb00+0x014
|
dt _LDR_DATA_TABLE_ENTRY 0x1153eb8
|
dt _LDR_DATA_TABLE_ENTRY 0x11543c8
|
使用x32dbg查询kernel32.dll的基址
程序运行的结果与windbg、x32dbg查询的对应
找出kernel32.dll导出表的地址
dt _IMAGE_DOS_HEADER 75f30000
|
dt _IMAGE_NT_HEADERS kernel32+0n248
|
dt _IMAGE_OPTIONAL_HEADER kernel32+0n248+0x018
|
dt _IMAGE_DATA_DIRECTORY kernel32+0n248+0x018+0x060
|
导出表的地址为75f30000+0x93e40,及0x75fc3e40
汇编语言定位导出表地址
#include "windows.h" #include "stdio.h"
int main() { int kernel32 = 0; int Export_table = 0; __asm { mov ebx, fs: [0x30] mov ebx, [ebx + 0xc] mov ebx, [ebx + 0x14] mov ebx, [ebx]; ntdll mov ebx, [ebx]; kernel mov ebx, [ebx + 0x10]; DllBase mov kernel32, ebx mov edx, [ebx + 0x3c] add edx, ebx mov edx, [edx + 0x78] add edx, ebx mov Export_table, edx } printf("Export=0x%x", Export_table); return 0; }
|
遍历地址,找出GetProcaddress
导出表地址偏移0x20处为AddressOfNames
,而AddressOfNames的结构是一个数组指针,每个机器位(4字节)都指向一个函数名的字符串
通过比较函数名字符串的方式确定GetProcAress函数的索引值
Get_Function: inc ecx lodsd add eax, ebx cmp dword ptr[eax], 0x50746547 // GetP jnz Get_Function cmp dword ptr[eax + 0x4], 0x41636f72 // rocA jnz Get_Function
|
这样就找到了GetProcAddress函数的索引值
内联汇编代码
然后在寻找GetProcAddress函数的绝对地址,使用GetProcAddress获取到kernel32.dll中LoadLibraryA的地址,在通过LoadLibraryA加载User32.dll动态链接库
就能找到MessageBoxA函数,就能实现弹窗
#include "windows.h" #include "stdio.h"
void main() { int kernel32 = 0; int Export_table = 0; __asm { mov ebx, fs: [0x30] mov ebx, [ebx + 0xc] mov ebx, [ebx + 0x14] mov ebx, [ebx]; ntdll mov ebx, [ebx]; kernel mov ebx, [ebx + 0x10]; DllBase mov kernel32, ebx mov edx, [ebx + 0x3c] add edx, ebx mov edx, [edx + 0x78] add edx, ebx mov esi, [edx + 0x20] add esi, ebx xor ecx, ecx
Get_Function: inc ecx lodsd add eax, ebx cmp dword ptr[eax], 0x50746547 jnz Get_Function cmp dword ptr[eax + 0x4], 0x41636f72 jnz Get_Function mov esi, [edx + 0x24] add esi, ebx mov cx, [esi + ecx * 2] dec ecx mov esi, [edx + 0x1c] add esi, ebx mov edx, [esi + ecx * 4] add edx, ebx
xor ecx, ecx; ECX = 0 push ebx push edx push ecx; 0 push 0x41797261; aryA push 0x7262694c; Libr push 0x64616f4c; Load push esp; "LoadLibrary" push ebx; call edx
add esp, 0xc; pop "LoadLibrary" pop ecx; ECX = 0 push eax; EAX = LoadLibrary push ecx mov cx, 0x6c6c; ll push ecx push 0x642e3233; d.23 push 0x72657355; resU push esp; "User32.dll" call eax; LoadLibrary("User32.dll")
add esp, 0x10 mov edx, [esp + 0x4] xor ecx, ecx push ecx mov ecx, 0x6c41786f; obAl push ecx sub dword ptr[esp + 0x3], 0x6c; Remove “l” push 0x42656761; Bega push 0x7373654d; sseM push esp; MessageBoxA push eax; User32.dll address call edx; GetProc(MessageBoxA)
add esp, 0x18; Cleanup stack push ebp xor ebx, ebx push ebx push 0x74736574; test mov esi, esp push ebx push 0x676e696d; ming mov ecx, esp push ebx push esi push ecx push ebx call eax
} }
|
对应的shellcode
IDA
反汇编exe程序,将对应的16进制代码取出
拿到反汇编网站上转换一下
https://defuse.ca/online-x86-assembler.htm
|
\x64\x8B\x1D\x30\x00\x00\x00\x8B\x5B\x0C\x8B\x5B\x14\x8B\x1B\x8B\x1B\x8B\x5B\x10\x89\x5D\xF4\x8B\x53\x3C\x03\xD3\x8B\x52\x78\x03\xD3\x8B\x72\x20\x03\xF3\x33\xC9\x41\xAD\x03\xC3\x81\x38\x47\x65\x74\x50\x75\xF4\x81\x78\x04\x72\x6F\x63\x41\x75\xEB\x8B\x72\x24\x03\xF3\x66\x8B\x0C\x4E\x49\x8B\x72\x1C\x03\xF3\x8B\x14\x8E\x03\xD3\x33\xC9\x53\x52\x51\x68\x61\x72\x79\x41\x68\x4C\x69\x62\x72\x68\x4C\x6F\x61\x64\x54\x53\xFF\xD2\x83\xC4\x0C\x59\x50\x51\x66\xB9\x6C\x6C\x51\x68\x33\x32\x2E\x64\x68\x55\x73\x65\x72\x54\xFF\xD0\x83\xC4\x10\x8B\x54\x24\x04\x33\xC9\x51\xB9\x6F\x78\x41\x6C\x51\x83\x6C\x24\x03\x6C\x68\x61\x67\x65\x42\x68\x4D\x65\x73\x73\x54\x50\xFF\xD2\x83\xC4\x18\x55\x33\xDB\x53\x68\x74\x65\x73\x74\x8B\xF4\x53\x68\x6D\x69\x6E\x67\x8B\xCC\x53\x56\x51\x53\xFF\xD0
|
shellcode效果
使用shellcode加载器加载到内存执行
#include <stdio.h> #include <windows.h> using namespace std; int main() { char shellcode[] = "\x64\x8B\x1D\x30\x00\x00\x00\x8B\x5B\x0C\x8B\x5B\x14\x8B\x1B\x8B\x1B\x8B\x5B\x10\x89\x5D\xF4\x8B\x53\x3C\x03\xD3\x8B\x52\x78\x03\xD3\x8B\x72\x20\x03\xF3\x33\xC9\x41\xAD\x03\xC3\x81\x38\x47\x65\x74\x50\x75\xF4\x81\x78\x04\x72\x6F\x63\x41\x75\xEB\x8B\x72\x24\x03\xF3\x66\x8B\x0C\x4E\x49\x8B\x72\x1C\x03\xF3\x8B\x14\x8E\x03\xD3\x33\xC9\x53\x52\x51\x68\x61\x72\x79\x41\x68\x4C\x69\x62\x72\x68\x4C\x6F\x61\x64\x54\x53\xFF\xD2\x83\xC4\x0C\x59\x50\x51\x66\xB9\x6C\x6C\x51\x68\x33\x32\x2E\x64\x68\x55\x73\x65\x72\x54\xFF\xD0\x83\xC4\x10\x8B\x54\x24\x04\x33\xC9\x51\xB9\x6F\x78\x41\x6C\x51\x83\x6C\x24\x03\x6C\x68\x61\x67\x65\x42\x68\x4D\x65\x73\x73\x54\x50\xFF\xD2\x83\xC4\x18\x55\x33\xDB\x53\x68\x74\x65\x73\x74\x8B\xF4\x53\x68\x6D\x69\x6E\x67\x8B\xCC\x53\x56\x51\x53\xFF\xD0"; LPVOID lpAlloc = VirtualAlloc(0, sizeof shellcode, MEM_COMMIT, PAGE_EXECUTE_READWRITE); memcpy(lpAlloc, shellcode, sizeof shellcode); ((void(*)())lpAlloc)(); return 0; }
|
参考链接
https://xz.aliyun.com/t/2108
https://migraine-sudo.github.io/2019/12/21/Shellcode-Write/
https://blog.csdn.net/mdp1234/article/details/110287856?spm=1001.2014.3001.5502
https://www.youtube.com/watch?v=mN9LopGgkjk