谈到远线程注入,首先肯定会想到使用CreateRemoteThread,但这个API无法对系统进程进行注入,而且根据我个人的验证发现这个函数在win10下也无法正常将dll注入进记事本进程(淦!)。好在在我的不懈努力的百度、google下发现了另一种思路,那就是使用ZwCreateThread这个内核API,CreateRemoteThread在底层也调用了这个API,CreateRemoteThread底层会调用内核函数ZwCreateThreadEx,而系统调用此函数时,如果发现是系统进程,会把函数的第七个参数CreateSuspended设置为1,导致线程创建完成后会一直处于挂起状态,无法恢复运行,导致注入失败。所以我们必须手动调用ZwCreateThread这一内核函数,将第七个参数设置为0即可。
想要对某个进程进行注入,除了创建线程外,还要记得先提升进程权限
代码:
BOOL GetDebugPrivilege(
_In_ LPCSTR lPcstr,
_Inout_ DWORD* backCode
)
{
HANDLE Token = NULL;
LUID luid = { 0 };
TOKEN_PRIVILEGES Token_privileges = { 0 };
//内存初始化为zero
memset(&luid, 0x00, sizeof(luid));
memset(&Token_privileges, 0x00, sizeof(Token_privileges));
//打开进程令牌
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &Token))
{
*backCode = 0x01;
return FALSE;
}
//获取特权luid
if (!LookupPrivilegeValue(NULL,lPcstr,&luid))
{
*backCode = 0x02;
return FALSE;
}
//设定结构体luid与特权
Token_privileges.PrivilegeCount = 1;
Token_privileges.Privileges[0].Luid = luid;
Token_privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
//修改进程特权
if (!AdjustTokenPrivileges(Token, FALSE, &Token_privileges, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
{
*backCode = 0x03;
return FALSE;
}
*backCode = 0x00;
return TRUE;
}
OpenProcessToken函数用于打开进程的令牌
LookupPrivilegeValue负责获取到要用权限的luid,此处获取进程调试权限
AdjustTokenPrivileges负责修改进程的权限
TOKEN_PRIVILEGES结构体包含一组权限信息的访问令牌,此处用来保存要修改的权限信息内容,供之后的AdjustTokenPrivileges函数使用
- PrivilegeCount应设置为Privileges元素个数
- Privileges是指定的LUID_AND_ATTRIBUTES结构体数组,每个LUID_AND_ATTRIBUTES结构体都包含luid以及特权属性
还有一个根据进程名称获取pid的函数,那个函数的基本思路为通过进程快照来获取进程名称当找到指定进程名称就将其pid拿出来即可。
int GetProcessPid(
_In_ const char* ProcessName,
_Inout_ DWORD* backCode
)
{
PROCESSENTRY32 P32 = { 0 };
HANDLE H32 = NULL;
//内存初始化为zeor
memset(&P32, 0X00, sizeof(P32));
//创建快照
H32 = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
P32.dwSize = sizeof(P32);
if (H32 == NULL)
{
*backCode = 0x01;
return -1;
}
//开始循环遍历进程
BOOL ret = Process32First(H32, &P32);
while (ret)
{
//发现指定进程存在
if (!strcmp(P32.szExeFile, ProcessName))
{
*backCode = 0x00;
return P32.th32ProcessID;
}
ret = Process32Next(H32, &P32);
}
*backCode = 0x01;
return -1;
}
接下来说程序的主函数部分,在进行注入前首先肯定要知道要注入的动态链接库的绝对地址(相对地址可能会出现不可知错误),其次就要知道要被注入的进程pid,在拿到这两项内容后,
- 先要提升我们的进程权限,让其拥有进程调试权限。
- 然后使用OpenProcess函数来打开要被注入的进程。
- 在使用VirtualAllocEx来在指定进程(被注入的进程)中申请创建一块内存用于存储要注入的dll库绝对路径。
- 之后使用WriteProcessMemory来将dll的路径写入这块内存区域。
- 然后使用GetModuleHandle获取kernel32.dll库的模块句柄,再调用GetProcAddress来获取LoadLibrary函数的地址。
- 然后使用LoadLibrary(非刚才获取到的)将ntdll.dll库加载进来以便我们获取内核函数ZwCreateThread。
- 然后要注意我们需要定义一个函数指针用于将刚刚从ntdll库中获取到的ZwCreateThread函数的地址存放进去,之后直接调用这个函数指针即可。
- 使用刚刚已经存放了ZwCreateThread地址的函数指针去在要被注入的进程中创建一个新的线程,在这个线程中调用刚刚从kernel32.dll库中获取的LoadLibray函数去加载我们已经获取到绝对路径的要注入的dll库路径字符串。
- 最后记得将申请的内存创建的句柄,该释放的释放,该关闭的关闭。
int main(int argv, char* argc[])
{
//对必要的变量进行声明以及初始化
DWORD backCode = 0;
HANDLE hProcess = NULL;
LPVOID Buff = NULL;
LPVOID LoadLibraryBase = NULL;
char DllPath[] = "D:\\cp\\pk\\x64\\Release\\pk.dll";
DWORD DllPathLen = strlen(DllPath) + 1;
HMODULE Ntdll = NULL;
SIZE_T write_len = 0;
DWORD dwStatus = 0;
HANDLE hRemoteThread = NULL;
//通过进程名获取pid
int pid = GetProcessPid("notepad.exe", &backCode);
if (pid == -1)
{
puts("pid get error");
return 0;
}
//提升进程特权,获得调试权限
if (!GetDebugPrivilege(SE_DEBUG_NAME, &backCode))
{
puts("DBG privilege error");
printf(" %d", backCode);
return 0;
}
//打开要被注入的进程
if ((hProcess=OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid))== NULL)
{
puts("process open erro");
return 0;
}
//在要被注入的进程中创建内存,用于存放注入dll的路径
Buff = VirtualAllocEx(hProcess, NULL, DllPathLen, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (Buff==NULL)
{
puts("Buff alloc error");
return 0;
}
//将dll路径写入刚刚创建的内存中
WriteProcessMemory(hProcess, Buff, DllPath, DllPathLen, &write_len);
if(DllPathLen != write_len)
{
puts("write error");
return 0;
}
//从kernel32.dll中获取LoadLibrary函数
LoadLibraryBase = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
if (LoadLibraryBase == NULL)
{
puts("kernel32 get error");
return 0;
}
//加载ntdll.dll并从中获取内核函数ZwCreateThread,并使用函数指针指向此函数
Ntdll = LoadLibrary("ntdll.dll");
pZwCreateThreadEx ZwCreateThreadEx =
(pZwCreateThreadEx)GetProcAddress(Ntdll, "ZwCreateThreadEx");
if (ZwCreateThreadEx == NULL)
{
puts("func get error");
return 0;
}
//执行ZwCreateThread函数,在指定进程中创建线程加载要被注入的dll
dwStatus = ZwCreateThreadEx(
&hRemoteThread,
PROCESS_ALL_ACCESS,
NULL,
hProcess,
(LPTHREAD_START_ROUTINE)LoadLibraryBase,
Buff,
0, 0, 0, 0,
NULL
);
if (hRemoteThread == NULL)
{
puts("zwcreatethread fun error");
return 0;
}
//释放不需要的变量以及内存
CloseHandle(hProcess);
FreeModule(Ntdll);
ExitProcess(0);
return 0;
}
要包含的头文件:
- string.h
- stdlib.h
- stdio.h
- Windows.h
- string.h
- TlHelp32.h
完整项目源码地址:
github:Pluviophile-BT