DLL卸载的工作原理

使用CreateRemoteThread()API进行DLL注入的工作原理概括如下:
驱使目标进程调用LoadLibrary() API
同样,DLL卸载工作原理也非常简单:
驱使目标进程调用FreeLibrary() API
也就是说,将FreeLibrary() API的地址传递给CreateRemoteThread()的lpStartAddress参数,并把要卸载的DLL
的句柄传递给lpParameter参数。

实现DLL卸载

EjectDll.exe程序,用来从进程目标(notepad.exe)卸载指定的DLL文件,源码如下:

  1. #include "windows.h"
  2. #include "tjhelp32.h"
  3. #include "tchar.h"
  4. #define DEF_PROC_NAME(L"notepad.exe")
  5. #define DEF_DLL_NAME (L"myhack.dll")
  6. DWORD FindProcessID(LPCTSTR szProcessName){
  7. DWORD dwPID=0xFFFFFFFF;
  8. HANDLE hSnapShot=INVALID_HANDLE_VALUE;
  9. PROCESSENTRY32 pe;
  10. //获取系统快照(Snapshot)
  11. pe.dwSize=sizeof(PROCESSENTRY32);
  12. hSnapShot=CreateToolhelp32Snapshot(TH32CS_SNAPALL,NULL);
  13. //查找进程
  14. Process32First(hSnapShot, &pe);
  15. do{
  16. if(!_tcs/Picmp(szProcessName, (LPCTSTR)pe.szExeFile)){
  17. dwPID=pe.th32ProcessID;
  18. break;
  19. }
  20. }
  21. while(Process32Next(hSnapShot, &pe));
  22. CloseHandle(hSnapShot);
  23. return dwPID;
  24. }
  25. BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege){
  26. TOKEN_PRIVILEGES tp;
  27. HANDLE hToken;
  28. LUID luid;
  29. if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)){
  30. _tprintf(L"OpenProcessToken error: %u\n", GetLastError());
  31. return FALSE;
  32. }
  33. if(!LookupPrivilegeValue(NULL, //lookup privilege on local system
  34. lpszPrivilege, //privilege to lookup
  35. &luid)){ //receives LUID of privilege
  36. _tprintf(L"LookupPrivilegeValue error: %u\n", GetLastError());
  37. return FALSE;
  38. }
  39. tp.PrivilegeCount=1;
  40. tp.Privileges[0].Luid=luid;
  41. if(bEnablePrivilege)
  42. tp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;
  43. else
  44. tp.Privileges[0].Attributes=0;
  45. //Enable the privilege or disable all privileges
  46. if(!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES) NULL, (PDWORD) NULL)){
  47. _tprintf(L"AdjustTokenPrivileges error: %u\n", GetLastError());
  48. return FALSE;
  49. }
  50. if(GetLastError()==ERROR_NOT_ALL_ASSIGNED){
  51. _tprintf(L"The token does not have the specified privilege. \n");
  52. return FALSE;
  53. }
  54. return TRUE;
  55. }
  56. BOOL EjectDll(DWORD dwPID, LPCTSTR szDllName){
  57. BOOL bMore=FALSE, bFound=FALSE;
  58. HANDLE hSnapshot,hProcess, hThread;
  59. HMODULE hModule=NULL;
  60. MODULEENTRY32 me={sizeof(me)};
  61. LPTHREAD_START_ROUTINE pThreadProc;
  62. //dwPID=notepad进程ID
  63. //使用TH32CS_SNAPMODULE参数,获取加载到notepad进程的DLL名称
  64. hSnapshot=CreateToolhelp32Snapshot(TH32CS-SNAPMODULE, dwPID);
  65. bMore=Module32First(hSnopshot, &me);
  66. for(; bMore; bMore=Module32Next(hSnapshot, &me)){ //对dll进行比较
  67. if(!_tcsicmp((LPCTSTR)me.szModule, szDllName) || !_tcsicmp((LPCTSTR)me.szExePath, szDllName)){ //szModule代表DLL的名称
  68. bFound=TRUE;
  69. break;
  70. }
  71. }
  72. if(!bFound){
  73. CloseHandle(hSnapshot);
  74. return FALSE;
  75. }
  76. //获取目标进程的句柄
  77. if(!(hProcess=OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID))){
  78. _tprintf(L"OpenProcess(%d) failed !!! [%d]\n", dwPID, GetLastError());
  79. return FALSE;
  80. }
  81. //获取FreeLibrary() API地址
  82. hModule=GetModuleHandle(L"kernel32.dll");
  83. pThreadProc=(LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "FreeLibrary");
  84. //在目标进程中运行线程
  85. hThread=CreateRemoteThread(hProcess, NULL, 0, pThreadProc, me.modBaseAddr, 0, NULL); //pThreadProc为FreeLibrary() API的地址,me.modBaseAddr是要卸载的DLL的加载地址
  86. WaitForSingleObject(hThread, INFINITE);
  87. CloseHandle(hThread);
  88. CloseHandle(hProcess);
  89. CloseHandle(hSnapshot);
  90. return TRUE;
  91. }
  92. int _tmain(int argc, TCHAR* argv[]){
  93. DWORD dwPID=0xFFFFFFFF;
  94. //查找process
  95. dwPID=FindProcessID(DEF_PROC_NAME);
  96. if(dwPID==0xFFFFFFFF){
  97. _tprintf(L"There is no %s process!\n", DEF_PROC_NAME);
  98. return 1;
  99. }
  100. _tprintf(L"PID of \"%s\" is %d\n", DEF_PROC_NAME, dwPID);
  101. //更改privilege
  102. if(!SetPrivilege(SE_DEBUG_NAME, TRUE))
  103. return 1;
  104. //eject dll
  105. if(EjectDll(dwPID, DEF_DLL_NAME))
  106. _tprintf(L"EjectDll(%d, \"%s\") success!!!\n", dwPID, DEF_DLL_NAME);
  107. else
  108. _tprintf(L"EjectDll(%d, \"%s\") gailed!!!\n", dwPID, DEF_DLL_NAME);
  109. return 0;
  110. }

卸载DLL的原理是驱使目标对象自己调用FreeLibrary() API,上述代码中的EjectDll()函数就是用来卸载DLL的。

获取进程中加载的DLL信息

hSnapshot=CreateToolhelp32Snapshot(TH32CS-SNAPMODULE, dwPID);
使用CreateToolhelp32Snapshot() API可以获取加载到进程的模块(DLL)信息。将获取的hSnapshot句柄传递
给Module32First()/Module32Next()函数后,即可设置与MODULEENTRY32结构体相关模块信息。代码24-2为该结构体的定义。

  1. typedef struct tagMODULEENTRY32
  2. {
  3. DWORD dwSize;
  4. DWORD th32ModuleID;
  5. DWORD th32ProcessID;
  6. DWORD GlblcntUsage;
  7. DWORD ProccntUsage;
  8. BYTE * modBaseAddr;
  9. DWORD modBaseSize;
  10. HMODULE hModule;
  11. char szModule[MAX_MODULE_NAME32 + 1];
  12. char szExePath[MAX_PATH];
  13. } MODULEENTRY32