首页
论坛
课程
招聘
雪    币: 116
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝

[调试逆向] [原创]dll模块隐藏的另一种方法及实现细节

2009-4-8 18:12 17087

[调试逆向] [原创]dll模块隐藏的另一种方法及实现细节

2009-4-8 18:12
17087
标 题: 【原创】dll模块隐藏的另一种方法及实现细节
作 者: J.Boy
EMail: ajunboy@GMail.com
时 间: 2009-04-08,18:13

    关于隐藏DLL模块的方法网上也说得很多,如 NetRoc 大牛的<进程中dll模块的隐藏>一文中提到的抹链的方法.

    自己印象比较深的还有另一种方法,也是很古老的时候就有大牛提出并实现了的,但却没有提及细节以及具体实现代码,且笔者至今也还没看到有详细介绍此种方法以及细节的文章,可能是小弟孤陋寡闻吧....

    于是翻出这份早已发霉在硬盘里的代码来和大家分享一下,小弟才疏学浅,难免有错误的地方,希望大牛们能给予指正.

    说了半天废话,现在回到正题,思路很简单,就是先让DLL正常加载,然后copy一份完整的DLL内存映象,把dll free掉,再以dll正常加载的imagebase为基址申请与原DLL内存映象相同大小的内存,

然后再把直接copy的映象备份复制回去,这样该DLL就在DLL链中消失了,并且免去了重定位的麻烦.

    思路说着顺畅,但是实现起来仍有一些细节需要注意:

    一.在我们FreeLibrary的时候,系统会连DLL中用到的一些对象或资源一起释放掉.

    二.同样是FreeLibrary的时候,系统可能会将LoadLibrary时一同加载的该模块用到的其他DLL一起卸载掉.

    在N年前刚看到大牛们说到这个思路,自己实现时就因为不了解上面2个问题的存在,而盘旋在各种错误框中....- -!

    其实这2个问题都是很好解决的:

    一.在FreeLibrary时,会调用EP进行一些资源的卸载,我的处理方法是直接把EP处改为retn,让它直接返回掉,而不去释放资源.

    二.FreeLibrary时系统会把该DLL所用到的其他DLL的LoadCount减1,然后判断LoadCount为0则把该Dll也Free掉,否则无做任何操作.这里我的处理方法是在之前就用LoadLibrary来使同进程中的其他DLL的LoadCount增加.

    这样就OK了,下面是Delphi的实现代码:

unit HideModuleUnit;

interface

uses Windows, Messages, SysUtils, Classes, TlHelp32;

type
  TVirtualAlloc = function (lpvAddress: Pointer; dwSize, flAllocationType,
    flProtect: DWORD): Pointer; stdcall;
  TVirtualProtect = function (lpAddress: Pointer; dwSize, flNewProtect: DWORD;
      var OldProtect: DWORD): BOOL; stdcall;
  TVirtualFree = function (lpAddress: Pointer;
    dwSize, dwFreeType: DWORD): BOOL; stdcall;
  TWriteProcessMemory = function (hProcess: THandle;
    const lpBaseAddress: Pointer; lpBuffer: Pointer; nSize: DWORD;
    var lpNumberOfBytesWritten: DWORD): BOOL; stdcall;
  TGetCurrentProcess = function : THandle; stdcall;
  TFreeLibrary = function (hLibModule: HMODULE): BOOL; stdcall;

  THideModuleRec = record
    pModule: pointer;
    pVirtualAlloc: TVirtualAlloc;
    pVirtualProtect: TVirtualProtect;
    pVirtualFree: TVirtualFree;
    pWriteProcessMemory: TWriteProcessMemory;
    pGetCurrentProcess: TGetCurrentProcess;
    pFreeLibrary: TFreeLibrary;
  end;
  PHideModuleRec = ^THideModuleRec;

  procedure HideModule(hModule: THandle);

implementation

