前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >.Net JIT二进制骚操DHVM破解篇

.Net JIT二进制骚操DHVM破解篇

作者头像
江湖评谈
发布2023-08-31 21:25:19
2280
发布2023-08-31 21:25:19
举报
文章被收录于专栏:天下风云天下风云

前言

经研究,号称最强.Net加密软件DNGuard HVM(以下简称DHVM),五行代码基本上可以优雅的破解它,本篇看下。友情提示,以下全是二进制汇编骚操,慎入。

概括

示例:

非常简单的示例

代码语言:javascript
复制
static void ABC()
{
   Console.WriteLine("Call ABC");
}
static void DEF()
{
   Console.WriteLine("Call DEF");
}
static void Main(string[] args)
{
   Console.WriteLine("Call Main");
   ABC();
   DEF();
   Console.ReadLine();
}

修改调用ABC函数的逻辑为调用DEF函数,Main函数的MSIL二进制代码如下:

代码语言:javascript
复制
00 72 25 00 00 70 28 0e 00 00 0a 00 28 06 00 00 06 00 28 07 00 00 06 00 28 0f 00 00 0a 26 2a

这里的MSIL二进制代码可以参考:罕见的技术:MSIL的机器码简析

1.难点 因为Hook JIT,简单的MSIL修改已经不起作用。DHVM的各种反调试,比如VS调试器无法进入某些内存地址。一进入就会报异常。它静态地址在运行的时候动态偏移,它进行了PE的IAT(导入表)的Name字段验证,当IAT的Name不为0的时候,就会报异常等。这些东西叠加在一起,无法调试,无法通过输入表注入DLL等。

2.蛛丝马迹 避开这些反调试手段,魔高一尺道高一丈嘛,蛛丝马迹即是破绽。 当我们通过一些可以调试的地址进入发现一些有趣的东西,比如以下代码:

代码语言:javascript
复制
0000000180497AB2: E9 A1 73 00 00 jmp  0000000180497AB8
0000000180497AB7: F8             clc
0000000180497AB8: 4C 89 5F 10    mov  qword ptr [rdi+10h],r11

这一段汇编代码是关键点,它通过jmp指令跳到地址0000000180497AB8。然后执行指令

代码语言:javascript
复制
mov  qword ptr [rdi+10h],r11

这里的r11寄存器保存的是通过DHVM加密后的托管DLL的真实的MSIL二进制代码。rdi寄存器是DHVM Hook的JIT的函数invokeCompileMethod的参数methodInfo地址,rdi+0x10即是methodInfo的成员变量IL_Code地址。这个IL_Code里面的值会被JIT编译器编译成机器码,然后运行。

那么这段指令的意思很明显,也就是说把DHVM加密后把保存的托管DLL的真实MSIL二进制代码赋值给IL_Code。它这么做的目的就是屏蔽掉原有托管DLL里面的MSIL,而用DHVM自己加密之后保存的MSIL。无论你怎么修改原有的托管DLL,都不会影响JIT的执行。

3.预破 既然探查到了以上蛛丝马迹,下面着手解决掉DHVM。这里的思路是,因为jmp是个跳转指令,所以可以让它跳转到自己的地址。这个自己的地址因为无法通过IAT注入DLL构建,上面说了DHVM会搜寻IAT的Name字段是否为0。因为Win11超强的PatchGuard,所以这里不考虑DLL注入了。直接在HVMRun64.dll内部构建。通过dumpbin,把HVMRun64.dll的汇编代码导出到记事本。HVMRun64.dll的最后的汇编地址如下:

代码语言:javascript
复制
000000018049EE53: 00 74 56 01  add byte ptr [rsi+rdx*2+1],dh
000000018049EE57: 00

它这个地址对应的是把HVMRun64二进制的地址如下:

代码语言:javascript
复制
00499253:00 74 56 01 00

从00499258地址开始后面全都是0,类似如下:

代码语言:javascript
复制
00499258:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
          00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
          00 00 00 00 00 00 00 00 00

可以从这里为零的数据开始构建。在构建数据之前还需要做一件事情,我们上面蛛丝马迹里面jmp指令

代码语言:javascript
复制
0000000180497AB2: E9 A1 01 00 00 jmp  0000000180497AB8

需要让它跳转到00499258这个地址来,然后在这个为零地址里面做自己想要做的事情。如何跳转呢?可以把jmp地址改成如下:

代码语言:javascript
复制
//jmp跳转地址减去jmp所在地址减去5等E9后面的数值,E9是jmp机器码
0000000180497AB2: E9 A1 73 00 00  jmp  000000018049EE58

这里的000000018049EE58地址指向的即是00499258所在全部是零的地址处。当它跳转到0区之后如下代码:

