只做了三道RE,还好队友解了一道PWN,最终成功苟进了决赛圈,决赛拿了第九,不亏
babyarm
简单题,给了个假的check,向下看可以发现是xxtea加密算法
xxtea decrypt,密钥 {2,2,3,4},明文128bits,加密轮次9轮,重复加密16次
1 | // [+] Dump 0x230B4 - 0x230F4 (64 bytes) : |
flag{D0cab07Fa45A2b6Ff7bAE6AaBbA5F5dcb3C9fDCE5afeF54DAC88A5093A}
Exception
这道题有点意思,最终也只有7队解了,主要是将几个部分揉成一团,再加上阴间反调试还有基本都是密码学,逆得是真的累,全靠静态硬刚。
这里程序通过异常处理调用真正的加密函数来进行反调试,导致patch非常麻烦
1 | .text:00401C6B ; __try { // __except at loc_401C7B |
flag有4part
part1:
魔改AES,s-box被魔改
1 | /* |
其他无变化,key和密文如下,解密即可
1 | uint8_t key[] = {35,1,0,3,99,5,143,7,8,9,199,11,239,13,0,15,238,17,222,19,16,21,142,23,126,25,16,27,46,29,30,31}; |
得到part1 pLeA5e_nEveR_UnD
part2:魔改base32,编码表为 0x20-0x51,直接解即可
1 | import base64 |
part2:eRe5t1m4t
part3:魔改RC6,初始化了子密钥,直接解密即可
1 | // __uint32_t keys[] = {0xb7e15163,0x5618cb1c,0xf45044d5,0x9287be8e,0x30bf3847,0xcef6b200,0x6d2e2bb9,0xb65a572,0xa99d1f2b,0x47d498e4,0xe60c129d,0x84438c56,0x227b060f,0xc0b27fc8,0x5ee9f981,0xfd21733a,0x9b58ecf3,0x399066ac,0xd7c7e065,0x75ff5a1e,0x1436d3d7,0xb26e4d90,0x50a5c749,0xeedd4102,0x8d14babb,0x2b4c3474,0xc983ae2d,0x67bb27e6,0x5f2a19f,0xa42a1b58,0x42619511,0xe0990eca,0x7ed08883,0x1d08023c,0xbb3f7bf5,0x5976f5ae,0xf7ae6f67,0x95e5e920,0x341d62d9,0xd254dc92,0x708c564b,0xec3d004,0xacfb49bd,0x4b32c376}; |
随手一搜,http://www.codeforge.com/read/65331/JG.TXT__html
直接get key
unsigned char key[] = {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10};
结合密文解密即可
unsigned char cipher[] ={0xa5,0xf9,0x6e,0x68,0x22,0x3a,0x40,0xc9,0x7d,0xf2,0x38,0xa5,0x8b,0xc4,0xc7,0xa5};
1 | #include <stdio.h> |
part3: e_7he_heArt_Of_a
part4:明文比对一个字节,剩下八个字节简单xor
1 | # >>> hex(0x461F6348^0x2b2b2b2b)[2:].decode('hex')[::-1] |
最终得到flag
pLeA5e_nEveR_UnDeRe5t1m4te_7he_heArt_Of_a_cH4mpI0n
MFC
简单mfc逆向,坑多了点,od大法好
提交按钮disable,先patch使其enable先
flag分为两个part,第一个part是smc(好像有反调试)od dump内存,patch回源文件重载进ida
1 | ((void (__fastcall *)(char *, int *, unsigned int))byte_11C5188)(&v10, &v12, a2); |
如下所示,只有两个函数sub_59E070、sub_875231修改了flag,第一个显然是rc4算法(sbox已初始化完,不管),第二个函数显然是base64换表(+35)
1 | int __usercall sub_59E070@<eax>(int a1@<xmm0>, int a2, int a3) |
1 | void __usercall sub_875231(int a1@<edx>, _BYTE *a2@<ecx>, int a3@<ebp>) |
直接od动态调试即可,这里根据常量”B=.NI;&3JBZ;$;?(I72’0&4GLZDS2V6&%AF.!5#+J[F^”的长度为44可以推测出flag_part1长度为33,然而这两个加密函数只对flag的前半部分进行加密,所以flag长度应该为66,od开调
在加密函数入口点断下,修改内存为
1 | '************************************************************************' |
1 | .text:00EF11F0 push eax |
在check函数断下,查看内存,得到两个值
1 | .text:00EE95F4 push offset aBNi3jbzI7204gl ; "B=.NI;&3JBZ;$;?(I72'0&4GLZDS2V6&%AF.!5#"... |
一个是我们的输入经过前面两个函数加密编码后的结果
=C-?@RIQ@F&-/P?8@a6?G4@O==D3CZ>,VI.:F/67B^7,:?N
另外一个是校验的常量
$HM%/NEX,79PBN\C/BQLVSW,*/‘8T#UMC4%EG@@@,.%5
根据RC4加密算法的原理,有 p1 ^ c1 = p2 ^ c2 (len(p1) == len(p2)),可以推出
c2 = p1 ^ c1 ^ p2(len(p2) > len(p1))
ps:p1、p2为明文,c1、c2为密文
再结合我们的明文
1 | '************************************' |
1 | import base64 |
三者xor即可得到flag_part1 Fr4nk1y_MfC_l5_t0O_ComPIeX_4nd_dl
flag_part2就比较简单了,显然就是一个普通的tea加密算法,delta的值被魔改为0x2433B95A
1 | do |
part2这里先校验了第一个字符是否为 ‘f’,剩下32个字节以8个字节为一组进行一次tea加密,每次加密密钥都不一样。然后就是算出加密后的密文,直接用key解密即可。
密文有4组,其中一组明文给出,另外三组是一个三元一次方程(这里涉及到整数溢出)
1 | // (v17 - v14) == 0x3F66B755B4490579 |
直接算得到的结果如下,显然不满足 v14<v17<v18
1 | // v18 = 0x68e95dc6c558d3ec |
所以这里 v14 + v18应该是溢出了,真正的方程应该为
1 | // (v17 - v14) == 0x3F66B755B4490579 |
解出三个值
1 | // 2d46347f5e79f6f4 |
然后直接tea解密得到part2
1 | #include <stdio.h> |
将得到的结果按小端序 decode即可
flag_part2:ff1cUlt_foR_THe_r0Ok1E_t0_REver5e
拼接得到flag
Fr4nk1y_MfC_l5_t0O_ComPIeX_4nd_dlff1cUlt_foR_THe_r0Ok1E_t0_REver5e