將dll從進程模組列表中移除並保持正常運行

來源:互聯網
上載者:User

將dll從進程模組列表中移除並保持正常運行,這玩意想想是挺簡單,n久前byshell就用了,

簡單的思路就是給當前的dll記憶體映像做份拷貝,然後跳到那份拷貝的地址空間的代碼,回頭free掉原來的dll,

然後馬上用VirtualAlloc在原基址上申請塊同樣大小的空間,並將那份拷貝還原回去,再跳回去執行。完。

代碼寫完後再實際程式中應用後發現問題,一旦調用到malloc或new的話就會崩潰,
於是去看了下byshell的代碼,發現他申請記憶體是用VirtualAlloc,但是我大量的代碼都是用new來分配,而且類對象不好用VirtualAlloc來分配吧~

經調式才清楚,原來每個dll,windows都會給其分配一個heap,
在freelibrary時系統會將其完全destroy掉,不怕你記憶體泄露。

調試時在FreeLibrary後HeapDestroy斷點正好觸發一次,就是這一次那個heap給銷毀了。

解決辦法就是在FreeLibrary前修改HeapDestroy的第一條指令為return,FreeLibrary後再修複。

實踐證明,穩定......

*/

#include <windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <psapi.h>

#pragma comment(lib, "Psapi.lib")

#define __printf printf

void HideLibrary(HMODULE hModule, LPVOID pCallBackAddr, LPVOID lParam);

typedef struct
{
      HMODULE lpDllBase;
      LPVOID lpNewDllBase;
      PTHREAD_START_ROUTINE pAddress;
      LPVOID lParam;
}UNLOADLIB_CALLBACK, *PUNLOADLIB_CALLBACK;

typedef
LPVOID WINAPI VIRTUALALLOC(
    LPVOID lpAddress,
    SIZE_T dwSize,
    DWORD flAllocationType,
    DWORD flProtect
);

typedef
BOOL WINAPI VIRTUALFREE(
    LPVOID lpAddress,
    SIZE_T dwSize,
    DWORD dwFreeType
);

typedef
BOOL WINAPI HEAPDESTROY(
    HANDLE hHeap
);

typedef
HMODULE WINAPI LOADLIBRARY(
    LPCTSTR lpFileName
);

typedef
HANDLE WINAPI CREATETHREAD(
    LPSECURITY_ATTRIBUTES lpThreadAttributes,
    SIZE_T dwStackSize,
    LPTHREAD_START_ROUTINE lpStartAddress,
    LPVOID lpParameter,
    DWORD dwCreationFlags,
    LPDWORD lpThreadId
);

typedef void *    __cdecl MEMCPY(void *, const void *, size_t);

BOOL incLibraryCount(HMODULE hMe)
{
      //FreeLibrary後很多系統dll也會free掉,所以將所有已載入的再load一次以增加計數
    
      HANDLE hModsSnap =    CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);

      if(INVALID_HANDLE_VALUE == hModsSnap)
      {
          return FALSE;
      }

      MODULEENTRY32 meModuleEntry;
      meModuleEntry.dwSize = sizeof(MODULEENTRY32);

      if(!Module32First(hModsSnap, &meModuleEntry))
      {
          CloseHandle(hModsSnap);
          return FALSE;
      }
      do
      {
          if(LoadLibrary(meModuleEntry.szModule) == hMe)
              FreeLibrary(hMe);

      } while(Module32Next(hModsSnap, &meModuleEntry));

      CloseHandle(hModsSnap);

      return TRUE;
}

//枚舉指定進程的所有線程
DWORD WINAPI EnumAndSetThreadState(LPVOID lParam)
{
      HANDLE hThreadSnap = NULL;
      THREADENTRY32 te32;
      memset(&te32,0,sizeof(te32));
      te32.dwSize = sizeof(THREADENTRY32);
      hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,0);

      DWORD myThreadId = GetCurrentThreadId();
      DWORD pid = GetCurrentProcessId();

      if (Thread32First(hThreadSnap, &te32))
      {
          do
          {
               if (pid == te32.th32OwnerProcessID)
               {
                   if(myThreadId != te32.th32ThreadID)
                   {
                       HANDLE hThread = OpenThread(
                           THREAD_SUSPEND_RESUME,
                           FALSE,
                           te32.th32ThreadID);

                       if(hThread != NULL)
                       {
                           if((int)lParam)
                               ResumeThread(hThread);
                           else
                               SuspendThread(hThread);

                           CloseHandle(hThread);
                       }
                   }
               }
          }
          while (Thread32Next(hThreadSnap,&te32));
      }
      CloseHandle( hThreadSnap );

      return 0;
}

