DLL注入浅析(下)



DLL注入浅析(下)

这一篇文章主要讲基于CreateRemoteThread的DLL注入,最后会以一个游戏修改器来结束此文。

关于DLL注入有注册表注入和远程线程注入两种方式。

此文主讲远程线程注入(即CreateRemoteThread)。

1.    LoadLibrary载入DLL

2.    CreateRemoteThread远程注入及遇到的问题

3.    VirtualAllocEx开辟内存

4.    WriteProcessMemory写入字符串

 

 

 

1.LoadLibrary  1

从 根本上说,DLL注入技术就是要求目标进程中的一个线程调用LoadLibrary来载入我们想要的DLL(上篇文章已经讲到了LoadLibrary的 用法)。但是我们不能轻易的控制别人进程中的线程,否则整个操作系统还不乱成一团。幸运的是操作系统给出了一种方式让我们在一个进程中创建自己的线程,这 样我们就可以在这个线程中调用LoadLibrary载入我们的DLL。这种方法就是调用CreateRemoteThread函数。

 

 

 

 

 

2.CreateRemoteThread  2
参数hProcess:这个参数表示创建的线程归哪个进程所有。
参数lpThreadAttributes:这个参数为NULL,线程就会得到默认的安全描述符,并且句柄不能被继承。更多情况自行查询MSDN(https://msdn.microsoft.com/zh-cn/vstudio/ms682437%28v=vs.95%29.aspx
参数dwStackSize:为线程分配的栈大小,如果是0,就分配默认大小。
参数lpStartAddress:这个参数是线程函数的内存地址。
参数lpParameter:线程参数地址。
参数dwCreationFlags:这个标记会控制线程的创建
参数lpThreadId:用来储存新建线程的ID,一般设置为0,代表我们对它没兴趣╭(╯^╰)╮

 

 
接下来就是远程线程注入最巧妙的环节:

我们来查看下CreateRemoteThread的第四个参数,这个参数要求一个线程函数的内存地址,线程函数的创建使用ThreadProc,其定义如下:

3

我们在查看下LoadLibrary的定义:

4

虽 然二者并非完全一样,但是已经足够接近了,二者都接收一个参数,返回一个值,都使用相同的调用约定WINAPI。所以我们现在要做的事就是创建一个线程, 并且把线程函数地址设置为LoadLibraryW(今天LoadLibrary宏多数被扩展为LoadLibraryW)

那我们实际上只需要一行代码:

HANDLE hThread = CreateRemoteThread(hProcessRemote, NULL, 0, LoadLibrary, LC:\\MyLib.dll”, 0, NULL)

我们用CreateRemoteThread在目标进程创建线程,并用线程调用LoadLibrary,并向LoadLibrary内传入C:\\MyLib.dll(需要再入的DLL地址)参数,载入我们的DLL。

一切都是这么美好,然而问题来了。

第 一个问题是我们并不能直接把LoadLibraryW当作第四个参数传给CreateRemoteThread,原因有点难以理解。在编译和链接一个程序 的时候,生成的二进制文件中会包含一个导入段。这个段由一系列转换函数构成,这些转换函数用来跳转到导入的函数。因此,当我们直接用 CreateRemoteThread调用LoadLibraryW时,该引用会被解析为我们模块中的LoadLibraryW地址,如果把这个转换函数 的地址作为远程线程的初始地址传入,我们就根本不知道会执行什么代码。

所以我们需要用到GetProcAddress(上一篇文章已经讲到了)来获取LoadLibrary的准确地址。

GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "LoadLibraryW");

第二个是DLL路径的问题。字符串“C:\\MyLib.dll位于调用进程的地址空间中,我们把这个地址传给新创建的远程线程,但是当LoadLibraryW访问的时候会发生违规访问。所以我们必须把要传入的地址字符串放入远程进程的地址空间中去。

我们需要用到VirtualAllocEx函数

 

 

 

 

3.VirtualAllocEx  5

