DLL注入技术

1.显式载入|卸载DLL模块

在任何时候,进程中的一个线程可以调用下面两个函数来将一个DLL映射到进程的地址空间中:

HMODULE LoadLibrary(PCTSTR pszDLLPathName);

HMODULE LoadLibraryEx(
	PCTSTR pszDLLPathName,
	HANDLE hFile,  //默认NULL
   	DWORD  dwFlags //默认0
);

卸载函数

BOOL FreeLibrary(HMODULE hInstDll);

VOID FreeLibraryAndExitThraed(
	HMODULE hInstDll,
	DWORD   dwExitCode
);

2.得到DLL符号地址

FARPROC GetProcAddress(
	HMODULE hInstDll,
	PCSTR  	pszSymbolName
);

3.Dll入口点函数

BOOL WINAPI DllMain(HINSTANCE hInstDll,DWORD fdwReason,PVOID fImpLoad){
	switch(fdwReason){
		//第一次将DLL映射到进程地址空间中调用
		case DLL_PROCESS_ATTACH:
			break;
		//只有dll已经映射到进程地址空间后,有新线程才会调用这个
		case DLL_THREAD_ATTACH:
			break;
		case DLL_THREAD_DETACH:
			break;
		//将DLL从进程地址空间释放时调用
		case DLL_PROCESS_DETACH:
			break;
	}
	return TRUE;
}

4.DLL注入

4.1.使用注册表注入DLL

首先查找注册表的下面表项:

HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\

该表项中包含AppInit_DLLs和LoadAppInit_DLLs两个键
我们要做的就是将DLL路径写入AppInit_DLLs中并且将LoadAppInit_DLLs置为1

如果想注入多个DLL,注意将DLL放到系统路径,AppInit_DLLs中每一个DLL用空格或者逗号隔开,且第二个DLL开始就不能写路径了。

原理:User32.dll被映射到一个新进程时,会受到DLL_PROCESS_ATTACH通知,当User32.dll对其进行处理的时候,会取得上述注册表的值并调用LoadLibrary载入DLL。

优点:最方便

缺点:
(1)DLL只会被映射到那些使用了User32.dll的进程中
(2)DLL会被映射到所有使用了User32.dll的进程中。

4.2.使用windows挂钩来注入DLL

首先了解一下挂钩函数

HHOOK hHook = SetwindowHookEx(
	int idHook,  //安装的挂钩类型
	HOOKPROC,		//一个函数的地址
	HINSTANCE hMod,		//一个DLL,这个DLL包含了函数
	DWORD dwThreadId				//哪个线程安装hook,为0代表所有线程
);

原理:如果最后一个参数设置为0,那么安装的就是全局消息钩子,这时要求HOOKPROC必须在DLL中,并且指定第3个参数hMod。这样,系统在其他进程中调用HOOKPROC时,如果发现目标DLL尚未加载,就会使用KeUserModeCallback函数回调User32.dll的__ClientLoadLibrary()函数,由User32.dll把这个DLL加载到目标进程中,从而实现将DLL注入到其他进程的目的。

/*-----------------------------------------------------------------------
第12章  注入技术
《加密与解密(第四版)》
(c)  看雪学院 www.kanxue.com 2000-2018
-----------------------------------------------------------------------*/

// MsgHook.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include "MsgHook.h"
#include <stdio.h>

LRESULT CALLBACK MsgHookProc(
										int code,
										WPARAM wParam,
										LPARAM lParam
	);
extern "C" MSGHOOK_API VOID InstallHook();
extern "C" MSGHOOK_API VOID UnInstallHook();

#pragma data_seg(".Share")
HHOOK g_hHook=NULL; //必须赋初值
HMODULE g_hModule = NULL ;
#pragma data_seg()

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
					 )
{
	char szModulePath[MAX_PATH]={0};
	char szBuffer[1024]={0};
    switch (ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH:
			{
				g_hModule = (HMODULE)hModule;
				GetModuleFileName(NULL,szModulePath,MAX_PATH);
				sprintf(szBuffer,"[MsgHook.dll] Injected into %s\n",szModulePath);
				OutputDebugString(szBuffer);
				break;
			}
		case DLL_THREAD_ATTACH:
			break;
		case DLL_THREAD_DETACH:
			break;
		case DLL_PROCESS_DETACH:
			{
				g_hModule = (HMODULE)hModule;
				GetModuleFileName(NULL,szModulePath,MAX_PATH);
				sprintf(szBuffer,"[MsgHook.dll] Unloaded from %s\n",szModulePath);
				OutputDebugString(szBuffer);
				break;
			}
			break;
    }
    return TRUE;
}

