使用汇编语言编写注入代码

代码注入-汇编

0x00-设置

修改EIP

关闭自动填充nop

0x01-修改汇编

输入字符串,Ctrl+E修改,注意字符串以0结尾,最后要加上00

可以发现得到的是汇编代码,OD会自动识别

只需要选中字符串按下Ctrl+A即可转为字符串

同理修改好www.reversecore.com字符串

提取汇编

接下来将插入的asm的机器码进行复制(在内存窗口中复制对应长度的十六进制数据),并修改格式

得到

1
2
3
4
5
6
7
8
9
char shellcode[] = {
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
};

0x02-代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include<stdio.h>
#include<Windows.h>
#include<string.h>

BYTE shellcode[] = {
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
};

typedef struct _Thread_Param
{
FARPROC pFunc[2];//Loadlibrary,GetProcAddress
}Thread_Param, * pThread_Param;

void Asm_Inject(DWORD PID)
{
HMODULE hMod = NULL;
HANDLE hProcess = NULL;
Thread_Param param[2] = { 0, };
HANDLE hThread = NULL;
LPVOID pRemoteBuf[2] = { 0, };

//获取kernel32.dll的句柄
hMod = GetModuleHandleA("kernel32.dll");
//将需要使用的函数存储
param->pFunc[0] = GetProcAddress(hMod, "LoadLibraryA");
param->pFunc[1] = GetProcAddress(hMod, "GetProcAddress");

//开启进程
if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID)))
{
printf("Open Failed!!Error Code:%d\n", GetLastError());
return;
}

//开辟空间并写入
if (!(pRemoteBuf[0] = VirtualAllocEx(hProcess, NULL, sizeof(Thread_Param), MEM_COMMIT, PAGE_READWRITE)))
{
printf("VirtualAlloc Failed!!Error Code:%d\n", GetLastError());
return;
}

if (!WriteProcessMemory(hProcess, pRemoteBuf[0], (LPVOID)&param, sizeof(Thread_Param), NULL))
{
printf("WriteProcess Failed!!Error Code:%d\n", GetLastError());
return;
}

//将我们的shellcode写入
if (!(pRemoteBuf[1] = VirtualAllocEx(hProcess, NULL, sizeof(shellcode), MEM_COMMIT, PAGE_READWRITE)))
{
printf("VirtualAlloc Failed!!Error Code:%d\n", GetLastError());
return;
}

if (!WriteProcessMemory(hProcess, pRemoteBuf[1], (LPVOID)&shellcode, sizeof(shellcode), NULL))
{
printf("WriteProcess Failed!!Error Code:%d\n", GetLastError());
return;
}
hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pRemoteBuf[1], pRemoteBuf[0], 0, NULL);

WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
return;
}


int main(int argc,char*argv[])
{
Asm_Inject((DWORD)atol(argv[1]));
return 0;
}

本质上和代码注入没区别,主要是我们将字符串也包含在了注入的代码里面,所以在线程函数中不再需要使用字符数组存储

0x03-分析

在OD中打开并注入,让其停在新线程

先提升堆栈,然后将函数的参数存入ESI中,也就是线程函数对应的THREAD_PARAM结构体,因为里面只有两个函数指针,所以大小为0x8

ESI的值

我们执行完移动到esi这一步,去内存中查看ESI存储的值

将其转为地址,注释中会告知我们其对应的API函数

接下来的四个PUSH+call可以知道是调用函数,LoadLibraryA只需要一个参数

前面三个PUSH是将函数名称压入栈,而最后一个是将当前的ESP的值压入栈,而当前ESP的值正好是存储字符串的地址,这样就实现了给LoadLibraryA传参

需要注意这里是小端序

接下来的四个PUSH和前面是一样的,关键是最后一个PUSH

因为返回值一般存储在EAX,并且函数调用时入栈顺序是从右往左依次入栈

这里的EAX存储的值就是user32.dll的句柄,也就是地址,在eax寄存器中OD也自动标明了

最后获取到MessageBoxA的地址

MessageBoxA的传参

这一段本来是字符串,但是OD给我们识别成了代码

执行完之后

可以看到我们的MessageBoxA的参数都在栈中了

调用MessageBoxA

最后就是清空eax和恢复堆栈了