远程线程注入(简单样例以及原理)
注入的目标是将我们的代码注入到目标进程的地址空间中
注入通常可以根据注入的内容分为两种类型:
- shellcode注入
:这种注入是将我们的代码直接注入到目标内存中,这就要保证我们的代码在贴到其他地址上后仍然能够正常执行
:这种方式优点是不容易检测,因为他的特征并不明确,缺点也很明显就是代码比较麻烦也不容易维护 - 模块注入
:模块注入的原理是通过在目标进程中创建一个新的线程让他帮我们执行loadlibrary的方式去加载一个新的模块到地址空间中
优点就是这种代码很好写,并且不需要我们自己做重定位等pe文件的加载动作,缺点则是特征比较明显
我们今天通过一个简单的测试代码来了解一下模块注入的大体流程
- 找到目标进程
- 获取目标进程的handle
- 在目标进程的地址空间中写入我们要加载的模块的文件名
- 为目标进程创建新的线程
下面来看一下我们要注入的模块代码
这个模块中主要是创建了一个静态线程对象,循环打印内容
// pch.cpp: 与预编译标头对应的源文件
#include "pch.h"
#include <stdio.h>
DWORD WINAPI threadProc(
LPVOID lpThreadParameter
) {
for (size_t i = 0; i < 10; i++)
{
printf("%d", i);
Sleep(1000);
}
return 0;
}
class MyClass
{
public:
MyClass();
~MyClass();
private:
HANDLE t = { 0 };
};
MyClass::MyClass()
{
DWORD threadid;
t = CreateThread(NULL, 0, threadProc, NULL, 0, &threadid);
}
MyClass::~MyClass()
{
CloseHandle(t);
}
static MyClass c;
然后是我们要注入的目标进程的代码
这个代码也很简单,就是循环打印
#include <Windows.h>
#include <stdio.h>
int main() {
//LoadLibrary(L"Dll1.dll");
for (size_t i = 0; i < 1000; i++)
{
printf("-------%d-------\n", i);
Sleep(1000);
}
return 0;
}
然后是存放我们的注入动作的代码
int main() {
UINT PID = 10744;// 这边可以通过tlhelp32或者psapi.dll提供的函数来找到pid
//这边暂时不做提权,正常的话应该通过processtoken提权一下,可以查msdn上有详细说明
HANDLE h = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
const WCHAR* dllName = L"Dll1.dll";
LPVOID addr = VirtualAllocEx(h, NULL, 0x100, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (addr == NULL) return -1;
DWORD realBytes = 0;
WriteProcessMemory(h, addr, dllName, MAX_PATH + 1, &realBytes);
DWORD threadId;
// 这边简单说一下,能够直接将loadlibrary函数作为线程的起始地址有三个原因
// 1:loadlibrary这个函数是kernel32.dll中的基本所有的win32进程都会存在这个dll
// 2:因为kernel32.dll这类的win32dll都是提前做了地址绑定并且imagebase都是在比较高的位置基本上不会触发重定位,
// 所以在每个进程内的函数地址都是一样的
// 3:可以发现loadlibrary的函数的声明结构跟createthread所需的函数结构是一样的所以可以直接用
HANDLE hThread = CreateRemoteThread(h, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibrary, addr, 0, &threadId);
if (hThread == NULL) return -1;
WaitForSingleObject(hThread, -1);
VirtualFreeEx(h, addr, NULL, MEM_RELEASE);
CloseHandle(h);
CloseHandle(hThread);
system("pause");
return 0;
}
接下来我们来看一下注入的效果
首先是没有注入的时候就是一直在打印
这是注入后的进程控制台打印效果: