矿大CTF逆向区通关啦!
本文不再更新,完结撒花~
2020年6月14日 00:19
0x01 入门1
双击str2,直接看到flag
注意:flag形式为xman{可见字符}
0x02 入门2(第一次用python解题)
加密算法为将输入的字符串(从v5开始)逐位与v3(v3=0,每次自增一)异或,加密后的字符串(从v12开始)与从unk_403010开始的字符串比较
从unk_403010开始的字符串的值为
(注意,h指十六进制)
python跑一波
1 | a = ['58','31','70','5C','35','76','59','69','38','7D','55','63','38','7F','6A'] |
答案为
学长的WP中给出的python代码:
1 | #coding=utf-8 |
0x03 入门3
刚打开,还没转C语言,就看到了
base64走一波
拿到flag
B4se64_i5_c0mmon
0x04 入门4
没做出来,主要是只知道是MD5,不知道怎么解题,特别是数据是如何在内存中存储的不清楚
看了学长的WP才知道就是对 7FEF6171469E80D32C0559F88B377245 进⾏还原
PS:
1、MD5中用的是十六进制
2、IDA中,右键数字选择 Hexadcecimal 把数字转成16进制显示(快捷键H。同样的R键能让数据以字符char的形式 显示)
一个对MD5算法讲解的笔记
https://www.cnblogs.com/arsense/p/6485073.html
0x05 入门7
不解释
0x06 软件逆向教学
由下面的语句可知,输入的字符串长度为21
选中数据后按R键,将十进制数转换成字符显示
转换后:
将v5和v6反着读(大头端和小头端的区别,具体的不清楚,只知道是存储和显示上的差异)
flag{eas
1est_cra
ckme
}
合起来就是flag{eas1est_crackme}
0x07 找到字符串就赢了
主函数中有:
进入check函数
a1是check的形参,实际为输入的字符串
我走入的误区:
1.好久没做这个类型的题了,把(i+a1)^6理解成了i+a1的六次方,^在c++中是异或,在python中才是n次方,搞混了
2、以为是i+a1的六次方是否和i+134520928相等,实际上,选中134520928后,按h键转换为十六进制后就很清晰了,实际上就是个地址,加上个i后表示偏移量
构造已经很清晰了,输入的字符串与6异或后和0x804A00处开始的字符串比较是否相同。
界面内按g键
我一看,尾端一个等号,这不就是类似base64吗(但base64没有|~>等符号),我以为异或解出字符串后还有base64一次
python:
1 | base64='bcg|nd4q6>cp1o7i0c|w4h|~6>?weios' |
输出 deazhb2w08ev7i1o6ezq2nzx089qcoiu
base64不行,因为base64解码后,有的字符甚至都超出了127。我看着想了好几个小时,一直以为这串乱七八糟的东西一定跟base64有关,因为逆向后出现了base64的字样。
最终没办法,只能把这串“乱码”加上flag{}直接提交,通过了。。。。
我该说什么好呢??
0x08 据说数学不好的不会做
第一次接触动态调试。
用ida32打开后,f5转c,再按f9显示如下界面,选Local Windows debugger,点OK
点Yes
若路径含中文,则提示
关闭,将文件移动到英文路径,并修改文件名不含中文,重新打开。
(学长wp的文件名似乎是中文的,不太清楚这一块,先改成英文名,将就着用吧)
v20往后的17位为输入的字符串
分析可知
string[i]=v15[i]/v13[i]
所以要知道v15和v13的值
由于代码繁琐,不易得知,所以通过动调直接获得二者的值。
f9进入动调界面,
把光标移到要下断点的地⽅,按 F2 下断点,那条语句就会变成红⾊。(也可以通过点击该语句前面的圆圈下断点)
(程序会我们下断点的地⽅中断运行)
这里我们在第66行,while处下断点(此时v15和v13的具体值已经计算出并存储到相关位置)
在弹出的程序运行界面中输入一个任意的长度为17的字符串,回车(因为前面的if语句判断是否长度为17,若不是17,并不计算v15和v13)
此时,已经计算出具体值,找到v15的地址
双击程序里的v15,
则00D3FD48就是v15的地址,选中,复制。
鼠标点击Hex View-1的框,按g,弹出一个搜索框,将v15的地址粘贴进去(地址中的字母可以是小写的)
会跳转至v15的起始位置(下图中数值为66的地方)
从起始位置数17个单元(一个单元有几个字节跟具体的数据类型有关),
DWORD全称Double Word,是指注册表的键值,每个word为2个字节的长度,DWORD 双字即为4个字节
故有:
由上可知
v15=[0x66,0xd8,0x123,0x19c,0x267,0x2b2,0x309,0x370,0x39f,0x3b6,0x4ba,0x4ec,0x4d3,0x594,0x5eb,0x6e0,0x84d]
注意读取方式,如第三个黄框内的23 01 00 00 ,读取为00 00 01 23
同理可以读出v13的值
明显看出,v13的值是从1到17
知道了v15和v13的值,就可以写py了
1 | v15=[0x66,0xd8,0x123,0x19c,0x267,0x2b2,0x309, |
得到flag{song_ni_fen}
0x09 简单逆向
直接打开题目链接,(pdf文件在我电脑上默认用chrome打开),弹出
试一下,这并不是flag(于子洋发现事情并没有那么简单)
下载下来,用binwalk看了下,并不是复合文件。
可是pdf的文件也没法用ida呀
用winhex看了下,文件首位都没有明显的flag提示,但抱着试试看的态度从头开始往后翻,翻了一定的长度后
base64走一遍
flag{easy_J5_c0de_in_PDf}
但是,这也隐藏的太深了吧,谁会往后翻这么多?
而且,这是逆向吗??hhh
0x0A Easy_CrackMe
用ida打开,alt+T打开搜索字符串界面,输入Incorrect Password查找
跳转到含有该字符串的地方
此处可以看到‘Incorrect Password’与‘Congratulation!!’均被引用,Ctrl+x跟踪上去。
f5转c
可以看出当v3=97,v4=a5y,v5=aR3versing,String=69
时输出Congratulation
即v3=a,v4=5y,v5=R3versing,String=E
String存储在v3前面,所以为Ea5yR3versing
根据格式,得到cutmctf{Ea5yR3versing}
[原创]Easy_CrackMe小练习
https://bbs.pediy.com/thread-216972.htm
0x0B 入门6
迷宫题,在攻防世界的新手区做过。可参考:http://blog.iyzyi.com/index.php/archives/308/#toc-0x12maze
1 | s = '********* * ** * ** ** * ** ** * #* ** **** ** *********' |
起始位置在光标处,走到#
处
xman{dddddrrrrruuuuullldddr}
0x0C 普通的逆向
解题时用的IDA7.0,但是写题解时发现卡在加载界面,故使用的6.8。
很容易确定flag{****_***_*******_666}
(第23行开始)第一段的四个字符分别为ord('f') ^ 5
ord('l') ^ 0x19
ord('a') ^ ord('\f')
ord('g') ^ 0x13
即cumt
,注意第三个字符是换页\f
,而不是字母f
(第45行开始)第二段的三个字符。已知v12 = ord('f')
,求v11 = 0x12 ^ v12 = t
, v10 = 0x17 ^ v11 = c
,即ctf
关键在于剩下的那段字符的解密。sub_4011C0将要求的字符加密后的密文为iueuihu。
掏出我的OD,在偏移首行0x1C0处下断点,f9直达。在程序中输入flag{cumt_ctf_iueuihu_666}
,其中iueuihu的目的是占位。
此时:
运行到偏移0x235处,之后就是加密。运行到偏移0x293时,可以发现ECX中已经出现了iueuihu加密后的qfufqrf。我们要找的就是加密后为iueuihu的字符串
进度回到运行到偏移0x235处。
来到0x3B3028处,
看来就是从这里取字符。
继续运行:
再结合IDA中的伪C代码,易于发现0x3B2FBF + ord('u') = 0x3B3034
所以取字符的原理已经知道了:对于字符x,其加密后的字符为地址0x3B2FBF + ord('x')
处的字符。
密文i地址为0x3B3031
,0x3b3031 - 0x3B2FBF = 114 = r
之后的6个字符的解密类似。最终为reverse。
flag{cumt_ctf_reverse_666}
0x0D Register
究极easy的android逆向。
apktool反编译:
1 | D:\计算机\CTF\工具\apktool_2.4.0.jar d -s D:\桌面\Register.apk -o D:\桌面\out |
然后打开D:\桌面\out\res\values\strings.xml
,发现flag.
0x0E brokenApk_00
一个损坏的apk,无法安装,也无法使用apktool反编译。解压后使用dex2jar将dex转换成jar。
关键代码整理为:ctext[count % textLen] = ctext[count % textLen] ^ key[count % keyLen]
但是只知道count的终值是50000,但是不知道count改动的过程是怎样的。
brokenApk_00\res\layout\main.xml
是一个二进制文件,强制打开后发现
那就是点击按钮50000次喽,count从1到50000,但是写出的脚本跑出的flag为'lag{apk_1s_Zip_Fi1e>
,尝试修改count从0到49999,跑出正确的flag.
1 | ctext = ['=', '6', '(', '-', '0', '5', '\'', '!', '\020', '}', |
0x10 linux
参考:
ida反编译后没发现什么有用的信息。放到linux下打开提示缺少libpython2.7
所以推测是python的打包后的可执行文件。
使用pyinstxtractor.py将exe转换为pyc文件。python pyinstxtractor.py [filename]
pyinstxtractor.py下载地址:https://github.com/countercept/python-exe-unpacker/blob/master/pyinstxtractor.py
将re04添加后缀名.py,打开,提示二进制文件,忽略并打开,看到源代码:
就是base64.
顺便说一下uncompyle的使用,虽然这题没用到。
1 | #安装 |
反编译多个文件实测在windows下无效,linux可以。
0x11 Hide&Seek
安卓逆向,做之前有50+个同学做出来了,以为不会很难,但是浪费了数十个小时。我吐了,这根本不是安卓逆向好吗?分明就是Misc.
拿到app后,模拟器走一波:
apktool提取资源,dex2jar反编译dex,jd-gui查看代码。
ctrl+shift+s全局搜索2131296272。实名吐槽jd-gui的全局搜索极其不好用,经常搜不到存在的字符串。
来到res\drawable-hdpi-v4
,里面一堆图片:
有两张明显的解码错误。
logi_.png末尾有一串疑似字符。
my_personal_not_login_bg.png左下角有flag字样:
所以就是flag{0HYOVFlNDM3}
。
0x12 KeygenMe
逻辑很简单,就一次异或而已。
对name字符串三个字符一组,分别与v6,v7,v8异或,得到的数据以2位16进制形式写入字符串v13。
aS02x:
%02x 格式控制: 以十六进制输出,2为指定的输出字段的宽度.如果位数小于2,则左端补0
1 | s = '5B134977135E7D13' |
有人可能会问为什么上上图中第40行是!strcmp(&v9, &v13)
。我说过很多次了,不要过分依赖IDA的F5.
我们来看一下它的汇编:
①输入serial
②判断两字符是否相同
③判断字符是否为\0
④判断两字符串的下一个字符是否相同
⑤下标加2
⑥eax清零
⑦没搞懂。只知道sbb是借位减法
⑧eax为0则ZF赋1,则correct
0x13 珍贵资料
拿到一个ANDROID BACKUP文件和一个apk。
对于ANDROID BACKUP的利用可以参考https://github.com/wnagzihxa1n/CTF-Mobile/blob/master/2015XCTF-RCTF/flag-system-100/WriteUp.md
1 | dd if=unknown bs=1 skip=24 of=compressed |
但是输入账号和密码,登录失败。
所以我们把apk逆向一波。
LogoActivity.class
中:
WelcomeAvtivity.class
中:
所以我们把字符串i异或0xB,得到:flag is password。
所以把密码dudqlvqrero1提交,不对,尝试添上flag{},cumtctf{},都不对。
那就再看看反编译的代码吧。
选中的那一行的上一行,是将密码加密。
然后加密后的字符串与dudqlvqrero1比较。
(我开始被坑了,以为dudqlvqrero1就是密码,没想到是加密后的)
encode方法调用了Encryption,Encryption:
其实就是凯撒密码,key值为3
写出解密脚本:
1 | SOURCE = "ijklmstuvwxyz0123abcdenopqrfgh456789" |
输出amanisnobody,添上flag{}提交。
0x14 入门8
主函数,输入,经过一系列处理得到v15(即v4),然后已知的数据byte_403024与v4异或。
由于v15是char,所以最多128种可能的取值,直接爆破就可以。不需要搞明白v15是怎么处理得到的。
35~39行的处理算法极其恶心,放个部分截图:
分析是极其困难的。所以还是爆破吧。
一开始我没注意到那一大串的处理其实并不是加密字符串,而是仅仅得到一个char类型的数据而已。
所以说不要程序走到一处后直到完全看明白再继续往下看呀!
1 | string = '!"#$%&()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\'' |
加上xman{}提交。
另外在这里写一个将十六进制从winhex导出的小技巧:
0x15 普普通通的逆向
upx壳,我使用命令行脱掉后程序无法运行。
看了看网上的学长的题解,他们都是使用upxshell脱壳,都可以正常运行。
但是我使用upxshell脱壳后,依然程序崩溃。
原因是选择的upx的版本不对。
选择UPX 2.03脱壳,就可以了。
这题输入用户名,然后对用户名加密,再与输入的密码比较。所以可以动态调试拿到加密后的用户名。
但是本题反调试比较多。
GetTickCount
第51行和第64行的GetTickCount(),作用是计算运算这段代码的时间,如果大于1秒,v7的值就会和正常运行时的值不同,然后v7代入到65行的sub_401240中,影响用户名的加密。
sub_401240:
上图的v3就是之前的v7。flag的字符和v7是相关的。
但是我们如果不去逆算法的话,根本没必要在第51行和第64行中间下断点,正常跑就行。所以不用刻意绕过这个反调试的机制,程序正常跑就可以。
CreateToolhelp32Snapshot
列出所有进程名称,与吾爱破解[LCG].exe 吾爱破解.exe idaq.exe idaq64.exe
比较,如果存在同名进程,则影响v1,v2的取值,从而影响flag的计算。
IDA对中文不友好,OD很容易看出吾爱破解的字样。
v1,v2最初为1,若存在相应进程,则赋值为0。v1负责吾爱破解[LCG].exe 吾爱破解.exe
,v2负责idaq.exe idaq64.exe
我们有两个思路,最简单的是把我们使用的吾爱破解[LCG].exe
改名。还有就是判断完所有进程后,我们手动修改v1,v2的值。
IDA中找到开始计算flag的地址4013b3,基址401000,偏移3b3,巧的是OD的基址也是401000,所以OD中来到4013b3的地方下断点。
F9运行到此处。
v1和v2分别存储在bl,bh.
此时的ebx是00000100,bx是后四位,bh是01,bl是00.
因为此时我没开IDA,所以对应IDA的v2的值是01。如果你同时开了IDA和OD,ebx应该是00000000.
这些不重要,我们直接把ebx改为00000101.然后继续运行。
IsDebuggerPresent
该函数是被Kernel32.dll导出的,该函数没有参数,如果当前程序正在被调试的话,返回值为1,没有被调试的话,返回值为0
所以直接将v3(eax)改为0即可。
找到flag开始处理的地址40145b,这里我的OD没分析处理来这是代码。右键->分析->分析代码。就可以把十六进制转换成代码了。
在此处下断点,F9,修改eax为0,有的OD自带绕过IsDebuggerPresent()这个函数,就不用你去修改了。
在IDA中观察到4014f2开始strcmp
所有我们在此处下断点。
拿到flag.
学长的题解
由于我个人水平所限,这里粘贴一下学长写的WP.
来源:http://ronpa.top/2018/09/19/cumt%E5%AE%9E%E8%AE%AD%E5%B9%B3%E5%8F%B0writeup/
考各种常见反调试的绕过 挺好的一题 去年写的很吃力还没写出来 今年调的很吃力好歹写出来了23333
这题其实完全不需要去逆向算法 因为题目只是要求得到给定username的密码 程序最后直接是把输入的密码和生成的密码进行比较
所有只需要绕过所有反调试 最后把比较的密码给提取出来即可得到flag
一上来发现文件加了壳 最简单的upx压缩壳 选择用upxshell脱壳
简单看一下主函数
开头有个输入检测 username是否是数字
具体把生成密码的分成了四段
第一个函数
0x4011E0
前有两个调用GetTickCount()
来检测时间间隔 并且中间有遍历文件入口检测int3断点的语句 一旦在两次调用间下断点或者有int3断点 就会得到错误的参数v7 然后进函数则会生成错误的密码的6-9位第二个函数
0x401240
内有一个获取进程的函数CreateToolhelp32Snapshot()
假如检测到常见调试器的进程 则会得到错误的中间第10-13位密码第三个函数
0x401420
内有IsDebuggerPresent()
函数检测调试状态 得到第14-17位密码第四个函数
0x4014b0
里直接给了flag的开头和结尾flag{}
最后把生成的密码和输入的密码进行对比所以最快速的方法就是绕过上面三个检测 在最后的比较里提取生成的密码
接下来就是绕过
GetTickCount() & int3检测
直接不用管,两次中间的夹的代码其实是一个int3断点的检测 例如OD,其是将代码设置为0xcc来实现普通断点 所以从头到尾遍历文件 假如有0xcc则存在调试.这个和GetTickCount()组合防止我们在while里更改 但可以在循环结束后把v7和v11改为0
我用的加了插件的OD把这个0xcc检测绕过去了
1 | v9 = GetTickCount(); |
CreateToolhelp32Snapshot()
查找进程信息,并和字符吾爱破解[LCG].exe 吾爱破解.exe idaq.exe idaq64.exe
假如进程中有同名进程 则代表检测到调试器 生成错误字符.
就比如下图,是我们第一个system进程和他要检测的调试器进程名吾爱破解[LCG].exe
进行比较
wcscmp()
(它用来比较两个Unicode字符串是否相等 和strcmp相同 相等返回0)
这里绕过的方法很多 最搞笑的方法就是你直接把调试器的名字给改了就行,比如我用的是吾爱破解[LCG].exe
改成OD.exe
就不会被检测了233333
或者可以在调试的时候最后直接把v1=1 v2=1改成正确的值 1
IsDebuggerPresent()
该函数是被Kernel32.dll导出的,该函数没有参数,如果当前程序正在被调试的话,返回值为1,没有被调试的话,返回值为0
这个直接改V3值为0就行 我的OD直接绕过了
都绕过之后提取正确密码
041751300132flag{antidebugabc}
总结
这题我的可能算是取巧的做法 完全没去关注算法 但要去逆向这题代码也需要知道反调试函数的返回值.
GetTickCount()
获取当前时间 通过两次调用相减可以得到时间差 防止中间下断点 绕过方法 最后修改值0xcc
int3断点检测 有些调试器下断会改变文件值 遍历全局去检测是否有字节为0xcc来检测是否被调试 绕过方法 用有插件的ODCreateToolhelp32Snapshot()
看是那种 检测父进程还是调试器还是权限 绕过方法 更改进程名称IsDebuggerPresent()
如果当前程序正在被调试的话,返回值为1,没有被调试的话,返回值为0 绕过方法 用有插件的OD/最后修改值
0x16 什么是密钥?
先用upx2.03脱壳:
前半部分是极其复杂的算法,有md5和sha1,将输入的字符串加密后放入source(最后一步变换是取sha1,结果是长度为20的一个字符串),然后Dest等于source的前20个字符加上v4,v5两个随机数,共22个字符。
虽然很复杂,但是完全不需要看懂前半部分。
后半部分总共进行了三次异或:
byte_405450 = byte_405450 ^ Dest ^ time_curent ^ Dest = byte_405450 ^ time_current
考虑到byte_405450[]的初值为table[time_current],上式可化为:
byte_405450 = table[time_current] ^ time_current
这样可以惊奇的发现,最终的结果与输入无关!只与已知的table数据表和time_current有关。那我们爆破time_current即可。
idc脚本提取table的数据,输出到D:/1.txt
,然后写出下面的脚本:
1 | #with open('D:\\1.txt', 'rb')as f: |
0x17 Hook&Crack
本题同时单独发表在安卓逆向之log插桩
java
c.c.a中找到tu.class:
红框处是反调试的代码,如果修改了apk重新打包,再次运行会终止进程。
进入sd.dfh中看一下:
进入jh.gfds:
最内侧的df.gr是一个很长的二进制字符串,外侧的函数都是解密操作。
由于变量名被混淆了,解密代码很难读懂,而且flag在程序运行中明文出现过,所以可以考虑插桩,当将二进制字符串解密出flag时,可以使用log输出flag。
删掉反调试代码
我们来到smali层:
打开tu.smali,删掉下面的代码:
1 | .line 35 |
log插桩
来到sd.smali:
把上图的第27行删掉,不要销毁flag。并在此处添加
1 | const-string v1, "iyzyi-flag" |
同时将第18行的.locals 1
改为.locals 2
,以增加一个临时变量。
编译成新的apk后,再次反编译,可以看到java代码变成了这样:
tu.class:
sd.class:
adb查看log
夜神模拟器
安装apk后,在夜神模拟器的安装目录下打开powershell,输入:
连接设备:
./adb.exe devices
输出log:
./adb.exe logcat -s iyzyi-flag:v
iyzyi-flag是log的标签。
base64解码后即得flag.
手机
以Honor v10为例。
多次点击版本号,进入开发者模式:
进入开发人员选项,启用USB调试
和“仅充电”模式下允许ADB调试
:
通过USB线和电脑连接,允许USB调试:
然后手机安装刚刚插桩的apk。
从网上下载adb,并在其目录下打开powershell,剩下的adb操作和夜神模拟器的操作是一样的:
./adb.exe logcat -c
的作用是清空log缓存区。