GeekChallenge_REVERSE Writeup
Shift_jmp | 简单花指令
将无条件跳转和loc_117A
patch为nop
重构主函数
简单的异或加密 解题Python脚本如下
key = [83, 88, 65, 120, 83, 54, 106, 100, 56, 100, 111, 84, 120, 66, 81, 123, 120, 34, 77, 97, 39, 99, 115, 69, 45, 124, 69, 108, 44, 111, 47, 123, 94, 92] for i in range(34): print(chr(key[i] ^ i ^ 0), end='') #SYC{W3lc0me_tO_th3_r3veR5e_w0r1d~}
点击就送的逆向题 | C源码的编译
.s
文件是由C源码编译而来的汇编指令 它与.S
文件的区别是它不支持预处理(#define...) gcc编译器可以将其经过链接等再编译为可执行文件 故可以先将.s
文件编译为可执行文件再用IDA分析
clang click.s -o click
Kali虚拟机预装的gcc编译器用以上指令可以直接将.s
文件编译为Linux可执行文件
简单的位移加密 解题Python脚本如下
key = 'Z`J[X^LMNO`PPJPVQRSIUTJ]IMNOZKMM' for each in key: print(chr(ord(each) - 7), end='') # SYCTQWEFGHYIICIOJKLBNMCVBFGHSDFF
Easymath | z3-solver简单使用
Exinfo查到无壳 64位win程序 IDA64打开
-
check函数逻辑简单 不分析(
-
checkposition函数逻辑也简单 不分析(
-
exchange函数
不理解 26行之前的代码作用 但是并不影响分析(
number在内存空间中每个有效元素之间插入三个0
与代码中的p_number[i] = number_[4 * v10]
对应 最后一个循环的作用就是将字符在table中的位置映射到这些有效元素上
回到主函数 主要的比较逻辑就是经过映射后的position
作为一个5阶方阵与另一个5阶方阵matrix
用特殊的矩阵乘法法则相乘得到的矩阵与单位矩阵相比 以下是Python代码实现 其中n
为矩阵的阶数
for i in range(n): for j in range(n): result[i][j] = 0 for k in range(n): result[i][j] = (M1[i][k] * M2[k][j] + result[i][j]) % 0x20
除此之外主函数中还能看到为了防止出现多解给出的flag提示(动态调试时发现其为flag中的元素 重构为数组可以将变量名从内存地址变为数组) 可以使用爆破的方式解出flag 也可以使用z3-solver库解出
from z3 import * table = '01234_asdzxcpoityumnbAOZWXGMY' matrix = [18, 29, 16, 19, 27, 8, 31, 8, 23, 30, 29, 3, 28, 10, 21, 18, 29, 8, 16, 28, 11, 30, 7, 20, 7] v7 = [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1] num = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 19, 22, 26, 27, 28, 29, 31, 32, 50, 51, 52, 53, 54, 55, 56] for w in range(5): mysolver = Solver() position = IntVector('position', 5) if w == 0: mysolver.add(position[1] == 19) elif w == 1: mysolver.add(position[2] == 22) elif w == 3: mysolver.add(position[2] == 22) mysolver.add(And([Or([position[i] == val for val in num]) for i in range(5)])) for j in range(5): expr = IntVal(0) for k in range(5): expr = (expr + position[k] * matrix[5 * k + j]) % 0x20 mysolver.add(expr == v7[5 * w + j]) result = mysolver.check() if result == sat: m = mysolver.model() position_values = [m[position[i]].as_long() for i in range(5)] for each in position_values: print(table[num.index(each)], end='') #xtd4co_ymiunbbx3Aypsmbzii else: print('No solution')
需要注意的是 可以一次性爆破全部不确定的22个元素 但这样会使复杂度提升到29^22(我猜的) 而每一次运算只用到position
中的一行元素 故可以一行一行地爆破 可以将复杂度降低到5*29^5(我猜的)所以选择一行一行爆破(z3学习笔记另写)
luck | 简单爆破
Exinfo查到无壳 64位win程序 IDA64打开
主函数定义了一个unsigned __int8
类型地数组 这个数据类型的大小和char
一样 qmemcpy(cmp_data, "\r\a", 2);
这句实际上只将"\r\a"
中地前两个字符复制到了cmp_data
的末尾 数组名代表首元素地址 所以
cmp_data[0] = '\'
cmp_data[1] = 'r'
同理
将"o96*#"
添加到了cmp_data
的第37个元素及之后
(其实最简单的方式是动态调试直接查看cmp_data中的数据)
主函数中的加密函数为用递归实现的等差数列求和
不管输入的数字是什么都会输出加密后的密文 根据flag前3个固定的字符爆破flag
key = [13, 7, 29] for i in range(1000): result = int(i * (i + 1) / 2) % 0xD3 if key[0] ^ (result) == ord('S') and key[1] ^ (result) == ord('Y') and key[2] ^ (result) == ord('C'): print(i) # 69 break
在程序中输入数字得到flag
砍树 | 安卓逆向
主函数的逻辑很简单
就是调用一个函数传入输入的字符串和key 判断返回值 这个函数在ezreeee
库中 导出这个库用IDA分析
在函数列表中检索I0o0I
函数 传入的参数有4个 其中有2个是JNIEnv
和jobject
JNI是为了让JAVA和C/C++互通构建的环境 jxxxxx是为了不与C原有的类型冲突 所以从下面的2个jstring_2unsigchar
函数也可以看出a3
和a4
分别为jeb伪代码中看到的两个参数与加密后的输入对比的是从内存0x14900中复制的35个字节型数据 以下是解密脚本
key = [0, 32, 32, 23, 27, 54, 14, 54, 38, 23, 4, 42, 41, 7, 38, 21, 82, 51, 45, 15, 58, 39, 17, 6, 51, 7, 70, 23, 61, 10, 60, 56, 46, 34, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] to_mod = "Sycloverforerver" i = 0 for each in key: print(chr(each ^ ord(to_mod[i % 7])), end='') i += 1 #SYC{t@ke_thE_bul1_By_the_h0rns_TAT}SycloveSyclov
听说cpp很难? | C++逆向
Exinfo查到无壳 64位win程序 IDA64打开 伪代码如下
int __cdecl main(int argc, const char **argv, const char **envp) { __int64 v3; // rax unsigned __int64 v4; // rbx __int64 v5; // rax _BYTE *addr_nowChar; // rax unsigned int now_char; // ebx _BYTE *v8; // rsi int v9; // ebx int v10; // ebx __int64 v11; // rax __int64 v12; // rax __int64 m; // [rsp+20h] [rbp-60h] BYREF __int64 k; // [rsp+28h] [rbp-58h] BYREF char copy_flag[32]; // [rsp+30h] [rbp-50h] BYREF char be_encoded[48]; // [rsp+50h] [rbp-30h] BYREF _BYTE enc[32]; // [rsp+80h] [rbp+0h] BYREF int v19[40]; // [rsp+A0h] [rbp+20h] BYREF char input_flag[40]; // [rsp+140h] [rbp+C0h] BYREF __int64 v21; // [rsp+168h] [rbp+E8h] BYREF __int64 v22; // [rsp+170h] [rbp+F0h] BYREF unsigned int len; // [rsp+17Ch] [rbp+FCh] int v24; // [rsp+180h] [rbp+100h] int v25; // [rsp+184h] [rbp+104h] int j; // [rsp+188h] [rbp+108h] int i; // [rsp+18Ch] [rbp+10Ch] _main(argc, argv, envp); v3 = std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "place input your flag:"); std::ostream::operator<<(v3, refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_); std::string::basic_string(input_flag); memset(v19, 0, sizeof(v19)); v19[0] = 0x4D; v19[1] = 0x5F; v19[2] = 0x3D; v19[3] = 0xFFFFFF85; v19[4] = 0x37; v19[5] = 0x68; v19[6] = 0x73; v19[7] = 0x57; v19[8] = 0x27; v19[9] = 0x68; v19[10] = 0x51; v19[11] = 0x59; v19[12] = 0x7F; v19[13] = 0x26; v19[14] = 0x6B; v19[15] = 0x59; v19[16] = 0x73; v19[17] = 0x57; v19[18] = 0x55; v19[19] = 0x5B; v19[20] = 0x59; v19[21] = 0x6F; v19[22] = 0x6A; v19[23] = 0x59; v19[24] = 0x27; v19[25] = 0x57; v19[26] = 0x72; v19[27] = 0x57; v19[28] = 0x4F; v19[29] = 0x57; v19[30] = 0x78; v19[31] = 0x78; v19[32] = 0xFFFFFF83; std::operator>><char>(refptr__ZSt3cin, input_flag); len = std::string::size(input_flag); std::vector<int>::vector(enc); for ( i = 0; ; ++i ) { std::vector<int>::push_back(enc, &v19[i]); if ( i > 34 ) break; } // 复制v19到enc中 encode::encode(be_encoded, len, input_flag, 10);// // be_encoded[0] = len(flag) // be_encoded[8...] = input_flag // be_encoded[40] = 10 // be_encoded[44] = 10 std::vector<char>::vector(copy_flag); for ( j = 0; ; ++j ) { v4 = j; if ( v4 >= std::string::size(input_flag) ) break; v5 = std::string::operator[](input_flag, j);// v5 = input_flag[j] std::vector<char>::push_back(copy_flag, v5); } v25 = 0; for ( k = std::vector<char>::begin(copy_flag);// for k in copy_flag ; __gnu_cxx::__normal_iterator<char *,std::vector<char>>::operator++(&k, 0i64) ) { v21 = std::vector<char>::end(copy_flag); if ( !__gnu_cxx::operator!=<char *,std::vector<char>>(&k, &v21) )// 读到最后一个字符时退出 break; addr_nowChar = __gnu_cxx::__normal_iterator<char *,std::vector<char>>::operator*(&k); *addr_nowChar += be_encoded[44]; now_char = *__gnu_cxx::__normal_iterator<char *,std::vector<char>>::operator*(&k); v8 = __gnu_cxx::__normal_iterator<char *,std::vector<char>>::operator*(&k); *v8 = text_67(be_encoded, now_char); // now_char += 10 // now_char = (10 ^ now_char) - 10 // now_char = v19[i] } v24 = 0; for ( m = std::vector<char>::begin(copy_flag); ; __gnu_cxx::__normal_iterator<char *,std::vector<char>>::operator++(&m, 0i64) ) { v22 = std::vector<char>::end(copy_flag); if ( !__gnu_cxx::operator!=<char *,std::vector<char>>(&m, &v22) ) break; v9 = *__gnu_cxx::__normal_iterator<char *,std::vector<char>>::operator*(&m); if ( v9 == *std::vector<int>::operator[](enc, v24) ) ++v25; v10 = *__gnu_cxx::__normal_iterator<char *,std::vector<char>>::operator*(&m); if ( v10 != *std::vector<int>::operator[](enc, v24) ) --v25; ++v24; } if ( v25 > 32 ) // v25统计正确的字符 { v11 = std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "yes yes you are right!"); std::ostream::operator<<(v11, refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_); } if ( v25 <= 32 ) { v12 = std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "no try again~"); std::ostream::operator<<(v12, refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_); } std::vector<char>::~vector(copy_flag); encode::~encode(be_encoded); std::vector<int>::~vector(enc); std::string::~string(input_flag); return 0; }
分析C++逆向时XXXXXXXoperatorxxx(a, b)
可以看作a
对b
做xxx
操作
其中的主要逻辑是先对be_encoded进行encode
操作 进入查看
需要注意的是主函数中定义的be_encoded
储存的数据长度为1byte 而C++中的双字类型长度为4bytes 所以其中的下标转化到原来的be_encoded
中需要乘上4 text_67
的逻辑很简单
__int64 __fastcall text_67(__int64 be_encoded, char now_char) { *(be_encoded + 40) = 9; return (((*(be_encoded + 40) + 1) ^ now_char) - *(be_encoded + 40) - 1); }
剩下的对比逻辑也很简单 以下是解密脚本
enc = [None] * 33 enc[0] = 77 enc[1] = 95 enc[2] = 61 enc[3] = 0x85 enc[4] = 55 enc[5] = 104 enc[6] = 115 enc[7] = 87 enc[8] = 39 enc[9] = 104 enc[10] = 81 enc[11] = 89 enc[12] = 127 enc[13] = 38 enc[14] = 107 enc[15] = 89 enc[16] = 115 enc[17] = 87 enc[18] = 85 enc[19] = 91 enc[20] = 89 enc[21] = 111 enc[22] = 106 enc[23] = 89 enc[24] = 39 enc[25] = 87 enc[26] = 114 enc[27] = 87 enc[28] = 79 enc[29] = 87 enc[30] = 120 enc[31] = 120 enc[32] = 0x83 for i in range(33): print(chr(((enc[i] + 10) ^ 10) - 10), end='') # SYC{Anma1nG_y0u_maKe_it_1alaIa~~}
flower-or-tea | 简单花指令和TEA加密
Exinfo查到无壳 32位win程序 IDA32打开 伪代码如下
int __cdecl main(int argc, const char **argv, const char **envp) { char v4; // [esp+0h] [ebp-1B0h] char v5; // [esp+0h] [ebp-1B0h] char v6; // [esp+0h] [ebp-1B0h] char v7; // [esp+0h] [ebp-1B0h] int v8[40]; // [esp+Ch] [ebp-1A4h] unsigned int _54; // [esp+ACh] [ebp-104h] int v10; // [esp+B0h] [ebp-100h] char *v11; // [esp+B4h] [ebp-FCh] int lenth; // [esp+B8h] [ebp-F8h] char *v13; // [esp+BCh] [ebp-F4h] int j; // [esp+C0h] [ebp-F0h] int i; // [esp+C4h] [ebp-ECh] int v16[40]; // [esp+CCh] [ebp-E4h] int v17[4]; // [esp+16Ch] [ebp-44h] BYREF int v18; // [esp+17Ch] [ebp-34h] BYREF int v19; // [esp+180h] [ebp-30h] char input[40]; // [esp+184h] [ebp-2Ch] BYREF int v21; // [esp+1ACh] [ebp-4h] int savedregs; // [esp+1B0h] [ebp+0h] BYREF v17[0] = 32; v17[1] = 27; v17[2] = 39; v17[3] = 44; memset(input, 0, sizeof(input)); print(&Format, v4); print(&byte_CA409C, v5); print(&byte_CA4090, v6); scanf(aS, input); v11 = &input[1]; v13 = &input[strlen(input) + 1]; v10 = v13 - &input[1]; lenth = v13 - &input[1]; _54 = 54; v16[0] = -1694939573; v16[1] = -1005078370; v16[2] = -1307072749; v16[3] = -918836760; v16[4] = -1795955634; v16[5] = -1244910923; v16[6] = 1146217516; v16[7] = 2055874714; v16[8] = 1405669384; v16[9] = 1846639433; v16[10] = -1677731948; v16[11] = 1593781753; v16[12] = 401024305; v16[13] = -541222535; v16[14] = -1886971078; v16[15] = 1944634796; v16[16] = -1299812186; v16[17] = 1526113129; v16[18] = 754440740; v16[19] = 880502447; v16[20] = -1178055328; v16[21] = -1860267729; v16[22] = -1118163045; v16[23] = -879332550; v16[24] = -979801922; v16[25] = -1610607639; v16[26] = -1053864284; v16[27] = -561628656; v16[28] = -1597713004; v16[29] = 1132501052; v16[30] = 2117039688; v16[31] = -447882103; v16[32] = 1059563152; v16[33] = -1249037927; v16[34] = 1615521047; v16[35] = -1668269692; v16[36] = -186628991; v16[37] = 1022684671; v16[38] = 0; v16[39] = 0; if ( v13 - &input[1] == 38 ) // flag长度为38 { if ( input[0] == 83 && input[1] == 89 && input[2] == 67 ) { if ( input[3] == 123 && input[37] == 125 ) { for ( i = 0; i < lenth / 2; ++i ) { v18 = input[i]; v19 = input[lenth - i - 1]; sub_CA10C3(_54, &v18, v17); v8[2 * i] = v18; // 奇数元素 v8[2 * i + 1] = v19; // 偶数元素 } // target:v8 = v16 for ( j = 0; j < lenth && v8[j] == v16[j]; ++j ) ; if ( j == lenth ) { print(aYesYes, v7); system(aPause_1); } else { print(aWrong, v7); system(aPause_2); } } else { print(&byte_CA4060, v7); system(aPause_0); } } else { print(aFlag, v7); system(aPause_3); } } else { print(&byte_CA407C, v7); system(Command); } return sub_CA15D1(&savedregs ^ v21); }
核心逻辑就是输入的数据加密后调换顺序并和v16
比较 这个加密算法(sub_CA10C3
)就是TEA加密 特点是没有数据丢失 可以逆向
需要注意的是传入的第二个参数是主函数中的&v18
打开v18
栈中的位置 发现v19
就是紧挨其的下一个
所以v5 = now_char[1];
这句实际上取的是v19
另外一点需要注意的是数据的上限 unsigned int
类型的数据最多储存4bytes 故每次执行都需要截去高32位
知道这些后就可以写出以下解密脚本
from z3 import * key = [None] * 4 key[0] = 32 key[1] = 27 key[2] = 39 key[3] = 44 enc = [None] * 38 enc[0] = 0x9AF9464B enc[1] = 0xC417B89E enc[2] = 0xB217A713 enc[3] = 0xC93BA9E8 enc[4] = 0x94F3E44E enc[5] = 0xB5CC2AB5 enc[6] = 0x4451E42C enc[7] = 0x7A8A289A enc[8] = 0x53C8D008 enc[9] = 0x6E117B49 enc[10] = 0x9BFFD794 enc[11] = 0x5EFF2DF9 enc[12] = 0x17E72531 enc[13] = 0xDFBD9979 enc[14] = 0x8F871B3A enc[15] = 0x73E8C5AC enc[16] = 0xB28670A6 enc[17] = 0x5AF6A369 enc[18] = 0x2CF7DA24 enc[19] = 0x347B66AF enc[20] = 0xB9C84D60 enc[21] = 0x911E912F enc[22] = 0xBD5A2F9B enc[23] = 0xCB96733A enc[24] = 0xC59968BE enc[25] = 0xA00013E9 enc[26] = 0xC12F4EA4 enc[27] = 0xDE863A10 enc[28] = 0xA0C4D594 enc[29] = 0x4380983C enc[30] = 0x7E2F7648 enc[31] = 0xE54DDC89 enc[32] = 0x3F27A690 enc[33] = 0xB58D3199 enc[34] = 0x604AE517 enc[35] = 0x9C903984 enc[36] = 0xF4E04481 enc[37] = 0x3CF4EDFF flag = [None] * 38 lenth = 38 for i in range(19): flag[i] = enc[2 * i] flag[lenth - i - 1] = enc[2 * i + 1] delta = 54 * 0x31415927 for _ in range(54): delta -= 0x31415927 flag[i] -= (key[delta % 4] + delta) ^ (flag[lenth - i - 1] + ((flag[lenth - i - 1] >> 5) ^ (16 * flag[lenth - i - 1]))) flag[i] &= 0xFFFFFFFF flag[lenth - i - 1] -= delta ^ (key[(delta >> 11) % 4] + delta) ^ (flag[i] + ((flag[i] >> 5) ^ (16 * flag[i]))) flag[lenth - i - 1] &= 0xFFFFFFFF for each in flag: print(chr(each), end='') # SYC{D0_Yov_1ike_To_dRink_Flow3r_teA??}
rainbow | 控制流平坦化
关于去控制流平坦化参考 利用符号执行去除控制流平坦化 - 博客 - 腾讯安全应急响应中心
这里用别人的工具脚本去平坦化 去平坦化后的代码如下
由于判断无用块的方法是排除法(排除掉序言块,分发器,预处理块和返回块) 而原程序中退出并不使用return
而是call exit
所以被判定为无用块被删除 故判断长度和flag不正确就退出的块被删去 观察原来的代码 剩下的代码就是加密后的flag和v8~v11
的数据进行对比 解密脚本如下
key = [101, 88, 65, 142, 80, 68, 123, 98, 87, 74, 126, 84, 73, 108, 125, 132, 79, 91, 149, 96, 96, 100, 119, 72, 125, 77, 123, 159, 104, 60, 45, 98] for i in range(32): key[i] ^= i key[i] -= 18 * (i % 3 == 0) print(chr(key[i]), end='') # SYC{TAke_1t_3asy_Just_a_STart!!}
mySelf | 简单SMC
Exinfo查到无壳 32位win程序 IDA32打开
VirtualProtect
函数给予一段内存地址(函数)修改自身字节码的权限 基本上是SMC的标志
如果不使用静态分析的方法可以直接动态调试 这样的可操作性高 在SMC的函数下断点 动态调试到断点处 F7步进函数 之后查看汇编代码 将SMC后的函数重新识别为函数再反编译为伪代码分析
unsigned int __cdecl encode(int a1) { int v1; // ebx int v2; // edi unsigned int v3; // esi int v4; // ebx unsigned int result; // eax unsigned int *v6; // [esp+Ch] [ebp-Ch] unsigned int *v7; // [esp+10h] [ebp-8h] int v8; // [esp+14h] [ebp-4h] v1 = 0; v8 = 0; do { v2 = 0; v3 = *(_DWORD *)(a1 + 4 * v1); v7 = (unsigned int *)(a1 + 4 * v1); v6 = (unsigned int *)(a1 + 4 * (v1 + 1)); v4 = 32; result = *v6; do { v2 -= 1640531527; v3 += ((result >> 5) + 2) ^ (16 * result + 2) ^ (v2 + result); result += ((v3 >> 5) + 4) ^ (16 * v3 + 3) ^ (v2 + v3); --v4; } while ( v4 ); v8 += 2; v1 = v8; *v7 = v3; *v6 = result; } while ( v8 < 8 ); return result; }
又是一个TEA加密 但是其中用到了一个伪随机数 需要动态调试获得 这个TEA加密和上一题中的不同点是每次被加密的字符顺序不同 同时也要注意每次加密的数据长度是4bytes 而原来传入的是以1byte分割的数组的地址 而在x86程序中双字型数据使用小端序储存 知道了这些之后写出解密脚本
o_key = [0xF0, 0xF9, 0xBD, 0xBD, 0xC4, 0x94, 0x61, 0xE2, 0x25, 0x91, 0x79, 0x80, 0x19, 0xC2, 0x0F, 0x1F, 0x15, 0x18, 0x6A, 0xEB, 0xC5, 0x72, 0xF5, 0x84, 0x85, 0x3A, 0xCC, 0x40, 0xBB, 0x2A, 0xA3, 0xD2] flag = [] for i in range(8): flag.append(o_key[4 * i] | o_key[4 * i + 1] << 8 | o_key[4 * i + 2] << 16 | o_key[4 * i + 3] << 24) for i in range(4): v2 = 0x61C88647 * (-32) for _ in range(32): flag[2 * i + 1] -= ((flag[2 * i] >> 5) + 4) ^ (16 * flag[2 * i] + 3) ^ (v2 + flag[2 * i]) flag[2 * i + 1] &= 0xFFFFFFFF flag[2 * i] -= ((flag[2 * i + 1] >> 5) + 2) ^ (16 * flag[2 * i + 1] + 2) ^ (v2 + flag[2 * i + 1]) flag[2 * i] &= 0xFFFFFFFF v2 += 0x61C88647 for each in flag: for i in range(4): print(chr((each >> (8 * i)) & 0xFF), end='')
小黄鸭 | Python逆向
Exinfo 查到为Python3.7写的程序 可以先用pyinstxtractor将其反汇编生成.pyc
文件 再用uncompyle6进行反编译生成.py
文件
python pyinstxtractor.py duck.exe uncompyle6.exe 1.pyc > duck.py
没有进行混淆
# --------duck.py---------- # uncompyle6 version 3.7.4 # Python bytecode 3.7 (3394) # Decompiled from: Python 3.11.4 (tags/v3.11.4:d2340ef, Jun 7 2023, 05:45:37) [MSC v.1934 64 bit (AMD64)] # Embedded file name: 1.py import sys arr = '~h|p4gs`gJdN`thPwR`jDn`te1w`2|RNH' arr = list(arr) print('快帮帮小黄鸭,找到它的钥匙') a = input('请输入你找到的钥匙>:') a = list(a) if not ord(a[0]) != 83: if ord(a[1]) != 89 or ord(a[2]) != 67: print('不对喔~') sys.exit(0) a = a[::-1] b = [] for i in range(len(a)): if a[i].isalpha(): c = a[i] c = chr(ord(c) + 13 - 26 if ord(c) + 13 > (90 if c <= 'Z' else 122) else ord(c) + 13) b.append(chr(ord(c) + 2)) else: b.append(chr(ord(a[i]) + 1)) if chr(ord(a[1])) != 's' or ord(a[2]) != 109 or chr(ord(a[17])) != 'C': print('呀呀呀,怎么算出来不对呀?') sys.exit(0) cnt = 0 for i in range(len(b)): if arr[i] == b[i]: cnt += 1 if cnt == len(b): print('密码正确啦!!!,快去告诉小黄鸭吧~') else: print('密码不对喔~,请再想想吧') # okay decompiling 1.pyc
简单的ROT13加密 写出脚本解密
key_o = '~h|p4gs`gJdN`thPwR`jDn`te1w`2|RNH' key_o = list(key_o) key_o = key_o[::-1] key = [ord(val) for val in key_o] for each in key: if(chr(each - 2).isalpha()): c = each -2 c += 13 if ord('a') <= c and c <= ord('m') or ord('A') <= c and c <= ord('M') else (-13) print(chr(c), end='') else: print(chr(each - 1), end='') # SYC{1_h0pe_yOu_ChAse_YoUr_dr3ams} 我Chase你的梦!
浪漫至死不渝 | JS逆向
源码对变量名进行了混淆 去除混淆并对网页元素进行删去 得到主要逻辑
let k={ a:0,b:1,c:2,d:3,e:4,f:5,g:6,h:7,i:8,j:9,k:10,l:11,m:12,n:13,o:14,p:15,q:16,r:17,s:18,t:19,u:20,v:21,w:22,x:23,y:24,z:25, A:0,B:1,C:2,D:3,E:4,F:5,G:6,H:7,I:8,J:9,K:10,L:11,M:12,N:13,O:14,P:15,Q:16,R:17,S:18,T:19,U:20,V:21,W:22,X:23,Y:24,Z:25 } let a=[ {name:"A",yin:7, str:, {name:"B" ,yin:24, str:, {name:"C" ,yin:1, str:, {name:"D" ,yin:4, str:, {name:"E" ,yin:5, str:, {name:"F" ,yin:2, str:, {name:"G" ,yin:6, str:, {name:"H" ,yin:5, str:, {name:"I" ,yin:8, str:, {name:"J" ,yin:3, str:, {name:"K" ,yin:9, str:, {name:"L" ,yin:8, str:, {name:"M" ,yin:11, str:, {name:"N" ,yin:3, str:, {name:"O" ,yin:7, str:, {name:"P" ,yin:14, str:, {name:"Q" ,yin:15, str:, {name:"R" ,yin:16, str:, {name:"S" ,yin:17, str:, {name:"T" ,yin:18, str:, {name:"U" ,yin:8, str:, {name:"V" ,yin:20, str:, {name:"W" ,yin:3, str:, {name:"X" ,yin:2, str:, {name:"Y" ,yin:23, str:, {name:"Z" ,yin:24, str: ]; let target_2=0; let key = new Array(125, 130, 131, 122, 117, 110, 123, 125, 130, 131, 122, 117, 110, 123, 99, 99, 99, 99); let s = ""; let target = false; function fl(e) { if(clickb==true) { const num_of_fence = 3; const fence_key = '53X211WH04N'; const Text1 = decryptRailFence(fence_key, num_of_fence); //Text1 = '510321H4XWN' let key = e.key; if((key<='z'&&key>='a')||(key<='Z'&&key>='A')) { let p=a[k[key]]; s += p.name; const intArr = []; for (let i = 0; i < s.length; i++) { const charCode = s.charCodeAt(i); intArr.push(charCode); //intArr记录s中每个字符对应的ASCII值 } if (s.length % 18 == 0) { let len = s.length / 18; for (let i = 0; i < len; i++) { for (let control = 0; control < 18; control++) { if (control < 14) { intArr[i + control] ^= Text1.charCodeAt(control % 7); intArr[i + control] += 10; } else { intArr[i+ control] ^= Text1.charCodeAt(control -7); intArr[i + control] += 99; } } } for (let i = 0; i < s.length; i++) { let cnt = 0; if (intArr[i] == key[0]) { for (let control = 0; control < key.length && i+ control < intArr.length; control++) { if (intArr[i + control] == key[control]) { cnt++; } } } if (cnt >= 18) { target = true; break; } } } if(target&&target_2==0) { print "ok" } } }
主要是先用栅栏密码对key进行加密 然后对输入逐位字符进行加密 写出解密脚本
key = [125, 130, 131, 122, 117, 110, 123, 125, 130, 131, 122, 117, 110, 123, 99, 99, 99, 99] Text1 = '5201314WXHN' for i in range(18): if i < 14: print(chr((key[i] - 10) ^ ord(Text1[i % 7])), end='') else: print(chr((key[i] - 99) ^ ord(Text1[i - 7])), end='') # FJIAXUEFJIAXUEWXHN
寻找初音未来 | Go语言逆向 | 一坨go史
go语言所有的字符串在内存上都是连续的 所以对一个字符串对应的长度和偏移量非常重要
附件放到虚拟机中打开 发现提示输入初音未来色 上网搜索知道是39C5BB
用IDA打开 发现这个输入通过某些加密算法得到key
再用该key进行RC4加密直接动态调试得到key为CCCCCCCCCCCCCCCCCC
用python内置的RC4解密算法进行解密
from Crypto.Cipher import ARC4 ciphertext = b'\x25\x6F\x3D\x6C\xF9\xE0\xCF\x3F\x2E\x24\xC6\x7B\x81\xBF\x55\x4F\x0D\x99\x87\x47\x48\xF7\xB9\x98\xFB\x1B\x22\xEC\x84\x23\xFD\xB2' key = b'CCCCCCCCCCCCCCCCCC' cipher = ARC4.new(key) plaintext = cipher.decrypt(ciphertext=ciphertext) print(plaintext) # b'SYC{N0thing_1s_sEriOus_But_MIku}'
AES! AES? | AES算法魔改
Exinfo查到无壳 64位win程序 IDA64打开 伪代码中看到核心加密逻辑是Shiftrow()
和transform()
伪代码如下
void __fastcall ShiftRow(__int64 flag) { int v1; // [rsp+0h] [rbp-10h] int j; // [rsp+4h] [rbp-Ch] int i; // [rsp+8h] [rbp-8h] int k; // [rsp+Ch] [rbp-4h] for ( i = 0; i <= 15; i += 4 ) { for ( j = 0; j < i / 4; ++j ) { v1 = *(4i64 * i + flag); for ( k = 0; k <= 2; ++k ) *(4i64 * (i + k) + flag) = *(flag + 4 * (i + k + 1i64)); *(4i64 * (i + k) + flag) = v1; } } } __int64 __fastcall tansform(__int64 flag) { __int64 result; // rax unsigned __int8 v2; // dl int v3; // eax _DWORD *v4; // rcx int v5[20]; // [rsp+0h] [rbp-60h] int k; // [rsp+50h] [rbp-10h] int j; // [rsp+54h] [rbp-Ch] int i; // [rsp+58h] [rbp-8h] int v9; // [rsp+5Ch] [rbp-4h] v9 = 0; for ( i = 0; i <= 15; ++i ) { result = i; v5[i] = *(4i64 * i + flag); } for ( j = 0; j <= 3; ++j ) { for ( k = 0; k <= 3; ++k ) { *(flag + 4i64 * v9) = v5[4 * k + j]; v2 = S[*(4i64 * v9 + flag)]; v3 = v9++; v4 = (flag + 4i64 * v3); result = v2; *v4 = v2; } } return result; } // 整体代码如下 其中还有一个异或加密 用来实现AES算法 int __cdecl main(int argc, const char **argv, const char **envp) { char Str[48]; // [rsp+20h] [rbp-60h] BYREF int flag1[16]; // [rsp+50h] [rbp-30h] BYREF _BYTE flag2[96]; // [rsp+90h] [rbp+10h] BYREF char v7[8]; // [rsp+F0h] [rbp+70h] BYREF __int64 v8; // [rsp+F8h] [rbp+78h] int v9; // [rsp+100h] [rbp+80h] char v10[20]; // [rsp+110h] [rbp+90h] BYREF char v11[15]; // [rsp+124h] [rbp+A4h] BYREF char v12[13]; // [rsp+133h] [rbp+B3h] BYREF __int64 v13[3]; // [rsp+140h] [rbp+C0h] BYREF int v14; // [rsp+158h] [rbp+D8h] __int16 v15; // [rsp+15Ch] [rbp+DCh] char v16[16]; // [rsp+160h] [rbp+E0h] BYREF int v17[40]; // [rsp+170h] [rbp+F0h] BYREF char v18[176]; // [rsp+210h] [rbp+190h] BYREF __int16 v19; // [rsp+2C0h] [rbp+240h] char v20; // [rsp+2C2h] [rbp+242h] int v21; // [rsp+2C4h] [rbp+244h] int _32; // [rsp+2C8h] [rbp+248h] int i2; // [rsp+2CCh] [rbp+24Ch] int cnt; // [rsp+2D0h] [rbp+250h] int i1; // [rsp+2D4h] [rbp+254h] int nn; // [rsp+2D8h] [rbp+258h] int mm; // [rsp+2DCh] [rbp+25Ch] int jj; // [rsp+2E0h] [rbp+260h] int kk; // [rsp+2E4h] [rbp+264h] int ii; // [rsp+2E8h] [rbp+268h] int n; // [rsp+2ECh] [rbp+26Ch] int m; // [rsp+2F0h] [rbp+270h] int k; // [rsp+2F4h] [rbp+274h] int j; // [rsp+2F8h] [rbp+278h] int i; // [rsp+2FCh] [rbp+27Ch] (_main)(argc, argv, envp); memset(v18, 0, sizeof(v18)); v19 = 0; v20 = 0; memset(v17, 0, sizeof(v17)); v17[0] = 0xE0; v17[1] = 0xFFFFFF05; v17[2] = 0xFFFFFF6E; v17[3] = 0xFFFFFFC2; v17[4] = 0xFFFFFF6E; v17[5] = 0xFFFFFF99; v17[6] = 0xFFFFFF68; v17[7] = 0x45; v17[8] = 0xFFFFFF7D; v17[9] = 0xFFFFFF1F; v17[10] = 0xFFFFFF3F; v17[11] = 0xFFFFFFF9; v17[12] = 0xFFFFFF97; v17[13] = 0xFFFFFF76; v17[14] = 0x3B; v17[15] = 0x92; v17[16] = 0x2F; v17[17] = 0xFFFFFF44; v17[18] = 0xFFFFFF06; v17[19] = 0xFFFFFF67; v17[20] = 0xFFFFFFA8; v17[21] = 0xFFFFFFEB; v17[22] = 0xFFFFFFEC; v17[23] = 0x4A; v17[24] = 0xFFFFFF6F; v17[25] = 0xFFFFFFE8; v17[26] = 0xFFFFFF35; v17[27] = 0xFFFFFFF9; v17[28] = 0xFFFFFFAC; v17[29] = 0xFFFFFFA7; v17[30] = 0x8C; v17[31] = 0x71; qmemcpy(v16, "nyi", 3); v16[3] = -125; v16[4] = 121; v16[5] = 127; v16[6] = 105; v16[7] = 117; v16[8] = 121; v16[9] = 120; v16[10] = -127; v16[11] = 105; v16[12] = 93; v16[13] = 99; v16[14] = 77; v16[15] = 73; v13[0] = 0x78732A6F6D6B767Ai64; v13[1] = 0x7C7F79832A7E7F79i64; v13[2] = 0x142A44716B76702Ai64; v14 = 0; v15 = 0; v10[0] = -125; v10[1] = 111; v10[2] = 125; v10[3] = 43; v10[4] = 42; v10[5] = -125; v10[6] = 121; v10[7] = 127; v10[8] = 42; v10[9] = 107; v10[10] = 124; v10[11] = 111; v10[12] = 42; v10[13] = 124; v10[14] = 115; v10[15] = 113; v10[16] = 114; v10[17] = 126; v10[18] = 43; v10[19] = 20; qmemcpy(v11, "\nxy", 3); v11[3] = -120; v11[4] = 126; v11[5] = 124; v11[6] = -125; v11[7] = 42; v11[8] = 107; v11[9] = 113; v11[10] = 107; v11[11] = 115; v11[12] = 120; v11[13] = -120; v11[14] = 20; qmemcpy(v12, "\nxy", 3); v12[3] = -120; v12[4] = 10; for ( i = 0; i <= 23; ++i ) *(v13 + i) -= 10; for ( j = 0; j <= 39; ++j ) v10[j] -= 10; *v7 = 0i64; v8 = 0i64; v9 = 0; printf("%s", v13); scanf("%s", Str); _32 = strlen(Str); v21 = strlen(v7); if ( _32 != 32 ) { printf("%s", &v12[1]); exit(0); } for ( k = 0; k <= 15; ++k ) { v16[k] -= 10; v7[k] = v16[k]; } if ( Str[5] != '.' || Str[10] != 'l' || Str[17] != '0' ) { printf("%s", &v12[1]); exit(0); } for ( m = 0; m <= 15; ++m ) v18[m] = v7[m]; // v18 = do_you_konw_SYC? for ( n = 1; n <= 10; ++n ) { for ( ii = 0; ii <= 31; ++ii ) v18[16 * n + ii] = v18[16 * n - 16 + ii] ^ S[v18[16 * n - 16 + ii]]; } HIBYTE(v19) = '9'; kk = 0; for ( jj = 0; jj < _32; ++jj ) flag1[jj] = S[Str[jj]]; for ( kk = 0; kk <= 0; ++kk ) { ShiftRow(flag1); // 将第n行的前(n - 1)个元素向右移动(5 - n)格 ShiftRow(flag2); tansform(flag1); tansform(flag2); for ( mm = 0; mm <= 15; ++mm ) { flag1[mm] ^= v18[16 * kk + mm]; // 将flag与key1异或 flag1[mm + 16] ^= v18[16 * kk + mm]; } for ( nn = 0; nn < _32; ++nn ) flag1[nn] = S[flag1[nn]]; // 将flag中的值替换为S中以flag的值为索引的值 } ShiftRow(flag1); ShiftRow(flag2); for ( i1 = 0; i1 <= 15; ++i1 ) // 将flag与key2异或 { flag1[i1] ^= v18[16 * kk + i1]; flag1[i1 + 16] ^= v18[16 * kk + i1]; } cnt = 0; for ( i2 = 0; i2 <= 31; ++i2 ) { if ( flag1[i2] == v17[i2] ) ++cnt; } if ( cnt <= 31 ) printf("%s", &v11[1]); else printf("%s", v10); return 0; }
据此写出解密脚本
S = [0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16] key_ = "do_you_konw_SYC?" v17 = [None] * 32 v17[0] = 0xE0 v17[1] = 0x05 v17[2] = 0x6E v17[3] = 0xC2 v17[4] = 0x6E v17[5] = 0x99 v17[6] = 0x68 v17[7] = 0x45 v17[8] = 0x7D v17[9] = 0x1F v17[10] = 0x3F v17[11] = 0xF9 v17[12] = 0x97 v17[13] = 0x76 v17[14] = 0x3B v17[15] = 0x92 v17[16] = 0x2F v17[17] = 0x44 v17[18] = 0x06 v17[19] = 0x67 v17[20] = 0xA8 v17[21] = 0xEB v17[22] = 0xEC v17[23] = 0x4A v17[24] = 0x6F v17[25] = 0xE8 v17[26] = 0x35 v17[27] = 0xF9 v17[28] = 0xAC v17[29] = 0xA7 v17[30] = 0x8C v17[31] = 0x71 def shift_row(flag:list) -> list: for i in range(0, 4): for _ in range(i): for k in range(3): flag[4 * i + 3 - k], flag[4 * i + 3 - k - 1] = flag[4 * i + 3 - k - 1], flag[4 * i + 3 - k] return flag key1 = [] for each in key_: key1.append(ord(each) & 0xFF) key2 = [] for i in range(16): key2.append(key1[i] ^ S[key1[i]] & 0xFF) flag_after = [] for i in range(32): temp = v17[i] ^ key2[i % 0x10] flag_after.append(temp) flag = shift_row(flag_after[0 :16]) flag += shift_row(flag_after[16:32]) for i in range(32): flag[i] = S.index(flag[i]) flag[i] ^= key1[i % 0x10] flag1 = [None] * 16 flag2 = [None] * 16 cnt = 0 for i in range(4): for j in range(4): flag1[4 * j + i] = S.index(flag[cnt]) cnt += 1 for i in range(4): for j in range(4): flag2[4 * j + i] = S.index(flag[cnt]) cnt += 1 flag_last = shift_row(flag1) flag_last += shift_row(flag2) for each in flag_last: t = S.index(each) try: print(chr(t), end='') except: pass # SYC{0.o_Thls_1s_n0t_A3s_(q^_^p)}
另外 不要手抄key不要手抄key不要手抄key不要手抄key不要手抄key不要手抄key不要手抄key不要手抄key不要手抄key不要手抄key
ezandroid | 安卓逆向
apk用jeb打开 有两个主要活动如下
package com.example.babyapk_1; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.view.View.OnClickListener; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; import b.f; public class MainActivity extends f { public class b { public int[] a; public b() { this.a = new int[]{2023708229, -158607964, 0x81963FFA, 0x458FAC58}; } } public EditText o; @Override // b.f public void onCreate(Bundle bundle0) { new Handler(); super.onCreate(bundle0); this.setContentView(0x7F0B001C); // layout:activity_main Button button0 = (Button)this.findViewById(0x7F080057); // id:button this.o = (EditText)this.findViewById(0x7F080097); // id:edit_text button0.setOnClickListener(new View.OnClickListener() { @Override // android.view.View$OnClickListener public void onClick(View view0) { String s = MainActivity.this.o.getText().toString(); if(s.length() > 24) { MainActivity.this.o.setText(""); s = MainActivity.this.o.getText().toString(); } if(((s.length() == 0 ? 1 : 0) | (s.length() % 24 == 0 ? 0 : 1)) != 0) { int v = 24 - s.length() % 24; StringBuilder stringBuilder0 = new StringBuilder(s); for(int v1 = 0; v1 < v; ++v1) { stringBuilder0.append('X'); } s = stringBuilder0.toString(); } StringBuilder stringBuilder1 = new StringBuilder(); StringBuilder stringBuilder2 = new StringBuilder(); for(int v2 = 0; v2 < s.length(); ++v2) { if(v2 % 2 == 0) { stringBuilder1.append(s.charAt(v2)); } else { stringBuilder2.append(s.charAt(v2)); } } b mainActivity$b0 = new b(MainActivity.this); String s1 = stringBuilder2.toString(); byte[] arr_b = stringBuilder1.toString().getBytes(); int v3 = arr_b.length >> 2; int[] arr_v = new int[v3]; int v5 = 0; for(int v4 = 0; v4 < arr_b.length; v4 += 4) { int v6 = arr_b[v4 + 3]; if(v6 < 0) { v6 += 0x100; } int v7 = arr_b[v4 + 2]; if(v7 < 0) { v7 += 0x100; } int v8 = arr_b[v4 + 1]; if(v8 < 0) { v8 += 0x100; } arr_v[v5] = v6 | v7 << 8 | v8 << 16 | arr_b[v4] << 24; ++v5; } int v9 = arr_v[0]; int v10 = arr_v[1]; int v11 = arr_v[2]; int[] arr_v1 = mainActivity$b0.a; int key_0 = arr_v1[0]; int key_1 = arr_v1[1]; int key_2 = arr_v1[2]; int key_3 = arr_v1[3]; int v16 = 0; for(int v17 = 0; v17 < 0x20; ++v17) { v16 += -1640531527; v9 += (v10 << 4) + key_0 ^ v10 + v16 ^ (v10 >> 5) + key_1; v10 += (v9 << 4) + key_2 ^ v9 + v16 ^ (v9 >> 5) + key_3; } arr_v[0] = v9; for(int v18 = 0; v18 < 0x20; ++v18) { v16 += -1640531527; v11 += (v10 << 4) + key_0 ^ v10 + v16 ^ (v10 >> 5) + key_1; v10 += (v11 << 4) + key_2 ^ v11 + v16 ^ (v11 >> 5) + key_3; } arr_v[1] = v11; arr_v[2] = v10; byte[] arr_b1 = new byte[v3 << 2]; int v20 = 0; for(int v19 = 0; v19 < v3; ++v19) { arr_b1[v20 + 3] = (byte)(arr_v[v19] & 0xFF); arr_b1[v20 + 2] = (byte)(arr_v[v19] >> 8 & 0xFF); arr_b1[v20 + 1] = (byte)(arr_v[v19] >> 16 & 0xFF); arr_b1[v20] = (byte)(arr_v[v19] >> 24 & 0xFF); v20 += 4; } for(int v21 = 0; v21 < new byte[]{-91, -8, -110, -55, -49, 75, 0x73, 13, -76, (byte)0x8F, 102, 80}.length; ++v21) { System.out.println(((int)arr_b1[v21])); } int v23 = 1; for(int v22 = 0; true; ++v22) { byte[] arr_b2 = new byte[]{-91, -8, -110, -55, -49, 75, 0x73, 13, -76, (byte)0x8F, 102, 80}; if(v22 >= arr_b2.length) { break; } if(arr_b1[v22] != arr_b2[v22]) { Toast.makeText(MainActivity.this, "You\'re Wrong!\n", 0).show(); v23 = 0; } } Intent intent0 = new Intent(MainActivity.this, MainActivity2.class); intent0.putExtra("ad@#E!@a123", s1); intent0.putExtra("eCAS213@!@3", arr_b1); if(v23 == 1) { MainActivity.this.startActivity(intent0); } } }); } } package com.example.babyapk_1; import android.content.Intent; import android.os.Bundle; import android.widget.Toast; import b.f; public class MainActivity2 extends f { @Override // b.f public void onCreate(Bundle bundle0) { super.onCreate(bundle0); this.setContentView(0x7F0B001D); // layout:activity_main2 Intent intent0 = this.getIntent(); byte[] arr_b = intent0.getStringExtra("ad@#E!@a123").getBytes(); byte[] arr_b1 = intent0.getByteArrayExtra("eCAS213@!@3"); int v1 = 1; for(int v = 0; v < arr_b1.length; ++v) { arr_b1[v] = (byte)(arr_b1[v] ^ arr_b[v % arr_b.length]); if(arr_b1[v] != new int[]{-107, -106, 0xFFFFFFA1, 0xFFFFFF8D, 0xFFFFFF89, 0x7F, 26, 0x79, -62, -20, 86, 9}[v]) { Toast.makeText(this, "N0T Right,Maybe try more harder?\n", 0).show(); v1 = 0; } } if(v1 == 1) { Toast.makeText(this, "Congratulations!", 1).show(); } } }
当MainActivity1中的匹配通过后才会进行MainActivity2的另一部分加密 主要是分奇偶索引进行TEA或异或加密 据此写出解密脚本
#include<stdio.h> int main(){ unsigned char arr_even[12] = {-107, -106, 0xFFFFFFA1, 0xFFFFFF8D, 0xFFFFFF89, 0x7F, 26, 0x79, -62, -20, 86, 9}; unsigned char key_even[12] = {-91, -8, -110, -55, -49, 75, 0x73, 13, -76, 0x8F, 102, 80}; char flag_even[12]; for(int i = 0; i < 12; i++){ flag_even[i] = arr_even[i] ^ key_even[i]; } int flag_odd_[3]; for(int i = 0; i < 12; i += 4){ flag_odd_[(int)(i / 4)] = key_even[i + 3] | key_even[i + 2] << 8 | key_even[i + 1] << 16 | key_even[i] << 24; } int TEA_key[4] = {2023708229, -158607964, 0x81963FFA, 0x458FAC58}; int delta = -1640531527 * 0x40; for(int i = 0; i < 0x20; i++){ flag_odd_[2] -= (flag_odd_[1] << 4) + TEA_key[2] ^ flag_odd_[1] + delta ^ (flag_odd_[1] >> 5) + TEA_key[3]; flag_odd_[1] -= (flag_odd_[2] << 4) + TEA_key[0] ^ flag_odd_[2] + delta ^ (flag_odd_[2] >> 5) + TEA_key[1]; delta += 1640531527; } for(int i = 0; i < 0x20; i++){ flag_odd_[2] -= (flag_odd_[0] << 4) + TEA_key[2] ^ flag_odd_[0] + delta ^ (flag_odd_[0] >> 5) + TEA_key[3]; flag_odd_[0] -= (flag_odd_[2] << 4) + TEA_key[0] ^ flag_odd_[2] + delta ^ (flag_odd_[2] >> 5) + TEA_key[1]; delta += 1640531527; } unsigned char flag_odd[12]; for(int i = 3; i >= 0; i--){ flag_odd[3 - i] = (flag_odd_[0] >> i * 8); } for(int i = 3; i >= 0; i--){ flag_odd[7 - i] = (flag_odd_[2] >> i * 8); } for(int i = 3; i >= 0; i--){ flag_odd[11 - i] = (flag_odd_[1] >> i * 8); } printf("SYC{"); for(int i = 0; i < 24; i++){ if(i % 2 == 0){ printf("%c", flag_odd[(int)(i / 2)]); } else{ printf("%c", flag_even[(int)(i / 2)]); } } printf("}"); // SYC{T00nV3tD3F34Tint0vict0rY} return 0; }
是男人就来扎针 | Unity引擎游戏逆向
Unity引擎游戏的控制逻辑一般存放在.\*_Data\Managed\Assembly-CSharp.dll
中 用dnspy打开 主要逻辑如下
using System; using System.Collections; using System.Security.Cryptography; using System.Text; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; // Token: 0x02000002 RID: 2 public class GameManager : MonoBehaviour { // Token: 0x06000002 RID: 2 RVA: 0x000020A9 File Offset: 0x000004A9 public static string RemoveDash(string str) { str = str.Replace("-", string.Empty); return str; } // Token: 0x06000003 RID: 3 RVA: 0x000020C0 File Offset: 0x000004C0 public static string what(string str) { MD5 md = MD5.Create(); byte[] bytes = Encoding.Default.GetBytes(str); byte[] value = md.ComputeHash(bytes); return GameManager.RemoveDash(BitConverter.ToString(value)); } // Token: 0x06000004 RID: 4 RVA: 0x000020F4 File Offset: 0x000004F4 private void Start() { this.startPoint = GameObject.Find("StartPoint").transform; this.spawnPoint = GameObject.Find("SpawnPoint").transform; this.mainCamera = Camera.main; this.SpawnPin(); } // Token: 0x06000005 RID: 5 RVA: 0x00002134 File Offset: 0x00000534 private void Update() { if (this.score == 30) { string text = string.Empty; for (int i = 0; i < this.magicc.Length; i++) { char c = (char)this.magicc[i]; text += c; } this.flagText.text = text; this.nothing += GameManager.what(this.flagText.text); this.nothing += '\0'; } if (this.score == 40) { string text2 = string.Empty; for (int j = 0; j < this.magic.Length; j++) { char c2 = (char)this.magic[j]; text2 += c2; } this.flagText.text = text2; text2 += '\0'; } if (this.score == 100) { this.scoreText.text = "!"; this.flagText.text = this.nothing; } if (this.isGameOver) { return; } if (Input.GetMouseButtonDown(0)) { this.score++; this.scoreText.text = this.score.ToString(); this.currentPin.StartFly(); this.SpawnPin(); for (int k = 0; k < this.magicc.Length; k++) { this.magicc[k] ^= this.score; this.magic[k] ^= this.score; } } } // Token: 0x06000006 RID: 6 RVA: 0x000022F6 File Offset: 0x000006F6 private void SpawnPin() { this.currentPin = UnityEngine.Object.Instantiate<GameObject>(this.pinPrefab, this.spawnPoint.position, this.pinPrefab.transform.rotation).GetComponent<Pin>(); } // Token: 0x06000007 RID: 7 RVA: 0x00002329 File Offset: 0x00000729 public void GameOver() { if (this.isGameOver) { return; } GameObject.Find("Circle").GetComponent<RotateSelf>().enabled = false; base.StartCoroutine(this.GameOverAnimation()); this.isGameOver = true; } // Token: 0x06000008 RID: 8 RVA: 0x00002360 File Offset: 0x00000760 private IEnumerator GameOverAnimation() { for (;;) { this.mainCamera.backgroundColor = Color.Lerp(this.mainCamera.backgroundColor, Color.red, this.speed * Time.deltaTime); this.mainCamera.orthographicSize = Mathf.Lerp(this.mainCamera.orthographicSize, 4f, this.speed * Time.deltaTime); if (Mathf.Abs(this.mainCamera.orthographicSize - 4f) < 0.01f) { break; } yield return 0; } yield return new WaitForSeconds(0.2f); SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex); yield break; } // Token: 0x04000001 RID: 1 private Transform startPoint; // Token: 0x04000002 RID: 2 private Transform spawnPoint; // Token: 0x04000003 RID: 3 private Pin currentPin; // Token: 0x04000004 RID: 4 private bool isGameOver; // Token: 0x04000005 RID: 5 private int score; // Token: 0x04000006 RID: 6 private Camera mainCamera; // Token: 0x04000007 RID: 7 private string nothing = string.Empty; // Token: 0x04000008 RID: 8 private int[] magicc = new int[] { 75, 109, 102, 63, 107, 112, 63, 108, 124, 112, 109, 122, 63, 43, 47, 63, 111, 112, 118, 113, 107, 108, 62 }; // Token: 0x04000009 RID: 9 private int[] magic = new int[] { 124, 90, 81, 8, 92, 71, 8, 90, 77, 73, 75, 64, 8, 25, 24, 24, 8, 88, 71, 65, 70, 92, 91, 9 }; // Token: 0x0400000A RID: 10 public Text scoreText; // Token: 0x0400000B RID: 11 public Text flagText; // Token: 0x0400000C RID: 12 public GameObject pinPrefab; // Token: 0x0400000D RID: 13 public float speed = 3f; }
可以看到游戏前30次每刷新一次分数就会与明文进行异或 最后进行md5加密然后在分数达到100时显示 显然不可能点到100 但是可以先点到30 再用CE改到100 写出解密脚本
import hashlib key = [ 75, 109, 102, 63, 107, 112, 63, 108, 124, 112, 109, 122, 63, 43, 47, 63, 111, 112, 118, 113, 107, 108, 62] convt = hashlib.md5() for i in range(1, 31): for j in range(len(key)): key[j] ^= i flag = '' for each in key: flag += chr(each) convt.update(flag.encode()) print(convt.hexdigest().upper()) # CBDDD133B60130856D3C695D9E5ED6A5
babycode | 不知道什么加密
IDA64打开 伪代码如下 其中两个简单加密算法不展示
__int64 __fastcall main(int a1, char **a2, char **a3) { int v3; // eax int v5; // [rsp+4h] [rbp-13Ch] int i; // [rsp+8h] [rbp-138h] int j; // [rsp+Ch] [rbp-134h] int k; // [rsp+10h] [rbp-130h] char enc[4]; // [rsp+20h] [rbp-120h] BYREF int v10[33]; // [rsp+24h] [rbp-11Ch] char input[113]; // [rsp+B7h] [rbp-89h] BYREF unsigned __int64 v12; // [rsp+128h] [rbp-18h] v12 = __readfsqword(0x28u); v5 = 1; puts("please input your flag:"); __isoc99_scanf("%s", &input[9]); *enc = 120; v10[0] = 91; v10[1] = 86; v10[2] = 122; v10[3] = 93; v10[4] = 84; v10[5] = 37; v10[6] = 49; v10[7] = 32; v10[8] = 104; v10[9] = 61; v10[10] = 100; v10[11] = -110; v10[12] = 118; v10[13] = 99; v10[14] = 123; v10[15] = 89; v10[16] = 87; v10[17] = 33; v10[18] = -124; v10[19] = 87; v10[20] = 118; v10[21] = -121; v10[22] = 114; v10[23] = -124; v10[24] = -123; v10[25] = 112; v10[26] = -98; v10[27] = 79; v10[28] = 112; v10[29] = 114; v10[30] = -124; v10[31] = 87; v10[32] = -120; strcpy(input, "fuwafuwa"); for ( i = 0; i < strlen(&input[9]); ++i ) input[i + 9] = (16 * input[i + 9]) | (input[i + 9] >> 4) & 0xF;// 互换输入中的高/低四位 encode(&input[9]); for ( j = 0; j < strlen(&input[9]); ++j ) { input[j + 9] += j; input[j + 9] ^= input[j % 8] % 32 + 1; ceasar(input, j); // 向后移j位的凯撒加密 v3 = strlen(input); reverse(input, v3); // 对称对换key(0->8, 1->7... } for ( k = 0; k <= strlen(enc); ++k ) { if ( input[k + 9] != *&enc[4 * k] ) { v5 = 0; puts("no way!"); break; } } if ( v5 == 1 ) puts("your input is flag!"); return 0LL; } char *__fastcall encode(const char *input) { unsigned __int8 v2; // [rsp+17h] [rbp-49h] int i; // [rsp+18h] [rbp-48h] int v4; // [rsp+1Ch] [rbp-44h] char *ptr_input; // [rsp+20h] [rbp-40h] char *key2; // [rsp+28h] [rbp-38h] char *ptr_input_; // [rsp+30h] [rbp-30h] unsigned __int64 j; // [rsp+38h] [rbp-28h] size_t len_of_input; // [rsp+40h] [rbp-20h] __int64 max_key; // [rsp+48h] [rbp-18h] char *v11; // [rsp+50h] [rbp-10h] unsigned __int64 last; // [rsp+58h] [rbp-8h] len_of_input = strlen(input); max_key = 3 * ((len_of_input >> 1) + 1); v11 = malloc(max_key + len_of_input); key2 = &v11[max_key]; // 输入的首地址 last = &v11[max_key - 1 + len_of_input]; // v11的最后一个元素 ptr_input = &v11[max_key]; memcpy(&v11[max_key], input, len_of_input); // 最后的一段元素和输入相同 while ( ptr_input <= last ) { if ( *ptr_input ) { v2 = 0; for ( ptr_input_ = ptr_input; ptr_input_ <= last; ++ptr_input_ ) { v4 = v2 << 8; v2 = *ptr_input_ & 0x3F; *ptr_input_ = (*ptr_input_ + v4) >> 6; } *--key2 = off_55B72E249018[v2]; } else { ++ptr_input; } } for ( i = 0; i <= 19; ++i ) key += rand() % 100; key = key % 0x20 + 1; for ( j = 0LL; j < &v11[max_key] - key2; ++j ) input[j] = key ^ key2[j]; input[j] = 0; return v11; }
核心加密是一个base64的换表 要注意的是其中的key动态调试出来并不正确 因为程序会检测动态调试改变key的初值
.init_array:000055B72E248D80 _init_array segment qword public 'DATA' use64 .init_array:000055B72E248D80 assume cs:_init_array .init_array:000055B72E248D80 ;org 55B72E248D80h .init_array:000055B72E248D80 00 62 24 2E B7 55 00 00 dq offset sub_55B72E246200 .init_array:000055B72E248D88 09 62 24 2E B7 55 00 00 dq offset sub_55B72E246209 .init_array:000055B72E248D88 _init_array ends ... ... .text:000055B72E246209 sub_55B72E246209 proc near ; DATA XREF: .init_array:000055B72E248D88↓o .text:000055B72E246209 .text:000055B72E246209 var_4= dword ptr -4 .text:000055B72E246209 .text:000055B72E246209 ; __unwind { .text:000055B72E246209 F3 0F 1E FA endbr64 .text:000055B72E24620D 55 push rbp .text:000055B72E24620E 48 89 E5 mov rbp, rsp .text:000055B72E246211 C7 45 FC 00 00 00 00 mov [rbp+var_4], 0 .text:000055B72E246218 BB 00 00 00 00 mov ebx, 0 ; ptrace_request .text:000055B72E24621D B9 00 00 00 00 mov ecx, 0 ; pid .text:000055B72E246222 BA 00 00 00 00 mov edx, 0 ; addr .text:000055B72E246227 B8 1A 00 00 00 mov eax, 1Ah .text:000055B72E24622C CD 80 int 80h ; LINUX - sys_ptrace .text:000055B72E24622C .text:000055B72E24622E 89 45 FC mov [rbp+var_4], eax .text:000055B72E246231 8B 45 FC mov eax, [rbp+var_4] .text:000055B72E246234 48 98 cdqe .text:000055B72E246236 48 83 F8 FF cmp rax, 0FFFFFFFFFFFFFFFFh .text:000055B72E24623A 75 0A jnz short loc_55B72E246246 .text:000055B72E24623A .text:000055B72E24623C C7 05 CA 2D 00 00 08 00 00 00 mov cs:key, 8 .text:000055B72E24623C .text:000055B72E246246 .text:000055B72E246246 loc_55B72E246246: ; CODE XREF: sub_55B72E246209+31↑j .text:000055B72E246246 90 nop .text:000055B72E246247 5D pop rbp .text:000055B72E246248 C3 retn .text:000055B72E246248 ; } // starts at 55B72E246209 .text:000055B72E246248 .text:000055B72E246248 sub_55B72E246209 endp
所以用key在(1, 31)
且加密后密文都在表中爆破出key为21 据此写出解密脚本
![image-20231128164528686](C:\Users\Orink\AppData\Roaming\Typora\typora-user-images\image-20231128164528686.png)def emulator(key:list, time:int): for i in range(8): key[i] = 97 + (key[i] - 97 + time) % 26 def antiemulator(key:list, time:int): for i in range(8): key[i] = 97 + (key[i] - 97 - time) % 26 table = [ord(val) for val in 'i5jLW7S0GX6uf1cv3ny4q8es2Q+bdkYgKOIT/tAxUrFlVPzhmow9BHCMDpEaJRZN'] enc = [val & 0xFF for val in [120, 91, 86, 122, 93, 84, 37, 49, 32, 104, 61, 100, -110, 118, 99, 123, 89, 87, 33, -124, 87, 118, -121, 114, -124, -123, 112, -98, 79, 112, 114, -124, 87, -120]] key_0 = 'fuwafuwa' key = [ord(val) for val in key_0] for i in range(34): emulator(key, i) key = key[::-1] for i in range(33, -1, -1): key = key[::-1] antiemulator(key, i) enc[i] ^= key[i % 8] % 32 + 1 enc[i] -= i for i in range(34): enc[i] ^= 21 enc = [table.index(val) for val in enc] flag = '' for each in enc: flag += "{:06b}".format(each) flag = flag[4:] flag = [flag[8 * i:8 * i + 8] for i in ra![image-20231128164528686](C:\Users\Orink\AppData\Roaming\Typora\typora-user-images\image-20231128164528686.png)nge(len(enc))] print("SYC{", end='') for each in flag: try: print(chr(int(each[4:8] + each[0:4], 2)), end='') except: pass print("}", end='') # SYC{HbwKqCOAOVXdHAbG0HeinZkez}
yakvm | 虚拟机逆向
main为生成的虚拟机 IDA打开发现是无符号go 用go paraser恢复符号 搜索有关操作码的函数
发现有一个ShowOpcodes函数 但 调试时不进过该函数 交叉引用找到调用它的函数
在判断处下断点 调试时通过改ZF寄存器步进ShowOpcodes函数 得到可读操作码 根据官方文档分析如下
2:6->2:9 0:OP:type byte 2:4->2:119 1:OP:type slice 2:11->2:13 2:OP:push 137 2:15->2:17 3:OP:push 108 2:19->2:21 4:OP:push 159 2:23->2:25 5:OP:push 114 2:27->2:29 6:OP:push 185 2:31->2:32 7:OP:push 90 2:34->2:36 8:OP:push 174 2:38->2:39 9:OP:push 68 2:41->2:43 10:OP:push 160 2:45->2:46 11:OP:push 81 2:48->2:50 12:OP:push 179 2:52->2:53 13:OP:push 41 2:55->2:57 14:OP:push 186 2:59->2:60 15:OP:push 89 2:62->2:64 16:OP:push 168 2:66->2:67 17:OP:push 78 2:69->2:71 18:OP:push 229 2:73->2:75 19:OP:push 121 2:77->2:79 20:OP:push 149 2:81->2:83 21:OP:push 106 2:85->2:87 22:OP:push 147 2:89->2:91 23:OP:push 103 2:93->2:95 24:OP:push 156 2:97->2:99 25:OP:push 114 2:101->2:103 26:OP:push 133 2:105->2:106 27:OP:push 98 2:108->2:110 28:OP:push 146 2:112->2:114 29:OP:push 116 2:116->2:118 30:OP:push 181 2:4->2:119 31:OP:typedslice 29 2:4->2:119 32:OP:list 1 2:0->2:0 33:OP:pushleftr 1 arg_1 = key_list[29] 2:0->2:0 34:OP:list 1 2:0->2:119 35:OP:assign 3:0->3:4 36:OP:pushid print 3:6->3:25 37:OP:push please input flag: 3:5->3:26 38:OP:call vlen:1 3:0->3:26 39:OP:pop 4:4->4:6 40:OP:pushid get 4:7->4:8 41:OP:call vlen:0 4:4->4:8 42:OP:list 1 4:0->4:0 43:OP:pushleftr 2 4:0->4:0 44:OP:list 1 arg_2 = input[29] 4:0->4:8 45:OP:assign 5:18->8:0 46:OP:new-scope 2 { 5:3->5:5 47:OP:pushid len 5:7->5:7 48:OP:pushr 2 5:6->5:8 49:OP:call vlen:1 len(arg_2) 5:12->5:14 50:OP:pushid len 5:16->5:16 51:OP:pushr 1 len(arg_1) 5:15->5:17 52:OP:call vlen:1 5:3->5:17 53:OP:gt len(arg_1) > len(arg_2) -> goto globle63 5:18->8:0 54:OP:jmpf -> 63 5:18->8:0 55:OP:new-scope 3 { 6:1->6:7 56:OP:pushid println 6:9->6:32 57:OP:push input string too long! printf(too long!) 6:8->6:33 58:OP:call vlen:1 6:1->6:33 59:OP:pop 7:1->7:6 60:OP:return 5:18->8:0 61:OP:end-scope } 5:18->8:0 62:OP:jmp -> 63 globle63: 5:18->8:0 63:OP:end-scope } 12:8->21:0 64:OP:push function params[1] codes[54] (copy) 12:8->21:0 65:OP:list 1 12:0->12:4 66:OP:pushleftr 8 arg_8 = func1 12:0->12:4 67:OP:list 1 12:0->21:0 68:OP:assign 25:4->25:8 69:OP:pushr 8 25:10->25:10 70:OP:pushr 2 25:9->25:11 71:OP:call vlen:1 arg2 = func1(arg_2) 25:4->25:11 72:OP:list 1 25:0->25:0 73:OP:pushleftr 2 25:0->25:0 74:OP:list 1 25:0->25:11 75:OP:assign 28:9->33:0 76:OP:push function params[0] codes[40] (copy) 28:9->33:0 77:OP:list 1 28:0->28:4 78:OP:pushleftr 14 28:0->28:4 79:OP:list 1 28:0->33:0 80:OP:assign 36:0->36:4 81:OP:pushr 14 36:5->36:6 82:OP:call vlen:0 func2() 36:0->36:6 83:OP:pop 38:11->48:0 84:OP:push function params[0] codes[52] (copy) 38:11->48:0 85:OP:list 1 38:0->38:7 86:OP:pushleftr 19 38:0->38:7 87:OP:list 1 38:0->48:0 88:OP:assign 51:14->53:0 89:OP:new-scope 24 51:3->51:10 90:OP:pushr 19 51:11->51:12 91:OP:call vlen:0 51:14->53:0 92:OP:jmpf -> 100 51:14->53:0 93:OP:new-scope 25 52:1->52:5 94:OP:pushid print 52:7->52:24 95:OP:push yes! you get it! 52:6->52:25 96:OP:call vlen:1 52:1->52:25 97:OP:pop 51:14->53:0 98:OP:end-scope 51:14->53:0 99:OP:jmp -> 108 51:14->53:0 100:OP:end-scope 51:14->53:0 101:OP:new-scope 26 53:6->55:0 102:OP:new-scope 27 54:1->54:5 103:OP:pushid print 54:7->54:24 104:OP:push no this not flag 54:6->54:25 105:OP:call vlen:1 54:1->54:25 106:OP:pop 53:6->55:0 107:OP:end-scope 51:14->53:0 108:OP:end-scope //func1 start anonymous 13:1->19:1 1:OP:new-scope 6 { 13:1->19:1 2:OP:pushleftr 5 arg_5 = arg_3 = arg_2 13:18->13:18 3:OP:pushr 3 13:1->19:1 4:OP:fast-assign 13:1->19:1 5:OP:enter-for-range -> 47 for i in range(47) 13:1->19:1 6:OP:range-next 13:5->13:5 7:OP:pushleftr 6 13:8->13:8 8:OP:pushleftr 7 13:5->13:8 9:OP:list 2 13:1->19:1 10:OP:assign 13:20->19:1 11:OP:new-scope 7 { 14:16->16:2 12:OP:new-scope 8 { 14:5->14:5 13:OP:pushr 6 14:9->14:9 14:OP:push 2 14:5->14:9 15:OP:mod 14:14->14:14 16:OP:push 0 14:5->14:14 17:OP:eq arg_6 % 2 == 0 goto global31 14:16->16:2 18:OP:jmpf -> 31 14:16->16:2 19:OP:new-scope 9 else{ 15:10->15:10 20:OP:pushr 7 15:14->15:17 21:OP:push 240 15:10->15:17 22:OP:xor 15:10->15:17 23:OP:list 1 15:3->15:3 24:OP:pushr 3 15:5->15:5 25:OP:pushr 6 arg_3[arg_6] = arg_7 ^ 240 15:3->15:6 26:OP:list 2 15:3->15:6 27:OP:list 1 15:3->15:17 28:OP:assign 14:16->16:2 29:OP:end-scope } 14:16->16:2 30:OP:jmp -> 44 14:16->16:2 31:OP:end-scope global31: } 14:16->16:2 32:OP:new-scope 10 { 16:9->18:2 33:OP:new-scope 11 { 17:10->17:10 34:OP:pushr 7 arg_7 ^ 15 17:14->17:17 35:OP:push 15 17:10->17:17 36:OP:xor 17:10->17:17 37:OP:list 1 17:3->17:3 38:OP:pushr 3 17:5->17:5 39:OP:pushr 6 arg_3[arg_6] = arg_7 ^ 15 17:3->17:6 40:OP:list 2 17:3->17:6 41:OP:list 1 17:3->17:17 42:OP:assign 16:9->18:2 43:OP:end-scope } 14:16->16:2 44:OP:end-scope } 13:20->19:1 45:OP:end-scope } 13:1->19:1 46:OP:exit-for-range -> 6 13:1->19:1 47:OP:pop 13:1->19:1 48:OP:end-scope } 20:8->20:8 49:OP:pushr 3 20:8->20:8 50:OP:list 1 20:1->20:8 51:OP:return //func1 end //func2 start anonymous 28:15->33:0 0:OP:new-scope 13 29:1->32:1 1:OP:new-scope 14 29:1->32:1 2:OP:pushleftr 10 29:18->29:18 3:OP:pushr 2 arg_10 = arg_2 29:1->32:1 4:OP:fast-assign 29:1->32:1 5:OP:enter-for-range -> 36 29:1->32:1 6:OP:range-next 29:5->29:5 7:OP:pushleftr 11 arg_12 = arg_11 ? 29:8->29:8 8:OP:pushleftr 12 29:5->29:8 9:OP:list 2 29:1->32:1 10:OP:assign 29:20->32:1 11:OP:new-scope 15 { 30:6->30:6 12:OP:pushr 11 arg_13 = arg_11 * 2 30:10->30:10 13:OP:push 2 30:6->30:10 14:OP:mul 30:6->30:10 15:OP:list 1 30:2->30:2 16:OP:pushleftr 13 30:2->30:2 17:OP:list 1 30:2->30:10 18:OP:assign 31:11->31:11 19:OP:pushr 12 //用与或非实现异或 31:10->31:11 20:OP:not arg_12 ^ arg_13 31:15->31:15 21:OP:pushr 13 31:10->31:15 22:OP:and 31:21->31:21 23:OP:pushr 12 31:26->31:26 24:OP:pushr 13 31:25->31:26 25:OP:not 31:21->31:26 26:OP:and 31:9->31:27 27:OP:or 31:9->31:27 28:OP:list 1 31:2->31:2 29:OP:pushr 2 31:4->31:4 30:OP:pushr 11 31:2->31:5 31:OP:list 2 31:2->31:5 32:OP:list 1 31:2->31:27 33:OP:assign 29:20->32:1 34:OP:end-scope } 29:1->32:1 35:OP:exit-for-range -> 6 29:1->32:1 36:OP:pop 29:1->32:1 37:OP:end-scope 28:15->33:0 38:OP:end-scope 28:9->33:0 39:OP:return //func2 end //func3 start 判断函数 anonymous 38:17->48:0 0:OP:new-scope 17 39:21->41:1 1:OP:new-scope 18 39:4->39:6 2:OP:pushid len 39:8->39:8 3:OP:pushr 2 39:7->39:9 4:OP:call vlen:1 39:14->39:16 5:OP:pushid len 39:18->39:18 6:OP:pushr 1 39:17->39:19 7:OP:call vlen:1 39:4->39:19 8:OP:neq 39:21->41:1 9:OP:jmpf -> 16 39:21->41:1 10:OP:new-scope 19 40:9->40:13 11:OP:push false 40:9->40:13 12:OP:list 1 40:2->40:13 13:OP:return 39:21->41:1 14:OP:end-scope 39:21->41:1 15:OP:jmp -> 16 39:21->41:1 16:OP:end-scope 42:1->46:1 17:OP:new-scope 20 42:1->46:1 18:OP:pushleftr 16 42:18->42:18 19:OP:pushr 2 42:1->46:1 20:OP:fast-assign 42:1->46:1 21:OP:enter-for-range -> 45 42:1->46:1 22:OP:range-next 42:5->42:5 23:OP:pushleftr 17 42:8->42:8 24:OP:pushleftr 18 42:5->42:8 25:OP:list 2 42:1->46:1 26:OP:assign 42:20->46:1 27:OP:new-scope 21 43:14->45:2 28:OP:new-scope 22 43:5->43:5 29:OP:pushr 18 43:10->43:10 30:OP:pushr 1 43:12->43:12 31:OP:pushr 17 43:11->43:13 32:OP:push false 43:11->43:13 33:OP:iterablecall off:1 op1: - op2: - 43:5->43:13 34:OP:neq 43:14->45:2 35:OP:jmpf -> 42 43:14->45:2 36:OP:new-scope 23 44:10->44:14 37:OP:push false 44:10->44:14 38:OP:list 1 44:3->44:14 39:OP:return 43:14->45:2 40:OP:end-scope 43:14->45:2 41:OP:jmp -> 42 43:14->45:2 42:OP:end-scope 42:20->46:1 43:OP:end-scope 42:1->46:1 44:OP:exit-for-range -> 22 42:1->46:1 45:OP:pop 42:1->46:1 46:OP:end-scope 47:8->47:11 47:OP:push true 47:8->47:11 48:OP:list 1 47:1->47:11 49:OP:return 38:17->48:0 50:OP:end-scope 38:11->48:0 51:OP:return //func3 end
据此写出解密脚本
key = [137, 108, 159, 114, 185, 90, 174, 68, 160, 81, 179, 41, 186, 89, 168, 78, 229, 121, 149, 106, 147, 103, 156, 114, 133, 98, 146, 116, 181] for i in range(29): if i % 2 == 0: key[i] ^= 2 * i key[i] ^= 240 else: key[i] ^= 2 * i key[i] ^= 15 for each in key: print(chr(each), end='') # yak{A_RE@LW0RLD_5TACKB@SE_VM}
最后 不论是CTF还是写文章本人都处于新手阶段 如有不足之处请见谅