DWORD WINAPI GotoCallBackAddr(LPVOID lParam)
{
      PUNLOADLIB_CALLBACK cbFunc = (PUNLOADLIB_CALLBACK)lParam;

      DWORD dwThreadId;
      HANDLE hThread;

      if(cbFunc->pAddress)
      {
          hThread = CreateThread(
              NULL,
              0,
              cbFunc->pAddress,
              cbFunc->lParam,
              0,
              &dwThreadId);

          if(hThread)
              CloseHandle(hThread);
      }

      //那份dll的拷貝不需要了,釋放~
      VirtualFree(cbFunc->lpNewDllBase, 0, MEM_DECOMMIT);
      delete cbFunc;

      return 0;
}

DWORD WINAPI UnLoadLibrary(LPVOID lParam)
{
      //__asm INT 3
      __printf("UnLoadLibrary Entry.\r\n");

      BYTE HeapDestroy_HookCode_bak[4];
      BYTE HeapDestroy_HookCode[4] = "\xC2\x04\x00";//RETN 0004
      MODULEINFO modinfo;
      DWORD oldProtect;

      PUNLOADLIB_CALLBACK cbFunc = (PUNLOADLIB_CALLBACK)lParam;

      HMODULE hDllInstance = cbFunc->lpDllBase;
      char dllpath_bak[MAX_PATH];

      GetModuleFileName(hDllInstance, dllpath_bak, sizeof(dllpath_bak));
      GetModuleInformation(GetCurrentProcess(), hDllInstance, &modinfo, sizeof(MODULEINFO));

      //給所有dll(除了自己)增加計數,防止FreeLibrary的時候那些dll給系統卸載掉
      incLibraryCount(hDllInstance);

      //保險起見,掛起其他線程,搞定後再恢複
      EnumAndSetThreadState((LPVOID)FALSE);

      //FreeLibrary之後原來存放api地址的記憶體也會被釋放,
      //但是FreeLibrary之後還有些動作,趁現在還沒free,關鍵API記下來
      VIRTUALALLOC *_VirtualAlloc = (VIRTUALALLOC*)
          GetProcAddress(GetModuleHandle("kernel32.dll"), "VirtualAlloc");
      LOADLIBRARY    *_LoadLibrary    = (LOADLIBRARY*)
          GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
      CREATETHREAD *_CreateThread = (CREATETHREAD*)
          GetProcAddress(GetModuleHandle("kernel32.dll"), "CreateThread");
      MEMCPY         *_memcpy         = (MEMCPY*)
          GetProcAddress(GetModuleHandle("ntdll.dll"), "memcpy");

      //這個很關鍵,並不是我要調用,是 FreeLibrary 時系統會調用,我要hook它,
      //不能給系統破壞這個heap,否則之後的dll貌似能工作,
      //但卻不能用new或malloc申請記憶體, VirtualAlloc可以代替之,
      //但如果改寫好多代碼是划不來的,況且一些代碼不好改,如list<T>的push內部的new

      HEAPDESTROY *_HeapDestroy    = (HEAPDESTROY*)
          GetProcAddress(GetModuleHandle("kernel32.dll"), "HeapDestroy");

      VirtualProtect(_HeapDestroy, 3, PAGE_EXECUTE_READWRITE, &oldProtect);

      //修改第一條指令為直接返回
      _memcpy(HeapDestroy_HookCode_bak, _HeapDestroy, 3);
      _memcpy(_HeapDestroy, HeapDestroy_HookCode, 3);

      //Sleep(100);
      //終於到這裡了~~~^_^!
      FreeLibrary(hDllInstance);//釋放

      //修複剛hook的函數
      _memcpy(_HeapDestroy, HeapDestroy_HookCode_bak, 3);
      //_memcpy(_RtlFreeHeap, RtlFreeHeap_HookCode_bak, 3);

      //在原來的dll基址申請同樣大小的記憶體,並把之前的那份dll拷貝還原回去
      if(_VirtualAlloc(hDllInstance,
          modinfo.SizeOfImage,
          MEM_COMMIT|MEM_RESERVE,
          PAGE_EXECUTE_READWRITE) == NULL
          )
      {
          //失敗,載入原來dll, 以正常方式工作
          //注意,不宜在dllmain中調用HideLibrary,LoadLibrary將導致dllmain再次被調用,導致死迴圈啦
          HMODULE hDll = _LoadLibrary(dllpath_bak);

          //重新計算回呼函數在hDll地址空間的地址
          cbFunc->pAddress = (LPTHREAD_START_ROUTINE)
              ((DWORD)cbFunc->pAddress - (DWORD)hDllInstance + (DWORD)hDll);

          LPTHREAD_START_ROUTINE pFunc1 = (LPTHREAD_START_ROUTINE)
              ((DWORD)EnumAndSetThreadState - (DWORD)hDllInstance + (DWORD)hDll);

          //恢複被掛起的線程
          _CreateThread(0, 0, pFunc1, (LPVOID)TRUE, 0, 0);

          //調用回呼函數
          if(cbFunc->pAddress)
              _CreateThread(0, 0, cbFunc->pAddress, cbFunc->lParam, 0, 0);

          return 0;
      }

      _memcpy(hDllInstance, cbFunc->lpNewDllBase, modinfo.SizeOfImage);

      //恢複被掛起的線程
      EnumAndSetThreadState((LPVOID)TRUE);

      //跳回原dll地址空間的GotoCallBackAddr,由它來釋放這邊VirtualAlloc申請的指標
      _CreateThread(0, 0, GotoCallBackAddr, cbFunc, 0, 0);

      return 0;
}