LRESULT CALLBACK MsgHookProc(
	int code,
	WPARAM wParam,
	LPARAM lParam
	)
{
	return CallNextHookEx(g_hHook,code,wParam,lParam);
}

extern "C" MSGHOOK_API VOID InstallHook()
{
	g_hHook = SetWindowsHookEx(WH_CALLWNDPROC,MsgHookProc,g_hModule,0);
}

extern "C" MSGHOOK_API VOID UnInstallHook()
{
	UnhookWindowsHookEx(g_hHook);
}

/*-----------------------------------------------------------------------
第12章  注入技术
《加密与解密(第四版)》
(c)  看雪学院 www.kanxue.com 2000-2018
-----------------------------------------------------------------------*/

// MsgHookInject.cpp : Defines the class behaviors for the application.
//

#include "stdafx.h"
#include "MsgHookInject.h"
#include "MsgHookInjectDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/
// CMsgHookInjectApp

BEGIN_MESSAGE_MAP(CMsgHookInjectApp, CWinApp)
	//{{AFX_MSG_MAP(CMsgHookInjectApp)
		// NOTE - the ClassWizard will add and remove mapping macros here.
		//    DO NOT EDIT what you see in these blocks of generated code!
	//}}AFX_MSG
	ON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()

/
// CMsgHookInjectApp construction

CMsgHookInjectApp::CMsgHookInjectApp()
{
	// TODO: add construction code here,
	// Place all significant initialization in InitInstance
}

/
// The one and only CMsgHookInjectApp object

CMsgHookInjectApp theApp;

/
// CMsgHookInjectApp initialization

BOOL CMsgHookInjectApp::InitInstance()
{
	AfxEnableControlContainer();

	// Standard initialization
	// If you are not using these features and wish to reduce the size
	//  of your final executable, you should remove from the following
	//  the specific initialization routines you do not need.

#ifdef _AFXDLL
	Enable3dControls();			// Call this when using MFC in a shared DLL
#else
	Enable3dControlsStatic();	// Call this when linking to MFC statically
#endif

	CMsgHookInjectDlg dlg;
	m_pMainWnd = &dlg;
	int nResponse = dlg.DoModal();
	if (nResponse == IDOK)
	{
		// TODO: Place code here to handle when the dialog is
		//  dismissed with OK
	}
	else if (nResponse == IDCANCEL)
	{
		// TODO: Place code here to handle when the dialog is
		//  dismissed with Cancel
	}

	// Since the dialog has been closed, return FALSE so that we exit the
	//  application, rather than start the application's message pump.
	return FALSE;
}

4.3.使用远程线程注入DLL

远程线程API

HANDLE CreateRemoteThread(
HANDLE hProcess,                            //用来表示新创建的线程所属进程
LPSECURITY_ATTRIBUTES 1pThreadAttributes,   //线程安全属性,NULL
SIZE_T dwStacksize,                         //新线程的堆栈空间大小
LPTHREAD_START_ROUTINE lpStartAddress       //新线程的函数地址
LPVOID lpParameter,                         //传递给新线程的数据
DWORD dwCreationFlags,						//创建线程的方法
LPDWORD lpThreadId                          //用来接收新线程的ID,若为NULL,不返回线程ID
);

两个问题:
(1)LoadLibrary不能直接放到CreateRemoteThread中去,因为对方进程不知道基址,应该先得到对方进程所在内存LoadLibrary的机制
(2)动态库字符串不能直接传递,因为对方内存地址没有该字符串,应该先把字符串传递到对方进程中。

