对hgame-week4-happyVM的思考

hgame-2019-wp归总:https://www.danisjiang.com/2019/02/22/hgame-2019-wp%E5%BD%92%E6%80%BB/

这篇文章是我一边做一边思考的时候一边写的,这就是为什么这篇文章会这么长,而且前面云里雾里的

IDA截图


没有截全,但是差不多就是这样

第一次看到题目的思考

现在是晚上10点,我准备开始做week4的RE题,第一题我昨天看了几眼,发现有几个点很难实现,所以暂时先搁置一边

查了一下,VM是虚拟机的意思。

但是最初看题目实在找不到所谓的虚拟机到底指什么,看了网上的很多VM re的题目都跟本题很像,但是我还是想不出来之所以被称为VM的原因是什么

整个对字符串的处理的函数受一个因素的影响,那就是一个表,表就像一个程序的流程,每一个byte代表一种处理,每运行完一个函数就将指针指向表中的下一个数据,这就有点虚拟机的意思了(虽然还是不太理解)

但是,我在这些函数中并没有看到对字符串处理的地方,这是一个疑问

看起来我知道这些函数是怎么对字符串进行影响了

它把字符串的地址存在0x602078处,之后在每个函数中都通过0x602078这个地址来找到字符串并对它进行处理

在到这一步时,我还没有仔细研究每个子函数的内容,但我通过“switch函数到底在switch谁?”&“到底怎么在子函数中影响字符串?”这两个问题对整个程序有了比较基本的认识

但是,我仍然有一个疑问,就是这个对result的判断,这是我看到这整个函数时的第一个问题,就是它是如何结束的?因为我看到这个函数的主体都在while(1)的循环之下,这势必得有一个办法返回,但是我对result的判断不太理解,看样子我们是在这个调用表的值为17h的时候返回,但是这个调用表并不是以17h结尾的,而且它中间有两个17h,这个问题在我还没有细看函数时无法解决,所以我带着它,先开始对子函数的解读

对case内函数的理解

接下来就是对子函数的解读了,做这一步就相当于对这个

实际的情况比我预想得要复杂很多,有非常多的变量,我不是很理解很多没有被明显初始化的变量到底是做什么用的,所以我用IDA PRO动态调试了一下

然后我在想,似乎这个程序的执行流程是已经确定了的,我要做的就是把这个整个流程所做的事提炼出来,然后再逆向

那么,我可以继续用switch语句,但是这次我在每个switch中输出这个函数所做的事(伪代码),这样做我最终应该能得到一个程序流程的伪代码

似乎很复杂,我所应该做的事还有很多。我觉得应该有更加好的理解方式。动态调试在有些地方也不是那么的直观,看不大懂说明我太菜了,在re方面的功力太弱了

ps:我前前后后看了两个小时了,我决定先停下。先看看别人是怎么理解的

看了十多分钟别人对VM的讲解,我大概知道了什么是VM,其实就是一种小型的解释器,将所进行的操作编程 OperationCode ,然后程序运行的时候用解释器去解释OperationCode。那么还是需要硬头皮去理解每个Bytecode所代表的含义

到这里我就有一个思考,那就是我输入的字串是否会影响到程序的执行流?如果是,那么我就只能把每一种都单独理解然后再switch。但如果不是,我是否可以用动态调试来得到每一个对字符串的操作,相当于我只要在每次有对字符操作的时候记录一下就可以了。

Emulator的指令一共有0x16个,而OperationCode似乎有57位,那么显然分析代码更加现实

试想一下,如果每次只输出对于字符的操作是不是会方便一些?

那么,接下来就试试吧

等等,这不就是brainfuck吗???!!!

现在是上午3点,过去了一个小时,我大体上完成了对解释器的解读,但是我遇到了许多问题,包括:看起来解释器在遇到17h时会结束,但是17h在OperationCode中出现地太早了,结果似乎任何实质性的改变都没有应该是我那个地方理解错了。因此,在接下来,我将着重处理这个问题

打印log

这时候我决定不单单通过眼睛看opcode,我打算模拟程序的流程,我把每一个case中的b1,b2,b3,b4,b5中的数据打印出来,再打印这个case中所作的事

代码如下

就用这种简易的“调试器”,我得到了一个3000多行的输出

分析

我硬着头皮看,通过查看b1,b2,b3,b4,b5的值的变化,我发现这个程序由三段循环构成:

第一段循环:

这个循环实际上是通过b3记录输入的字符串的长度,当遇到’\0’时跳出循环

第二段循环:

第二段循环终于看到了^和对字符的修改。至于到底是什么,多看几个就会发现,实际上是将 50+3i 跟每个字符做xor

第三段循环:

细看发现跟上一段循环差不多,只不过xor的初始值从50变为22

逆向

最终,我得到了逆向代码

一点总结

在之前,在我还没有接触逆向这个事情之前,就有人跟我讲,一般的逆向不难,逆虚拟机才难。今天,我感受到了,因为这还只是一个为题目而生的小型虚拟机。

运行了这么长的代码,最后就为了执行三个循环,足以看到虚拟机的优缺点

看了几篇文章,发现现在虚拟机是一个热门的东西,只要是反逆向,基本都会在核心代码上加上虚拟机,以此来加大逆向的难度

这又是ctf的一个趋势,以后势必会出现更多的VM题,下次有空再去总结一波逆向VM的常见技巧

后续

今天o爷爷介绍了更加合适的做法(就是资料中chip学长的理解成汇编代码然后打log的方式),我决定再来看一遍case,然后总结一下每个case所对应的汇编指令。我觉得这个的一个好处就是不用跑程序,直接把opcode打出来就是汇编代码了,比较直观而且方便解读

(这里原先忘记保存了,名称改了,v6==原来的v7,v7==v8)

这里这个v6的值是下一个opcode,暂时不知道它的汇编指令是什么

从函数内以及之前的调试可以看出,这个q1是栈,b4是一个控制栈的指针,那么在汇编里它应该相当于RSP

那么其实这个sub_400a4f应该是push xxx

这个case应该是push 下一个字符,到底怎么表述我不知道,先暂时放在这

那么这些分别对应 push v6,push b1,push b2,push b3

后面这些的话,进去看到其实这个就是pop

所以这些就对应pop b1,pop b2,pop b3(b1,b…对应什么等会再追究)

case 7的这个也比较明了,就是 add b1,b2

接下来这几个都是有两个参数的了,里面也是加法

所以这个三个分别是 add b1,v6; add b2,v6; add b3,v6

case 0xb的汇编就是sub b1,b2

这几个相当于sub b1,v6; sub b2,b6; sub b3,v6

case 0xf就是xor b1,b2

case 0x10就是test b1,b2

case 0x11的参数是v6,里面这个参数是i,i是opcode的指针,所以这个是跳转用的。push 当前的地址并跳转,哇哦,这个vm的汇编真的是麻雀虽小,五脏俱全,还能函数调函数

case 0x12的参数是&i,那么这个应该就是ret了吧。

有点懒得写了,先鸽一下,反正想应该是这么想,等我明后天再来补这个坑吧

说点什么

avatar

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据

  Subscribe  
提醒