首页
社区
课程
招聘
[原创]调试器设计(5)
发表于: 2010-3-14 23:25 14656

[原创]调试器设计(5)

2010-3-14 23:25
14656
加载exe:
(调试器代码:http://bbs.pediy.com/showthread.php?t=107005)

先看看代码

int LoadDebuggedProcess(LPCWSTR FilePath)
{//加载被调试进程
        STARTUPINFO         stStartInfo;

        memset ( &stStartInfo   , NULL , sizeof ( STARTUPINFO         ) ) ;
        memset ( &stProcessInfo , NULL , sizeof ( PROCESS_INFORMATION ) ) ;

        if(!CreateProcess(FilePath, NULL, NULL, NULL, FALSE, CREATE_NEW_CONSOLE | DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &stStartInfo, &stProcessInfo))
        {
                int err = GetLastError();
                AfxMessageBox(_T("创建进程失败"));
                return err;
        }

        StopOnDebugEvent(CREATE_PROCESS_DEBUG_EVENT); //在进程加载事件时候挂起
        return 0;
}

1.利用CreateProcess函数,创建一个新的进程,然后将对应的exe模块加载进程。
然后看到StopOnDebugEvent函数。这个函数只是简单的调用了WaitForDebugEvent和ContinueDebugEvent事件。我们在CREATE_PROCESS_DEBUG_EVENT事件暂停程序。

2.进程加载完了。但是程序被暂停在CREATE_PROCESS事件上,我们还不能在内存里面看到代码段的数据。
所以,我在test程序里面自己添加了                                ResumeDebuggedThread()        StopOnException();这两个函数。让程序停在第一个异常。

3.第一个异常来自于系统。对于我们和系统都有很重要的作用。它在进程里面所有模块都加载过后,马上进入exe模块代码空间之前。异常的细节和对于系统有什么重要作用不是我们现在需要讨论的话题。对于我们的调试器的重要作用才是我们现在的话题。由于exe模块已经被加载。那么我们可以获取exe模块加载后的基地址。这个地址是被加载到内存的基地址。有可能是400000,也可能是一个被重定位的地址。

4.我们利用

DWORD GetBaseAddress(DWORD dwPID)
{//获取exe模块的加载地址

  HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
  MODULEENTRY32 me32;

  // Take a snapshot of all modules in the specified process.
  hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, dwPID );
  if( hModuleSnap == INVALID_HANDLE_VALUE )
  {
          AfxMessageBox(_T("Invalid handle value"));
      return 0;
  }

  // Set the size of the structure before using it.
  me32.dwSize = sizeof( MODULEENTRY32 );

  // Retrieve information about the first module,
  // and exit if unsuccessful
  if( !Module32First( hModuleSnap, &me32 ) )
  {
    CloseHandle( hModuleSnap );           // clean the snapshot object
    return 0;
  }

DWORD Value = (DWORD)me32.modBaseAddr;

  CloseHandle( hModuleSnap );

  return Value;

}
这个函数获取实际加载地址。原理很简单。没有什么内部api,完全利用MS提供给我们的,获取进程相关信息的函数实现。代码就不多做介绍了。细节可以参考msdn关于CreateToolhelp32Snapshot例子代码

5.得到实际加载的地址后。我们需要获取程序实际的入口地址。

DWORD GetEntryPoint()
{//获取被调试程序的入口地址
        IMAGE_DOS_HEADER  RemoteImageBase;
        IMAGE_OPTIONAL_HEADER RemoteOptionalHeader;

        PIMAGE_DOS_HEADER ImageBase = (PIMAGE_DOS_HEADER)GetBaseAddress(stProcessInfo.dwProcessId);
        ReadProcessMemory(stProcessInfo.hProcess, ImageBase, &RemoteImageBase, sizeof(IMAGE_DOS_HEADER), NULL);
       
        PIMAGE_NT_HEADERS NtHeaders = (PIMAGE_NT_HEADERS)((DWORD)RemoteImageBase.e_lfanew + (DWORD)ImageBase);

        PIMAGE_OPTIONAL_HEADER OptionalHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)NtHeaders + 24);//24即sizeof(IMAGE_FILE_HEADER)+sizeof(DOWRD)
        ReadProcessMemory(stProcessInfo.hProcess, OptionalHeader, &RemoteOptionalHeader, sizeof(IMAGE_OPTIONAL_HEADER), NULL);
       
        return (DWORD)ImageBase + RemoteOptionalHeader.AddressOfEntryPoint;
}

