前言
在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 //ebx置0
push ebx
push 0x74736574; test //这些push会将数据压入栈中,而esp对应的为当前栈顶的地址
mov esi, esp //mov指令会将esp,当前栈顶的地址放入到eax
push ebx //push ebx的作用为插入一个空字节,隔开两个数据
push 0x676e696d; ming //将ming字符串压入到栈中
mov ecx, esp //将当前栈顶的地址放入到ecx,现在eax与ecx分别存放着test、ming数据的内存地址
push ebx
push esi //将eax压入栈中,为后续MessageBoxA函数执行提供参数
push ecx //将ecx压入栈中,为后续MessageBoxA函数执行提供参数
push ebx
mov esi, 0x77D5050B
call esi //调用MessageBoxA函数,实现弹窗
}
}
编译为可执行程序之后放入到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] //通过FS寄存器访问PEB结构
mov ebx, [ebx + 0xc] //获取PEB结构中Ldr字段的值
mov ebx, [ebx + 0x14] //获取PEB结构中InMemoryOrderModuleList字段的值
mov ebx, [ebx]; ntdll //获取InMemoryOrderModuleList第一个模块的基址
mov ebx, [ebx]; kernel
mov ebx, [ebx + 0x10]; DllBase //获取kernel32.dll模块的基址
mov kernel32, ebx
}
printf("kernel32=0x%x", kernel32);
return 0;
}
使用windbg查询kernel32.dll的基址
为了验证内联汇编程序得到的kerenl32.dll的基址位置正确,我们可以使用windbg
运行一个32位程序查看kerenl32.dll的基址
dt _PEB @$peb
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] //通过FS寄存器访问PEB结构
mov ebx, [ebx + 0xc] //获取PEB结构中Ldr字段的值
mov ebx, [ebx + 0x14] //获取PEB结构中InMemoryOrderModuleList字段的值
mov ebx, [ebx]; ntdll //获取InMemoryOrderModuleList第一个模块的基址
mov ebx, [ebx]; kernel
mov ebx, [ebx + 0x10]; DllBase //获取kernel32.dll模块的基址
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] //通过FS寄存器访问PEB结构
mov ebx, [ebx + 0xc] //获取PEB结构中Ldr字段的值
mov ebx, [ebx + 0x14] //获取PEB结构中InMemoryOrderModuleList字段的值
mov ebx, [ebx]; ntdll //获取InMemoryOrderModuleList第一个模块的基址
mov ebx, [ebx]; kernel
mov ebx, [ebx + 0x10]; DllBase //获取kernel32.dll模块的基址
mov kernel32, ebx
mov edx, [ebx + 0x3c]
add edx, ebx
mov edx, [edx + 0x78]
add edx, ebx //获取导出表的地址
mov esi, [edx + 0x20]
add esi, ebx //获取AddressOfNames
xor ecx, ecx
Get_Function: // 搜索GetProcAddress,在kernel32.dll的导出函数中,GetProcA就能确认到
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
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 // EDX = GetProcAddress
xor ecx, ecx; ECX = 0
push ebx // Kernel32.dll的基地址
push edx // GetProcAddress的基址
push ecx; 0
push 0x41797261; aryA
push 0x7262694c; Libr
push 0x64616f4c; Load
push esp; "LoadLibrary"
push ebx;
call edx // GerProcAddress(Kernel32,"LoadLibraryA") 使用GetProcAddress加载loadlibraryA-
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 // Clean stack
mov edx, [esp + 0x4] // EDX = GetProcAddress
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 //ebx置0
push ebx
push 0x74736574; test //这些push会将数据压入栈中,而esp对应的为当前栈顶的地址
mov esi, esp //mov指令会将esp,当前栈顶的地址放入到eax
push ebx //push ebx的作用为插入一个空字节,隔开两个数据
push 0x676e696d; ming //将ming字符串压入到栈中
mov ecx, esp //将当前栈顶的地址放入到ecx,现在eax与ecx分别存放着test、ming数据的内存地址
push ebx
push esi //将eax压入栈中,为后续MessageBoxA函数执行提供参数
push ecx //将ecx压入栈中,为后续MessageBoxA函数执行提供参数
push ebx
call eax //调用MessageBoxA函数,实现弹窗
}
}
对应的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://migraine-sudo.github.io/2019/12/21/Shellcode-Write/
https://blog.csdn.net/mdp1234/article/details/110287856?spm=1001.2014.3001.5502