procedure ExecuteHide(HM: THideModuleRec);
var
  pBakMemory: pointer;
  ImageOptionalHeader: TImageOptionalHeader;
  ImageDosHeader: TImageDosHeader;
  td: dword;
  i: Integer;
begin
  { 取得映象数据 }
  ImageDosHeader := PImageDosHeader(HM.pModule)^;
  ImageOptionalHeader := PImageOptionalHeader(Pointer(integer(HM.pModule) +
    ImageDosHeader._lfanew + SizeOf(dword) +
      SizeOf(TImageFileHeader)))^;
  { 申请内存以备份原始模块数据 }
  pBakMemory := HM.pVirtualAlloc(nil, ImageOptionalHeader.SizeOfImage,
    MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
  if pBakMemory = nil then
    exit;
  { 修改原始内存为可读写属性 }
  HM.pVirtualProtect(HM.pModule, ImageOptionalHeader.SizeOfImage,
    PAGE_EXECUTE_READWRITE, td);
  { 备份原始模块数据 }
  HM.pWriteProcessMemory(HM.pGetCurrentProcess, pBakMemory, HM.pModule,
    ImageOptionalHeader.SizeOfImage, td);
  { 修改原DllEntryPoint为retn,防止FreeLibrary时的一些卸载操作 }
  pByte(integer(HM.pModule) + ImageOptionalHeader.AddressOfEntryPoint)^ := $C3;
  { 卸载原模块,这里多次卸载防止因为LoadCount的关系一次卸载不掉 }
  //HM.pFreeLibrary(integer(HM.pModule));
  i := 0;
  repeat
    Inc(i);
  until not HM.pFreeLibrary(integer(HM.pModule)) or (i >= 30);
  { 申请dll原始加载地址空间 }
  HM.pModule := HM.pVirtualAlloc(HM.pModule, ImageOptionalHeader.SizeOfImage,
    MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
  if HM.pModule = nil then
    exit;
  { 写回原始数据 }
  HM.pWriteProcessMemory(HM.pGetCurrentProcess, HM.pModule, pBakMemory,
    ImageOptionalHeader.SizeOfImage, td);
  { 释放备份时用的内存 }
  HM.pVirtualFree(pBakMemory, 0, MEM_RELEASE);
end;

(*注意该间隔处不能添加任何代码, 且不能改变上下2个函数位置
  因为下面使用了2个函数入口来计算上一函数Size*)

procedure LockedAllModule(CurrentModuleHandle: THandle);
var
  ModuleList: THandle;
  pm: tagMODULEENTRY32;
begin
  pm.dwSize := sizeof(tagMODULEENTRY32);
  ModuleList := CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
  if not Module32First(ModuleList, pm) then
  begin
    CloseHandle(ModuleList);
    exit;
  end;

  //不处理第一个模块,因为那是主模块
  { 对每个模块LoadLibrary一次,是为了把LoadCount加1 }
  while Module32Next(ModuleList, pm) do
  begin
    if pm.hModule <> CurrentModuleHandle then
      LoadLibrary(PAnsiChar(@GetModuleName(pm.hModule)[1]));
  end;

  CloseHandle(ModuleList);
end;

procedure HideModule(hModule: THandle);
type
  TExecuteHide = procedure (HM: THideModuleRec);
var
  HM: THideModuleRec;
  pExecuteHide: pointer;
  ExecuteHideSize: integer;
  MyExecuteHide: TExecuteHide;
  td: dword;
  Module_kernel32: integer;
begin
  Module_kernel32 := GetModuleHandle('kernel32.dll');
  HM.pModule := pointer(hModule);
  HM.pVirtualAlloc := GetProcAddress(Module_kernel32, 'VirtualAlloc');
  HM.pVirtualProtect := GetProcAddress(Module_kernel32, 'VirtualProtect');
  HM.pVirtualFree := GetProcAddress(Module_kernel32, 'VirtualFree');
  HM.pWriteProcessMemory := GetProcAddress(Module_kernel32, 'WriteProcessMemory');
  HM.pGetCurrentProcess := GetProcAddress(Module_kernel32, 'GetCurrentProcess');
  HM.pFreeLibrary := GetProcAddress(Module_kernel32, 'FreeLibrary');

  ExecuteHideSize := integer(@LockedAllModule) - integer(@ExecuteHide);
  pExecuteHide := VirtualAlloc(nil, ExecuteHideSize,
    MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
  if pExecuteHide = nil then
    exit;

  { 防止系统将需要的Dll卸载掉 }
  LockedAllModule(integer(HM.pModule));

  CopyMemory(pExecuteHide, @ExecuteHide, ExecuteHideSize);
  MyExecuteHide := pExecuteHide;
  MyExecuteHide(HM);
end;

end.
  

    以上代码就实现了Dll的隐藏,不过由于该份代码早已发霉,没有处理以下几个特殊问题:

    一.多线程.

    二.模块内部取模块信息时的处理.

    同样的,处理方法也很简单:

    一.在Free模块前.先把进程内当前线程以外的所有线程Suspend,处理完之后再resume,就OK了.

    二.Hook GetModuleName GetModuleHandle等取模块信息的函数,判断到是隐藏的DLL的代码段在调用(判断返回地址),就给予相应的信息即可.

    不过这里第2个问题的解决方法有失隐秘性,希望有大牛能提点下更好的办法.

    第一次写文,有错误之处还请各位大大见谅并给予指正.

2020,给你一个诚意满满的夏令营!

最新回复 (26)
雪    币: 441
活跃值: 活跃值 (20)
能力值: ( LV12,RANK:600 )
在线值:
发帖
回帖
粉丝
Sysnap 活跃值 14 2009-4-8 18:20
2
0
检测办法:   1  搜索内存获取PE相关的特征....
                  2 HOOK常见的API...判断返回地址
            3 MM State
雪    币: 116
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
JBoy 活跃值 2009-4-8 18:23
3
0
当然拉...任何一种隐藏检测的方法都很多啊............只是提个思路而已```谁又敢说他的方法完全不被检测呢?
雪    币: 234
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
梦魇颖雨 活跃值 2009-4-8 18:23
4
0
这个方法我也用过。。还是不错的。。。
雪    币: 70
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
yanxizhen 活跃值 2009-4-8 19:16
5
0
记得n年前某个开源滴木马里面用过这个技术
雪    币: 116
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
JBoy 活跃值 2009-4-8 19:30
6
0
记得那个只是一小段的代码的实现....根本无法做通用额..呵呵`````就是因为没说过细节....
当然,有可能我们看到的不是同一个东西``呵呵
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
colboy 活跃值 2009-4-8 19:52
7
0
[QUOTE=;]...[/QUOTE]
思路很好,期待完整的代码~~~~~~~~~~~
雪    币: 212
活跃值: 活跃值 (53)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
freecat 活跃值 1 2009-4-8 21:23
8
0
似乎更好的办法是:
做A,B二个DLL
A负责由内存或资源中加载BDLL加载完后Free了A就行了吧
雪    币: 204
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
PentiumM 活跃值 2009-4-9 15:23
9
0
这种做法,达到的效果和抹链一样吧。
雪    币: 116
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
JBoy 活跃值 2009-4-9 16:33
10
0
不一样,试下就明白,呵呵
雪    币: 204
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
PentiumM 活跃值 2009-4-9 16:45
11
0
能否告知有什么区别呢?
雪    币: 2648
活跃值: 活跃值 (134)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
cvcvxk 活跃值 10 2009-4-9 17:32
12
0
直接在内存里构造一个PE,重定位和导入都弄好,然后call to EntryPoint就行了~
entrypoint还可以破坏pe头部,使搜索法无法使用~~
雪    币: 70
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
yanxizhen 活跃值 2009-4-9 19:54
13
0
pullout...
雪    币: 651
活跃值: 活跃值 (10)
能力值: ( LV12,RANK:300 )
在线值:
发帖
回帖
粉丝
轩辕小聪 活跃值 7 2009-4-9 20:00
14
0
先Load再Free,还要考虑N多问题,还不如搞个类似壳的功能,自己申请内存把映像写入,然后自己做重定位和填充IAT。
雪    币: 651
活跃值: 活跃值 (10)
能力值: ( LV12,RANK:300 )
在线值:
发帖
回帖
粉丝
轩辕小聪 活跃值 7 2009-4-9 20:08
15
0
还有,到底什么是“模块隐藏”?因为之前加载的dll其实已经被卸载了。
本质上,这其实是你copy出来一份内存映像,直接把它当可执行映像使用了而已。你之所以这么做,只是免去了我14楼所说的需要自己加载PE的工作,实质上这个内存映像,算是一个进程模块,还是只是一块包含有EXECUTE属性的可执行代码的内存?如果这算“隐藏模块”,那么如我14楼所说的任何一个自己用VitualAlloc把内存映像Load进内存的情况,都可以说成是“隐藏模块”。
到底什么才算是“Module”?如果不是Module,那么使之看不见的行为就不能算是“Module隐藏”了。这个问题我也不确定,大家怎么看?
雪    币: 204
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
PentiumM 活跃值 2009-4-9 22:46
16
0
我觉得,抹链这这些方法表现出来的结果都一样,而且还简单。
雪    币: 7171
活跃值: 活跃值 (23)
能力值: ( LV9,RANK:610 )
在线值:
发帖
回帖
粉丝
achillis 活跃值 15 2009-4-10 09:48
17
0
对付API就用抹链,对付抹链就用暴搜,对付暴搜就把PE头抹了,对付抹PE头就ZwQueryVirtualMemory,对付QueryMemory就自己做Loader或ReloadandRun然后再抹一抹就只有a piece of code了,但是还是占了一块内存。我在想怎么改VAD把内存占用也隐藏掉~~
雪    币: 2648
活跃值: 活跃值 (134)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
cvcvxk 活跃值 10 2009-4-10 14:47
18
0
VAD改了的话,没有问题么~
雪    币: 116
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
JBoy 活跃值 2009-4-10 18:20
19
0
shadow walker试下?哈,全部让它读成0
雪    币: 104
活跃值: 活跃值 (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
sethseth 活跃值 2009-4-10 22:00
20
0
呵呵 学习了 太有才了
雪    币: 441
活跃值: 活跃值 (20)
能力值: ( LV12,RANK:600 )
在线值:
发帖
回帖
粉丝
Sysnap 活跃值 14 2009-4-10 22:21
21
0
直接读物理内存就行了...
雪    币: 7171
活跃值: 活跃值 (23)
能力值: ( LV9,RANK:610 )
在线值:
发帖
回帖
粉丝
achillis 活跃值 15 2009-4-11 07:34
22
0
怎么把VAD改成一种既不会显示,又不会被占用的状态,就像硬盘上的隐藏分区一样~~
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ezme 活跃值 2009-4-11 15:04
23
0
飘....................
雪    币: 369
活跃值: 活跃值 (11)
能力值: ( LV9,RANK:460 )
在线值:
发帖
回帖
粉丝
zhzhtst 活跃值 11 2009-4-11 15:44
24
0
这个木马最近又小规模的出现了
雪    币: 116
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
JBoy 活跃值 2009-4-11 18:00
25
0
啊确实..所以说都是见招拆招,一招降一招哇....哈哈````
雪    币: 15
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
许胜 活跃值 2009-4-11 23:08
26
0
没什么说的,我要暗恋你了
雪    币: 191
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wangjietx 活跃值 2018-11-16 23:47
27
0
这个方法现在遇到了一个问题了,以前在XP跟Win7用的很好,现在Win10出问题了,主要问题出在FreeLibrary身上,在Win10里FreeLibrary返回成功了,但是内存却没有被Free,结果异常了。楼主出来一起研究下。
游客
登录 | 注册 方可回帖
返回