和读取PE文件进行分析没有什么区别。但是,需要清楚意识到,我们操作的是另一个进程空间的地址。
所以,在获取文件头的信息时,需要先在被调试进程中读取这部分信息。

6.我们获得了程序的入口地址。这个当然是为了能动态从内存里面获取代码段的指令进行反汇编。为了确认是否正确。我们将入口地址后1000个字节的代码进行反汇编。
加载debuggee程序



对比winhex,可以看到我们得到正确的入口地址。但是,大家肯定看到call后的地址没有加上实际加载的基地址。这个问题……  需要你去解决。

我们在来看看调试器exe加载的过程:

首先,创建进程,这个时候调试器得到进程加载事件。从而,调试器开始控制被调试程序(给我们的感觉是),进行控制debuggee到系统的软断点异常到来。这个时候,程序马上将进入exe模块的代码空间。而所有的dll都已经加载完成。Exe加载完成。即 进程已经完成了自己的初始化,所以,我们可以利用MS提供的API获取进程的exe模块的真实加载地址。然后,我们找到程序的入口点。当然,我们还需要找到代码段的开始地址。然后将我们想看到的指令,进行反汇编。
程序停在ntdll空间,马上将进入exe模块。调试器这个时候完成自己需要的“初始化”。

很快,调试器将和程序的设计者开始打交道了。

待续……

[课程]FART 脱壳王!加量不加价!FART作者讲授!

上传的附件:
收藏
免费 7
支持
分享
最新回复 (24)
雪    币: 254
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
辛苦了,插入学习。。。。。。。。。
2010-3-15 00:07
0
雪    币: 576
活跃值: (1500)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
3
学习了。呵呵。有个问题向楼主请教。我之前给一个带MoleBox 壳的 A.exe  写Loader程序,当我用
CreateProcess(FilePath, NULL, NULL, NULL, FALSE, DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &stStartInfo, &stProcessInfo)

