矿大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跑一波
a = ['58','31','70','5C','35','76','59','69','38','7D','55','63','38','7F','6A']
b = []
for i in a:
b.append(int(i,16)) #先前误写成了int(a,16),编译不通过
v3=0
for i in b:
print(chr(i^v3),end='')
v3=v3+1
答案为
学长的WP中给出的python代码:
#coding=utf-8
a=[0x58,0x31,0x70,0x5C,0x35,0x76,0x59,0x69,0x38,0x7D,0x55,0x63,0x38,0x7F,0x6A]
flag=''
for i in range(len(a)):
flag+=chr(a[i]^i)
print flag
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:
base64='bcg|nd4q6>cp1o7i0c|w4h|~6>?weios'
flag=''
for i in range(32):
flag+=chr(ord(base64[i])^6)
print(flag)
输出 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了
v15=[0x66,0xd8,0x123,0x19c,0x267,0x2b2,0x309,
0x370,0x39f,0x3b6,0x4ba,0x4ec,0x4d3,0x594,0x5eb,0x6e0,0x84d]
flag=''
for i in range(17):
flag+=chr(v15[i]//(i+1)) #不是整除的话会报错
print(flag)
得到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
s = '********* * ** * ** ** * ** ** * #* ** **** ** *********'
for i in range(len(s)):
if i % 8 == 7:
print(s[i],'\n',end='')
else:
print(s[i],end='')
起始位置在光标处,走到#
处
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反编译:
D:\计算机\CTF\工具\apktool_2.4.0.jar d -s D:\桌面\Register.apk -o D:\桌面\out
d <file.apk>:执行decode,<file.apk>表示被反编译的apk文件的绝对路径。
-s:指定不反编译源码,即保留classes.dex文件。
-o <outdir>:指定反编译之后生成文件的路径。
然后打开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.
ctext = ['=', '6', '(', '-', '0', '5', '\'', '!', '\020', '}',
'"', '\013', '\r', '9', '!', '\r', '\027', '9', 'd', '7',
'a' ]
ctext = [ord(i) for i in ctext]
key = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z' ]
key = [ord(i) for i in key]
keyLen, textLen = len(key), len(ctext)
for count in range(0, 50000):
ctext[count % textLen] = ctext[count % textLen] ^ key[count % keyLen]
print(''.join(list(map(chr, ctext))))
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的使用,虽然这题没用到。
#安装
pip install uncompyle
#反编译单个文件
uncompyle6 foo.pyc > foo.py
#反编译多个文件
uncompyle6 -o . *.pyc
反编译多个文件实测在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
s = '5B134977135E7D13'
serial = []
for i in range(len(s)//2):
serial.append(int(s[i*2:i*2+2], 16))
print(serial)
v6 = [16, 32, 48]
for i in range(len(serial)):
serial[i] ^= v6[i % 3]
print(serial)
print(''.join(list(map(chr, serial))))
有人可能会问为什么上上图中第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
dd if=unknown bs=1 skip=24 of=compressed
printf "\x1f\x8b\x08\x00\x00\x00\x00\x00" | cat - compressed | gunzip -c > decompressed.tar
tar xf decompressed.tar
但是输入账号和密码,登录失败。
所以我们把apk逆向一波。
LogoActivity.class
中:
WelcomeAvtivity.class
中:
所以我们把字符串i异或0xB,得到:flag is password。
所以把密码dudqlvqrero1提交,不对,尝试添上flag{},cumtctf{},都不对。
那就再看看反编译的代码吧。
选中的那一行的上一行,是将密码加密。
然后加密后的字符串与dudqlvqrero1比较。
(我开始被坑了,以为dudqlvqrero1就是密码,没想到是加密后的)
encode方法调用了Encryption,Encryption:
其实就是凯撒密码,key值为3
写出解密脚本:
SOURCE = "ijklmstuvwxyz0123abcdenopqrfgh456789"
LEN = len(SOURCE)
s = list('dudqlvqrero1')
flag = []
for i in range(len(s)):
k = SOURCE.index(s[i])
j = k
if k == LEN - 1:
j = 1
k = j
if j == LEN - 2:
k = 2
j = k
if k == LEN -3:
j = 3
flag.append(SOURCE[j-3])
print(''.join(flag))
输出amanisnobody,添上flag{}提交。
0x14 入门8
主函数,输入,经过一系列处理得到v15(即v4),然后已知的数据byte_403024与v4异或。
由于v15是char,所以最多128种可能的取值,直接爆破就可以。不需要搞明白v15是怎么处理得到的。
35~39行的处理算法极其恶心,放个部分截图:
分析是极其困难的。所以还是爆破吧。
一开始我没注意到那一大串的处理其实并不是加密字符串,而是仅仅得到一个char类型的数据而已。
所以说不要程序走到一处后直到完全看明白再继续往下看呀!
string = '!"#$%&()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\''
string_list = list(map(ord,string))
byte_403024 = [ 0x64, 0x00, 0x47, 0x47, 0x43, 0x04, 0x46, 0x50, 0x6B, 0x05, 0x47, 0x6B, 0x40, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x6B, 0x58, 0x04, 0x04, 0x53, 0x15]
for i in range(128):
flag = []
for j in range(63):
if byte_403024[j] ^ i not in string_list:
break
else:
flag.append(chr(byte_403024[j] ^ i))
if len(flag) == 63:
print(''.join(flag))
加上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检测绕过去了
v9 = GetTickCount();
v5 = &loc_401052;
v6 = 64;
do
{
if ( !v6 )
break;
v4 = *v5++ == -52;
--v6;
}
while ( !v4 );
if ( v4 )
v11 = 1;
v7 = (GetTickCount() - v9) / 0x3E8 > 1;
sub_4011E0(v11, (int)&v19, v7);
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
,然后写出下面的脚本:
#with open('D:\\1.txt', 'rb')as f:
# table = f.read()
table = b'N\x0b\x0c!8{7\x17no{ku\x1f-V*\x0cZYe+n\x16\x0b\x17-DZ\x17Zys~xdVqojk@vl@q/k@tzfb\x0c\x15\x16,BXd\x15tzfb\x0c\x15\x16,BXd\x15u\x1f-V*\x0cZYe+n\x16qojk@vl@q/k@qojk@vl7\x17no{kZ\x17ys~x\x1f-V*\x00\x00\x00\x00\x00\x00\x00\x00'
for time in range(60):
flag = ''
for j in range(22):
flag += chr(table[time + j] ^ time)
if 'flag' in flag:
print(flag)
0x17 Hook&Crack
本题同时单独发表在安卓逆向之log插桩
java
c.c.a中找到tu.class:
红框处是反调试的代码,如果修改了apk重新打包,再次运行会终止进程。
进入sd.dfh中看一下:
进入jh.gfds:
最内侧的df.gr是一个很长的二进制字符串,外侧的函数都是解密操作。
由于变量名被混淆了,解密代码很难读懂,而且flag在程序运行中明文出现过,所以可以考虑插桩,当将二进制字符串解密出flag时,可以使用log输出flag。
删掉反调试代码
我们来到smali层:
打开tu.smali,删掉下面的代码:
.line 35
invoke-virtual {p0}, Lc/c/a/tu;->getApplicationInfo()Landroid/content/pm/ApplicationInfo;
move-result-object v1
iget v2, v1, Landroid/content/pm/ApplicationInfo;->flags:I
.line 36
and-int/lit8 v2, v2, 0x2
.line 35
iput v2, v1, Landroid/content/pm/ApplicationInfo;->flags:I
if-eqz v2, :cond_0
.line 37
invoke-static {}, Landroid/os/Process;->myPid()I
move-result v1
invoke-static {v1}, Landroid/os/Process;->killProcess(I)V
.line 39
:cond_0
invoke-static {}, Landroid/os/Debug;->isDebuggerConnected()Z
move-result v1
if-eqz v1, :cond_1
.line 40
invoke-static {}, Landroid/os/Process;->myPid()I
move-result v1
invoke-static {v1}, Landroid/os/Process;->killProcess(I)V
.line 42
:cond_1
invoke-virtual {p0}, Lc/c/a/tu;->isRunningInEmualtor()Z
move-result v1
if-eqz v1, :cond_2
.line 43
invoke-static {}, Landroid/os/Process;->myPid()I
move-result v1
invoke-static {v1}, Landroid/os/Process;->killProcess(I)V
.line 45
:cond_2
const-string v1, "com.example.secret"
invoke-virtual {p0, v1}, Lc/c/a/tu;->getSignature(Ljava/lang/String;)I
move-result v0
.line 49
.local v0, "sig":I
invoke-direct {p0}, Lc/c/a/tu;->checkCRC()Z
move-result v1
if-eqz v1, :cond_3
.line 50
invoke-static {}, Landroid/os/Process;->myPid()I
move-result v1
invoke-static {v1}, Landroid/os/Process;->killProcess(I)V
log插桩
来到sd.smali:
把上图的第27行删掉,不要销毁flag。并在此处添加
const-string v1, "iyzyi-flag"
invoke-static {v1, v0}, Landroid/util/Log;->v(Ljava/lang/String;Ljava/lang/String;)I
同时将第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缓存区。
今天又上新了八道题目,由于是我出的题,自己提交自己的flag是不是不太合适啊。