收获

  • 熟悉伪代码中 Base64 算法的不同实现方法

  • 熟悉一些逆向的代码逻辑


(2023年5月1日-2023年5月25日)【ISCC 2023】变形记


思路

定位到主函数:

ISCC2023-变形记1.png

查看字符串:

ISCC2023-变形记2.png

ISCC2023-变形记3.png

发现形似 Base64 加密的字符串和 Base64 加密的码表

跟进码表位置:

ISCC2023-变形记4.png

这里先将 Base64 码表 BASE64_table_404200 赋值给了 v6v56

由于每一步 if else 都会执行 sub_401960() 函数,跟进:

ISCC2023-变形记5.png

该函数的作用是在堆上分配一段新的内存空间,并将原始数据从源地址复制到新分配的内存中
即:该函数可用于扩展内存空间并复制数据

根据 sub_401180() 代码中的替换规则:

步骤代码
一:第一个字符右移 2 位,取前 6 位作为索引值,查找对应字符v10 = v6[*(v7 - 2) >> 2]
sub_401960(Src, v8, v48, v10)
二:第一个字符取后 2 位与第二个字符的前 4 位拼接,查找对应字符v13 = 16 * (*(v7 - 2) & 3)
v31 = v56[v13]
sub_401960(Src, v32, v42, v31)

v14 = v56[v13 + (*(v7 - 1) >> 4)]
sub_401960(Src, v15, v47, v14)
三:第二个字符取后 4 位与第三个字符的前 2 位拼接,查找对应字符v18 = 4 * (*(v7 - 1) & 0xF)
v26 = v56[v18]
sub_401960(Src, v27, v44, v26)

v19 = v56[v18 + (*v7 >> 6)]
sub_401960(Src, v20, v46, v19)
四:第三个字符取后 6 位作为索引,查找对应字符v23 = v56[*v7 & 0x3F]
sub_401960(Src, v24, v45, v56[*v7 & 0x3F])

符合 Base64 加密的算法逻辑
所以 sub_401180() 函数其实就是 Base64Encode()
即:主函数中 sub_4014C0(v39, Block, v17, v32) 进行的是 Base64 加密

分析可知 sub_401820()strcpy() 函数
存在 Base64 的字符串:=onQzpXUjFldhl3YWpnVyEWe6Jke
后面这一段代码进行了字符串的反转:

ISCC2023-变形记6.png

v37[] 存储的是 =onQzpXUjFldhl3YWpnVyEWe6Jkev49[] 是一个新的数组,v18v37[] 的长度,v20 为索引位置,v49[v20 - 2] = *(v22 + v18 - v20 - 1)v37[] 反向复制到 v49[]

于是使用 在线字符串反转 反向并进行 Base64 解密发现解密成功:zBzya2VzVcyavQcQzsBz
但是 ISCC{zBzya2VzVcyavQcQzsBz} 结果不对

发现在前面还有一段代码:

ISCC2023-变形记7.png

其中:

while ( 1 )
      {
        v13 = v37;
        v14 = v37;
        if ( v39 >= 0x10 )
          v13 = v9;
        if ( v39 >= 0x10 )
          v14 = v9;
        if ( *(v13 + v11) != *(v14 + v11 + 1) )
          break;
        ++v12;
LABEL_37:
        if ( ++v11 >= v38 )
          goto LABEL_38;
      }

设置了两个指针 v13v14,用于寻找两个相同且相邻的元素
如果遇到两个相同且相邻的元素,就累加重复次数 v12,直到遇到不相等的元素为止

并且:

LABEL_37:
        if ( ++v11 >= v38 )
          goto LABEL_38;
      }
      v15 = v37;
      if ( v39 >= 0x10 )
        v15 = v9;
      v49[v10 - 2] = *(v15 + v11);
      if ( v12 < 10 )
      {
        ++v10;
        if ( v12 == 1 )
        {
LABEL_36:
          v12 = 1;
          goto LABEL_37;
        }
      }
      else
      {
        v49[v10 - 1] = v12 / 10 + 48;
        v10 += 2;
      }
      v9 = v37[0];
      v49[v10++ - 2] = v12 % 10 + 48;
      goto LABEL_36;
    }

这段代码根据重复次数 v12 将重复的字母替换成对应的数字,并存储在数组 v49

换成数字的规则是:
① 如果重复次数 v12 小于 10,则直接将其转换为字符存储
② 如果重复次数 v12 大于等于 10,则将十位数和个位数分别转换为字符存储

因此,直接将数字替换为前面一个字符即可,得到 flag 为:ISCC{zBzyaaVzVcyavQcQzsBz}

综上所述,程序逻辑如下:
① 在用户输入的数据中,先将重复的字母替换成对应的数字
② 然后进行 Base64 加密
③ 再将加密后的字符串反转
④ 与 =onQzpXUjFldhl3YWpnVyEWe6Jke 进行比较,如果相等则正确


脚本

import base64  
ss = "=onQzpXUjFldhl3YWpnVyEWe6Jke"  
res = ss[::-1]  
re = ""  
re += str(base64.b64decode(res).decode('utf-8'))  # Base64解密  
print(re)  
result = ""  
for i in range(0, len(re)):  
    if ord(re[i]) <= 57:  # 重复次数小于等于9(个位数)  
        tmp = ord(re[i]) - 48 - 1  
        result += re[i - 1] * tmp  
        continue  
    result += re[i]  
print("ISCC{" + result + "}")

结果

ISCC{zBzyaaVzVcyavQcQzsBz}

ISCC2023-变形记8.png