VirtualAllocEx函数可以让一个进程在另一个进程的地址空间中分配一块内存。
参数hProcess:分配内存的进程句柄。
参数lpAddress:指向分配内存的起始地址,如果为NULL,函数将自己决定在哪里分配内存。
参数dwSize:需要分配的内存大小。
参数flAllocationType:分配类型
参数flProtect:这个参数给区域指定的保护属性
 

 

 

 

 

 
一旦为字符串分配了一块内存,我们还需要一种方法来把字符串从进程地址空间中复制到远程地址空间中去,这里我们就需要用到WriteProcessMemory函数、

 

 

 

 

 

 

 

4.WriteProcessMemory  6
参数hProcess:指向远程进程
参数lpBaseAddress:表示远程进程中的地址
参数lpBuffer:将写入远程进程内存中的包含数据的本地内存地址
参数nSize:传输字节数
参数*lpNumberOfBytesWritten:表示实际传输字节数,如果为NULL,表示忽略。

 

 

 

 

 

 

 

 

 

 

我们来总结下具体流程:

(1).用VirtualAllocEx函数为我们在远程地址空间中分配一块内存。

(2).用WriteProcessMemory函数把我们的DLL路径复制到第1步分配的内存中去。

(3).用GetProcAddress函数来得到LoadLibraryW在kernel32.dll中的实际地址。

(4).用CreateRemoteThread函数在远程进程中创建一个线程,让新线程调用正确的LoadLibrary函数并传入第1步写有DLL路径的内存地址。此时DLL已经被注入到了远程进程的地址空间中了。

(5).使用VirtualFreeEx函数来释放内存。

5.代码如下(RemoteMain.cpp)
// RemoteMain.cpp : 定义控制台应用程序的入口点。

//



#include "stdafx.h"

#include <Windows.h>

#include <tchar.h>



intSetDebugPrivileges(void) {

TOKEN_PRIVILEGES priv = { 0 };

HANDLE hToken = NULL;



if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,&hToken)) {

priv.PrivilegeCount= 1;

priv.Privileges[0].Attributes= SE_PRIVILEGE_ENABLED;



if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &priv.Privileges[0].Luid)) {

if (AdjustTokenPrivileges(hToken, FALSE,&priv, 0, NULL, NULL) ==0) {

printf("AdjustTokenPrivilege Error![%u]\n",GetLastError());

}

}



CloseHandle(hToken);

}

return GetLastError();

}



//注入的主函数

BOOLInjectDll(DWORD dwPID, LPCTSTR szDllPath)

{

HANDLE hProcess = NULL, hThread = NULL;

HMODULE hMod = NULL;

LPVOID pRemoteBuf = NULL;

DWORD dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(TCHAR);

LPTHREAD_START_ROUTINE pThreadProc;



//取得进程句柄

if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))

{

_tprintf(L"OpenProcess(%d)failed!!! [%d]\n", dwPID, GetLastError());

return FALSE;

}

_tprintf(L"hProcess:%x\n", hProcess);



//分配内存

pRemoteBuf =VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);

_tprintf(L"pRemoteBuf:%x\n", pRemoteBuf);



//把DLL地址写入内存

WriteProcessMemory(hProcess,pRemoteBuf, (LPVOID)szDllPath,dwBufSize, NULL);


//获取LoadLibrary具体位置

pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "LoadLibraryW");\

_tprintf(L"pThreadProc:%x\n", pThreadProc);



//创建远程线程,实现DLL注入

hThread =CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);

WaitForSingleObject(hThread,INFINITE);

CloseHandle(hThread);

CloseHandle(hProcess);



return TRUE;



}


//分别传入2个参数,第一个是远程进程的PID码,第二个是我们的DLL地址

int _tmain(int argc, TCHAR *argv[])

