[攻防世界]game 超详细题解(逆向CTF)

深入浅出,知无不答,言无不尽,力求详尽

老规矩查壳, Die打开看一眼

无壳, windows可以直接启动

启动之后是这样, 这个游戏是这么玩的, 输入对应的序号就会让那个符号对应的灯亮起来, 同时左右两边的符号也会受影响, 我随便输一下看一下情况就大概明白了。

输入2之后符号变化

输入3之后的变化

把上面这些符号全部点亮就可以胜利了。

规则挺简单的, 按规则来很快就可以通关, 以前上课摸鱼还想过这样的游戏。

按顺序输入12345678就可以了, 输一个数字回车一次。

就出来了....

但是我们也不想在比赛的时候偷偷玩游戏吧, 还是牢样子, 打开ida或者od啥的。

IDA 64位 一切默认

这题我们F5之后一直跳转跳转...发现找不到熟悉的main函数啊, 好多代码要分析, 太难了,

我们可以直接ALT+T, 输入main

 

搜索是空白的, 记得点开这两个选项(只开其中一个也可以), 找到后双击跳转

再按F5看下伪c代码, 双击main_0跳转

上面这串sub_45A78E就是在屏幕输出字符的函数, 类似print, 对做题没啥用

我们观察下面的代码结构

制作过小游戏的同学应该知道, 一般游戏主体是要放在一个无限循环里的, 如果这是一个动态游戏还会有tick机制...所以开头29行是一个无限循环条件, 35-39行又是输出函数和捕获输入函数, 下面才是输入判断部分.

v5 是我们输入的值(第38行), 如果v5 > 9, 弹出信息, 继续第二个循环, < 9 则跳出第二个循环继续第一个循环。

44-54行做了边界检测, 具体细节很复杂, 不好详细讲, 和做题关系也不大, 大概能明白它是用来捕获异常的就行。(53行report_rangecheck_failure)

57行开了一个提示符窗口, sub_458054函数里面是每次输入后的分隔符号还有换行的输出.

60-67行是游戏的主体判断, 用了逻辑或来判断有没有达成规则(胜利条件), || 这个操作符的特点是两边的操作数为假才会输出假值, 一个真值则是真, 它后面又 != 1的判断条件, 则是反过来了。

既然这是胜利条件, 那sub_457AB4()这个函数就是胜利后的输出内容了。

(因为这是windows执行程序, 而且胜利条件特别简单, 你也可以直接改条件让游戏直接胜利...

或者od里调用到这个模块然后输出)

双击函数跳转(两次跳转)

int sub_45E940()
{
  int i; // [esp+D0h] [ebp-94h]
  char v2[22]; // [esp+DCh] [ebp-88h] BYREF
  char v3[32]; // [esp+F2h] [ebp-72h] BYREF
  char v4[4]; // [esp+112h] [ebp-52h] BYREF
  char v5[64]; // [esp+120h] [ebp-44h]

  sub_45A7BE("done!!! the flag is ");
  v5[0] = 18;
  v5[1] = 64;
  v5[2] = 98;
  v5[3] = 5;
  v5[4] = 2;
  v5[5] = 4;
  v5[6] = 6;
  v5[7] = 3;
  v5[8] = 6;
  v5[9] = 48;
  v5[10] = 49;
  v5[11] = 65;
  v5[12] = 32;
  v5[13] = 12;
  v5[14] = 48;
  v5[15] = 65;
  v5[16] = 31;
  v5[17] = 78;
  v5[18] = 62;
  v5[19] = 32;
  v5[20] = 49;
  v5[21] = 32;
  v5[22] = 1;
  v5[23] = 57;
  v5[24] = 96;
  v5[25] = 3;
  v5[26] = 21;
  v5[27] = 9;
  v5[28] = 4;
  v5[29] = 62;
  v5[30] = 3;
  v5[31] = 5;
  v5[32] = 4;
  v5[33] = 1;
  v5[34] = 2;
  v5[35] = 3;
  v5[36] = 44;
  v5[37] = 65;
  v5[38] = 78;
  v5[39] = 32;
  v5[40] = 16;
  v5[41] = 97;
  v5[42] = 54;
  v5[43] = 16;
  v5[44] = 44;
  v5[45] = 52;
  v5[46] = 32;
  v5[47] = 64;
  v5[48] = 89;
  v5[49] = 45;
  v5[50] = 32;
  v5[51] = 65;
  v5[52] = 15;
  v5[53] = 34;
  v5[54] = 18;
  v5[55] = 16;
  v5[56] = 0;
  qmemcpy(v2, "{ ", 2);
  v2[2] = 18;
  v2[3] = 98;
  v2[4] = 119;
  v2[5] = 108;
  v2[6] = 65;
  v2[7] = 41;
  v2[8] = 124;
  v2[9] = 80;
  v2[10] = 125;
  v2[11] = 38;
  v2[12] = 124;
  v2[13] = 111;
  v2[14] = 74;
  v2[15] = 49;
  v2[16] = 83;
  v2[17] = 108;
  v2[18] = 94;
  v2[19] = 108;
  v2[20] = 84;
  v2[21] = 6;
  qmemcpy(v3, "`S,yhn _uec{", 12);
  v3[12] = 127;
  v3[13] = 119;
  v3[14] = 96;
  v3[15] = 48;
  v3[16] = 107;
  v3[17] = 71;
  v3[18] = 92;
  v3[19] = 29;
  v3[20] = 81;
  v3[21] = 107;
  v3[22] = 90;
  v3[23] = 85;
  v3[24] = 64;
  v3[25] = 12;
  v3[26] = 43;
  v3[27] = 76;
  v3[28] = 86;
  v3[29] = 13;
  v3[30] = 114;
  v3[31] = 1;
  strcpy(v4, "u~");
  for ( i = 0; i < 56; ++i )
  {
    v2[i] ^= v5[i];
    v2[i] ^= 0x13u;
  }
  return sub_45A7BE("%s\n");
}