代码语言:javascript
复制
首先在00499258地址处写入二进制代码:4D 89 DF,
这三个十六进制代表的汇编是mov  r15,r11,上面说了r11
保存的是MSIL需要编译的二进制代码,通过跟踪发现如果
直接更改r11寄存器,则会导致异常。跟踪也发现r15寄存器
为零,所以这里把r11赋值给r15。然后把蛛丝马迹里面的代码

0000000180497AB8: 4C 89 5F 10    mov  qword ptr [rdi+10h],r11
也就是这里的r11替换成r15,让它称为最后编译的MSIL

我们需要做的就是在r15里面修改MSIL二进制即可。

如何把r11替换成r15呢?看它的代码:

代码语言:javascript
复制
0000000180497AB8: 4C 89 5F 10  mov  qword ptr [rdi+10h],r11

修改成如下:

代码语言:javascript
复制
0000000180497AB8 4C 89 7F 10   mov  qword ptr [rdi+10h],r15 

把机器码5F改成7F即可。以上所有准备好了,我们开始替换MSIL代码,也即是r15寄存器修改。

4.破解 上面把十六进制的4D 89 DF写入了00499258地址,也即是000000018049EE58所指向的地址。

因为4D89DF

占3个字节,所以下面的地址

0x000000018049EE58+0x3==000000018049EE5B.

000000018049EE5B这个地址写入如下:

代码语言:javascript
复制
0049925B:49 C6 47 0D 07
转换成汇编也即是如下:
000000018049EE5B 49 C6 47 0D 07  mov ptr byte [r15+D],07

这里是把07这个数值赋值给r15偏移的0xD的位置处。这里修改r15偏移的0xD位置的数值,实际上是把示例里面的调用的ABC函数修改成调用DEF函数,也就是改变函数逻辑。示例的结果是:

代码语言:javascript
复制
Call Main
Call ABC
Call DEF

我们通过hook DHVM之后的结果是

代码语言:javascript
复制
Call Main
Call DEF
Call DEF

示例里面的MSIL二进制代码是:

代码语言:javascript
复制
00 72 25 00 00 70 28 0e 00 00 0a 00 28 06 00 00 06 00 28 07 00 00 06

调用ABC函数的MSIL二进制代码是:

代码语言:javascript
复制
28 06 00 00 06

调用DEF函数的二进制代码是:

代码语言:javascript
复制
28 07 00 00 06

可以看到ABC和DEF函数的MSIL二进制代码,只是基本上相同,上面偏移的0x1的位置一个是06,一个是07。如果想要把调用ABC改成调用DEF,这里只需要把06改成07即可,也就是这段汇编代码的意义

代码语言:javascript
复制
000000018049EE5B 49 C6 47 0D 07  mov ptr byte [r15+D],07

这里改了之后,还得跳回去,因为上面的汇编占了五个字节,所以这里下一个地址是:

0x000000018049EE5B+0x5==0x000000018049EE60

在地址0x000000018049EE60里面跳转到原来的jmp需要跳转的地址也即是

代码语言:javascript
复制
00499260:E9 53 8C FF FF
000000018049EE60 E9 53 8C FF FF jmp 0000000180497AB8

这样就完成了整个闭环的操作,在Hook DHVM里面这里只是简单的修改了一个字节数值,当然可以修改更多以满足自己的需求。

本篇用的是:.Net JIT的骚操作DNGuard HVM原理简析。里面提到的第二种方法也即是破二。第一种方法也可,而且能够做的更多。但是规模和成本上去了。个人比较喜欢简洁,所以选择了第二种。

5.整体 那么整体的代码是:

代码语言:javascript
复制
DHVM跳转代码和r11替换成r15
0000000180497AB2: E9 A1 73 00 00  jmp  000000018049EE58
0000000180497AB8 4C 89 7F 10      mov  qword ptr [rdi+10h],r15

hook代码:
000000018049EE58 4D 89 DF        mov   r15,r11
000000018049EE5B 49 C6 47 0D 07  mov   ptr byte [r15+D],07
000000018049EE60 E9 53 8C FF FF  jmp   0000000180497AB8

可以看到,真正的代码,也就那么几行,甚至也就是修改一个字节。所谓返璞归真,即是这个道理。

以上DHVM的整体过程,仅用于学习用途。

结尾

作者:江湖评谈

本文参与?腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-08-07,如有侵权请联系?cloudcommunity@tencent.com 删除

本文分享自 江湖评谈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 可以看到,真正的代码,也就那么几行,甚至也就是修改一个字节。所谓返璞归真,即是这个道理。
  • 结尾
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com