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

    DIE界面

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

    脱壳

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

    反编译结果

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

    cyberchef解密

SignIn

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

    main函数

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

    字符串表

SignIn2

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

    提示

    提示ROT47,应该是某种加密

  • 附件程序用IDA打开,按F5反编译main函数:

    main函数

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

    cyberchef

    当时解完没细看flag内容,复现这题来写博客的时候看到这个flag还有点怀疑对不对😂

ZZZ

  • 附件程序用IDA打开,直接就是main函数了,按F5反编译一下:

    main函数反编译结果

  • 可以看出程序拿到输入的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
    24
    from 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看看反编译:

    main函数反编译结果

  • 如注释写的,程序对我们的输入没有任何操作,而是拿了flag明文来比较,所以可以直接动态调试拿到flag明文。这里需要知道一些汇编小知识:函数的返回值存放在寄存器rax里。然后开始我们的操作:

    在main函数反编译界面选中解密函数decrypt_string()所在行,右键选择jump to disasm跳到汇编界面:

    jump to disasm

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

    光标停在这里

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

    F2下断点

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

    local windows debugger

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

    随便输点东西

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

    停在了断点位置

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

    flag的位置

作者

SydzI

发布于

2025-10-10

更新于

2026-02-15

许可协议

评论