首页
社区
课程
招聘
[原创]聊聊游戏辅助那些事上部第五篇——如何隐藏一个DLL模块?
发表于: 2017-7-24 12:45 17436

[原创]聊聊游戏辅助那些事上部第五篇——如何隐藏一个DLL模块?

2017-7-24 12:45
17436

聊聊游戏辅助那些事上部第五篇——如何隐藏一个DLL模块?

隐藏DLL第一个方法是断链,这个比较简单:

VOID HideModule(HMODULE hLibModule)
{
 PPEB_LDR_DATA pLdr = NULL;
 PLDR_MODULE  FirstModule = NULL;
 PLDR_MODULE  GurrentModule = NULL;
 __try
 {
  __asm
  {
   mov esi, fs:[0x30]
   mov esi, [esi + 0x0C]
   mov pLdr, esi
  }

  FirstModule = (PLDR_MODULE)(pLdr->InLoadOrderModuleList.Flink);
  GurrentModule = FirstModule;
  while (!(GurrentModule->BaseAddress == hLibModule))
  {
   GurrentModule = (PLDR_MODULE)(GurrentModule->InLoadOrderModuleList.Blink);
   if (GurrentModule == FirstModule)
    break;
  }
  if (GurrentModule->BaseAddress != hLibModule)
   return;

  //
  // Dll解除链接
  //
  ((PLDR_MODULE)(GurrentModule->InLoadOrderModuleList.Flink))->InLoadOrderModuleList.Blink = GurrentModule->InLoadOrderModuleList.Blink;
  ((PLDR_MODULE)(GurrentModule->InLoadOrderModuleList.Blink))->InLoadOrderModuleList.Flink = GurrentModule->InLoadOrderModuleList.Flink;

  memset(GurrentModule->FullDllName.Buffer, 0, GurrentModule->FullDllName.Length);
  memset(GurrentModule, 0, sizeof(PLDR_MODULE));

  PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)hLibModule;
  PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)(LPBYTE(hLibModule) + dosHeader->e_lfanew);

  if ((dosHeader->e_magic == IMAGE_DOS_SIGNATURE) && (ntHeaders->Signature == IMAGE_NT_SIGNATURE))
  {
   //
   // 这里可能引起写保护异常,需要用 VirtualProtect 解除写保护。
   //
   memset(dosHeader, 0, sizeof(*dosHeader));
   memset(ntHeaders, 0, sizeof(*ntHeaders));
  }
 }

 __except (EXCEPTION_EXECUTE_HANDLER)
 {
  return;
 }
}

源码包中,HideDll.cpp 中保留了相关源码,虽然没有在程序中用到。
缺点是,只是常规的搜索方法中找不到相关的DLL模块,比如用 CreateToolhelp32Snapshot 查找模块。或者用NT函数。但用下面的源码就可以找到断链的DLL模块:

 MEMORY_BASIC_INFORMATION mbi_thunk;
 PVOID AllocationBase = NULL;
 TCHAR FilePath[MAX_PATH];
 for (LPSTR Addr = (LPSTR)0x00000000; ::VirtualQueryEx(hProcess, Addr, &mbi_thunk, sizeof(mbi_thunk)); Addr = LPSTR(mbi_thunk.BaseAddress) + mbi_thunk.RegionSize)
 {
  if ((mbi_thunk.AllocationBase > AllocationBase) && (GetMappedFileName(hProcess, mbi_thunk.BaseAddress, FilePath, _countof(FilePath)) > 0))
  {
   AllocationBase = mbi_thunk.AllocationBase;
   KdPrint((_T("MODULE:%x, %s\r\n"), AllocationBase, FilePath));
  }
 }

原形毕露了吧,哈哈。

第二个方法直接加载到内存。源码是一个宿主LoadDll.dll, 和一个要加载的寄生Inject.dll。Inject.dll加载为LoadDll.dll的一个资源 IDR_INJECT。LoadDll.dll 在加载完 Inject.dll 后主动 FreeLibrary 自己。

有几点需要注意:
1、VS2012以上版本编译的寄生DLL需要添加:项目属性->C/C++->命令行的其它选项中添加:/Zc:threadSafeInit- ,否则会产生莫名其妙的错误,分析是执行TLS时出错,暂时没有找到完美的解决方法。
2、dll_dllmain.cpp 的包含目录添加 $(VCInstallDir)crt\src\vcruntime,测试通过的是VS2015版本运行库。
3、dll_dllmain.cpp 的预处理器添加 _VCRT_BUILD
4、项目属性->链接器->高级的入口点设置为:_DllMainCRTStartup

