占个坑 比赛的时候硬刚的catchme,逆完SO找JNI_LOADER之后除了一堆sub函数和一个b64表啥也看不出来,然后直接润了。听大爹说直接findcrypt就能找到AES加密算法,打算做完Ffuction回来再做这个。
Ffunction 解压后发现是一个exe文件和几个dll文件,由导入表知识可知,dll文件存的是一些API,在可执行文件运行到某处的时候会发生一次jmp跳转进而运行导入表中的内容,所以我们对exe文件启用动调,在Output输出框中尝试找到关键的dll文件是哪一个。
可执行文件可以直接试出flag长度为30位,我们按死F8,等程序跑飞时,输入30位测试输入,输出框跳出了load my_plugin.dll
,说明关键代码在my_plugin.dll
里面。
我们用打开my_plugin.dll
,看到有一个叫f
的函数,考虑到这个题目名字就是Ffunction
,应该是关键函数,点进去看,大概分为两部分,先看第二部分 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 bool __fastcall f (_BYTE *a1, const void *a2) { 第一部分 ___________________________ v15 = 2 * (int )v3 / 8 ; if ( v15 > 0 ) { for ( i = 0 i64; i < v15; v12[2 * i++ + 1 ] = v19 ) { v17 = v12[2 * i]; v18 = 0 ; v19 = v12[2 * i + 1 ]; v20 = 32 i64; do { v18 += 2042207799 ; v17 -= (v18 + v19) ^ (dword_180003000 + (v19 >> 5 )) ^ (dword_180003004 + 16 * v19); v19 -= (v18 + v17) ^ (dword_180003008 + (v17 >> 5 )) ^ (dword_18000300C + 16 * v17); --v20; } while ( v20 ); v12[2 * i] = v17; } } v21 = memcmp (v12, a2, 2 * (int )v3) == 0 ; free (v12); return v21; }
一眼TEA加密算法,稍微改了改,key可以直接扒出来,密文就是v21 = memcmp(v12, a2, 2 * (int)v3) == 0;
这句中的a2
。
动态调试断点下到函数开始处,可以dump出a2密文,根据后面的tea加密算法可知,密文的格式为unsigned int_32
型(8位16进制),所以按D键改成dd格式,dump出前20个数据。
动态调试dll文件的方法就是把调试器改为调用它的可执行exe文件(即改变下面这个图中的Application路径)
然后再看第一部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 bool __fastcall f (_BYTE *a1, const void *a2) { v2 = a1; v3 = -1 i64; do ++v3; while ( a1[v3] ); if ( (int )v3 % 8 ) return 0 ; v5 = (int )v3 / 2 ; v6 = (int )v3; if ( (int )v3 / 2 > 0 ) { v7 = a1; v8 = &a1[(int )v3 - 1 ]; do { v9 = *v8--; v10 = *v7; *v7++ = v9; v8[1 ] = v10; --v5; } while ( v5 ); } v11 = malloc (2 i64 * (int )v3); v12 = v11; if ( (int )v3 > 0 ) { v13 = (char *)v11; do { v13 += 2 ; v14 = *v2++ & 0xF0 ; *(v13 - 2 ) = v14; *(v13 - 1 ) = *(v2 - 1 ) & 0xF ; --v6; } while ( v6 ); }
这样我们先把第 k 和第 k + 1位用或运算合并,再对称调换就可以了。
放上exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define uint32 unsigned int using namespace std;uint32 delta = 2042207799 ; uint32 enc[22 ] = { 0x5C15754C , 0x0D1D781E7 , 0x501BF173 , 0x0CB4DB222 , 0x215D61F5 , 0x3FCA9EE7 , 0x7C76B5C7 , 0x0C7DD8CB9 , 0x990D23FA , 0x0BAB1AD3 , 0x8E12C932 , 0x0D307BAF2 , 0x0E52DD123 , 0x0FBB68F2C , 0x0BDD853E3 , 0x892E1E4E , 0x39DD66FA , 0x87FEEC65 , 0x307C5E60 , 0x340C6C00 }; char flag[100 ];uint32 key[4 ] = {0x0DEADBEEF , 0x0BABEC0FE , 0x0DEADC0DE , 0x0FACEB00C }; void tea_decrypt (int x) { uint32 sum = delta * 32 ; uint32 v0 = enc[x], v1 = enc[x + 1 ]; for (int i = 0 ; i < 32 ; i++) { v1 += (sum + v0) ^ (key[2 ] + (v0 >> 5 )) ^ (key[3 ] + 16 * v0); v0 += (sum + v1) ^ (key[0 ] + (v1 >> 5 )) ^ (key[1 ] + 16 * v1); sum -= delta; } enc[x] = v0; enc[x + 1 ] = v1; } int main () { for (int i = 0 ; i < 20 ; i += 2 ) tea_decrypt (i); int len = sizeof (enc); char *buffer = (char *)enc; for (int i = 0 ; i < len; i += 2 ) flag[i >> 1 ] = buffer[i] | buffer[i + 1 ]; len >>= 1 ; for (int i = 0 ; i < (len >> 1 ); ++i) swap (flag[i], flag[len - i - 1 ]); for (int i = 0 ;i < len; i++)printf ("%c" , flag[i]); }
动调显示字符长度应该是80,但是这里的sizeof(len) = 88
,所以多输出了俩空格(我不理解,但是确实能做。。。)
感觉是个base64解码,解码出来结果是
f}l!a!gC{_Ehmtp10ww_erre_tFt1u
一眼栅栏密码分栏为2:flag{Emp0wer_F1}!!C_ht1w_rettu
还是不对,考虑把后一半flag倒过来,得出最终flag{Emp0wer_F1utter_w1th_C!!}