代码注入——汇编编写注代码
代码注入——汇编编写注代码
0x00 思路
在准备些汇编代码之前,我们先要理清楚整个调用过程的思路,以及过程中的参数传递过程。我的的目标是在指定的目标程序中注入一个messagebox线程,首先要调用messageBox就需要用到LoadLibrary装入user32库函数。我们还需要GetprocessAddress来取得messagebox的函数地址,取得了messagebox的地址,我们需要向messagebox传递四个参数。Messagebox的原型如下:
MessageBox(hWnd: HWND; Text, Caption: PChar; Type: Word): Integer;
四个参数中Text和PChar是一定要传递进去的。结合上篇中利用c++编写的注入代码我们可以整理出这样一个方案:
1)使用c++编写获取LoadLibrary,GetprocessAddress函数地址的程序。
2)使用汇编编写获取messagebox地址的函数,以及存储messagebox的各个参数。
3)使用c++调用createRemotetThread函数开启一个子线程运行汇编代码。
0x01 汇编部分的代码编写
1)用od载入一个空的exe文件,我们将代码写入开始地址位401000h的地方。如下图:
这里需要注意的是,之所以要先开辟两个堆栈空间,是因为我们需要调用LoadLibrary以及GetProcessAddress。在堆栈开辟两个四字节的空间就是为了存放传过来的两个函数的地址。
2)接下来编写需要传入的字符串,我们将光标移至401033处。使用快捷键ctrl+e输入字符串ReverseCore,记得以00结尾。如下图:
下一字符串就放在起始地址为401044处。输入www.reversecore.com。如下图。
在401058输入返回函数,如下图:
至此,汇编代码输入完毕。我们接下来保存一下文件。
0x01 汇编指令说明
被注入的汇编指令从地址401000开始执行,执行到40102e处有一个call 40103f,进入地址40103f,又是一个call指令,call 401058转向地址401058,401058后面的指令就是退出指令。
00401000 55 push ebp
00401001 8BEC mov ebp,esp
00401003 8B75 08 mov esi,dword ptr ss:[ebp+0x8]
上面这三条指令就是保存现场,让后为传入的两参数开辟堆栈空间。ebp+0——ebp+3存放着函数LoadLibrary的起始地址,ebp+4——ebp+7存放着函数GetProcAddress的函数地址。
00401006 68 6C6C0000 push 0x6C6C
0040100B 68 32322E64 push 0x642E3232
00401010 68 75736572 push 0x72657375
00401015 54 push esp
00401016 FF16 call dword ptr ds:[esi]
上面的五条指令就是先将字符串user32.dll压栈,然后使用esp指向字符串,最后一句调用API函数LoadLibrary,其返回值存放在eax中。()
00401018 68 6F784100 push 0x41786F
0040101D 68 61676542 push 0x42656761
00401022 68 4D657373 push 0x7373654D
00401027 54 push esp
00401028 50 push eax
00401029 FF56 04 call dword ptr ds:[esi+0x4]
上面的六条指令先将字符串MessageBox压栈,再让esp指向字符串,eax存放着上个函数LoadLibrary的返回值,这里作为GetProccessAddress的参数。最后一句调用GetProcessAddress函数取得函数MessageBox的地址。
0040102C 6A 00 push 0x0
0040102E E8 0C000000 call asmtest1.0040103F
00401033 52 push edx
00401034 65:76 65 jbe short 0040109c
00401037 72 73 jb short asmtest1.004010AC
00401039 65:43 inc ebx
0040103B 6f outs dx,dword ptr ds:[esi]
0040103C 72 65 jb short asmtest1.004010A3
0040103E 00E8 add al,ch
0040103F E8 14000000 call asmtest1.00401058
00401044 /77 77 ja short asmtest1.004010BD
00401046 |77 2E ja short asmtest1.00401076
00401048 |72 65 jb short asmtest1.004010AF
0040104A |76 65 jbe short asmtest1.004010B1
0040104C |72 73 jb short asmtest1.004010C1
00401058 6A 00 push 0x0
0040105A FFD0 call eax
0040105C 33C0 xor eax,eax
0040105E 8BE5 mov esp,ebp
00401060 5D pop ebp
00401061 C3 retn
上面的标蓝代码是真正的代码,其余的的实际上存储的是字符串,仔细观察就以可以发现,我们这是我们之前存放的地址。分别从地址00401033以及00401044开始。
其实我们关心的是0040102E 和0040103F 地址处的call跳转,这两个跳转有啥作用?答:其实这两个跳转是来传递参数的,这两个参数分别是00401033处的字符串ReverseCore以及00401044处的字符串www.reversecore.com。这两个参数都是MessageBox的参数。那为啥用call 跳转来传递参数?答:其实这样传递参数具有一定的安全性,而且也不用调用ebp,省事。那其中的原理是什么?答:我们简单回想一下call指令的整个过程,先将返回地址压栈,执行完在跳转。其实就是push 和jmp指令的结合。现在我们来看看这两个call后面的首地址,是的。其实就是要传入的字符串地址!这两个call指令把字符串的首地址当作了返回值地址来压栈,这样也就相当于把两个字符串参数入栈!四个参数的入栈过程即:40102c 处push 0,传递第四个参数,两个call分别传递三二两个参数,最后401058处传递最后一行参数。地址0040105A 的call指令调用函数MessageBox。执行到这一步就会弹出messagebox的对话框。
最后几行代码恢复栈帧并且返回。
0x02 c++代码编写
1)先将之前保存汇编文件用OD打开,转到40100处,复制40100——401061机器码。如下图:
整理得到如下机器码数组:
0x55, 0x8B, 0xEC, 0x8B, 0x75, 0x08, 0x68, 0x6C, 0x6C, 0x00,
0x00, 0x68, 0x33, 0x32, 0x2E, 0x64, 0x68, 0x75, 0x73, 0x65,
0x72, 0x54, 0xFF, 0x16, 0x68, 0x6F, 0x78, 0x41, 0x00, 0x68,
0x61, 0x67, 0x65, 0x42, 0x68, 0x4D, 0x65, 0x73, 0x73, 0x54,
0x50, 0xFF, 0x56, 0x04, 0x6A, 0x00, 0xE8, 0x0C, 0x00, 0x00,
0x00, 0x52, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x43, 0x6F,
0x72, 0x65, 0x00, 0xE8, 0x14, 0x00, 0x00, 0x00, 0x77, 0x77,
0x77, 0x2E, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x63,
0x6F, 0x72, 0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x00, 0x6A, 0x00,
0xFF, 0xD0, 0x33, 0xC0, 0x8B, 0xE5, 0x5D, 0xC3
2)编写c++程序。代码如下:
// CodeInjection2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include <iostream>
#include<windows.h>
#include "stdio.h"
using namespace std;
typedef struct _THREAD_PARAM
{
FARPROC pFunc[2]; // LoadLibraryA(), GetProcAddress()
} THREAD_PARAM, *PTHREAD_PARAM;
//ThreadProc()
BYTE g_InjectionCode[] =
{
0x55, 0x8B, 0xEC, 0x8B, 0x75, 0x08, 0x68, 0x6C, 0x6C, 0x00,
0x00, 0x68, 0x33, 0x32, 0x2E, 0x64, 0x68, 0x75, 0x73, 0x65,
0x72, 0x54, 0xFF, 0x16, 0x68, 0x6F, 0x78, 0x41, 0x00, 0x68,
0x61, 0x67, 0x65, 0x42, 0x68, 0x4D, 0x65, 0x73, 0x73, 0x54,
0x50, 0xFF, 0x56, 0x04, 0x6A, 0x00, 0xE8, 0x0C, 0x00, 0x00,
0x00, 0x52, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x43, 0x6F,
0x72, 0x65, 0x00, 0xE8, 0x14, 0x00, 0x00, 0x00, 0x77, 0x77,
0x77, 0x2E, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x63,
0x6F, 0x72, 0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x00, 0x6A, 0x00,
0xFF, 0xD0, 0x33, 0xC0, 0x8B, 0xE5, 0x5D, 0xC3
};
//提权函数
BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
{
TOKEN_PRIVILEGES tp;
HANDLE hToken;
LUID luid;
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken))
{
printf("OpenProcessToken error: %u\n", GetLastError());
return FALSE;
}
if (!LookupPrivilegeValue(NULL, // lookup privilege on local system
lpszPrivilege, // privilege to lookup
&luid)) // receives LUID of privilege
{
printf("LookupPrivilegeValue error: %u\n", GetLastError());
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if (bEnablePrivilege)
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
tp.Privileges[0].Attributes = 0;
// Enable the privilege or disable all privileges.
if (!AdjustTokenPrivileges(hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES)NULL,
(PDWORD)NULL))
{
printf("AdjustTokenPrivileges error: %u\n", GetLastError());
return FALSE;
}
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
{
printf("The token does not have the specified privilege. \n");
return FALSE;
}
return TRUE;
}
//注入函数
BOOL Injection(DWORD dwPID)
{
HMODULE hMod = NULL;
THREAD_PARAM param = {0,};
HANDLE hProcess = NULL;
HANDLE hThread = NULL;
LPVOID pRemoteBuf[2] = { 0, };
hMod = GetModuleHandleA("kernel32.dll");
//设置线程参数
param.pFunc[0] = GetProcAddress(hMod, "LoadLibraryA");
param.pFunc[1] = GetProcAddress(hMod, "GetProcessAddress");
//打开线程
// Open Process
if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, // dwDesiredAccess
FALSE, // bInheritHandle
dwPID))) // dwProcessId
{
printf("OpenProcess() fail : err_code = %d\n", GetLastError());
return FALSE;
}
//给LoadLibraryA函数分配空间
if (!(pRemoteBuf[0] = VirtualAllocEx(hProcess, // hProcess
NULL, // lpAddress
sizeof(THREAD_PARAM), // dwSize
MEM_COMMIT, // flAllocationType
PAGE_READWRITE))) // flProtect
{
printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());
return FALSE;
}
//将LoadLibraryA写入内存
if (!WriteProcessMemory(hProcess, // hProcess
pRemoteBuf[0], // lpBaseAddress
(LPVOID)¶m, // lpBuffer
sizeof(THREAD_PARAM), // nSize
NULL)) // [out] lpNumberOfBytesWritten
{
printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());
return FALSE;
}
//给GetProcessAddress函数分配空间
if (!(pRemoteBuf[1] = VirtualAllocEx(hProcess, // hProcess
NULL, // lpAddress
sizeof(g_InjectionCode), // dwSize
MEM_COMMIT, // flAllocationType
PAGE_EXECUTE_READWRITE))) // flProtect
{
printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());
return FALSE;
}
//将GetProcessAddress写入内存
if (!WriteProcessMemory(hProcess, // hProcess
pRemoteBuf[1], // lpBaseAddress
(LPVOID)&g_InjectionCode, // lpBuffer
sizeof(g_InjectionCode), // nSize
NULL)) // [out] lpNumberOfBytesWritten
{
printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());
return FALSE;
}
if (!(hThread = CreateRemoteThread(hProcess, // hProcess
NULL, // lpThreadAttributes
0, // dwStackSize
(LPTHREAD_START_ROUTINE)pRemoteBuf[1],
pRemoteBuf[0], // lpParameter
0, // dwCreationFlags
NULL))) // lpThreadId
{
printf("CreateRemoteThread() fail : err_code = %d\n", GetLastError());
return FALSE;
}
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
return TRUE;
}
int main()
{
std::cout << "Hello World!\n";
}
编译生成名为CodeInjection2.exe的Released文件
0x03 验证是否成功
步骤和一篇一样,用管理员权限打开cmd,将CodeInjection2.exe和messageBox.dll放d盘。打开notepad.exe。打开processexploer查看PID,cmd输入d:转自d盘,输入CodeInjection.exe 10584 。结果如下图:
显然注入成功了。