syc二面 -2019
这是syc-19年二面题目。
以下为自己的报告,
二面结束后又了解到的关于这道题更多的信息算法是base58编码和c++的做法,目前先鸽,以后更新。
很不错的题目,学到了许多,
附赠文件:⬇⬇ ⬇⬇
链接:https://pan.baidu.com/s/1aP3KaZFQD0P4T-xKn5n-jQ
提取码:1ahh
复制这段内容后打开百度网盘手机App,操作更方便哦
文章目录
以下是二面最后提交的报告。
syc二面 – 令则
本报告是题目完成后写成,步骤都由复现后写就,可能会有在所难免的细节的疏漏,但基本完全还原整个解题思路和遇到的问题及解决。并且将自己遇到的小的细节和具体的操作原理写了些。
有部分工作是动调和标注,报告忽略了这些繁琐的部分,写出了这些工作的思路,并写出了分析出来的程序逻辑。
题目分析-整体分析
拿到题目后解压,
首先注意到一个exe文件就是我们的题目,然后两个dll是环境配置,然后一个后缀jpg_encode文件,我们首先会了解这是一个加密的jpg文件,在文件内解压,flag极可能在里面,
然后检查题目文件,32位无壳exe文件,其实应该庆幸这样的还可以动调,肝也能有方向,
载入ida,观察主函数,这是个c++写的程序,伪代码的可读性不高。
我们发现字符串sorry和wait的位置,发现sorry以后会直接结束程序,而wait以后会处理第三方文件,那么这个判定的位置就是一个关键,
后面的一部分使用的c语法,可读性极高,我们观察到载入了我们的jgp_encode文件和另一个jgp文件,然后一个读取一个写入,我们需输入一个值,用于异或解密,得到最后的jgp文件。
这样我们有一个大概的想法,使用动调确定了下整个函数的流程,然后答题思路大致有了:
- 首先我们要得到flag,应该第一个字符串str1输入正确,
- 应该会在第一个字符串中提示第二个字符串str2,
- 得到第二个字符串用于解密第三方文件,得到正确解密的end.jgp文件
- 就是图片直接显示flag或者隐写flag。
逆向1-第一层逆向
由于c++伪代码的复杂,分析直接看的汇编,
工具:
ida,特别是图形界面
od/x32dbg的动态调试,
其实在我的使用中差不多更推荐x32一些,双击后,在栈中可以偏移和地址一起观察到:
分析的两条主线:
- 根据判定一步步逆向得到正确的数据和字符串,
- 使用一串abcdefghijklmnopqr来正向了解程序中对字符串的处理。
逆向得到加密后字符串str2
首先从sorry字符串判定的位置入手:
我们看到是判定的是两个寄存器的值,我们要在这里下断点,,观察整个判定过程
图示中蓝色为数据的准备,橙色部分为判定,红色部分为错误法显示“sorry”并退出程序,绿色是整个循环的判定,判定字符串是否已经检测完,
注意循环计数器,这里为[ebp-20]
;
审视判定的两个值,ebx和eax:
我们可以发现每次的ebx的值就是这个明文字符串s的按计数器选到的值。
审查eax的值,发现前面经历了一系列的计算,然后将这个值和[ebp-70]变量中储存的内存地址传参,调用一个函数,然后将储存eax中的函数返回值拿出来进行比较。
- 计算部分,涉及到两个:循环计数器
[ebp-20]
、已定义的变量[ebp-5c]
形成的数组。 - 函数调用,我们先观察
[ebp-70]
指向的地址,然后看到这里是一串字符串,观察函数调用后的内存、栈、寄存器,发现这个函数作用:按照参数的数值在指向的内存地址的字符串中选值,并返回这个值。
由此,我们大致可以猜测,这个[ebp-70]
指向的地址就是加密完以后的字符,然后按照计算得到的数值选择出值,和明文的字符串s比较。
由此我们可以确定这个加密后字符串的长度,并得到这个加密后的字符串。
但还差一步,即这个计算出的值,我们可以找到[ebp-5c]和周围几个值组成的数组,配合循环计数器,通过汇编语言和动调分析,得到计算过程,并写出脚本得到这些值组成的列表,但里面出现了部分较复杂的汇编语句,我使用了下面更简单的方式。
我们进行动调,并记录下这些值,得到列表:
arr = [0x1,0x5,0x4,0x2,0x3,0x0,0x7,0xB,0xA,0x8,0x9,0x6,0xD,0x11,0x10,0xE,0xF,0xC]
我们可以知道,加密后的字符串str1
的第arr[i]
位等于s
的第i
位,可以得到str1:
s = 'PrSDvCZirMmpygyGhb'
arr = [0x1,0x5,0x4,0x2,0x3,0x0,0x7,0xB,0xA,0x8,0x9,0x6,0xD,0x11,0x10,0xE,0xF,0xC]
v20 = [0]*18
str1 = ''
for i in range(18):
v20[arr[i]] = s[i]
for i in range(18):
str1 += v20[i]
print(str1)
如此我们得到了加密后的字符串str1:
str2 = 'CPDvSrpZMmribyGhyg'
下一步继续向上需要定位生成str2的关键加密部分,我们使用一个自己生成的字符串进行动调测试来进行寻找。
###程序流程分析
在main函数最开始下断点,并一直跟踪main函数。
观察调用函数:
*关注函数传入的参数,相对应的内存、栈中的变量和寄存器,
参数、参数指向的地址和寄存器一般就是函数作用到的所有内容了。
main函数的作用:(对照下图
- 赋值了几个栈内地址,当作定义的局部变量(主要在绿色部分
- 接收输入的字符串,并调用函数将输入的字符串赋值到内存的三处位置(主要在黄色部分和几个函数直接调用
- 将内存地址3,内存地址3的最后一位字符,内存地址1分别传入一个lam函数,这个函数会将地址3的数值加密,形成我们得到的加密后的函数。(橙色框中的红色的断点位置
- 然后进行判定,即我们首先关注并分析完成的sorry的判定位置。(后面紫色粉色部分
- 判定成功进入第三方文件的处理部分。(蓝色部分
这样我们知道了整个加密的关键在于这个lam函数,进入分析:
lam函数的作用:(对照下图
- 首先处理数据赋值一些局部变量,其中有一部分函数的作用是清空栈内数据,一部分函数是将某变量指向的地址同样赋值给另一变量。(蓝色部分
- 然后进入循环,共两个大循环,一个负责处理数值,一个使用处理出的数值根据一串字符串进行替换字符的加密。
- 处理数值的大循环,内部嵌套一个小循环,每个字符会经历小循环,然后结束后进入大循环获取到下一个字符,计算的结果会保留在内存中。(大循环即绿色部分,小循环为黄色部分
- 替换字符的循环,从内存中取出前面保留下的计算结果,然后得到字符串中的对应位,替换掉main函数内存地址3中的原字符串。替换完成的这个字符串也是main函数sorry判定部分判定的那个字符串。(紫色部分
我们分析得到了加密过程的程序流程。
字符测试text
由于是较为复杂且细节并没有太明确的程序,我们使用自己已知测试字符串进行测试,尝试正向实现和逆向求解加密过程,对比动调中的数据,得到正确的逆向脚本。
我们的测试字符串:
str3 = 'abcdefghijklmnopqr'
得到对应数据
先在main函数sorry判定位置下断点。找到变量[ebp-70]
指向的内存地址,得到测试字符串str3加密后得到的字符串:
cipher = 'YDXEJ7jpxdNwq9Tj8c4HbeAs'
然后重新载入程序,我们在lam函数的生成字符串处下断点。
通过变量[ebp-64]
查找到存放加密后数据的地址,并得到加密后的数据:
arr = [0x02,0x0F</