解题思路
打开就有的加密
easyRE1
打开ida,mainF5反编译即可找到flag
lucknum
打开ida,mainF5反编译即可找到flag
base64
```python import base64 e = "ZmxhZ3t0aGlzX2lzX2EfdGVzdH0=" d = base64.b64decode(e) print("解码结果:", d.decode('utf-8')) flag:{this_is_a_test}
ASCII加密
Reversing-x64Elf-100
1.无壳
2.拖进ida,ctrlF找main函数
__int64 __fastcall main(int a1, char **a2, char **a3)//a1参数个数a2参数字符串数组a3环境变量 { char s[264]; // [rsp+0h] [rbp-110h] BYREF栈前移信息 unsigned __int64 v5; // [rsp+108h] [rbp-8h]v5用来储存金丝雀值,栈保护 v5 = __readfsqword(0x28u);//设置栈保护 printf("Enter the password: ");//提示输入 if ( !fgets(s, 255, stdin) )//s目标缓冲区,255最大读取字符数(留1字节给字符串结束符\0),stdin标准输入流(通常是键盘输入) return 0LL; if ( (unsigned int)sub_4006FD((__int64)s) ) {//调用验证函数sub_4006FD puts("Incorrect password!"); return 1LL; } else { puts("Nice!"); return 0LL; } }分析验证函数sub_4006FD
__int64 __fastcall sub_4006FD(__int64 a1) {// 参数:a1 用户输入的字符串地址 int i; // [rsp+14h] [rbp-24h] 循环计数器i,栈偏移位置 // 声明一个数组v3,包含4个64位整数(字符串指针) __int64 v3[4]; // [rsp+18h] [rbp-20h] // 初始化v3数组:三个加密字符串 v3[0] = (__int64)"Dufhbmf"; // 字符串1 v3[1] = (__int64)"pG`imos"; // 字符串2 v3[2] = (__int64)"ewUglpt"; // 字符串3 // 循环12次(因为要验证12个字符) for ( i = 0; i <= 11; ++i ) { if ( *(char *)(v3[i % 3] + 2 * (i / 3)) - *(char *)(i + a1) != 1 ) return 1LL; // 验证失败返回1 } return 0LL; // 全部验证通过返回0 }关键验证逻辑: v3[i % 3]:选择三个字符串中的一个(0,1,2循环) 2 * (i / 3):每3个字符跳过一个,实际上是取字符位置 *(char *)(v3[i % 3] + 2 * (i / 3)):取加密字符 *(char *)(i + a1):取用户输入的第i个字符 相减必须等于1才通过
v3 = ["Dufhbmf", "pG`imos", "ewUglpt"] password = "" for i in range(12): s = i % 3 c = 2 * (i // 3) e = v3[s][c] password += chr(ord(e) - 1) print(f"密码: {password}") # 输出: Code_Talkers三个加密字符串: v3[0] = "Dufhbmf" v3[1] = "pG`imos" v3[2] = "ewUglpt"
取字符时用的是 2 * (i / 3) 所以实际上是从每个字符串中取第0、2、4、6...位置的字符
字符提取规律 循环i从0到11: i=0: v3[0] + 2(0/3)= v3[0] + 0 :"D" (第0个字符) i=1: v3[1] + 2(0)= v3[1] + 0 : "p" (第0个字符) i=2: v3[2] + 2(0)= v3[2] + 0 : "e" (第0个字符) i=3: v3[0] + 2(1)= v3[0] + 2 :"f" (第2个字符,跳过h) i=4: v3[1] + 2(1)= v3[1] + 2 :"`" (第2个字符,跳过G) i=5: v3[2] + 2(1)= v3[2] + 2 :"U" (第2个字符,跳过w)
提取出的加密字符序列 从三个字符串中每隔一个字符取一个:
字符串1 "Dufhbmf": 取 D(0), f(2), b(4), f(6)但i最大到11,只取前4个 字符串2 "pG`imos": 取 p(0), '(2), m(4), s(6)只取前4个 字符串3 "ewUglpt": 取 e(0), U(2), l(4), t(6)只取前4个
排列顺序(按i循环): i=0: D (来自v3[0]) i=1: p (来自v3[1]) i=2: e (来自v3[2]) i=3: f (来自v3[0]) i=4: ` (来自v3[1]) i=5: U (来自v3[2]) i=6: b (来自v3[0]) i=7: m (来自v3[1]) i=8: l (来自v3[2]) i=9: f (来自v3[0]) i=10: s (来自v3[1]) i=11: t (来自v3[2])
验证公式
加密字符 - 用户输入字符 = 1 即:用户输入字符 = 加密字符 - 1
加密字符序列(ASCII值):
D(68), p(112), e(101), f(102), `(96), U(85), b(98), m(109), l(108), f(102), s(115), t(116) 计算用户输入(每个减1):
D-1 = C(67) 'C' p-1 = o(111) 'o' e-1 = d(100) 'd' f-1 = e(101) 'e' `-1 = _(95) _ (下划线) U-1 = T(84) 'T' b-1 = a(97) 'a' m-1 = l(108) 'l' l-1 = k(107) 'k' f-1 = e(101) 'e' s-1 = r(114) 'r' t-1 = s(115) 's' 最终密码: Code_Talkers
666
1.无壳
2.拖进IDA看main
// argc命令行参数个数,argv命令行参数字符串数组,envp环境变量数组 int __cdecl main(int argc, const char **argv, const char **envp) { // 定义两个字符数组(字符串缓冲区) char s[240]; // [rsp+0h] [rbp-1E0h] BYREF //用于存储用户输入的密钥 char v5[240]; // [rsp+F0h] [rbp-F0h] BYREF栈偏移:从rbp向下偏移0xF0(240)字节 //内存设置函数 memset(s, 0, 0x1EuLL); printf("Please Input Key: "); // 从标准输入读取用户输入的字符串到v5数组 __isoc99_scanf("%s", v5); // 调用encode函数对用户输入进行编码 encode(v5, s); //比较用户输入的长度是否等于预设的key值 if ( strlen(v5) == key ) { //比较编码后的结果是否等于预设的enflag if ( !strcmp(s, enflag) ) // 两个条件都满足:长度正确且编码后匹配,输出正确提示 puts("You are Right"); else puts("flag{This_1s_f4cker_flag}"); } return 0; }查看encode
//a1输入字符串(用户输入的密钥),a2输出缓冲区地址(编码后结果的存储位置) int __fastcall encode(const char *a1, __int64 a2) { // 定义一个局部数组v3 char v3[104]; // [rsp+10h] [rbp-70h]栈偏移信息 int v4; // [rsp+78h] [rbp-8h] int i; // [rsp+7Ch] [rbp-4h] i = 0; v4 = 0; // 检查输入字符串长度是否等于预设的key值 if ( strlen(a1) != key ) return puts("Your Length is Wrong"); // 长度错误,直接返回 //开始编码循环,每次处理3个字符(i每次增加3) for ( i = 0; i < key; i += 3 ) { // 第1个字符的编码(a1[i] + 6) 再与 key 异或,存储到v3[i + 64]位置(注意偏移64) v3[i + 64] = key ^ (a1[i] + 6); // 第2个字符的编码(a1[i+1] - 6) 再与 key 异或 v3[i + 33] = (a1[i + 1] - 6) ^ key; // 第3个字符的编码a1[i+2] 先与6异或,再与key异或 v3[i + 2] = a1[i + 2] ^ 6 ^ key; // 将v3[i+64]复制到a2的第i个位置 *(_BYTE *)(a2 + i) = v3[i + 64]; // 将v3[i+33]复制到a2的第i+1个位置 *(_BYTE *)(a2 + i + 1LL) = v3[i + 33]; // 将v3[i+2]复制到a2的第i+2个位置 *(_BYTE *)(a2 + i + 2LL) = v3[i + 2]; } // 函数返回a2(输出缓冲区的地址) return a2; }解密脚本:
enflag = r'izwhroz""w"v.K".Ni' key = 18 flag = "" counter = 1 for i in range(0,18,3): ascii_1 = ord(enflag[i]) ascii_2 = ord(enflag[i+1]) ascii_3 = ord(enflag[i+2]) flag += chr((ascii_1 ^ key) - 6) flag += chr((ascii_2 ^ key) + 6) flag += chr(ascii_3 ^ key ^ 6) print(flag)
定义3个字符串变量,v3[32]、v4[32]、v5[40],整型变量v6和i。
对接收的变量a1的长度进行判断
循环体代码块:
对key和a1[i]+6进行按位异或的值赋值给v5[i]
对a1[i+1]-6和key进行按位异或的值赋值给v4[i+1]
对a1[i+2]先对6进行异或再对key进行异或的值赋值给v3[i+2]
将v5[i]的值赋值给a2+i
将v4[i+1]的值赋值给a2+i+1
将v3[i+2]的值赋值给a2+i+2
循环体内代码块的单次循环算法解析:
1.a2每次循环填充3位
2.a2的第一位填充的值为v5[i]
3.a2的第二位填充的值为v4[i+1]
4.a2的第三位填充的值为v3[i+2]
6的二进制数:110
返回a2,key的值为0x12(18) 二进制10010,共循环了6次,即flag应该为18位字符串
enflag的值为izwhroz"“w"v.K”.Ni
最终被异或算法处理之后的flag为izwhroz"“w"v.K”.Ni通过逆向算法对字符串"izwhroz"“w"v.K”.Ni"进行逆向处理
编写算法
将字符串转换为ASCII码,将ASCII码转换为二进制数,按每三个字符为一组进行逆向异或操作,将二进制数值转换为ASCII码,将ASCII码转换为字符,找出FLAG
需要对异或运算符的计算使用小括号提高优先级,否则无法得到正确的答案
RC4
crypt
1.无壳,IDA找main
2.分析main
int __cdecl main(int argc, const char **argv, const char **envp) { unsigned int v3; // eax unsigned int v4; // eax __int64 v5; // rax __int64 v7; // // 循环计数器 int i; // [rsp+24h] [rbp-D4h] // 动态分配的内存指针 void *v9; // [rsp+28h] [rbp-D0h] // 存放用户输入的字符串 char v10[32]; // [rsp+30h] [rbp-C8h] BYREF // 固定字符串(密钥或初始数据) char Str[32]; // [rsp+50h] [rbp-A8h] BYREF // 临时缓冲区(96字节) char v12[96]; // [rsp+70h] [rbp-88h] BYREF // 初始化固定字符串(实际上"12345678abcdefghijklmnopqrspxyz"有31个字符+1个\0=32,刚好填满) // 这是一个看起来随机的字符串,可能是密钥或初始向量 strcpy(Str, "12345678abcdefghijklmnopqrspxyz"); // 清空v12缓冲区(96字节全设为0) memset(v12, 0, sizeof(v12)); // 清空v10缓冲区的前23字节(0x17=23) // 注意:v10是32字节,但只清空前23个,可能因为输入最多22个字符+1个\0 memset(v10, 0, 0x17ui64); // 读取用户输入到v10 // sub_1400054D0很可能是scanf或类似的输入函数 // "%s"表示读取字符串 sub_1400054D0("%s", v10); // 动态分配内存:分配0x408=1032字节 v9 = malloc(0x408ui64); // 获取Str字符串的长度 v3 = strlen(Str); // 调用第一个处理函数:可能是初始化函数 // 参数:v9(分配的内存), Str(固定字符串), v3(Str的长度) sub_140001120(v9, Str, v3); // 获取用户输入的长度 v4 = strlen(v10); // 调用第二个处理函数:可能是加密/处理函数 // 参数:v9(之前初始化的内存), v10(用户输入), v4(输入长度) sub_140001240(v9, v10, v4); // 核心验证循环:检查22个字符(注意:不是检查整个输入,只检查前22个) for ( i = 0; i < 22; ++i ) { // 验证逻辑: // 1. v10[i] ^ 0x22:对输入字符与0x22(34)进行异或 // 2. byte_14013B000[i]:从内存地址获取预设的字节 // 3. 比较两者是否相等 if ( ((unsigned __int8)v10[i] ^ 0x22) != (unsigned __int8)byte_14013B000[i] ) { // 验证失败:输出"error" v5 = sub_1400015A0(&off_14013B020, "error"); // 调用对象的成员函数(可能是输出函数) _CallMemberFunction0(v5, sub_140001F10); return 0; // 程序退出 } } // 验证成功:输出"nice job" v7 = sub_1400015A0(&off_14013B020, "nice job"); _CallMemberFunction0(v7, sub_140001F10); return 0; // 程序退出 }3.跟进1120函数
__int64 __fastcall sub_140001120(_DWORD *a1, __int64 a2, int a3) { __int64 result; // rax int i; // [rsp+0h] [rbp-28h] int j; // [rsp+0h] [rbp-28h] int v6; // [rsp+4h] [rbp-24h] int v7; // [rsp+8h] [rbp-20h] int v8; // [rsp+Ch] [rbp-1Ch] _DWORD *v9; // [rsp+10h] [rbp-18h] *a1 = 0; a1[1] = 0; v9 = a1 + 2; for ( i = 0; i < 256; ++i ) v9[i] = i; v6 = 0; result = 0i64; LOBYTE(v7) = 0; for ( j = 0; j < 256; ++j ) { v8 = v9[j]; v7 = (unsigned __int8)(*(_BYTE *)(a2 + v6) + v8 + v7); v9[j] = v9[v7]; v9[v7] = v8; if ( ++v6 >= a3 ) v6 = 0; result = (unsigned int)(j + 1); } return result; }发现256,是RC4-KSA加密
解密脚本
def rc4_decrypt(ciphertext: bytes, key: bytes) -> bytes: # === Step 1: KSA (Key Scheduling Algorithm) === S = list(range(256)) j = 0 for i in range(256): j = (j + S[i] + key[i % len(key)]) & 0xFF S[i], S[j] = S[j], S[i] # === Step 2: PRGA (Pseudo-Random Generation Algorithm) === i = j = 0 plaintext = bytearray() for byte in ciphertext: i = (i + 1) & 0xFF j = (j + S[i]) & 0xFF S[i], S[j] = S[j], S[i] k = S[(S[i] + S[j]) & 0xFF] plaintext.append(byte ^ k) return bytes(plaintext) if __name__ == "__main__": key = b"your_secret_key_here" # 读取密文(可以是文件、硬编码、网络数据等) with open("encrypted.bin", "rb") as f: ciphertext = f.read() # 解密 plaintext = rc4_decrypt(ciphertext, key) # 输出结果 try: print("Decrypted text:", plaintext.decode('utf-8')) except UnicodeDecodeError: print("Decrypted data (hex):", plaintext.hex()) # 或写入文件 with open("decrypted_output.bin", "wb") as f: f.write(plaintext)异或
xxxorrr
1.无壳找main
// a1: 命令行参数个数(argc) // a2: 指向命令行参数字符串数组的指针(argv) // a3: 环境变量指针 __int64 __fastcall main(int a1, char **a2, char **a3) { // 循环变量 i,用于后面遍历输入字符串 int i; // [rsp+Ch] [rbp-34h] // 定义一个长度为 40 的字符数组 s,用于从用户输入读取数据 char s[40]; // [rsp+10h] [rbp-30h] BYREF // 用于存储栈保护 unsigned __int64 v6; // [rsp+38h] [rbp-8h] // 从线程局部存储(TLS)中读取栈保护 v6 = __readfsqword(0x28u); // 调用某个初始化函数(可能是设置缓冲区、初始化全局变量等) // sub_A90 和 sub_916 是 IDA 自动生成的函数名,实际功能需进一步分析 // 这里很可能是调用了 __libc_start_main 的一部分或自定义初始化 sub_A90(sub_916, a2, a3); // 从标准输入(键盘)读取最多 34 个字符(+1 个 '\0' 结尾),存入数组 s fgets(s, 35, stdin); // 核心逻辑:将全局数组 s1 的前 34 个字节,与用户输入的 s 逐字节异或(XOR) for ( i = 0; i <= 33; ++i ) s1[i] ^= s[i]; // 相当于:s1[i] = s1[i] XOR s[i] return 0LL; }程序通过 s1[i] ^= s[i] 将用户输入 s 作为密钥,对 s1 进行异或解密
s1 全局数组里初始存储的是一段密文(34字节),程序用输入的34个字符作为密钥
2.寻址s1s2
.data:0000000000201020 s1 db 'qasxcytgsasxcvrefghnrfghnjedfgbhn',0 .data:0000000000201060 s2 db 'VNWXQQ',9,'F' ; DATA XREF: sub_916+17↑o
s2字段前面的部分隐藏了,选择s2,换到Hex的十六进制窗口查看,得到一串数组
验证s1有无其它加密
打开s1后 Ctrl+x 发现sub_84A也使用了s1
unsigned __int64 sub_84A() { int i; // [rsp+Ch] [rbp-14h] unsigned __int64 v2; // [rsp+18h] [rbp-8h] v2 = __readfsqword(0x28u); for ( i = 0; i <= 33; ++i ) s1[i] ^= 2 * i + 65; return __readfsqword(0x28u) ^ v2; }解密脚本:
# 验证 s1_initial = b"qasxcytgsasxcvrefghnrfghnjedfgbhn" flag = b"flag{c0n5truct0r5_functi0n_in_41f}" s2_calculated = b"" for i in range(34): key = 2 * i + 65 s1_before_main = s1_initial[i] ^ key s2_byte = s1_before_main ^ flag[i] s2_calculated += bytes([s2_byte]) print("Calculated s2:", s2_calculated.hex()) print("Actual s2:", s2_bytes.hex()) # 应该匹配re50
1.main
int __fastcall __noreturn main(int argc, const char **argv, const char **envp) { int n31; // [sp+18h] [+18h] for ( n31 = 0; n31 < 31; ++n31 ) { if ( meow[n31] != (char)(argv[1][n31] ^ 0x37) )// "cbtcqLUBChERV[[Nh@_X^D]X_YPV[CJ" { print("NOOOOOOOOOOOOOOOOOO\n"); exit_funct(); } } puts("C0ngr4ssulations!! U did it.", argv, envp); exit_funct(); }`程序接受一个命令行参数(
argv[1])遍历这个参数的前31个字符
对每个字符与
0x37(十进制55)进行异或结果必须等于
meow数组中的对应字符
解密脚本
meow = "cbtcqLUBChERV[[Nh@_X^D]X_YPV[CJ" flag = "" for i in range(31): # 解密:flag_char = meow_char ^ 0x37 flag_char = chr(ord(meow[i]) ^ 0x37) flag += flag_char print("Flag:", flag)迷宫思路
找到地图
找到限制字符(也就是方向键)
分析路径
得到路径
reverse_re3
1.打开ida,找到main,发现调用sub940,跟进
__int64 sub_940() { int v0; // eax int v2; // [rsp+8h] [rbp-218h] int v3; // [rsp+Ch] [rbp-214h] char v4[520]; // [rsp+10h] [rbp-210h] BYREF unsigned __int64 v5; // [rsp+218h] [rbp-8h] v5 = __readfsqword(0x28u); v3 = 0; memset(v4, 0, 0x200uLL); _isoc99_scanf(&unk_1278, v4, v4);//先对字符串变量v4进行了初始化操作,大小为0x200,而后调用函数_isoc99_scanf()接收用户输入,之后开始执行循环操作 while ( 1 ) { do { v2 = 0; sub_86C();//每次循环的开始前都会执行函数sub_86C(), v0 = v4[v3]; if ( v0 == 100 ) { v2 = sub_E23(); } else if ( v0 > 100 ) { if ( v0 == 115 ) { v2 = sub_C5A(); } else if ( v0 == 119 ) { v2 = sub_A92(); } } else { if ( v0 == 27 ) return 0xFFFFFFFFLL; if ( v0 == 97 ) v2 = sub_FEC(); } ++v3; } while ( v2 != 1 ); if ( dword_202AB0 == 2 ) break; ++dword_202AB0; } puts("success! the flag is flag{md5(your input)}"); return 1LL; }跟进函数sub_86C()
unsigned __int64 sub_86C() { int i; // [rsp+0h] [rbp-10h] int j; // [rsp+4h] [rbp-Ch] unsigned __int64 v3; // [rsp+8h] [rbp-8h] v3 = __readfsqword(0x28u); for ( i = 0; i <= 14; ++i ) { for ( j = 0; j <= 14; ++j ) { if ( dword_202020[225 * dword_202AB0 + 15 * i + j] == 3 ) { dword_202AB4 = i; dword_202AB8 = j; break; } } } return __readfsqword(0x28u) ^ v3; }其主体是一个两层循环搜索的过程,而且循环的边界值都是<=14,当数组dword_202AB0[]中的某个值==3时循环结束,并将i,j的值分别赋给全局变量dword_202AB4,dword_202AB8 查看数组dword_202AB0[]的内容,发现是一串由0,1,3,4构成的数组,数组长度为675(15 * 15 * 3),再结合循环的边界值正好为0-14,将数组以15列一行的形式对齐可以看到,这是一个迷宫问题,在结合代码中循环结束条件是dword_202AB0 == 2,要连续解出三个迷宫才能得到flag,回到代码,记录走出迷宫路径的wasd,md5解密后可得flag
easy_Maze
1.无壳ida分析main
// main函数:程序入口点 int __cdecl main(int argc, const char **argv, const char **envp) { __int64 v3; // rax - 临时变量,用于C++输出流的返回值 // v5数组:49个整数(7x7的矩阵),用于存储最终结果或中间状态 int v5[49]; // [rsp+0h] [rbp-270h] BYREF // v6数组:104个整数的大数组,但实际用作7x7的迷宫(49个元素)+ 其他用途 int v6[104]; // [rsp+D0h] [rbp-1A0h] BYREF // 7x7迷宫 // 第1行(共7行,每行7个元素) v6[52] = 1; // (0,0) v6[53] = 1; // (0,1) v6[54] = -1; // (0,2) v6[55] = 1; // (0,3) v6[56] = -1; // (0,4) v6[57] = 1; // (0,5) v6[58] = -1; // (0,6) // 第2行 v6[59] = 0; // (1,0) v6[60] = 0; // (1,1) v6[61] = 0; // (1,2) v6[62] = 0; // (1,3) v6[63] = 1; // (1,4) v6[64] = -1; // (1,5) v6[65] = 0; // (1,6) // 第3行 v6[66] = 0; // (2,0) v6[67] = 1; // (2,1) v6[68] = 0; // (2,2) v6[69] = 0; // (2,3) v6[70] = 1; // (2,4) v6[71] = 0; // (2,5) v6[72] = -1; // (2,6) // 第4行 v6[73] = -1; // (3,0) v6[74] = 0; // (3,1) v6[75] = 1; // (3,2) v6[76] = 0; // (3,3) v6[77] = 1; // (3,4) v6[78] = -1; // (3,5) v6[79] = 0; // (3,6) // 第5行 v6[80] = -1; // (4,0) memset(&v6[81], 0, 20); // (4,1)到(4,5) = 20字节=5个int=0(墙) // 计算:v6[81], v6[82], v6[83], v6[84], v6[85] = 0 v6[86] = 1; // (4,6) // 第6行 v6[87] = -1; // (5,0) v6[88] = -1; // (5,1) v6[89] = 1; // (5,2) v6[90] = -1; // (5,3) v6[91] = 0; // (5,4) v6[92] = -1; // (5,5) v6[93] = 2; // (5,6) // 第7行 v6[94] = 1; // (6,0) v6[95] = -1; // (6,1) v6[96] = 0; // (6,2) v6[97] = 0; // (6,3) v6[98] = -1; // (6,4) v6[99] = 1; // (6,5) v6[100] = 0; // (6,6) // 数组初始化 // 清空v6的前196字节(49个int) // 这可能是初始化其他区域为0 memset(v6, 0, 196); // 清空v5数组(49个int=196字节) memset(v5, 0, sizeof(v5)); // 迷宫求解步骤 // Step_0: 处理迷宫数据(可能准备迷宫) // 参数:&v6[52] - 迷宫数据起始地址 // 7 - 迷宫大小(7x7) // v6 - 输出/中间结果存储位置(v6的前49个元素) Step_0((int (*)[7])&v6[52], 7, (int (*)[7])v6); // Step_1: 进一步的迷宫处理(可能是路径搜索) // 参数:v6 - 上一步的结果 // 7 - 迷宫大小 // v5 - 输出存储位置 Step_1((int (*)[7])v6, 7, (int (*)[7])v5); // 输出提示信息 // std::operator<< 是C++的输出流操作符 v3 = std::operator<<<std::char_traits<char>>(&_bss_start, "Please help me out!"); std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>); // Step_2: 可能是最终验证或结果输出 // 参数:v5 - 上一步的结果 Step_2((int (*)[7])v5); // 暂停程序,等待用户按键 system("pause"); return 0; }Step_0:将初始矩阵 M(位于 &buf[52])变换到 buf 的前 49 个元素中。
Step_1:将 buf 中的矩阵变换到 buf__1中
Step_2:处理 buf__1 中的矩阵
Step_0:矩阵旋转
for ( n7_1 = 0; n7_1 < n7; ++n7_1 ) for ( n7_2 = 0; n7_2 < n7; ++n7_2 ) (*buf)[7 * n7_1 + n7_2] = (*a1)[7 * n7_2 - 1 + n7 - n7_1];
这个变换相当于将输入矩阵逆时针旋转90度。即:M1i = Mj,其中n=7
Step_1:矩阵变换
APA7_ii = getA(buf, n7); // 获取一个值(可能是行列式) getAStart(buf, n7, (int (*)[7])v5); // 获取另一个矩阵到v5 // 然后将v5的每个元素除以APA7_ii (*a3)[7 * n7_2 + n7_1] = v5[7 * n7_2 + n7_1] / APA7_ii;
Step_2:迷宫行走
while ( n29 <= 29 && (*buf)[7 * n6 + n6_1] == 1 ) { // 输入wasd控制方向 if ( p_n119 == 100 ) ++n6_1; // 'd' 右 else if ( p_n119 == 115 ) ++n6; // 's' 下 else if ( p_n119 == 119 ) --n6; // 'w' 上 else if ( p_n119 == 97 ) --n6_1; // 'a' 左 } // 最终检查是否到达(6,6) if ( n6 == 6 && n6_1 == 6 ) { output(v7, n29); // 输出flag }getA: 递归计算矩阵的行列式(determinant)
getAStart: 计算矩阵的伴随矩阵(adjugate matrix)
Step_1: 计算逆矩阵 = 伴随矩阵 / 行列式
考虑动态调试。Ubuntu:./linux_server找ida-debugger调试,输入(端口不变)F9远程调试,去找V3的值(local)
{1,0,0,1,1,1,1,1,0,1,1,0,0,1,1,1,1,0,1,1,1,0,0,0,1,1,0,0,1,1,1,1,0,0,0,1,0,0,0,1,1,1,1,1,1,1,1,0,1,0,0,0}BFS
from collections import deque maze = [ [1,0,0,1,1,1,1], [1,0,1,1,0,0,1], [1,1,1,0,1,1,1], [0,0,0,1,1,0,0], [1,1,1,1,0,0,0], [1,0,0,0,1,1,1], [1,1,1,1,1,0,1] ] def bfs_path(maze, start=(0,0), end=(6,6)): n = 7 if maze[start[0]][start[1]] != 1 or maze[end[0]][end[1]] != 1: return None queue = deque([(start[0], start[1], "")]) visited = {(start[0], start[1])} directions = {'w':(-1,0), 's':(1,0), 'a':(0,-1), 'd':(0,1)} while queue: x, y, path = queue.popleft() if x == end[0] and y == end[1]: return path for move, (dx, dy) in directions.items(): nx, ny = x + dx, y + dy if 0 <= nx < n and 0 <= ny < n and maze[nx][ny] == 1 and (nx, ny) not in visited: visited.add((nx, ny)) queue.append((nx, ny, path + move)) return None path = bfs_path(maze) print("最短路径:", path) print("路径长度:", len(path) if path else "无路径")Ubuntu中输入,得到flag
maze
1.main
__int64 __fastcall main(int a1, char **a2, char **a3) { __int64 n5; // rbx int n79; // eax char v5; // bp char v6; // al const char *Wrong_flag; // rdi unsigned int v9; // [rsp+0h] [rbp-28h] BYREF _DWORD v10[9]; // [rsp+4h] [rbp-24h] BYREF v10[0] = 0; v9 = 0; puts("Input flag:"); scanf("%s", &s1); if ( strlen(&s1) != 24 || strncmp(&s1, "nctf{", 5u) || algn_6010C1[22] != 125 ) { LABEL_22: puts("Wrong flag!"); exit(-1); } n5 = 5; if ( strlen(&s1) - 1 > 5 ) { while ( 1 ) { n79 = *(&s1 + n5); v5 = 0; if ( n79 > 78 ) { if ( (unsigned __int8)n79 == 79 ) { v6 = sub_400650(v10); goto LABEL_14; } if ( (unsigned __int8)n79 == 111 ) { v6 = sub_400660(v10); goto LABEL_14; } } else { if ( (unsigned __int8)n79 == 46 ) { v6 = sub_400670(&v9); goto LABEL_14; } if ( (unsigned __int8)n79 == 48 ) { v6 = sub_400680(&v9); LABEL_14: v5 = v6; } } if ( !(unsigned __int8)sub_400690(asc_601060, v10[0], v9) )// " ******* * **** * **** * *** *# *** *** *** *********" goto LABEL_22; if ( ++n5 >= strlen(&s1) - 1 ) { if ( v5 ) break; LABEL_20: Wrong_flag = "Wrong flag!"; goto LABEL_21; } } } if ( asc_601060[8 * v9 + v10[0]] != 35 ) // " ******* * **** * **** * *** *# *** *** *** *********" goto LABEL_20; Wrong_flag = "Congratulations!"; LABEL_21: puts(Wrong_flag); return 0; }'O' (79): 向左 (x--)
'o'`(111): 向右(x++)
'.'`(46): 向上 (y--)
'0' (48): 向下 (y++)
内存获取迷宫,终点是#
.data:0000000000601060 00000041 C ******* * **** * **** * *** *# *** *** *** *********
解密脚本
maze = [0,0,1,1,1,1,1,1,1,0,0,0,1,0,0,1,1,1,1,0,1,0,1,1,1,1,0,0,1,0,1,1,1,0,0,1,2,0,0,1,1,1,0,1,1,1,0,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1] # 检查位置是否有效。。。这里里面是,1是墙,0是路,2是出口 def check(map, x, y): if 0 <= x < 8 and 0 <= y < 8: # 这里初始化迷宫 pos = x * 8 + y return map[pos] != 1 # 只要不是墙就可以走 return False # 生成下一步可能的移动方向 def gen_nex(map, x, y): all_dir = [] # 注意方向定义:a=左, d=右, w=上, s=下 if check(map, x, y - 1): # 左 all_dir.append((x, y - 1, 'O')) if check(map, x, y + 1): # 右 all_dir.append((x, y + 1, 'o')) if check(map, x - 1, y): # 上 all_dir.append((x - 1, y, '.')) if check(map, x + 1, y): # 下 all_dir.append((x + 1, y, '0')) return all_dir # 检查是否到达终点 def check_success(map, x, y): pos = x * 8 + y return map[pos] == 2 # DFS搜索路径 def dfs(map, x, y, path, visited=None): if visited is None: visited = set() # 当前位置标记 pos = x * 8 + y visited.add((x, y)) # 检查是否到达终点 if check_success(map, x, y): return path # 获取所有可能的方向 next_points = gen_nex(map, x, y) #先获得这一个位置的上下左右的四个方向的可能性 for nx, ny, direction in next_points: #对元组的分解 if (nx, ny) not in visited: #查找这个点位是否被找过 # 递归搜索 #如果没有被找过就进去递归 new_path = dfs(map, nx, ny, path + direction, visited.copy()) #递归 if new_path is not None: return new_path return None # 当前路径无解 # 从起点(0,1)开始搜索 start_x, start_y = 0, 0 #这是起始位置 path = dfs(maze, start_x, start_y, "") print("找到的路径:", path)