几个重点函数:

DLL入口函数:汇编的工作是加载完成后直接调用FreeLibrary

extern "C" __declspec(naked) __declspec(dllexport) BOOL WINAPI _DllMainCRTStartup(
    HINSTANCE const instance,
    DWORD     const reason,
    LPVOID    const reserved
    )
{
 //
 // 建立堆栈
 //
 _asm push ebp
 _asm mov ebp, esp
 
 if (reason == DLL_PROCESS_ATTACH)
    {
        // The /GS security cookie must be initialized before any exception
        // handling targeting the current image is registered.  No function
        // using exception handling can be called in the current image until
        // after this call:
        __security_init_cookie();
    }

 if (dllmain_dispatch(instance, reason, reserved))
 {
  if (reason == DLL_PROCESS_ATTACH)
  {
   _asm mov esp, ebp
   _asm pop ebp
   _asm pop eax      // 返回地址
   _asm add esp, 0x0C    // 调用参数堆栈
   _asm push DWORD PTR[esp - 0x0C] // FreeLibrary 的参数 instance 压栈
   _asm push eax      // 返回地址 压栈
   _asm jmp FreeLibrary    // 转去调用 FreeLibrary
  }

  _asm mov eax, 1
 }
 else
 {
  _asm xor eax, eax
 }

 _asm mov esp, ebp
 _asm pop ebp
 _asm ret 0x0C
}

//
// 设置加载次数,静态加载的DLL的加载次数是 -1, FreeLibrary 不能释放DLL
//
void PreprocessUnloadDll(HMODULE hLibModule)
{
 PPEB_LDR_DATA pLdr = NULL;
 PLDR_MODULE  FirstModule = NULL;
 PLDR_MODULE  GurrentModule = NULL;
 __try
 {
  __asm
  {
   mov esi, fs:[0x30]
   mov esi, [esi + 0x0C]
   mov pLdr, esi
  }

  FirstModule = (PLDR_MODULE)(pLdr->InLoadOrderModuleList.Flink);
  GurrentModule = FirstModule;
  while (!(GurrentModule->BaseAddress == hLibModule))
  {
   GurrentModule = (PLDR_MODULE)(GurrentModule->InLoadOrderModuleList.Blink);
   if (GurrentModule == FirstModule)
   {
    return;
   }
  }

  //
  // 设置 LDRP_PROCESS_ATTACH_CALLED
  //
  GurrentModule->Flags |= LDRP_PROCESS_ATTACH_CALLED;

  //
  // 设置
  //
  int oldLoadCount = GurrentModule->LoadCount;
  GurrentModule->LoadCount = 1;
  return;
 }

 __except (EXCEPTION_EXECUTE_HANDLER)
 {
  return;
 }
}

//
// 装载寄生DLL, GetClassInfoEx, RegisterClassEx 在这儿可不是为了创建窗口,只是为了保存一下加载到内存的DLL模块。我想来想去好像是这对哥们最造合这个工作
//
HMODULE LoadInjectModule(HMODULE hModule)
{
 KdFunctionLog();
 HMODULE memdll = NULL;
 HRSRC hRsrc = FindResource(hModule, MAKEINTRESOURCE(IDR_INJECT), _T("DLL"));
 if (hRsrc)
 {
  HGLOBAL hGlobal = ::LoadResource(hModule, hRsrc);
  if (hGlobal)
  {
   LPCVOID pData = ::LockResource(hGlobal);

   WCHAR ProcessMappedFileNameW[MAX_PATH];
   size_t nLength = LoadString(hModule, IDR_INJECT, ProcessMappedFileNameW, sizeof(ProcessMappedFileNameW));
   if (nLength < 1)
   {
    wcscpy_s(ProcessMappedFileNameW, L"LOADDLL_UNNAMED");
    nLength = 15;
   }

   //
   // 其实这步可以省略,GetClassInfoEx, RegisterClassEx 使用的名字空间限于进程内。
   //
   swprintf_s(ProcessMappedFileNameW + nLength, sizeof(ProcessMappedFileNameW)/sizeof(ProcessMappedFileNameW[0]) - nLength, _T("#%04X"), GetCurrentProcessId());

   WNDCLASSEX wndcls = { sizeof(WNDCLASSEX) };
   if (GetClassInfoEx((HINSTANCE)GetModuleHandle(NULL), ProcessMappedFileNameW, &wndcls))
   {
    memdll = (HMODULE)wndcls.lpfnWndProc;
    if (memdll)
    {
     KdPrint((_T("DLL 再次加载 : %x\r\n"), memdll));
    }
   }
   else
   {
    memdll = CustomLoadLibrary(pData, SizeofResource(hModule, hRsrc));
    LPSTR ThisModuleNameA = (LPSTR)CustomGetProcAddress(memdll, "ThisModuleFileNameA");
    if (ThisModuleNameA)
    {
     GetThisModuleFileNameA(ThisModuleNameA, MAX_PATH);
    }

    if (memdll)
    {
     wndcls.hInstance = (HINSTANCE)GetModuleHandle(NULL);
     wndcls.lpfnWndProc = (WNDPROC)memdll;
     wndcls.lpszClassName = ProcessMappedFileNameW;
    }

    ATOM w = RegisterClassEx(&wndcls);

    KdPrint((_T("DLL 首次加载 : %x\r\n"), memdll));
   }
   UnlockResource(hRsrc);
  }
 }
 return memdll;
}