{

if (argc != 3)

{

_tprintf(L"USAGE: %s pid dll_path\n", argv[0]);

return 1;

}

//调整访问令牌,提升权限

_tprintf(L"[+]Setting Debug Privileges [%ld]\n",SetDebugPrivileges());



if (InjectDll((DWORD)_tstol(argv[1]), argv[2]))

_tprintf(TEXT("InjectDll(\"%s\") success!!!\n"), argv[2]);

else

_tprintf(L"InjectDll(\"%s\")failed!!!\n", argv[2]);



system("pause");

return 0;

}

仔细分析下这段代码,很容易理解的。

注意:请保证要被注入的程序与注入程序和注入DLL是同位的,不要尝试使用32位程序注入64位(或64位注入32位),建议以管理员权限运行

 


 

 

 

 

我们接下来实际看一下DLL注入的作用(接下讲API Hook和进程隐藏的文章都以DLL注入为基础):

笔者最近在玩一款游戏,我将通过演示用DLL注入来修改游戏中储存金币的内存,其中用CE找金币内存一项步骤已省去,读者有兴趣还可以去查找基址,不过这里都不是我们讨论的内容,省去不表。

7

用CE找到储存金币的内存为2C3F0E08(当然用CE直接修改此处内存也是可以的,不过我们目地是演示DLL注入的作用)

我此时拥有的金币为

8

(原谅手残党的我已经修改过无数次了。。。。。。)

编写注入程序YunxueMain.cpp(是不是暴露什么游戏了。。。)和DLL程序RemoteInjection.cpp

YunxueMain.cpp代码同RemoteMain.cpp代码一样

RemoteInject.cpp代码如下:

// RemoteInjection.cpp : 定义DLL 应用程序的导出函数。

//



#include "stdafx.h"

#include <Windows.h>

#include <tchar.h>

#include <iostream>



using namespacestd;



HINSTANCEg_hInstance = NULL;



DWORD WINAPIThreadProc(LPVOID lParam)

{

//修改金币

int *gold = (int*)0x2C3F0E08;

*gold = 9999999;

MessageBoxA(NULL, "注入成功!修改金币!", "注入成功!",MB_OK);

return 0;

}



BOOL APIENTRYDllMain(HMODULE hModule,

DWORD  ul_reason_for_call,

LPVOID lpReserved

)

{

HANDLE hThread = NULL;

switch (ul_reason_for_call)

{

case DLL_PROCESS_ATTACH:

g_hInstance = hModule;

//使用DebugView可以看到我们注入成功并输出了<RemoteHook.dll> Injection!!!一句话

OutputDebugString(L"<RemoteHook.dll>Injection!!!");

hThread = CreateThread(NULL,0, ThreadProc, NULL, 0, NULL);

CloseHandle(hThread);

break;

case DLL_THREAD_ATTACH:

case DLL_THREAD_DETACH:

case DLL_PROCESS_DETACH:

break;

}

return TRUE;

}

然后我们在命令行里打开,用任务管理器查看游戏PID码

9

命令行里执行程序,输入PID码和DLL路径

10

回车后显示注入并修改成功

11

12

接下来的文章会以DLL注入技术为基础,实现API Hook和进程隐藏一系列功能

 




DLL注入浅析(下)

这一篇文章主要讲基于CreateRemoteThread的DLL注入,最后会以一个游戏修改器来结束此文。

关于DLL注入有注册表注入和远程线程注入两种方式。

此文主讲远程线程注入(即CreateRemoteThread)。

1.    LoadLibrary载入DLL

2.    CreateRemoteThread远程注入及遇到的问题

3.    VirtualAllocEx开辟内存

4.    WriteProcessMemory写入字符串

 

 

 

1.LoadLibrary  1

从 根本上说,DLL注入技术就是要求目标进程中的一个线程调用LoadLibrary来载入我们想要的DLL(上篇文章已经讲到了LoadLibrary的 用法)。但是我们不能轻易的控制别人进程中的线程,否则整个操作系统还不乱成一团。幸运的是操作系统给出了一种方式让我们在一个进程中创建自己的线程,这 样我们就可以在这个线程中调用LoadLibrary载入我们的DLL。这种方法就是调用CreateRemoteThread函数。

 

 

 

 

 

