x64dbg-反调试二
9unk Lv5

目标程序

程序下载:DaXXoR

解压密码:9unk

程序简介:本程序运行之前会检测杀死 x32dbg 进程。

任务目标:绕过反调试机制,正确运行程序。

进程名检测原理

先打开 DaXXoR(32).ExE,然后再运行 x64dbg,发现 x64dbg 马上就退出了。如果我们用 x64dbg 加载该 CrackMe 然后运行起来,会发生两者一起退出了。

面对这种情况,我们先用 x64dbg 加载程序到主模块。

1.jpg

“Ctrl+N”,先看一下这个程序使用了哪些 API。

2.jpg

从上图中可以看到很多 API 函数,但都不是用于检测进程名的,可能这些重要的 API 函数被隐藏起来了,没出现该列表中。显然如果程序不直接导入某些 API 的话,会使用 GetProAddress 这个 API 函数来获取这些 API 函数的地址进行间接调用。

使用 GetProAddress 函数加载的一些 API 函数并不会出现该 API 函数列表中,我们给 GetProAddress 设置一个断点。

3.jpg

4.jpg

运行起来,此时程序断在 GetProAddress API 处。当前获取的函数是 _CPPdebugHook,该函数与检测进程名没有关系,继续运行。

5.jpg

我们继续按 “F9” 运行程序直到获取的 API 是与检测进程名相关为止,这里带获取的 API 函数是 EnumProcesses。

6.jpg

这里使用 “Ctrl+F9” 执行到返回,这个时候 eax 寄存器中保存的就是 EnumProcesses 这个 API 函数的地址。

7.jpg

我们右键 “EAX”—>在反汇编中转到,定位到这个 API 之后,并设置断点。

8.jpg

继续按照上面的步骤操作,继续按 “F9” 看看程序还用到了哪些 API。

9.jpg

这里是获取枚举进程模块函数地址,我们还是执行到返回,接着给使用命令 “bp eax” 把 EAX 中保存的地址设置断点。

10.jpg

这里另外一个可以的 API 函数 GetModuleBaseNameA,我们跟之前一样给函数设置断点。

11.jpg

12.jpg

然后继续运行 “F9” 运行程序,此时断在了 EnumProcesses 函数入口处。

13.jpg

我们看一下 EnumProcesses 函数的参数信息

14.jpg

EnumProcesses 函数就是获取所有进程的 pid,并给它设置一个数组

  • lpidProcess:表示这个数组的指针
  • cb:表示数组的大小
  • lpcbNeeded:表示数组的数据

也就是说我们实际的所有进程 pid 数据都存在第 3 个(lpcbNeeded)参数中。

我们先计算一下 pid 的十六进制。

15.jpg

接下来我们转到第 3 个参数位置处

16.jpg

我们执行到返回,把当前的 pid 数据加载到数组中。

17.jpg

这里的 pid 数据太多了,为了快速查找到位置,我们对内存数据部分进行搜索,快捷键 “Ctrl+B”

18.jpg

19.jpg

双击搜索的字符串,x64dbg 自动跳转到内存数据窗口。

20.jpg

“F9” 继续运行程序,可以看到此时断在 GetModuleBaseNameA

21.jpg

我们看一下这个 API 函数的参数和返回值。

22.jpg

该函数是获取指定进程模块的名称,lpBaseName 这个参数是用来保存模块名称

我们运行到返回看一下获取到的参数和返回值

23.jpg

1D0 就是 ECAgent.exe 的句柄

我们继续按 “F9” 运行,此时又断在了 GetModuleBaseNameA

24.jpg

继续执行到返回,可以看到这一次获取的是 SGTool.exe 模块

25.jpg

我们继续按上面的操作进行,直到获取到 x32dbg.exe 模块

26.jpg

可以看到 x32dbg.exe 模块的句柄是 1CC

继续单步运行程序,可以看到这里用了 CloseHandle 函数。

27.jpg

CloseHandle 函数是用来关闭指定句柄

28.jpg

我们步入看一下 CloseHandle 函数的返回值

29.jpg

30.jpg

我们继续单步执行,这里指向了两个进程名

31.jpg

继续步入查看这个子程序的功能

32.jpg

33.jpg

34.jpg

35.jpg

36.jpg

从上面的图片可以看出这是将获取的进程名转换成大写

继续单步执行,可以看到这里把要比较的进程名弹出,很明显下一步要开始进行比较

37.jpg

继续步入查看子程序,获取两个进程名,初始化寄存器

38.jpg

判断两个进程的第一个字母是否相等,不相等就跳出循环

39.jpg

判断获取的进程名是否为空,值为空就跳出循环

40.jpg

判断第2、3、4个字符串,最后再继续循环

41.jpg

比较结束后,继续单步执行到 OpenProcess 函数

42.jpg

注意:这里使用的是 sub 指令,也就是进程名相同 eax 的返回值就是 0

继续单步执行,如果 eax 不为 0 就跳出关闭进程的子程序

43.jpg

我们看到下面有一个 OpenProcess 函数

44.jpg

我们执行到返回,可以看到此时 x64dbg 的句柄是 1C4

45.jpg

把旧的句柄替换成新的句柄

46.jpg

下面使用 TerminateProcess 杀死进程

47.jpg

总结一下整个检测过程: EnumProcesses(获取所有进程的 pid)—> GetModuleBaseNameA(获取一个个进程名)—> CloseHandle(关闭获取的句柄) —> 判断进程名是不是 x32dbg.exe —> OpenProcess(打开 x32dbg.exe 进程并获得其句柄)—> TerminateProcess(杀死进程的句柄)

方法一(修改指令)

这里把关键跳转的命令(jne)改成(jmp)

48.jpg

49.jpg

程序运行时有异常,这里把内存空间设为异常忽略

50.jpg

再次运行程序,发现此时程序正常运行

51.jpg

方法二(修改程序名)

52.jpg

53.jpg

方法三(插件)

SharpOD 勾选 “Hide Process” 和 “Hook *ZwFunctions” 然后保存。

54.jpg

重启 x32dbg ,就可以调试运行程序了。

55.jpg

reference

WindowsAPI详解——TerminateProcess 终止|杀死其它进程

OpenProcess()函数

C++进阶—> CloseHandle详解及CloseHandle后线程未停

GetModuleBaseNameA function (psapi.h)

Pushing the Limits of Windows: Handles

CloseHandle函数(handleapi.h)

  • 本文标题:x64dbg-反调试二
  • 本文作者:9unk
  • 创建时间:2021-01-02 21:26:28
  • 本文链接:https://9unkk.github.io/2021/01/02/x64dbg-fan-diao-shi-er/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!