2022RCTF RE 部分wp CheckYourKey 逆向签到题,apk没什么好说的,就是调用so库里面的ooxx文件对输入进行检查,直接看so库
在函数栏中直接搜索ooxx,定位到
Java_com_ctf_CheckYourKey_MainActivity_ooxx函数 定位到这里
第227行和第231行的函数分别是base58,base64加密
base58函数 可以看到这里的(138 * a2 / 0x64) + 2 和 div(quot_low + (v14[–v8] << 8), 58),所以猜测是base58加密
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 unsigned __int8 *__fastcall sub_F7DC (__int64 a1, unsigned __int64 a2) { unsigned __int64 v2; __int64 v3; bool v5; unsigned __int64 v6; div_t v7; unsigned __int64 v8; unsigned __int64 v9; int quot_low; unsigned __int64 v11; unsigned __int64 j; unsigned __int64 v13; unsigned __int8 *v14; unsigned __int64 i; for ( i = 0LL ; !*(_BYTE *)(a1 + i); ++i ) ; v14 = (unsigned __int8 *)malloc ((unsigned int )(138 * a2 / 0x64 ) + 2 ); memset (v14, 0 , 138 * a2 / 0x64 + 2 ); for ( j = 0LL ; j < i; ++j ) { v2 = j; v14[v2] = *(_BYTE *)base58_table; } v11 = 0LL ; while ( j < a2 ) { v3 = j++; quot_low = *(unsigned __int8 *)(a1 + v3); v9 = 0LL ; v8 = 138 * a2 / 0x64 + 1 ; while ( 1 ) { if ( quot_low || (v5 = 0 , v9 < v11) ) v5 = v8 != 0 ; if ( !v5 ) break ; v7 = div(quot_low + (v14[--v8] << 8 ), 58 ); v14[v8] = v7.rem; quot_low = LOBYTE(v7.quot); ++v9; } v11 = v9; } v13 = i; v6 = 138 * a2 / 0x64 + 1 - v11 - i; while ( v13 < i + v11 ) { v14[v13] = *((_BYTE *)base58_table + v14[v13 + v6]); ++v13; } if ( v6 ) v14[v13] = 0 ; return v14; }
可以找到长度为58的表,但是内容不太对,X找交叉引用
找到这里有异或的操作
直接上脚本测试一下,得到了base58的码表,确定这里是base58加密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 byte_41010 = [ 0x9E , 0x9D , 0x9C , 0x9B , 0x9A , 0x99 , 0x98 , 0x97 , 0x96 , 0xEE , 0xED , 0xEC , 0xEB , 0xEA , 0xE9 , 0xE8 , 0xE7 , 0xE5 , 0xE4 , 0xE3 , 0xE2 , 0xE1 , 0xFF , 0xFE , 0xFD , 0xFC , 0xFB , 0xFA , 0xF9 , 0xF8 , 0xF7 , 0xF6 , 0xF5 , 0xCE , 0xCD , 0xCC , 0xCB , 0xCA , 0xC9 , 0xC8 , 0xC7 , 0xC6 , 0xC5 , 0xC4 , 0xC2 , 0xC1 , 0xC0 , 0xDF , 0xDE , 0xDD , 0xDC , 0xDB , 0xDA , 0xD9 , 0xD8 , 0xD7 , 0xD6 , 0xD5 , 0xAF , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ] table = '' for i in range (58 ): byte_41010[i] ^= 0xAF table+=chr (byte_41010[i])print (table)
base64函数 这里的就很容易看出来是base64了
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 __int64 __fastcall sub_13788 (__int64 a1, unsigned int a2, __int64 a3) { __int64 v3; __int64 v4; __int64 v5; __int64 v6; __int64 v7; __int64 v8; __int64 v9; __int64 v10; __int64 v11; unsigned __int8 v13; unsigned __int8 v14; unsigned int v15; unsigned int v16; unsigned int v17; unsigned int v18; unsigned int i; int v20; v20 = 0 ; v13 = 0 ; v15 = 0 ; for ( i = 0 ; i < a2; ++i ) { v14 = *(_BYTE *)(a1 + i); if ( v20 ) { if ( v20 == 1 ) { v20 = 2 ; v4 = v15++; *(_BYTE *)(a3 + v4) = base64_table[(16 * (v13 & 3 )) | ((int )v14 >> 4 ) & 0xF ]; } else { v20 = 0 ; v5 = v15; v16 = v15 + 1 ; *(_BYTE *)(a3 + v5) = base64_table[(4 * (v13 & 0xF )) | ((int )v14 >> 6 ) & 3 ]; v6 = v16; v15 = v16 + 1 ; *(_BYTE *)(a3 + v6) = base64_table[v14 & 0x3F ]; } } else { v20 = 1 ; v3 = v15++; *(_BYTE *)(a3 + v3) = base64_table[((int )v14 >> 2 ) & 0x3F ]; } v13 = v14; } if ( v20 == 1 ) { v7 = v15; v17 = v15 + 1 ; *(_BYTE *)(a3 + v7) = base64_table[16 * (v13 & 3 )]; v8 = v17++; *(_BYTE *)(a3 + v8) = 61 ; v9 = v17; v15 = v17 + 1 ; *(_BYTE *)(a3 + v9) = 61 ; } else if ( v20 == 2 ) { v10 = v15; v18 = v15 + 1 ; *(_BYTE *)(a3 + v10) = base64_table[4 * (v13 & 0xF )]; v11 = v18; v15 = v18 + 1 ; *(_BYTE *)(a3 + v11) = 61 ; } *(_BYTE *)(a3 + v15) = 0 ; return v15; }
可以看出来是换表的base64加密
在该函数的最后找到strcmp函数
可以知道最后的密文就是asc_41120,x交叉引用找到了另外的调用
先异或后解密,但是结果不对,猜测有其他的加密
利用findcrypt找到了AES加密,直接定位到该函数
sub_14FC0函数 该函数的整体逻辑和上面的ooxx函数十分相似,所以直接定位到
整体的加密逻辑就是先AES后base58最后base64
AES的key刚好就是该函数的第二个参数,直接提取出来
1 2 3 4 5 6 7 unsigned char key[] = { 0x98 , 0x90 , 0x90 , 0x9B , 0x93 , 0x8A , 0x9C , 0x94 , 0x8C , 0x92 , 0x9E , 0x8D , 0x8B , 0x92 , 0x9E , 0x91 , 0xFF , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 };
刚才在找asc_41120的交叉引用是定位到了一个函数,在那里同时也找到了key的处理,直接输出得到key
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <stdio.h> #include <stdlib.h> #include <iostream> #define _BYTE unsigned char int main () { unsigned char key[] = { 0x98 , 0x90 , 0x90 , 0x9B , 0x93 , 0x8A , 0x9C , 0x94 , 0x8C , 0x92 , 0x9E , 0x8D , 0x8B , 0x92 , 0x9E , 0x91 , 0xFF , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 }; for (int i = 0 ; i < 32 ; i++) { printf ("%c" , (_BYTE)~key[i]); } return 0 ; }
最后比较的密文也不一样,但是同样给要先经过异或处理
最后是从NU1L那里学来的python脚本,也可以用cyberchef直接解
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 import base64import binasciifrom Crypto.Cipher import AES b58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' def base58decode (s ): result = 0 for c in s: result = result * 58 + b58.find(c) return result changed_base64 = "+/EFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789ABCD" base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" enflag = "SVTsfWzSYGPWdYXodVbvbni6doHzSi==" right_enflag = '' enflag1 = '' flag = '' for i in range (len (enflag)): right_enflag += base[changed_base64.find(enflag[i])] enflag1 = base64.b64decode(right_enflag)print (enflag1)print (hex (base58decode("A4juLPXCTmefm6mfX8naqB" ))) data = binascii.unhexlify(hex (base58decode("A4juLPXCTmefm6mfX8naqB" ))[2 :].encode("utf8" )) mode = AES.MODE_ECB key = list (b'goodlucksmartman' ) cryptos = AES.new(bytes (key), mode) flag = cryptos.decrypt(bytes (data))print (flag)
HuoWang 一个迷宫题
程序的符号表是被去除了,但是群里大哥看出来是unicorn写的了
使用bandiff恢复一下符号表,顺便搜索了一下各个函数的功能
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 int __cdecl main (int argc, const char **argv, const char **envp) { __int64 v3; int result; int v5; int i; int len; int v8; __int64 uc; __int64 v10[3 ]; int v11; char input[264 ]; unsigned __int64 v13; v13 = __readfsqword(0x28 u); scanf ((__int64)"%s" , input); len = strlen (); v5 = 0xFFFFF ; for ( i = 0 ; i <= 6 ; ++i ) qword_19537E0[i] = &test[i]; uc_open(4u , 8 , &uc); uc_mem_map(uc, (__int64)&dword_400000, 0x200000 LL, 7u ); uc_mem_map(uc, 0LL , 0x100000 LL, 7u ); v10[1 ] = 0LL ; v10[2 ] = 0LL ; v11 = 0 ; uc_mem_write(uc, (__int64)&qword_400080, (__int64)&loc_145E010, 0x7AA8 uLL); v8 = uc_mem_write(uc, (__int64)&loc_407B34, (__int64)input, len); v8 = uc_reg_write(uc, 30 , (__int64)&v5); v8 = uc_hook_add(uc, v10, 2 , (__int64)sub_401FC5, 0LL , 1LL , 0LL , 0x2BB , (int )argv); v8 = uc_emu_start(uc, 0x400119 LL, 0x40016A LL, 0LL , 0LL ); v8 = uc_emu_start(uc, 0x40016A LL, &qword_4000D8, 0LL , 0LL ); uc_close(uc, 0x40016A LL, v3); maze((__int64)asc_18D7180, (__int64)input, len); if ( check1 && check2 ) { puts ((__int64)"GrandFather Dao, I've made it!(ohhhhh. The flag is RCTF{(md5(your input))}." ); } else if ( check1 || check2 ) { puts ((__int64)"Mom, I really can't tell the difference!" ); } else { puts ((__int64)"CTF is fun! Jie Jie Jie" ); } result = 0 ; if ( __readfsqword(0x28 u) != v13 ) sub_1404EA0(); return result; }
主要的迷宫还是在maze函数中
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 __int64 __fastcall maze (__int64 a1, __int64 a2, int a3) { __int64 result; int v4; int v5; unsigned int i; v4 = 0 ; v5 = 1 ; for ( i = 0 ; ; ++i ) { result = i; if ( (int )i >= a3 ) break ; result = *(unsigned __int8 *)((int )i + a2); if ( (_DWORD)result == 'w' ) { if ( !v4 ) return result; result = *(unsigned __int8 *)(a1 + 24LL * v4 - 24 + v5); if ( (_BYTE)result != ' ' ) return result; --v4; } else { if ( *(unsigned __int8 *)((int )i + a2) > 0x77 u ) goto LABEL_20; if ( (_DWORD)result == 's' ) { result = *(unsigned __int8 *)(a1 + 24 * (v4 + 1LL ) + v5); if ( (_BYTE)result != ' ' ) return result; ++v4; } else { if ( *(unsigned __int8 *)((int )i + a2) > 0x73 u ) goto LABEL_20; if ( (_DWORD)result == 'a' ) { if ( !v5 ) return result; result = *(unsigned __int8 *)(a1 + 24LL * v4 + v5 - 1 ); if ( (_BYTE)result != ' ' ) return result; --v5; } else { if ( (_DWORD)result != 'd' ) LABEL_20: sub_13AF360(-1 ); result = *(unsigned __int8 *)(a1 + 24LL * v4 + v5 + 1 ); if ( (_BYTE)result != ' ' ) return result; ++v5; } } } } if ( v4 == 22 && v5 == 21 ) check1 = 1 ; return result; }
其中a1是
可以得知我们要做的就是从左上角走到右下角,wsad分别对应上下左右,需要找到最短路径
主要方法还是遍历破解,来自Arr3stY0u战队的wp(orz)
2022 RCTF writeup by Arr3stY0u (qq.com)
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 43 44 45 46 47 48 49 50 51 52 53 from pwn import * maze_map = [ "0S000000000000000000000" , "01011111011111110111110" , "01010101000101000101010" , "01110101111101011101010" , "01010100000000010100010" , "01010111110111110101110" , "01010000010101000001010" , "01110101110111111111010" , "00010101000101000100010" , "01110101110101011101110" , "01000100010101011100010" , "01110101110101011111010" , "01010101000101000001010" , "01011101111111110111010" , "01000000000001000101010" , "01110111110111111101010" , "01000000000100000000010" , "01110010001101111111010" , "01110100001001000001010" , "01110101101111011111010" , "01010101010000010000010" , "01111111011111011111110" , "000000000000000000000E0" ]def dfs_solve_maze (map , x, y, walks, path, allpath ): if x < 0 or x >= 23 : return allpath if y < 0 or y >= 23 : return allpath if (x, y) in walks: return allpath if map [y][x] not in 'S1E' : return allpath if map [y][x] == 'E' : allpath.add(path) return allpath new_walks = walks.copy() new_walks.add((x, y)) dfs_solve_maze(map , x-1 , y, new_walks, path+'a' , allpath) dfs_solve_maze(map , x+1 , y, new_walks, path+'d' , allpath) dfs_solve_maze(map , x, y-1 , new_walks, path+'w' , allpath) dfs_solve_maze(map , x, y+1 , new_walks, path+'s' , allpath) return allpath allpath = dfs_solve_maze(maze_map, 1 , 0 , set (), '' , set ())for p in allpath: io=process("./HuoWang" ) io.sendline(p) out=io.recvall() if b"flag" in out: print (p) io.close()
通过dfs遍历所有的情况
然后md5加密得到flag
1 2 3 4 5 6 7 8 import hashlib a = "sssddwwddssssddddssaassddssaassddddwwwwwwwwddddwwddwwddddssssaassaassaassddddssaassaaaaaassassdddwwddddddssaaaassdddddds" enc = hashlib.md5() enc.update(a.encode('utf-8' ))print ('flag{' ,end='' )print (enc.hexdigest(),end='' )print ('}' )
RTTT 一道有点抽象的rust逆向(rust都抽象)
首先静态分析一下
找到了五个很可疑的数组
看到他们后续的操作是异或,直接上手,发现结果就是
1 2 Welc0me to RCTF 2 O22Congratulations
所以猜测congratulations前面的那个数组大概率是密文
直接在左侧搜索memcmp函数,x找交叉引用定位到最后密文做比较的地方
直接开始动调,F8到输入处开始跟踪,然后发现第一个操作是打乱输入顺序
input:RCTF{abcdefghijklmnopqrstuvwxyz0123456789}
打乱后t4u6f2rysRxvoin}dkh3wacgeF1C{9l58Tz7pbq0jm
看F61d大佬们的wp
RCTF 2022 WriteUp By F61d | CTF导航 (ctfiot.com)
得知第二步加密居然是RC4,我是看了两个多小时没看出来,居然只凭借这点信息就能判断出来是RC4,真厉害
借用一下脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from Crypto.Cipher import ARC4 rc4 = ARC4.new(b'Welc0me to RCTF 2O22' ) t1 = [52 , 194 , 101 , 45 , 218 , 198 , 177 , 173 , 71 , 186 , 6 , 169 , 59 , 193 , 204 , 215 , 241 , 41 , 36 , 57 , 42 , 192 , 21 , 2 , 126 , 16 , 102 , 123 , 94 , 234 , 94 , 208 , 89 , 70 , 225 , 214 , 110 , 94 , 178 , 70 , 107 , 49 ] f = rc4.decrypt(bytes (t1)).decode()print (f) s1 = 'RCTF{abcdefghijklmnopqrstuvwxyz0123456789}' s2 = 't4u6f2rysRxvoin}dkh3wacgeF1C{9l58Tz7pbq0jm' dic = {}for i in range (len (s2)): dic[i] = s1.index(s2[i]) f1 = ['' for i in range (42 )]for i in range (len (f)): f1[dic[i]] = f[i]print ('' .join(f1))
Web_run wasm逆向, ez_wasm.2 改为 ez_ca.wasm ,ez_wasm.1 改为 ez_wasm.html
然后直接用jeb打开.wasm
main()
f11()
关键函数主要是f10(), f6(),f9()这三个
f10()
主要是输入data,格式为xxxx/xx/xx xx:xx,解析成为一个整数
要留意的是202211110054这个数
f6()
主要功能就是把f10()返回的result值赋给全局变量
f30() 其中有对0xA30处值的初始化
f9() 分情况对这个整数进行加密
具体是”xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx”
红框部分就是主要的加密过程
f7()
f31()
主要的加密就是f31()的返回值模16,然后按照对应规则转换为16进制
写脚本
由于是复现,所以直接与已知的官方flag进行比较
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 #include <iostream> #include <string> using namespace std;int main () { long long magic = 6364136223846793005 ; long long data = 202211110054 ; long long A30 = (int )data - 1 ; string test = "RCTF{40959ea7-26e0-4c9d-8f4a-62faf14ff392}" ; string flag = "RCTF{" ; char str[] = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx" ; for (int i = 0 ; i < strlen (str); i++) { if (str[i] != '4' && str[i] != '-' ) { long long res = A30 * magic + 1 ; A30 = res; unsigned int v0 = (unsigned int )(res >> 33 ); int num = v0 % 16 ; if (str[i] == 'y' ) { num = ((num & 3 ) | 8 ); } if (num >= 0 && num <= 9 ) { flag += num + '0' ; } else { flag += num - 10 + 'a' ; } } else { flag += str[i]; } } flag += '}' ; std::cout << flag << endl; if (flag == test) { printf ("correct!\n" ); } return 0 ; }