2.CreateRemoteThread  2
参数hProcess:这个参数表示创建的线程归哪个进程所有。
参数lpThreadAttributes:这个参数为NULL,线程就会得到默认的安全描述符,并且句柄不能被继承。更多情况自行查询MSDN(https://msdn.microsoft.com/zh-cn/vstudio/ms682437%28v=vs.95%29.aspx
参数dwStackSize:为线程分配的栈大小,如果是0,就分配默认大小。
参数lpStartAddress:这个参数是线程函数的内存地址。
参数lpParameter:线程参数地址。
参数dwCreationFlags:这个标记会控制线程的创建
参数lpThreadId:用来储存新建线程的ID,一般设置为0,代表我们对它没兴趣╭(╯^╰)╮

 

 
接下来就是远程线程注入最巧妙的环节:

我们来查看下CreateRemoteThread的第四个参数,这个参数要求一个线程函数的内存地址,线程函数的创建使用ThreadProc,其定义如下:

3

我们在查看下LoadLibrary的定义:

4

虽 然二者并非完全一样,但是已经足够接近了,二者都接收一个参数,返回一个值,都使用相同的调用约定WINAPI。所以我们现在要做的事就是创建一个线程, 并且把线程函数地址设置为LoadLibraryW(今天LoadLibrary宏多数被扩展为LoadLibraryW)

那我们实际上只需要一行代码:

HANDLE hThread = CreateRemoteThread(hProcessRemote, NULL, 0, LoadLibrary, LC:\\MyLib.dll”, 0, NULL)

我们用CreateRemoteThread在目标进程创建线程,并用线程调用LoadLibrary,并向LoadLibrary内传入C:\\MyLib.dll(需要再入的DLL地址)参数,载入我们的DLL。

一切都是这么美好,然而问题来了。

第 一个问题是我们并不能直接把LoadLibraryW当作第四个参数传给CreateRemoteThread,原因有点难以理解。在编译和链接一个程序 的时候,生成的二进制文件中会包含一个导入段。这个段由一系列转换函数构成,这些转换函数用来跳转到导入的函数。因此,当我们直接用 CreateRemoteThread调用LoadLibraryW时,该引用会被解析为我们模块中的LoadLibraryW地址,如果把这个转换函数 的地址作为远程线程的初始地址传入,我们就根本不知道会执行什么代码。

所以我们需要用到GetProcAddress(上一篇文章已经讲到了)来获取LoadLibrary的准确地址。

GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "LoadLibraryW");

第二个是DLL路径的问题。字符串“C:\\MyLib.dll位于调用进程的地址空间中,我们把这个地址传给新创建的远程线程,但是当LoadLibraryW访问的时候会发生违规访问。所以我们必须把要传入的地址字符串放入远程进程的地址空间中去。

我们需要用到VirtualAllocEx函数

 

 

 

 

3.VirtualAllocEx  5

VirtualAllocEx函数可以让一个进程在另一个进程的地址空间中分配一块内存。
参数hProcess:分配内存的进程句柄。
参数lpAddress:指向分配内存的起始地址,如果为NULL,函数将自己决定在哪里分配内存。
参数dwSize:需要分配的内存大小。
参数flAllocationType:分配类型
参数flProtect:这个参数给区域指定的保护属性
 

 

 

 

 

 
一旦为字符串分配了一块内存,我们还需要一种方法来把字符串从进程地址空间中复制到远程地址空间中去,这里我们就需要用到WriteProcessMemory函数、

 

 

 

 

 

 

 

4.WriteProcessMemory  6
参数hProcess:指向远程进程
参数lpBaseAddress:表示远程进程中的地址
参数lpBuffer:将写入远程进程内存中的包含数据的本地内存地址
参数nSize:传输字节数
参数*lpNumberOfBytesWritten:表示实际传输字节数,如果为NULL,表示忽略。

 

 

 

 

 

 

 

 

 

 

我们来总结下具体流程:

(1).用VirtualAllocEx函数为我们在远程地址空间中分配一块内存。

