【攻防世界】reverse | xxxorrr 详细题解 WP
下载附件
看这道题1积分有很多人解出来,以为是简单的异或题目,还有一个隐藏sub_84A函数
需要通过s1变量来定位到隐藏sub_84A函数
main函数伪代码:
__int64 __fastcallmain(inta1,char**a2,char**a3){inti;// [rsp+Ch] [rbp-34h]chars[40];// [rsp+10h] [rbp-30h] BYREFunsigned__int64 v6;// [rsp+38h] [rbp-8h]v6=__readfsqword(0x28u);sub_A90((void(__fastcall*)(void*))sub_916);fgets(s,35,stdin);for(i=0;i<=33;++i)s1[i]^=s[i];return0LL;}sub_916函数伪代码:
unsigned__int64sub_916(){unsigned__int64 v1;// [rsp+8h] [rbp-8h]v1=__readfsqword(0x28u);if(!strcmp(s1,s2))puts("Congratulations!");elseputs("Wrong!");return__readfsqword(0x28u)^v1;}sub_84A函数伪代码:
unsigned__int64sub_84A(){inti;// [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;}exp:
defcrack_flag():# 1. 原始s1数组(34字节,i=0~33)s1_original=[0x71,0x61,0x73,0x78,0x63,0x79,0x74,0x67,0x73,0x61,0x73,0x78,0x63,0x76,0x72,0x65,0x66,0x67,0x68,0x6E,0x72,0x66,0x67,0x68,0x6E,0x6A,0x65,0x64,0x66,0x67,0x62,0x68,0x6E,0x00]# 2. 目标s2数组(34字节,i=0~33)s2_target=[0x56,0x4E,0x57,0x58,0x51,0x51,0x09,0x46,0x17,0x46,0x54,0x5A,0x59,0x59,0x1F,0x48,0x32,0x5B,0x6B,0x7C,0x75,0x6E,0x7E,0x6E,0x2F,0x77,0x4F,0x7A,0x71,0x43,0x2B,0x26,0x89,0xFE]# 3. 计算输入s_inputs_input_bytes=[]s_input_chars=[]foriinrange(34):# 动态密钥:2*i + 65(0x41 + 2*i)key=2*i+65# 核心公式:s_input = s1_original ^ key ^ s2_targetinput_byte=s1_original[i]^key^s2_target[i]s_input_bytes.append(input_byte)# 转换为字符(可打印字符直接显示,不可打印用\xXX表示)if32<=input_byte<=126:input_char=chr(input_byte)else:input_char=f"\\x{input_byte:02x}"s_input_chars.append(input_char)# 4. 验证结果正确性verify=[]foriinrange(34):key=2*i+65# 模拟程序处理流程:s1_original ^ key ^ s_input → 应等于s2_targetprocessed=s1_original[i]^key^s_input_bytes[i]verify.append(processed)is_valid=verify==s2_target# 5. 输出结果print("=== 解题结果 ===")print(f"1. 输入字节(十六进制):{' '.join(f'{b:02x}'forbins_input_bytes)}")print(f"2. 输入字符:{''.join(s_input_chars)}")print(f"3. 验证结果:{'通过'ifis_validelse'失败'}")print(f"4. Flag格式:{''.join(s_input_chars)}")returns_input_bytes,s_input_charsif__name__=="__main__":crack_flag()运行 exp 脚本:
flag{c0n5truct0r5_functi0n_in_41f}【攻防世界】reverse | xxxorrr 详细题解 WP 原来深度解析:
CTF 逆向实战:xxxorrr 深度解析与异或类题目通用解题框架
在 CTF 逆向题目中,异或(XOR)运算因其可逆性和简单性被广泛应用,是入门级到进阶级题目中常见的加密手段。本文以攻防世界 reverse 板块的 xxxorrr 题目为例,从程序逻辑拆解、关键数据提取、数学原理分析到代码实现,全方位展示异或类题目的解题思路,并总结通用解题框架,帮助读者实现 “举一反三”。
一、题目初探:程序执行流程全景扫描
1.1 逆向工程初步成果
通过 IDA 或 Ghidra 对二进制文件进行反编译,我们得到三个关键函数的伪代码,这三个函数构成了程序的核心逻辑链:
main 函数:输入处理核心
__int64 __fastcallmain(inta1,char**a2,char**a3){inti;// [rsp+Ch] [rbp-34h]chars[40];// [rsp+10h] [rbp-30h] BYREFunsigned__int64 v6;// [rsp+38h] [rbp-8h]v6=__readfsqword(0x28u);sub_A90((void(__fastcall*)(void*))sub_916);// 注册退出回调fgets(s,35,stdin);// 读取输入(最多34字节)for(i=0;i<=33;++i)s1[i]^=s[i];// 输入与s1异或return0LL;}sub_916 函数:验证逻辑
unsigned__int64sub_916(){unsigned__int64 v1;// [rsp+8h] [rbp-8h]v1=__readfsqword(0x28u);if(!strcmp(s1,s2))// 比较处理后的s1与目标s2puts("Congratulations!");elseputs("Wrong!");return__readfsqword(0x28u)^v1;}sub_84A 函数:隐藏的异或处理
unsigned__int64sub_84A(){inti;// [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;// s1与动态密钥异或return__readfsqword(0x28u)^v2;}1.2 执行流程梳理
通过函数调用关系和程序运行机制分析,完整执行链路如下:
- 程序启动后,
sub_A90函数通过__cxa_atexit机制将sub_916注册为退出回调(程序正常结束时自动执行) sub_84A函数在初始化阶段对原始s1数组进行处理(关键隐藏步骤)main函数读取用户输入s,并与处理后的s1执行异或操作- 程序退出时,
sub_916函数比较最终的s1与目标s2,输出验证结果
二、关键数据提取:从二进制到数组
逆向分析的核心步骤之一是精准提取关键数据。通过查看程序.data段(数据段),我们可以获取s1和s2的原始值:
2.1 原始数组s1(地址 0x201020)
在反编译工具的内存窗口中,s1被定义为:
.data:0000000000201020 s1 db 'qasxcytgsasxcvrefghnrfghnjedfgbhn',0转换为十六进制字节数组(共 34 字节,含终止符):
s1_original=[0x71,0x61,0x73,0x78,0x63,0x79,0x74,0x67,0x73,0x61,0x73,0x78,0x63,0x76,0x72,0x65,0x66,0x67,0x68,0x6E,0x72,0x66,0x67,0x68,0x6E,0x6A,0x65,0x64,0x66,0x67,0x62,0x68,0x6E,0x00# 终止符]2.2 目标数组s2(地址 0x201060)
通过内存 Dump 提取s2的 34 字节数据:
s2_target=[0x56,0x4E,0x57,0x58,0x51,0x51,0x09,0x46,0x17,0x46,0x54,0x5A,0x59,0x59,0x1F,0x48,0x32,0x5B,0x6B,0x7C,0x75,0x6E,0x7E,0x6E,0x2F,0x77,0x4F,0x7A,0x71,0x43,0x2B,0x26,0x89,0xFE]三、数学原理:异或运算的可逆性
异或运算(XOR)是本题破解的核心,其数学性质决定了逆向的可行性:
3.1 异或的基本性质
- 交换律:
a ^ b = b ^ a - 结合律:
(a ^ b) ^ c = a ^ (b ^ c) - 自反性:
a ^ a = 0 - 恒等性:
a ^ 0 = a
3.2 本题运算链推导
根据程序逻辑,s1经历两次异或处理后需等于s2:
- 初始处理:
s1_after_84A[i] = s1_original[i] ^ (2*i + 65)(sub_84A 函数) - 输入处理:
s1_final[i] = s1_after_84A[i] ^ s_input[i](main 函数) - 验证条件:
s1_final[i] = s2_target[i](sub_916 函数)
联立得:
s1_original[i] ^ (2*i + 65) ^ s_input[i] = s2_target[i]根据异或性质移项,推导输入的计算公式:
s_input[i] = s1_original[i] ^ (2*i + 65) ^ s2_target[i]四、解题实现:Python 代码还原与验证
4.1 核心代码实现
defcrack_xxxorrr():# 1. 原始数据定义s1_original=[0x71,0x61,0x73,0x78,0x63,0x79,0x74,0x67,0x73,0x61,0x73,0x78,0x63,0x76,0x72,0x65,0x66,0x67,0x68,0x6E,0x72,0x66,0x67,0x68,0x6E,0x6A,0x65,0x64,0x66,0x67,0x62,0x68,0x6E,0x00]s2_target=[0x56,0x4E,0x57,0x58,0x51,0x51,0x09,0x46,0x17,0x46,0x54,0x5A,0x59,0x59,0x1F,0x48,0x32,0x5B,0x6B,0x7C,0x75,0x6E,0x7E,0x6E,0x2F,0x77,0x4F,0x7A,0x71,0x43,0x2B,0x26,0x89,0xFE]# 2. 计算输入s_input=[]foriinrange(34):key=2*i+65# 动态密钥input_byte=s1_original[i]^key^s2_target[i]s_input.append(input_byte)# 3. 验证结果verify=[]foriinrange(34):key=2*i+65processed=s1_original[i]^key^s_input[i]verify.append(processed)# 4. 转换为字符形式flag_chars=[]forbyteins_input:if32<=byte<=126:# 可打印字符flag_chars.append(chr(byte))else:# 不可打印字符用\xXX表示flag_chars.append(f"\\x{byte:02x}")# 5. 输出结果print(f"Flag: flag{{{''.join(flag_chars)}}}")print(f"验证结果:{'成功'ifverify==s2_targetelse'失败'}")if__name__=="__main__":crack_xxxorrr()4.2 执行结果与验证
运行代码后得到:
Flag: flag{c0n5truct0r5_functi0n_in_41f} 验证结果: 成功验证过程至关重要:通过反向模拟程序的异或处理流程,确认推导的输入能使s1最终等于s2,确保结果正确性。
五、举一反三:异或类逆向题通用解题框架
5.1 题目特征识别
当遇到以下特征时,可判定为异或类逆向题:
- 程序中存在循环的
^运算(汇编中为xor指令) - 存在明显的输入缓冲区和目标校验数组
- 验证逻辑为字符串比较或内存比较(如
strcmp、memcmp)
5.2 通用解题步骤
- 定位关键函数
- 寻找输入处理函数(通常含
fgets、read等输入函数) - 识别异或处理循环(寻找数组遍历 +
xor操作) - 确定验证函数(含比较指令的函数)
- 寻找输入处理函数(通常含
- 提取核心数据
- 原始数组(被异或的初始数据)
- 目标数组(验证的基准数据)
- 密钥信息(固定密钥 / 动态密钥生成规则)
- 推导逆向公式
- 正向流程建模:
目标 = 原始数据 ^ 密钥 ^ 输入 - 逆向公式推导:
输入 = 原始数据 ^ 密钥 ^ 目标 - 注意多轮异或的顺序(利用结合律简化)
- 正向流程建模:
- 代码实现与验证
- 用 Python 实现逆向计算(处理字节数组方便)
- 模拟正向流程验证结果正确性
- 处理不可打印字符(保留原始字节或用 \xXX 表示)
5.3 实战案例扩展
案例 1:固定密钥异或
若题目中使用固定密钥(如key=0x12),则公式简化为:
输入 = 原始数据 ^ 固定密钥 ^ 目标案例 2:密钥与输入相关
若密钥依赖输入本身(如key = input[i-1]),则需从第一个字节开始逐位推导,利用已知条件(如第一个字节的密钥常为固定值)突破。
案例 3:多轮异或嵌套
对于多轮异或(如a ^ b ^ c ^ d = 目标),可通过结合律转化为a ^ (b ^ c ^ d) = 目标,逐步拆解运算链。
六、总结
xxxorrr 题目作为典型的异或类逆向题,核心考察两点:一是对程序隐藏流程(如退出回调、初始化函数)的识别能力,二是对异或运算数学性质的灵活应用。通过本文的分析可见,此类题目虽看似复杂,但只要掌握 “流程建模→数据提取→公式推导→验证实现” 的解题框架,就能快速突破。
在 CTF 实战中,面对异或类题目,应牢记异或的可逆性本质,通过动态调试与静态分析结合的方式厘清运算链,再利用 Python 等工具快速实现逆向计算。随着对这类题目的深入理解,读者将能应对更复杂的变异题型,如嵌套异或、动态密钥异或等,真正实现从 “会做题” 到 “会解题” 的跨越。