BOOL WINAPI InjectDLLToProcess(DWORD dwTargetPid, LPCSTR DLLPath){
    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwTargetPid);
    if (hProc == NULL) {
        printf("OpenProcess Faild\n");
        return FALSE; 
    }
    //使用VirtualAllocEx函数在远程进程内存地址分配DLL文件名缓冲区
    LPTSTR psLibFileRemote = (LPTSTR)VirtualAllocEx(hProc, NULL, lstrlen((LPTSTR)DLLPath) + 1, MEM_COMMIT, PAGE_READWRITE);
    if (psLibFileRemote == NULL) {
        printf("VirtualAllocEx Faild\n");
        return FALSE;
    }
    //使用WriteProcessMemory函数将DLL路径名复制到远程的内存空间中
    if (WriteProcessMemory(hProc, psLibFileRemote, (LPCVOID)DLLPath, lstrlen((LPTSTR)DLLPath) + 1, NULL) == 0) {
        printf("WriteMemory Failed\n");
        return FALSE;
    }
    //获得远程进程的LoadLibrary地址
    PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryA");

    HANDLE hThread = CreateRemoteThread(hProc, NULL, 0, pfnThreadRtn,psLibFileRemote, 0, NULL);
    if (hThread == NULL) {
        printf("CreateRemoteThread Failed\n");
        return FALSE;
    }
    printf("Inject Successfull\n");
    return TRUE;
}

4.4.QueueUserApc/NtQueueAPCThread APC注入法

当一个线程从等待状态中苏醒时(线程调用SleepEx,SingalObjectAndWait,MsgWaitForMultiple等等)他会检测有没有APC交付给自己。如果有,它就会执行这些APC过程。在用户层,我们可以像创建远程线程一样,使用QueueUserAPC把APC过程添加到目标线程的APC队列中,等这个线程恢复执行时,就会执行我们插入的APC过程了。

DWORD WINAPI QueueUserAPC(
	PAPCFUNC pfnAPC,    //APC函数的地址
	HANDLE   hThread,
	ULONG_PTR dwData    //APC函数的地址
);

在添加用户模式APC后线程不会直接调用APC函数,所以为了增加调用机会,应向所有线程插入APC。

BOOL WINAPI InjectDLLToProcessAPC(DWORD dwTargetPid, LPCSTR DLLPath) {
    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwTargetPid);
    if (hProc == NULL) {
        printf("OpenProcess Faild\n");
        return FALSE;
    }
    //使用VirtualAllocEx函数在远程进程内存地址分配DLL文件名缓冲区
    LPTSTR psLibFileRemote = (LPTSTR)VirtualAllocEx(hProc, NULL, lstrlen((LPTSTR)DLLPath) + 1, MEM_COMMIT, PAGE_READWRITE);
    if (psLibFileRemote == NULL) {
        printf("VirtualAllocEx Faild\n");
        return FALSE;
    }
    //使用WriteProcessMemory函数将DLL路径名复制到远程的内存空间中
    if (WriteProcessMemory(hProc, psLibFileRemote, (LPCVOID)DLLPath, lstrlen((LPTSTR)DLLPath) + 1, NULL) == 0) {
        printf("WriteMemory Failed\n");
        return FALSE;
    }
    CloseHandle(hProc);
    BOOL status = FALSE;
    //定义线程信息结构
    THREADENTRY32 te32 = { sizeof(te32) };
    //创建系统当前线程快照
    HANDLE hThreadShot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
    if (hThreadShot == INVALID_HANDLE_VALUE)
        return FALSE;
    //循环枚举线程信息
    if (Thread32First(hThreadShot, &te32)) {
        do {
            //判断是不是目标进程的子进程
            if (te32.th32OwnerProcessID == dwTargetPid) {
                HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID);
                if (hThread){
                    //向指定线程添加APC
                    DWORD dwRet = QueueUserAPC((PAPCFUNC)LoadLibraryA, hThread, (ULONG_PTR)psLibFileRemote);
                    if (dwRet > 0)
                        status = TRUE;
                    CloseHandle(hThread);
                }
            }
        } while (Thread32Next(hThreadShot, &te32));
    }
    CloseHandle(hThreadShot);
    return status;
}

在实际使用中,由于条件苛刻,能成功利用的机会并不多。但是,如果能够加载驱动,就可以在驱动中向目标进程插入APC,并直接修改线程对象的某些域,使得该线程满足调用APC的条件了。

