DASCTF2022-FATE
CrackMe
MFC的逆向分析CracckMe,写题的时候在网上搜到了逆向分析CrackMe 这篇文章,通过OD+IDA的方式定位到关键代码处,然后进行解密的操作。
知识点: MD5 Sha1 Hashcat Wincrpt
定位关键函数
OD来定位关键代码和提取数据,因为这个程序有反调试,而OD之中有插件,能够很好的反反调试而提取相关的数据。
IDA能够很好的反编译相应的代码,搞清楚函数的逻辑。
搜索MessageBoxA,因为当输入flag和key之后,输入错误会弹出弹窗,而MessageBoxA 有可能是表示弹窗的函数位置
(Ctrl+G)跟踪表达式【TextA/W】 就找到引用这个函数的地址,看到这个地址之后,发现它的下方就是“Wrong!!!“字符串,就找到对应输出Wrong的这个函数的位置,
定位到输出wrong!!!的代码处
并且在它的下面就是输出successful的代码处
在ida之中能更加容易的分析代码,所以通过这两个输出wrong和Success的地址,在ida之中定位到这两个函数,并且通过他们向上就找(交叉引用的方法)到对输入的key和flag进行验证的函数。
关键函数的位置:0x4C31E0
分析关键函数逻辑
这里将key分成了两部分进行加密,前4个字节使用MD5加密,后4个字节使用Sha1加密,key的md5加密的结果会作为AES加密的密钥,flag的加密是AES加密
4个加密函数
这里有4个加密的函数,在每个函数的里面都是调用Wincrypt的函数来进行加密的操作,但当时我做的时候完全没有注意到这个点,都是通过加密之后的长度来判断用的是什么加密(所以把key解密出来了),不然也不至于最后一步没有解出来。
所以自己以后对于这种情况,要查明这里面调用的函数中引用的参数表示什么意思,有什么作用!!!!!
Algid 的数值:将输入的8个字节的key分成两个部分,前4个字节用md5加密,后4个字节用Sha1加密
所以第一个是对输入的flag进行md5的hash加密
提取加密结果的数据(OD)
加密结果的对比的函数
三个对比加密数据的汇编代码处
通过ida之中三个对输入的加密结果判断的函数的地址,在od之中对应的地址下断点,动调调试找到对比的数据(密文)
md5解密
第一组(md5) =》 找个在线网址解密
数据地址009AF460
密文:
1 | 009AF460 9F 77 C2 A4 AC 5C 0A 67 焪陇琝.g |
密文:9F77C2A4AC5C0A671321BBE1E9972AF6 明文:NocT
SHA1解密
第二组(SHA1)=》(使用HashCat解密) Hashcat使用手册
长度是:20
密文:
1 | 010FF59C D5 9F 8E 94 B0 E1 DE 6E 諢帞搬辬 |
密文:d59f8e94b0e1de6e329518a0c444aa94de7c8d44 明文:uRne
所以key的值就是: NocTuRne
最后得到的key: NocTuRne
Wincrpt解密AES
第三组(AES)
长度是 0x30
密文:
1 | 003AFC20 5B 9C EE B2 3B B7 D7 34 [滎?纷4 |
1 | 0x5B,0x9C,0xEE,0xB2,0x3B,0xB7,0xD7,0x34,0xF3,0x1B,0x75,0x14,0xC6,0xB2,0x1F,0xE8,0xDE,0x33,0x44,0x74,0x75,0x1B,0x47,0x6A,0xD4,0x37,0x51,0x88,0xFC,0x67,0xE6,0x60,0xDA,0x0D,0x58,0x07,0x81,0x43,0x53,0xEA,0x7B,0x52,0x85,0x6C,0x86,0x65,0xAF,0xB4 |
AES的密钥:
1 | 0x5c,0x53,0xa4,0xa4,0x1d,0x52,0x43,0x7a,0x9f,0xa1,0xe9,0xc2,0x6c,0xa5,0x90,0x90 |
我实在是没有想到啊,最后这一步居然是AES解密
Wincrpt
最后一步直接使用Wincrpt中的CryptEncrypt对应的CryptDecrypt这个函数来进行解密
CryptDecrypt的讲解
在调用CryptEncrypt之前,应用程序必须通过调用CryptCreateHash函数获取散列对象的 句柄。加密完成后,可以使用CryptGetHashParam函数获取哈希值,也可以使用 CryptSignHash函数对哈希进行签名 。
1 | BOOL CryptEncrypt( |
CryptDecrypt的讲解
1 | BOOL CryptDecrypt( |
解密脚本
这里调用了CryptEncrypt进行AES的加密的操作,所以这里我们直接使用CryptDecrypt来解密就可以了,可以直接从网上去找用它来解密的脚本,也可以直接把加密的函数脱出来,将里面CryptEncrypt函数修改成CryptDecrypt函数就可以了(两个脚本的逻辑都是一样的)
第一种脚本:
1 | #include <tchar.h> |
第二种脚本:
1 | #include <windows.h> |
最后解得的结果是:
最后的flag :DASCTF{H@sh_a^d_Aes_6y_W1nCrypt}
fakePica
先用PKID查壳,太久没做APK的题目,当时做的时候都忘了进行查壳了,以后一定要注意,不要直接就jeb里面拖
可以看到这是一个梆梆加固
分析APK加固的方法
这里讲述的是通过反编译代码分析是梆梆加固的过程
先用jeb打开文件,在文件的主函数之中并没有找到MainActivity,但是发现引入了有关AppAplication的包,就可以猜测加了壳
再用APKTool得到程序的相关的文件,查看lib文件
这两个so文件就是梆梆加固的so文件(通过下面lib文件和对应的加固方法的字典就能知道),可以用python写一个脚本来辨认
1 | markNameMap.put("libchaosvmp.so", "娜迦"); |
blackdex-APK脱壳
使用blackdex软件进行脱壳
判断32位还是64位的APK文件
首先判断一个APK是32位还是64位的
32位和64的架构
查看lib文件夹
这里是armeabi-v7a 就说明是32位的
所以运行32位的blackdex(将对应位数的blackdex的apk文件放入模拟器之中),将需要脱壳的apk文件也拖入模拟器之中,运行脱壳的Apk文件,在这个程序之中选中需要脱壳的这个软件,就可以直接脱壳了,它脱壳完成之后将脱壳得到的dex文件存入下面这个文件目录之中
在虚拟器的文件管理之中打开该文件夹,并且选中该文件夹(这个文件夹之中有4个dex文件),再打开共享文件夹,在共享文件夹的地方选中图中选项
共享文件打开的地方:(高级功能之中能够对电脑端的共享文件夹进行重新的设置)
这样达到的效果就是电脑端的该文件夹和模拟器端的该文件夹是同步的。
分析脱壳得到的dex文件
将这些dex文件拖进jeb之中分析:
dex文件:
dex文件是Android系统的可执行文件,包含应用程序的全部操作指令以及运行时数据。
当java程序编译成class后,还需要使用dex工具将所有的class文件整合到一个dex文件,目的是其中各个类能够共享数据,在一定程度上降低了冗余,同时也是文件结构更加经凑,dex文件是传统jar文件大小的50%左右。
dex文件的作用是记录整个工程(通常是一个Android工程)的所有类文件的信息。
逐个打开每个dex文件,在cookie_8836564.dex文件之中找到MainActivity函数
AES解密
打开这个mainActivity这个类就能很直观的看到用的是AES加密,并且是直接调用的Java的接口,所以就是标准的AES解密
AES的 key = “picapicapicapica”
AES的 IV向量 IV = “0102030405060708”
第一组的密文
1 | -114, 0x5F, -37, 0x7F, -110, 0x71, 41, 74, 40, 73, 19, 0x7C, -57, -88, 39, -116, -16, -75, -3, -45, -73, -6, -104, -6, -78, 0x79, 110, 74, -90, -47, -28, -28 |
第二组的密文:
1 | -40, 26, 0x5F, -49, -40, -123, 72, -90, -100, -41, 0x7A, -4, 25, -101, -58, 0x74 |
flag{picacomic@gmail.compicacomic}
奇怪的交易
当时用ida打开这个文件的时候就发现这个文件里面怎么提到了python的东西,当时还以为是在c语言之中运行python文件,结果发现是用python来打包elf文件,自己平时知识点的积累还是太少了
使用pyinstxtractor解包就得到了对应的pyc文件
pyc文件以及其中的pyz文件解包
这个地方用pyinstxtractor解包源文件的时候要使用python3.10的环境运行这个pyinstxtractor.py文件。这样pyz文件解包的文件才能够在PYZ-00.pyz_extracted文件夹之中获得
安装和使用Miniconda3 这个软件的作用是用来管理环境的(这里用来在本机上配置python3.10环境)
如何判断这个文件打包的时候使用的是python版本的是3.10的呢?
答:随便使用一个版本的pyinstxtractor来解包文件,通过它的依赖文件就可以进行判断
这道题目的python版本是3.10,它的magic的num可以通过struct.pyc文件的前4个字节获得,或者通过PYZ-00.pyz文件的5到8字节数据能够得到
用这个magic number将pyc文件补全(因为在打包文件的时候常常会将pyc文件的magic number去掉,我们在恢复的时候需要自己去补全,补全之后就能够进行反编译了,但是这里反编译因为是python3.10,所以不能使用python-uncompyle6得到,要使用pycdc来进行反编译
对奇怪的交易.pyc 文件头4个字节进行重新设置
python3.10版本的pyc文件的文件头是
设置完之后,用在线pyc反编译软件可以反编译,也可以使用pycdc进行反编译 ,再用pycdas得到字节码
对照着字节码对下面反编译的结果进行修复
RSA低指数加密
从cpu包中引入函数encrypt()加密
1 | # Source Generated with Decompyle++ |
但是因为这个反编译的代码不太正确,所以使用pycdas得到它的字节码: 字节码
1 | [Code] |
pyz文件解密
送上面修复的反编译的结果之中我们能够看到,这里引用了一个函数,这个函数是重cup这个包中导入的
这个包的获得是在PYZ-00.pyz_extracted文件夹:
一般一个稍微大一点的项目都会分成多个py文件,甚至会依赖其他模块,这些被依赖的文件解析后都会放入PYZ-00.pyz_extracted文件夹之中,这个文件之中相当于放的是核心代码,这个文件之中放入的就是PYZ-00.pyz这个文件解压之后得到的文件,注意这个文件想要用pyinstxtractor.py正常得到,我们需要在这个pyc相对应的python环境之中运行该脚本(上面已经介绍了怎么管理多个python环境)
这里的pyz文件进行了加密,因为pyinstaller可以将文件pyc进行一定的压缩加密,以防止被逆向
pyz文件加密的密钥和过程的文件
通过pyinstxtractor获得了部分没有加密的pyc文件,其中就有archive.pyc文件(得到加密过程)和crypto_key文件(得到具体key参数)
archive.pyc文件:(反编译)
加密的关键代码
crypto_key文件:(反编译)
1 | key = '0000000000000tea' |
因为在源加密代码之中加密函数是从cup这个包之中引入的,所以我们在那个解压缩之后得到的文件夹之中找到对应的对应的包的加密文件
pyz文件解密脚本
解密该文件:
1 | import tinyaes |
各个python版本打包的pyc文件的文件头:
1 | Python 2.7: \x03\xf3\x0d\x0a\0\0\0\0 |
xxtea解密
得到的加密函数encrypt()的pyc文件,所以对这个文件反编译就能够知道函数逻辑
可以看到这里加密的方法是xxtea,这里反编译得到的代码还是有点问题,但是我们可以明显的知道这个地方用的都是xxtea加密
1 | #!/usr/bin/env python |
所以xxtea解密:
因为加密的数据是按照大序端存放的,并且给得到的是经过rsa加密的数字的字符串
1 |
|
xxtea解密结果:(RSA的密文)
1 | 10610336534759505889607399322387179316771488492347274741918862678692508953185876570981227584004676580623553664818853686933004290078153620168054665086468417541382824708104480882577200529822968531743002301934310349005341104696887943182074473298650903541494918266823037984054778903666406545980557074219162536057146090758158128189406073809226361445046225524917089434897957301396534515964547462425719205819342172669899546965221084098690893672595962129879041507903210851706793788311452973769358455761907303633956322972510500253009083922781934406731633755418753858930476576720874219359466503538931371444470303193503733920039 |
RSA低解密指数攻击
RSA解密,在题目之中提供了n和公钥
素数分解的网站 http://www.factordb.com/index.php(这道题用不上)
1 | e = 0x647327833ACFEF1F9C83E74E171FC300FA347D4A6769476C33DA82C95120ACB38B62B33D429206FE6E9BB0BB7AB748A1036971BEA36EC47130B749C1C9FF6FE03D0F7D9FC5346EB0E575BDFA6C530AA57CD676894FC080D2DD049AB59625F4B9C78BCFD95CDCD2793E440E26E189D251121CB6EB177FEDB596409034E8B0C5BBD9BD9342235DBB226C9170EFE347FF0FD2CFF9A1F7B647CC83E4D8F005FD7125A89251C768AFE70BDD54B88116814D5030F499BCAC4673CCCC342FB4B6AC58EA5A64546DC25912B6C430529F6A7F449FD96536DE269D1A1B015A4AC6B6E46EE19DCE8143726A6503E290E4BAE6BD78319B5878981F6CFFDB3B818209341FD68B |
这里的RSA是低解密指数攻击
在RSA中d也称为解密指数,当d比较小的时候,e也就显得特别大了。
适用情况:e过大或过小(一般e过大时使用)使用工具:工具rsa-wiener-attack
利用这个工具是为了得到d,然后再使用RSA解密运算 pow(c,d,n) 得到最终的密文
RSA解密脚本:
1 | from Crypto.PublicKey import RSA |
所以最后的flag是 flag{You_Need_Some_Tea}