0xGame2025Week1WriteUp
0xGame2025 Week1 Reverse方向全解&详解
EasyXor
附件程序用IDA打开就是main函数的位置了,按F5反编译得到如下:

进行一些小小的变量名优化可以得到:

可以看出,函数获取flag输入,然后对flag每一位进行异或,异或的值来自一个给定的字符串,要求异或后的值要等于str[i]-i。所以解密思路是,把异或关系中的Char和str[i]-i进行对调,即由Char^key[index%length] == str[i]-i 得到 ( str[i]-i )^key[index%length] == Char。解密脚本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#include<stdio.h>
#include<string.h>
int main() {
char str[] = {
0x42, 0x1A, 0x39, 0x17, 0x1D, 0x09, 0x51, 0x55, 0x2C, 0x5F, 0x63, 0x0C, 0x0D, 0x16, 0x62, 0x27,
0x55, 0x64, 0x55, 0x26, 0x6D, 0x6A, 0x18, 0x34, 0x88, 0x65, 0x6E, 0x1C, 0x21, 0x6E, 0x3D, 0x23,
0x6A, 0x25, 0x6B, 0x63, 0x68, 0x7E, 0x77, 0x75, 0x9A, 0x7D, 0x39, 0x43, 0x00, 0x00, 0x00, 0x00
};
char key[] = "raputa0xGame2025";
char flag[45];
for (int i = 0; i <= 43; i++) {
flag[i] = (str[i] - i) ^ key[i % strlen(key)];
}
flag[44] = '\0';
printf("%s", flag);
return 0;
}
BaseUpx
题目提示有Upx壳,用DIE打开附件程序可以看出这是标准upx4.24

在Github上下载Release UPX 4.2.4 · upx/upx,使用指令upx -d脱壳

然后就可以把脱壳后的程序拿到IDA里分析,打开就是main函数了,按F5反编译:

可以看到函数对输入的enc(实际上是flag)进行了base64encode,然后和str进行比较。因此思路是拿到str,直接用CyberChef来进行base64解码:

SignIn
附件程序用IDA打开,直接就是main函数了,按F5反编译,提示flag在程序里

shift+F12查看字符串表,就可以找到flag了

SignIn2
运行附件程序可以看到一些提示:

提示ROT47,应该是某种加密
附件程序用IDA打开,按F5反编译main函数:

可以看到程序里本来就有一个flag,程序拿到我们输入的数字后对flag进行了加密,然后检查加密后的flag开头几个字母是不是”0xGame”。既然提到了ROT47,就把flag的数据拿到CyberChef里看看能不能解密,刚好可以找到一个ROT 47 Brust Force(flag就直接复制程序里的就好了):

当时解完没细看flag内容,复现这题来写博客的时候看到这个flag还有点怀疑对不对😂
ZZZ
附件程序用IDA打开,直接就是main函数了,按F5反编译一下:

可以看出程序拿到输入的flag后先检查了一下格式,然后截取除了4个部分,拿这4个部分建立了等式。结合题目ZZZ可以推测这是在考z3求解。题目为了保证唯一解还给了flag的sha256来验证
用z3复现一下等式,再加上sha256验证:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24from z3 import *
import hashlib
sha256='4aba519d4666f5421488afaaf89efdcbe48e7a53f814ce5c1d82b46b55032651'
s=Solver()
x1=BitVec('x1',32)
x2=BitVec('x2',32)
x3=BitVec('x3',32)
x4=BitVec('x4',32)
s.add(3 * x2 + 5 * x1 + 7 * x4 + 2 * x3 == -1445932505)
s.add(2 * (2 * (2 * x2 + x3) + x1) + x4 == -672666814)
s.add(7 * x2 + 3 * x1 + 5 * x4 + 4 * x3 == 958464147)
s.add(((x1 ^ x2) << 6) + ((x3 >> 6) ^ 0x4514) == 123074281)
while s.check() == sat:
model=s.model()
x1_val=model[x1].as_long()
x2_val=model[x2].as_long()
x3_val=model[x3].as_long()
x4_val=model[x4].as_long()
flag=f"0xGame{{{x1_val:08x}{x2_val:08x}{x3_val:08x}{x4_val:08x}}}"
if hashlib.sha256(flag.encode()).hexdigest()==sha256:#验证是否为正确flag
print(flag)
exception=Or(x1!=x1_val,x2!=x2_val,x3!=x3_val,x4!=x4_val)#排除同一解
s.add(exception)
DyDebug
附件程序用IDA打开,直接就是main函数了,按F5看看反编译:

如注释写的,程序对我们的输入没有任何操作,而是拿了flag明文来比较,所以可以直接动态调试拿到flag明文。这里需要知道一些汇编小知识:函数的返回值存放在寄存器rax里。然后开始我们的操作:
在main函数反编译界面选中解密函数decrypt_string()所在行,右键选择jump to disasm跳到汇编界面:

光标停在的地方附近就有一个call _Z14decrypt_stringPKcy指令,后面有注释是解密函数decrypt_string()

其实光标停在的位置的指令就是取函数返回值的操作了。函数的返回值放在rax里,而这条指令把rax mov(移动)到了[rbp-10h+decrypted_str],看不懂没关系,只需要知道函数返回值可以通过这里的rax看到就行了。这个返回值就是解密后得到的flag明文。在光标停在的位置按F2下断点

把界面正上方的”no debugger”改选成”local windows debugger”,按旁边绿色的三角形开始调试

这时候会弹出命令行窗口,随便输入一点东西,按回车

可以看到IDA里程序停在了我们刚刚下断点的地方

把鼠标移到rax上就可以看到flag了,双击rax到flag的位置,可以把这一串数据复制给AI提取

0xGame2025Week1WriteUp
https://sydzi.github.io/2025/10/10/0xGame2025Week1ReverseWriteUp/