DWORD WINAPI HideLibrary02(LPVOID lParam)
{
      //__asm INT 3
      __printf("HideLibrary02 Entry.\r\n");

      PUNLOADLIB_CALLBACK cbFunc = (PUNLOADLIB_CALLBACK)lParam;

      MODULEINFO modinfo;

      GetModuleInformation(GetCurrentProcess(), cbFunc->lpDllBase, &modinfo, sizeof(MODULEINFO));

      //申請一塊和當前dll同樣大小的記憶體
      cbFunc->lpNewDllBase = VirtualAlloc(NULL, modinfo.SizeOfImage, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);

      if(cbFunc->lpNewDllBase == NULL)
          return FALSE;

      //給當前dll做份拷貝,複製所有資料到剛申請的記憶體,
      memcpy(cbFunc->lpNewDllBase, modinfo.lpBaseOfDll, modinfo.SizeOfImage);

      //計算在copy中UnLoadLibrary的地址,並另起線程到該地址執行
      void *pNewUnLoadLibrary = LPVOID((DWORD)cbFunc->lpNewDllBase + (DWORD)UnLoadLibrary - (DWORD)modinfo.lpBaseOfDll);

      DWORD ThreadId;
      HANDLE hThread = CreateThread(0,0,
          (LPTHREAD_START_ROUTINE)pNewUnLoadLibrary, (LPVOID)cbFunc, CREATE_SUSPENDED, &ThreadId);

      if(hThread == NULL)
      {
          VirtualFree(cbFunc->lpNewDllBase, 0, MEM_DECOMMIT);
          delete cbFunc;

          return FALSE;
      }

      ResumeThread(hThread);
      CloseHandle(hThread);

      return TRUE;
}

void HideLibrary(HMODULE hModule, LPVOID pCallBackAddr, LPVOID lParam)
{
      __printf("HideLibrary Entry.\r\n");

      PUNLOADLIB_CALLBACK lparam = new UNLOADLIB_CALLBACK;

      lparam->lpDllBase      = hModule;
      lparam->lpNewDllBase = NULL;
      lparam->pAddress       = (PTHREAD_START_ROUTINE)pCallBackAddr;
      lparam->lParam         = lParam;

      HANDLE hThread = CreateThread(0,0,
          HideLibrary02, (LPVOID)lparam, 0, NULL);

      if(hThread == NULL)
      {
          __printf("CreateThread HideLibrary02 Failed.\r\n");

          delete lparam;
          return;
      }

      CloseHandle(hThread);

      return;
}

///////////////////////////////////////////////////////////
//example
//rundll32 UnloadLibrary.dll,Test

DWORD WINAPI testThread(LPVOID lParam)
{
      char *ptest = new char[512];

      sprintf(ptest, "My Pid: %d\r\n 用一些軟體查看下,找不到這個dll吧?", GetCurrentProcessId());

      MessageBox(0, ptest, (char*)lParam, 0);

      delete ptest;

      return 0;
}

DWORD WINAPI testThread02(LPVOID lParam)
{

      HMODULE hModule = (HMODULE)lParam;

      char *ptest = new char[512];

      sprintf(ptest, "testThread02\r\n");

      Sleep(500);

      MessageBox(0, ptest, "start", 0);

      //不設定回呼函數,成功還好,一旦失敗則gameover
      HideLibrary(hModule, 0, 0);

      sprintf(ptest, "My Pid: %d\r\n 用一些軟體查看下,找不到這個dll吧?", GetCurrentProcessId());
      MessageBox(0, ptest, "ok", 0);

      delete ptest;

      return 0;
}

HMODULE hDll;
extern "C"
{
__declspec(dllexport) int Test();
}

int Test()
{
      //1
      //主要線程開始工作後再HideLibrary不是很安全
      //CreateThread(0, 0, testThread02, hDll, 0, 0);
//
      //2
      //free掉dll後再開啟主要背景工作執行緒妥當些
      char *p = strdup("hello");
      HideLibrary(hDll, testThread, p);

      Sleep(60000);

      return 0;
}

BOOL APIENTRY DllMain( HANDLE hModule,
                         DWORD    ul_reason_for_call,
                         LPVOID lpReserved
                       )
{
      switch (ul_reason_for_call)
      {
      case DLL_PROCESS_ATTACH:
          {
              hDll = (HMODULE)hModule;

          }
          break;

      }
    
      return TRUE;
}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.