MemoryModule.cpp 是网上找到的内存加载DLL源码修改而成,我把它改成的模板函数,支持多种加载形式。

本系列的第六篇估计要夭折了,本来计划以发布一个辅助模板作为上部的结尾,但是现在看来技术上还不够成熟。不过想一试半成品的可以给我留言,需要的人多我也不介意发出来的。或者QQ: 1872681795。
调试工具包几个月前准备发,后来发现有些保护又有了针对性的改进,另外也测试了TXP下的DXNXF,所以就拖到现在才发。


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 0
支持
分享
最新回复 (15)
雪    币: 12848
活跃值: (9142)
能力值: ( LV9,RANK:280 )
在线值:
发帖
回帖
粉丝
2
然而几年前就已加入游戏环境异常全家桶
2017-7-24 13:21
0
雪    币: 768
活跃值: (530)
能力值: ( LV13,RANK:460 )
在线值:
发帖
回帖
粉丝
3
强大的东东啊。。楼主再加把劲,做成个通用debuger吧。
2017-7-24 13:34
0
雪    币: 6524
活跃值: (4316)
能力值: ( LV10,RANK:163 )
在线值:
发帖
回帖
粉丝
4
没清除MMVAD/FileObject都是扯.....
2017-7-24 14:19
0
雪    币: 25
活跃值: (506)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
这个是啥啊
2017-7-24 14:21
0
雪    币: 543
活跃值: (170)
能力值: ( LV2,RANK:140 )
在线值:
发帖
回帖
粉丝
6
yimingqpa 没清除MMVAD/FileObject都是扯.....
还有啥的FileObject,早让FreeLibrary整到哇呱岛去了。加载后的dll文件可直接删除。
2017-7-24 23:04
0
雪    币: 108
活跃值: (35)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
mark
2017-7-25 09:30
0
雪    币: 4884
活跃值: (3301)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
路过好奇是什么半成品
2017-7-26 12:36
0
雪    币: 543
活跃值: (170)
能力值: ( LV2,RANK:140 )
在线值:
发帖
回帖
粉丝
9
过保护驱动分离调试进程时蓝屏,驱动无法加载到vs2015,估计vs各版本有相同的问题。修改版己放到QQ群:120836343
2017-7-27 23:20
0
雪    币: 10
活跃值: (71)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
MFC    DLL动态内存加载不支持
2017-8-18 21:56
0
雪    币: 712
活跃值: (121)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
厉害厉害
2017-8-30 17:36
0
雪    币: 6
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
支持先  再下载    学习学习
2018-1-14 21:56
0
雪    币: 211
活跃值: (193)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
DLL  隐藏
2018-2-8 10:59
0
雪    币: 5
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
支持先    再下载        学习学习
2018-4-21 00:45
0
雪    币: 0
活跃值: (60)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
大神厉害  学习啦!
2019-12-18 17:06
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
16
youngboz 过保护驱动分离调试进程时蓝屏,驱动无法加载到vs2015,估计vs各版本有相同的问题。修改版己放到QQ群:120836343
QQ群现在搜索不到了,还有没有其它交流群
2020-7-6 11:54
0
游客
登录 | 注册 方可回帖
返回
//