前面这些不说了, 就是初始化和赋值操作, 值得注意的是中间有qmemcpy函数还有末尾的strcpy函数, 举个例子就明白这个函数大概是干嘛的了, a是一个32位的数组, b是需要填入a的数据, c是填入多少位, qmemcpy(a, b, c)  就是往a里填入c位 "b"的意思, strcpy函数则是直接添加在数组中, 前者参数是数组, 后者参数是需要填入的数据。

接下来最后一块, 109行

对两个数组的元素进行了异或操作, 从我这边ida看到的代码来看v2根本没有56位, 是会索引溢出范围的, 从上面v3不知所云的操作, 应该是v3、v2、v4结合起来了(也是我研究了一下flag结构才发现的, 有坑啊!)

写一下python脚本

# [攻防世界]game 解题脚本

# IDA 多变量提取脚本
def multivariable_extraction(m_string):
    i = 0
    result = []
    while i != len(m_string):
        try:
            # 读取后缀 ; 符号检测赋值
            if m_string[i] == ";":
                if m_string[i-3].isdigit():
                    result.append(int(m_string[i-3:i]))
                elif m_string[i-2].isdigit():
                    result.append(int(m_string[i-2:i]))
                else:
                    result.append(int(m_string[i-1]))
            else:
                i += 1
                continue
            i += 1
        except IndexError:
            print("索引异常")
            continue
    return result

# ascii码转字符
def ascii_conversion(m_list):
    result = []
    for digit in m_list:
        result.append(chr(digit))

    result = ''.join(result)
    return result

# 字符转换ASCII码
def char_conversion_ascii(string):
    result = []
    for str1 in string:
        result.append(ord(str1))
    
    return result




# 数据输入
v5 = """ v5[0] = 18;
    v5[1] = 64;
    v5[2] = 98;
    v5[3] = 5;
    v5[4] = 2;
    v5[5] = 4;
    v5[6] = 6;
    v5[7] = 3;
    v5[8] = 6;
    v5[9] = 48;
    v5[10] = 49;
    v5[11] = 65;
    v5[12] = 32;
    v5[13] = 12;
    v5[14] = 48;
    v5[15] = 65;
    v5[16] = 31;
    v5[17] = 78;
    v5[18] = 62;
    v5[19] = 32;
    v5[20] = 49;
    v5[21] = 32;
    v5[22] = 1;
    v5[23] = 57;
    v5[24] = 96;
    v5[25] = 3;
    v5[26] = 21;
    v5[27] = 9;
    v5[28] = 4;
    v5[29] = 62;
    v5[30] = 3;
    v5[31] = 5;
    v5[32] = 4;
    v5[33] = 1;
    v5[34] = 2;
    v5[35] = 3;
    v5[36] = 44;
    v5[37] = 65;
    v5[38] = 78;
    v5[39] = 32;
    v5[40] = 16;
    v5[41] = 97;
    v5[42] = 54;
    v5[43] = 16;
    v5[44] = 44;
    v5[45] = 52;
    v5[46] = 32;
    v5[47] = 64;
    v5[48] = 89;
    v5[49] = 45;
    v5[50] = 32;
    v5[51] = 65;
    v5[52] = 15;
    v5[53] = 34;
    v5[54] = 18;
    v5[55] = 16;
    v5[56] = 0; """
v2 = """ v2[2] = 18;
    v2[3] = 98;
    v2[4] = 119;
    v2[5] = 108;
    v2[6] = 65;
    v2[7] = 41;
    v2[8] = 124;
    v2[9] = 80;
    v2[10] = 125;
    v2[11] = 38;
    v2[12] = 124;
    v2[13] = 111;
    v2[14] = 74;
    v2[15] = 49;
    v2[16] = 83;
    v2[17] = 108;
    v2[18] = 94;
    v2[19] = 108;
    v2[20] = 84;
    v2[21] = 6; """
v3 = """ v3[12] = 127;
    v3[13] = 119;
    v3[14] = 96;
    v3[15] = 48;
    v3[16] = 107;
    v3[17] = 71;
    v3[18] = 92;
    v3[19] = 29;
    v3[20] = 81;
    v3[21] = 107;
    v3[22] = 90;
    v3[23] = 85;
    v3[24] = 64;
    v3[25] = 12;
    v3[26] = 43;
    v3[27] = 76;
    v3[28] = 86;
    v3[29] = 13;
    v3[30] = 114;
    v3[31] = 1; """
    
v2_ = "{ "
v3_ = "`S,yhn _uec{"
v4_ = "u~"
_space_ = [0] # 字符型列表结束符

# 数字列表转换
v2_list = char_conversion_ascii(v2_)
v3_list = char_conversion_ascii(v3_)
v4_list = char_conversion_ascii(v4_)

# 从变量中提取数字
v2_list += multivariable_extraction(v2)
v3_list += multivariable_extraction(v3)
v5_list = multivariable_extraction(v5)

# 数据处理
v2v3_list = v2_list + v3_list + v4_list + _space_
for i in range(0, 56):
    v2v3_list[i] ^= v5_list[i]
    v2v3_list[i] ^= 0x13

# 整数答案列表转换成字符答案
print(ascii_conversion(v2v3_list))

flag:

zsctf{T9is_tOpic_1s_v5ry_int7resting_b6t_others_are_n0t}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值