(2).用WriteProcessMemory函数把我们的DLL路径复制到第1步分配的内存中去。

(3).用GetProcAddress函数来得到LoadLibraryW在kernel32.dll中的实际地址。

(4).用CreateRemoteThread函数在远程进程中创建一个线程,让新线程调用正确的LoadLibrary函数并传入第1步写有DLL路径的内存地址。此时DLL已经被注入到了远程进程的地址空间中了。

(5).使用VirtualFreeEx函数来释放内存。

5.代码如下(RemoteMain.cpp)
// RemoteMain.cpp : 定义控制台应用程序的入口点。

//



#include "stdafx.h"

#include <Windows.h>

#include <tchar.h>



intSetDebugPrivileges(void) {

TOKEN_PRIVILEGES priv = { 0 };

HANDLE hToken = NULL;



if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,&hToken)) {

priv.PrivilegeCount= 1;

priv.Privileges[0].Attributes= SE_PRIVILEGE_ENABLED;



if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &priv.Privileges[0].Luid)) {

if (AdjustTokenPrivileges(hToken, FALSE,&priv, 0, NULL, NULL) ==0) {

printf("AdjustTokenPrivilege Error![%u]\n",GetLastError());

}

}



CloseHandle(hToken);

}

return GetLastError();

}



//注入的主函数

BOOLInjectDll(DWORD dwPID, LPCTSTR szDllPath)

{

HANDLE hProcess = NULL, hThread = NULL;

HMODULE hMod = NULL;

LPVOID pRemoteBuf = NULL;

DWORD dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(TCHAR);

LPTHREAD_START_ROUTINE pThreadProc;



//取得进程句柄

if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))

{

_tprintf(L"OpenProcess(%d)failed!!! [%d]\n", dwPID, GetLastError());

return FALSE;

}

_tprintf(L"hProcess:%x\n", hProcess);



//分配内存

pRemoteBuf =VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);

_tprintf(L"pRemoteBuf:%x\n", pRemoteBuf);



//把DLL地址写入内存

WriteProcessMemory(hProcess,pRemoteBuf, (LPVOID)szDllPath,dwBufSize, NULL);


//获取LoadLibrary具体位置

pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "LoadLibraryW");\

_tprintf(L"pThreadProc:%x\n", pThreadProc);



//创建远程线程,实现DLL注入

hThread =CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);

WaitForSingleObject(hThread,INFINITE);

CloseHandle(hThread);

CloseHandle(hProcess);



return TRUE;



}


//分别传入2个参数,第一个是远程进程的PID码,第二个是我们的DLL地址

int _tmain(int argc, TCHAR *argv[])

{

if (argc != 3)

{

_tprintf(L"USAGE: %s pid dll_path\n", argv[0]);

return 1;

}

//调整访问令牌,提升权限

_tprintf(L"[+]Setting Debug Privileges [%ld]\n",SetDebugPrivileges());



if (InjectDll((DWORD)_tstol(argv[1]), argv[2]))

_tprintf(TEXT("InjectDll(\"%s\") success!!!\n"), argv[2]);

else

_tprintf(L"InjectDll(\"%s\")failed!!!\n", argv[2]);



system("pause");

return 0;

}

仔细分析下这段代码,很容易理解的。

注意:请保证要被注入的程序与注入程序和注入DLL是同位的,不要尝试使用32位程序注入64位(或64位注入32位),建议以管理员权限运行

 


 

 

 

 

我们接下来实际看一下DLL注入的作用(接下讲API Hook和进程隐藏的文章都以DLL注入为基础):

笔者最近在玩一款游戏,我将通过演示用DLL注入来修改游戏中储存金币的内存,其中用CE找金币内存一项步骤已省去,读者有兴趣还可以去查找基址,不过这里都不是我们讨论的内容,省去不表。

7

用CE找到储存金币的内存为2C3F0E08(当然用CE直接修改此处内存也是可以的,不过我们目地是演示DLL注入的作用)

