护网杯REFINAL——write up

前言

由于时间原因没有在比赛结束前提交flag,所以干脆写一个详细的解答过程(自认为),适合小白阅读,Pizza大佬请绕过。?

分析

首先通过字符串搜索,很容易定位到关键的地方。

根据前面的一些信息,判断出长度为0x30,经过如下设置,我们可以很快开始进行动态调试。

主要引起我们关注的(*(void (**)(void))(*(_DWORD *)Memorya + 112))();函数,跟进去很明显是一个vm的结构。

所以此题的难点就在于逆向vm代码。一般套路:

  1. 提取出bytecode
  2. 根据op代入函数
  3. 转化成伪汇编代码
  4. 转化成高级语言代码(C/C++/Python)
  5. 逆向算法,写出解密脚本

第一步

这里的bytecode很明显就是byte_3D4018[]我们可以使用lazyida快速的将数据提取出来。

第二步

接下来我们就需要对bytecode进行处理,此题中通过一个结构体来存储相关的信息。

从上自下可以依次认为r0-r9,可以类比为寄存器,这种思想一定要有。我们可以先跟进几个函数,查看大致结构。

this[9]对应的就是结构体的第九个值即r9也就是我们的bytecode,因为vm就是根据bytecode进行执行的,所以需要一个指针来指向我们的vm代码执行到何处。

`v1[3]`便代表着`bytecode[3]`
`v1[2]`代表`bytecode[2]`
`v1[1]`代表`bytecode[1]`

结合代码可以猜测此处应该为数据处理部分,因此我们记d1=v1[1] d2=v1[2] ...

那么`v2 = v1[3] + ((v1[2] + (v1[1] << 8)) << 8);`
便可转化为`v2 = (d2+d1<<8)<<8+d3)`

同样`v3 = v1[4];`->`v3=d4`
最后此函数便可以转化为`r8 = ((d2+d1<<8)<<8+d3)<<8+d4`

在该函数的最后r9+=5也就意味着我们的bytecode往前走了5字节 接下来便是体力活了,我们需要将所有case对应的函数进行转化,同时对bytecode进行处理,在调试时我们不必跟着bytecode的逻辑走,比如我们需要查看case 0x44对应的函数,完全可以手动修改result值也就是rax寄存器进行跳转,并且为了实时的查看r[0-9]的变化,可以取消stack syn

经过一番体力劳动之后,终于将bytecode进行了初步的转化,代码太多我会在文章末尾附上完整代码?

第三步

OK!接下来,结合具体的bytecode写出伪汇编代码

这一步没有什么难点,也是体力活?

第四步

写完伪汇编代码之后,我们最好总览一下全局,便可以大致了解代码结构,为接下来的分析提供可靠的猜测。 其实大致看一眼,便可以知道是多个循环,求和,比较的结构。 当然我们不能靠猜(比赛的时候建议大胆猜测一下) 我在这里简单分析一下:

r8 = 0x2F
continue        goto L:1
r4=r8=0x2f
r1=*r6            input[0]
r3=r3^r3=0
cmp r1,r3       r5=1
continue        goto L:28

这一段容易看出是一个初始化,寄存器置零,传入输入字符

r4=r8=0x2f
r1=*r6            input[0]
r3=r3^r3=0
cmp r1,r3       r5=1
continue        goto L:28
r6++
r8=0x46
r2=r8=0x46 'F'
cmp r1,r2        r5=0 or -1
continue        goto L:28
r8=0x30
r2=r8=0x30 '0'
cmp r1,r2        r5=0 or 1
continue        goto L:25
r8=0x39
r2=r8=0x39 '9'
cmp r1,r2        r5=0 or 1
continue        goto L: 31
r8=0x41
r1=r8=0x41 'A'
cmp r1,r2        r5=0 or 1
continue        goto L:28
r1=r1^r1=0
cmp r1,r1         r5=0
r9=r9+r1+2        goto L:31
r1=r1^r1
r1=r1+1
continue        goto L:3
# L:num    代表跳转到第几行

此处是一个循环,并对输入进行判断,我们完全可以猜测,或者跟着代码去走一遍流程,也还是简单的,后面的代码也是类似的。

第五步

结合着上面的分析和转化,在理解伪汇编代码的基础上,我们尝试转化为高级语言代码,比如说第一部分的代码可以变成如下形式:

    for i in range(0x2f):
        cipher =ord(flag[i])
        if  (cipher > 0x46 and cipher <0x41) or (cipher > 0x39 and cipher < 0x30):
            break

最后我们便可以写出解密脚本

从加密的过程来看,其实就是key的倒序,费了这么半天劲,结果加密算法这么简单,不过最后也只有15支队伍做出了此题。?

完整代码

附件下载地址:https://pan.baidu.com/s/12ubo4pJbjSwBrVvgoRbC0w 密码:6dv7

注意行号❕ 建议大家自己去写一遍,别嫌工作量大!?

总结

除了手动分析bytecode我们还可以写一个解析器进行自动化分析,这里便不做介绍了 希望对大家有所帮助(如有问题还请师傅们指出!)

原文发布于微信公众号 - 安恒网络空间安全讲武堂(cyberslab)

原文发表时间:2018-10-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏华章科技

入门科普:什么时候要用Python?用哪个版本?什么时候不能用?

Python使用面向对象编程(object-oriented programming,OOP)和构造,你可以像任何其它面向对象的语言一样使用它,譬如Java。

1472
来自专栏深度学习自然语言处理

【收藏】这些Python代码技巧,你肯定还不知道

人们还经常把 Python 笑称为「可执行伪码(executable pseudocode)」。但是,当你可以编写这样的代码时,很难去反驳这种言论:

833
来自专栏算法与数据结构

PTA 银行排队问题之单队列多窗口服务

假设银行有K个窗口提供服务,窗口前设一条黄线,所有顾客按到达时间在黄线后排成一条长龙。当有窗口空闲时,下一位顾客即去该窗口处理事务。当有多个窗口可选择时,假设顾...

32910
来自专栏Kirito的技术分享

八个层面比较 Java 8, RxJava, Reactor

这是一篇译文,原文出处(http://alexsderkach.io/comparing-java-8-rxjava-reactor/)。其实很久以前我就看完了...

8586
来自专栏GreenLeaves

C# 多线程五之Task(任务)

为什么MS要推出Task,而不推Thread和ThreadPool,以下是我的见解:

2451
来自专栏iKcamp

翻译连载 | 第 10 章:异步的函数式(上)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇

原文地址:Functional-Light-JS 原文作者:Kyle Simpson-《You-Dont-Know-JS》作者 第 10 章:异步的函数式(上)...

2139
来自专栏有趣的Python和你

千里之行,始于足下变量字符串

1013
来自专栏java一日一条

程序员你为什么这么累【续】:编码习惯之接口定义

工作中,少不了要定义各种接口,系统集成要定义接口,前后台掉调用也要定义接口。接口定义一定程度上能反应程序员的编程功底。列举一下工作中我发现大家容易出现的问题:

1072
来自专栏牛客网

面经总结

面试记录 头条 - 一面 - 自我介绍 - 连续子数组的最大和 - 二叉树任意两个节点之间路径的最大长度 - 二叉树的深度 - 一面上个周只记得这么多了 - 二...

4007
来自专栏微服务生态

究竟怎样写代码才算是好代码

今天让我们来谈谈代码吧。代码重要吗?当然,代码就是设计(Jack W.Reeves, 1992);代码是最有价值的交付物。我们需要好代码吗?在给“好代码”下个定...

873

扫码关注云+社区

领取腾讯云代金券