4.5.SetThreadContext法

在注入DLL时,可以将目标进程中的线程暂停,然后向其写入ShellCode,把线程的context的eip设置为shellcode的地址,这样线程恢复执行时就会先执行我们的shellcode了。在ShellCode中加载目标DLL,然后调回原来的eip执行。

typedef struct INJECT_DATA {
    BYTE ShellCode[0x30];         //0x00
    ULONG_PTR AddrofLoadLibraryA; //0x30
    PBYTE lpDLLPath;              //0x34
    ULONG_PTR OriginalEIP;        //0x38
    char szDllPath[MAX_PATH];     //0x3C
};

__declspec(naked)
VOID ShellCodeFun(VOID) {
    __asm {
        push eax
        pushad
        popfd
        call L001
     L001:
        pop ebx
        sub ebx,8
        push dword ptr ds:[ebx+0x34]
        call dword ptr ds:[ebx+0x30]
        mov eax,dword ptr ds:[eax+0x38]
        xchg eax,[esp+0x24]
        popfd
        popad
        retn
    }
}

BOOL WINAPI InjectDLLToProcessSTC(DWORD dwTargetPid, LPCSTR DLLPath) {
    DWORD dwTidList[1024] = { 0 };
    BOOL status = FALSE;
    int index = 0;
    //首先获取进程中的线程ID
    //定义线程信息结构
    THREADENTRY32 te32 = { sizeof(te32) };
    //创建系统当前线程快照
    HANDLE hThreadShot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
    if (hThreadShot == INVALID_HANDLE_VALUE)
        return FALSE;
    //循环枚举线程信息
    if (Thread32First(hThreadShot, &te32)) {
        do {
            //判断是不是目标进程的子进程
            if (te32.th32OwnerProcessID == dwTargetPid) {
                status = TRUE;
                dwTidList[index++] = te32.th32ThreadID;
            }
        } while (Thread32Next(hThreadShot, &te32));
    }
    CloseHandle(hThreadShot);
    if (!status) {
        printf("该进程无线程");
        return status;
    }
    //打开进程和线程,暂停线程
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwTargetPid);
    HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, dwTidList[0]);
    if (hThread == NULL) {
        printf("打开线程失败");
        return FALSE;
    }
    DWORD swSuppendCnt = SuspendThread(hThread);
    //获得线程的CONTEXT
    CONTEXT context;
    ULONG_PTR uEIP = 0;
    ZeroMemory(&context, sizeof(CONTEXT));
    context.ContextFlags = CONTEXT_FULL;
    GetThreadContext(hThread, &context);
    uEIP = context.Eip;
    //申请内存准备写入ShellCode
    PBYTE lpData = (PBYTE)VirtualAllocEx(hProcess, NULL, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    INJECT_DATA data;
    PBYTE pShellCode = (PBYTE)ShellCodeFun;
    if (pShellCode[0] == 0xE9) {
        pShellCode = pShellCode + *(ULONG*)(pShellCode + 1) + 5;
    }
    memcpy(data.ShellCode, pShellCode, 0x30);
    lstrcpyA(data.szDllPath,DLLPath);
    PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryA");
    data.AddrofLoadLibraryA = (ULONG_PTR)pfnThreadRtn;
    data.OriginalEIP = uEIP;
    data.lpDLLPath = lpData + FIELD_OFFSET(INJECT_DATA,szDllPath);
    printf("Shellcode填充完毕");
    if (!WriteProcessMemory(hProcess, lpData, &data, sizeof(INJECT_DATA), NULL)) {
        printf("写入失败!");
        return FALSE;
    }
    context.Eip = (ULONG)lpData;
    SetThreadContext(hThread, &context);
    ResumeThread(hThread);
    CloseHandle(hProcess);
    CloseHandle(hThread);
    printf("注入动态库成功!");
    return TRUE;
}

4.6.输入表项DLL替换法(DLL劫持法)

注册表有一个叫KnownDLLs的设置项

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs

这里面存在的库是不能替换的,因为加载的时候首先会查找该目录,如果该目录没有,可以放在exe根目录下面进行替换。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
远程注入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、付费专栏及课程。

余额充值