我此时拥有的金币为

8

(原谅手残党的我已经修改过无数次了。。。。。。)

编写注入程序YunxueMain.cpp(是不是暴露什么游戏了。。。)和DLL程序RemoteInjection.cpp

YunxueMain.cpp代码同RemoteMain.cpp代码一样

RemoteInject.cpp代码如下:

// RemoteInjection.cpp : 定义DLL 应用程序的导出函数。

//



#include "stdafx.h"

#include <Windows.h>

#include <tchar.h>

#include <iostream>



using namespacestd;



HINSTANCEg_hInstance = NULL;



DWORD WINAPIThreadProc(LPVOID lParam)

{

//修改金币

int *gold = (int*)0x2C3F0E08;

*gold = 9999999;

MessageBoxA(NULL, "注入成功!修改金币!", "注入成功!",MB_OK);

return 0;

}



BOOL APIENTRYDllMain(HMODULE hModule,

DWORD  ul_reason_for_call,

LPVOID lpReserved

)

{

HANDLE hThread = NULL;

switch (ul_reason_for_call)

{

case DLL_PROCESS_ATTACH:

g_hInstance = hModule;

//使用DebugView可以看到我们注入成功并输出了<RemoteHook.dll> Injection!!!一句话

OutputDebugString(L"<RemoteHook.dll>Injection!!!");

hThread = CreateThread(NULL,0, ThreadProc, NULL, 0, NULL);

CloseHandle(hThread);

break;

case DLL_THREAD_ATTACH:

case DLL_THREAD_DETACH:

case DLL_PROCESS_DETACH:

break;

}

return TRUE;

}

然后我们在命令行里打开,用任务管理器查看游戏PID码

9

命令行里执行程序,输入PID码和DLL路径

10

回车后显示注入并修改成功

11

12

接下来的文章会以DLL注入技术为基础,实现API Hook和进程隐藏一系列功能

 



发表评论

沙发空缺中,还不快抢~

  1. 首页 › 
  2. 创作 › 
  3. DLL注入浅析(下)