创建进程时,A.exe 就弹出一个崩溃的异常错误。无奈之下,我用了这个DebugActiveProcess   Api,虽然最终达到目的,但是我始终不明白为什么我CreateProcess出错了?我试过upx,aspack的壳,都是可以的。难道是MoleBox 壳有对调试启动进行anti?如果有,该如何解决呢?
2010-3-15 08:27
0
雪    币: 213
活跃值: (147)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
浅显易懂,表述清楚
Mark一记
2010-3-15 10:16
0
雪    币: 118
活跃值: (44)
能力值: ( LV12,RANK:240 )
在线值:
发帖
回帖
粉丝
5
我用我写的调试引擎加载Molebox加壳的程序,正常啊?
2010-3-15 11:48
0
雪    币: 576
活跃值: (1500)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
6
太怪异了。
2010-3-15 15:43
0
雪    币: 2362
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
没抢到沙发 mark一下
2010-3-15 16:20
0
雪    币: 118
活跃值: (44)
能力值: ( LV12,RANK:240 )
在线值:
发帖
回帖
粉丝
8
也许你是保存文件路径的变量设置的不够长.导致stack异常.然后崩溃!
2010-3-15 16:46
0
雪    币: 153
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
DWORD GetBaseAddress(DWORD dwPID)
{//获取exe模块的加载地址

??? 不明白
CREATE_PROCESS_DEBUG_EVENT事件里面没有exe模块的加载地址吗?
2010-3-17 10:26
0
雪    币: 118
活跃值: (44)
能力值: ( LV12,RANK:240 )
在线值:
发帖
回帖
粉丝
10
得到exe加载地址有很多办法:

在进程创建时,读取DEBUG_EVENT里面的异常地址。可以得到进程exe加载到内存里面的地址。

或者 注入debuggee ,用GetModuleHandle也可以得到

我用那个方法是因为,我后面还要获取加载dll模块l的地址。DEBUG_EVETN里面我只找到获取exe模块地址的方法,没有找到获取dll模块地址(这个只是我的认识,你觉得有问题,提出来就行)
2010-3-17 13:10
0
雪    币: 191
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
期待下篇!!!
2010-3-17 14:35
0
雪    币: 153
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
LOAD_DLL_DEBUG_EVENT可以得到DLL模块的地址。
2010-3-17 17:12
0
雪    币: 118
活跃值: (44)
能力值: ( LV12,RANK:240 )
在线值:
发帖
回帖
粉丝
13
恩? 可以具体说下,如何得到吗?  我没有找到返回dll模块地址的值
2010-3-17 22:36
0
雪    币: 153
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
晕倒,写调试器的话居然不仔细研究一下DEBUG_EVENT的结构?受不了你了。
2010-3-18 06:40
0
雪    币: 124
活跃值: (38)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
15
mark
等会再看
2010-3-18 09:01
0
雪    币: 118
活跃值: (44)
能力值: ( LV12,RANK:240 )
在线值:
发帖
回帖
粉丝
16
我确实没有深入研究过debug_event结构体。就好像我知道OD的功能实现细节,却对OD的操作不是很熟悉。

然后,我知道你说的获取DLL模块地址的方法。但是,我开始就觉得你压根就没有试过,只是猜测,所以,心里很不满意。故意问你怎么做的。想为难你。   我在此道歉

最后,还是要谢谢你的建议。希望你能提更多的意见,我一定认真接受。
2010-3-18 12:48
0
雪    币: 253
活跃值: (89)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
都是高人,仰望ing!
2010-3-18 13:01
0
雪    币: 45
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
1.。既然想设计一个新的调试器 !国人的制作的首先得先起个  中文名字! 然后附带一个英文名字!

2.。。设计的调试器 应该要比其他的调试器要有出众的地方! 要取 windbg Softice OD 等等的精华!  等等

首先得选个界面吧  ! 建议用OD界面然后  基于OD设计调试器 因为OD用的 人多! 把OD的几乎所有功能包括在内

然后 取其他的 调试器的 优秀功能 附加进去!  而且OD的界面也蛮漂亮的!

3.。。。 OD是ring3级别的调试器 楼主 有没有可能 做个想softice 一样ring0级别的  然后 实在不能到达 ring0级别  
就以一个提示说明 在ring3里面!

4.。。。。一些小的功能如  OllyICE 里面就没有附加自定义工具选项等!

5.。。。。建议楼主 先做出个 程序的简单界面出来 !   但有一点..    必须 有点特色功能!  然后发到论坛上面  绝
对 有很多很多的 人提意见  而且 你如果技术上遇到 问题 也会有很多牛人来帮助你! 你现在 一部一部的 提示代码

  有很多牛人对调试器代码 没兴趣的  当然也帮不上忙了! 当你放上软件的时候  别人帮你调试的会很多很多的

意见 当然就很多啦 到时候 你有某些不会的 相信会有很多人支持你  你自己的技术也会有很大的提升的!

以上是本人菜鸟的  简简单单的 意见!
2010-3-18 13:58
0
雪    币: 45
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
还有  那个 你设计的 OD 能不能支持 16为的
2010-3-19 17:25
0
雪    币: 156
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
终于一口气看到5了,期待更精彩的内容.
2010-4-12 22:26
0
雪    币: 285
活跃值: (16)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
21
楼主这是转载吧,前面有人发过源码,咋和你这个一样啊
2010-4-14 19:30
0
雪    币: 266
活跃值: (332)
能力值: ( LV9,RANK:550 )
在线值:
发帖
回帖
粉丝
22
占个座位,学习
2010-4-14 19:44
0
雪    币: 233
活跃值: (26)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
远程CALL调用工具
2010-4-16 09:58
0
雪    币: 233
活跃值: (26)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
看错了 SORRY
创建进程注入DLL   抢先机

虽然这种方式不是100%成功的 运用的好可以80%-90%  好处是注入的DLL是独立了。成功后立刻关闭线程句柄,脱离注入的主程序。  爆破用这个方法很好  关键最后调试程序的最后的自校验 如果存在多次自校验那还是一样会被发现的。。。
2010-4-16 10:09
0
雪    币: 132
活跃值: (30)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
25
一个最重要滴问题呀。。。怎么反汇编滴哇。。。

获取程序入口地址后。。。1000个字节反汇编 就没有细节了呢
2010-8-31 22:40
0
游客
登录 | 注册 方可回帖
返回
//