远程注入DLL方法有很多种,也是很多木马病毒所使用的隐藏进程的方法,因为通过程序加载的DLL在进程管理器是没有显示的.这里介绍一种用 CreateRemoteThread 远程建立线程的方式注入DLL. 首先,我们要提升自己的权限,因为远程注入必不可免的要访问到目标进程的内存空间,如果没有足够的系统权限,将无法作任何事.下面是这个函数是用来提升我们想要的权限用的. function EnableDebugPriv: Boolean; var hToken: THandle; tp: TTokenPrivileges; rl: Cardinal; begin Result := false; //打开进程令牌环 OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken); //获得进程本地唯一ID if LookupPrivilegeValue(nil, 'SeDebugPrivilege', tp.Privileges[0].Luid) then begin tp.PrivilegeCount := 1; tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED; //调整权限 Result := AdjustTokenPrivileges(hToken, false, tp, SizeOf(tp), nil, rl); end; end; 关于 OpenProcessToken() 和 AdjustTokenPrivileges() 两个 API 的简单介绍: OpenProcessToken():获得进程访问令牌的句柄. function OpenProcessToken( ProcessHandle: THandle; //要修改访问权限的进程句柄 DesiredAccess: DWORD; //指定你要进行的操作类型 var TokenHandle: THandle//返回的访问令牌指针 ): BOOL; AdjustTokenPrivileges() :调整进程的权限. function AdjustTokenPrivileges( TokenHandle: THandle; // 访问令牌的句柄 DisableAllPrivileges: BOOL; // 决定是进行权限修改还是除能(Disable)所有权限 const NewState: TTokenPrivileges; { 指明要修改的权限,是一个指向TOKEN_PRIVILEGES结构的指针,该结构包含一个数组, 数据组的每个项指明了权限的类型和要进行的操作; } BufferLength: DWORD; //结构PreviousState的长度,如果PreviousState为空,该参数应为 0 var PreviousState: TTokenPrivileges; // 指向TOKEN_PRIVILEGES结构的指针,存放修改前的访问权限的信息 var ReturnLength: DWORD //实际PreviousState结构返回的大小 ) : BOOL; 远程注入DLL其实是通过 CreateRemoteThread 建立一个远程线程调用 LoadLibrary 函数来加载我们指定的DLL,可是如何能让远程线程知道我要加载DLL呢,要知道在Win32系统下,每个进程都拥有自己的4G虚拟地址空间,各个进程之间都是相互独立的。所我们需要在远程进程的内存空间里申请一块内存空间,写入我们的需要注入DLL 的路径. 需要用到的 API 函数有: OpenProcess():打开目标进程,得到目标进程的操作权限,详细参看MSDN function OpenProcess( dwDesiredAccess: DWORD; // 希望获得的访问权限 bInheritHandle: BOOL; // 指明是否希望所获得的句柄可以继承 dwProcessId: DWORD // 要访问的进程ID ): THandle; VirtualAllocEx():用于在目标进程内存空间中申请内存空间以写入DLL的文件名 function VirtualAllocEx( hProcess: THandle; // 申请内存所在的进程句柄 lpAddress: Pointer; // 保留页面的内存地址;一般用nil自动分配 dwSize, // 欲分配的内存大小,字节单位;注意实际分 配的内存大小是页内存大小的整数倍 flAllocationType: DWORD; flProtect: DWORD ): Pointer; WriteProcessMemory():往申请到的空间中写入DLL的文件名 function WriteProcessMemory( hProcess: THandle; //要写入内存数据的目标进程句柄 const lpBaseAddress: Pointer; //要写入的目标进程的内存指针, 需以 VirtualAllocEx() 来申请 lpBuffer: Pointer; //要写入的数据 nSize: DWORD; //写入数据的大小 var lpNumberOfBytesWritten: DWORD //实际写入的大小 ): BOOL; 然后就可以调用 CreateRemoteThread 建立远程线程调用 LoadLibrary 函数来加载我们指定的DLL. CreateRemoteThread() //在一个远程进程中建立线程 function CreateRemoteThread( hProcess: THandle; //远程进程的句柄 lpThreadAttributes: Pointer; //线程安全描述字,指向SECURITY_ATTRIBUTES结构的指针 dwStackSize: DWORD; //线程栈大小,以字节表示 lpStartAddress: TFNThreadStartRoutine; // 一个TFNThreadStartRoutine类型的指针,指向在远程进程中执行的函数地址 lpParameter: Pointer; //传入参数的指针 dwCreationFlags: DWORD; //创建线程的其它标志 var lpThreadId: DWORD //线程身份标志,如果为0, 则不返回 ): THandle; 整个远程注入DLL的具体实现代码如下: function InjectDll(const DllFullPath: string; const dwRemoteProcessId: Cardinal): Boolean; var hRemoteProcess, hRemoteThread: THandle; pszLibFileRemote: Pointer; pszLibAFilename: PwideChar; pfnStartAddr: TFNThreadStartRoutine; memSize, WriteSize, lpThreadId: Cardinal; begin Result := false; // 调整权限,使程序可以访问其他进程的内存空间 if EnableDebugPriv then begin //打开远程线程 PROCESS_ALL_ACCESS 参数表示打开所有的权限 hRemoteProcess := OpenProcess(PROCESS_ALL_ACCESS, false, dwRemoteProcessId); try // 为注入dll文件路径分配内存大小,由于为WideChar,故要乘2 GetMem(pszLibAFilename, Length(DllFullPath) * 2 + 1); // 之所以要转换成 WideChar, 是因为当DLL位于有中文字符的路径下时不会出错 StringToWideChar(DllFullPath, pszLibAFilename, Length(DllFullPath) * 2 + 1); // 计算 pszLibAFilename 的长度,注意,是以字节为单元的长度 memSize := (1 + lstrlenW(pszLibAFilename)) * SizeOf(WCHAR); //使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名空间 pszLibFileRemote := VirtualAllocEx(hRemoteProcess, nil, memSize, MEM_COMMIT, PAGE_READWRITE); if Assigned(pszLibFileRemote) then begin //使用WriteProcessMemory函数将DLL的路径名写入到远程进程的内存空间 if WriteProcessMemory(hRemoteProcess, pszLibFileRemote, pszLibAFilename, memSize, WriteSize) and (WriteSize = memSize) then begin lpThreadId := 0; // 计算LoadLibraryW的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'LoadLibraryW'); // 启动远程线程LoadLbraryW,通过远程线程调用创建新的线程 hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, pszLibFileRemote, 0, lpThreadId); // 如果执行成功返回 True; if (hRemoteThread 0) then Result := true; // 释放句柄 CloseHandle(hRemoteThread); end; end; finally // 释放句柄 CloseHandle(hRemoteProcess); end; end; end; 接下来要说的是如何卸载注入目标进程中的DLL,其实原理和注入DLL是完全相同的,只是远程调用调用的函数不同而已,这里要调用的是FreeLibrary,代码如下: function UnInjectDll(const DllFullPath: string; const dwRemoteProcessId: Cardinal): Boolean; // 进程注入和取消注入其实都差不多,只是运行的函数不同而已 var hRemoteProcess, hRemoteThread: THandle; pszLibFileRemote: PChar; pszLibAFilename: PwideChar; pfnStartAddr: TFNThreadStartRoutine; memSize, WriteSize, lpThreadId, dwHandle: Cardinal; begin Result := false; // 调整权限,使程序可以访问其他进程的内存空间 if EnableDebugPriv then begin //打开远程线程 PROCESS_ALL_ACCESS 参数表示打开所有的权限 hRemoteProcess := OpenProcess(PROCESS_ALL_ACCESS, false, dwRemoteProcessId); try // 为注入dll文件路径分配内存大小,由于为WideChar,故要乘2 GetMem(pszLibAFilename, Length(DllFullPath) * 2 + 1); // 之所以要转换成 WideChar, 是因为当DLL位于有中文字符的路径下时不会出错 StringToWideChar(DllFullPath, pszLibAFilename, Length(DllFullPath) * 2 + 1); // 计算 pszLibAFilename 的长度,注意,是以字节为单元的长度 memSize := (1 + lstrlenW(pszLibAFilename)) * SizeOf(WCHAR); //使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名空间 pszLibFileRemote := VirtualAllocEx(hRemoteProcess, nil, memSize, MEM_COMMIT, PAGE_READWRITE); if Assigned(pszLibFileRemote) then begin //使用WriteProcessMemory函数将DLL的路径名写入到远程进程的内存空间 if WriteProcessMemory(hRemoteProcess, pszLibFileRemote, pszLibAFilename, memSize, WriteSize) and (WriteSize = memSize) then begin // 计算GetModuleHandleW的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'GetModuleHandleW'); //使目标进程调用GetModuleHandleW,获得DLL在目标进程中的句柄 hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, pszLibFileRemote, 0, lpThreadId); // 等待GetModuleHandle运行完毕 WaitForSingleObject(hRemoteThread, INFINITE); // 获得GetModuleHandle的返回值,存在dwHandle变量中 GetExitCodeThread(hRemoteThread, dwHandle); // 计算FreeLibrary的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'FreeLibrary'); // 使目标进程调用FreeLibrary,卸载DLL hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, Pointer(dwHandle), 0, lpThreadId); // 等待FreeLibrary卸载完毕 WaitForSingleObject(hRemoteThread, INFINITE); // 如果执行成功返回 True; if hRemoteProcess 0 then Result := true; // 释放目标进程中申请的空间 VirtualFreeEx(hRemoteProcess, pszLibFileRemote, Length(DllFullPath) + 1, MEM_DECOMMIT); // 释放句柄 CloseHandle(hRemoteThread); end; end; finally // 释放句柄 CloseHandle(hRemoteProcess); end; end; end;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值