【ISCC 2023】变形记
收获
- 熟悉伪代码中 Base64 算法的不同实现方法 
- 熟悉一些逆向的代码逻辑 
(2023年5月1日-2023年5月25日)【ISCC 2023】变形记
思路
定位到主函数:
查看字符串:
发现形似 Base64 加密的字符串和 Base64 加密的码表
跟进码表位置:
这里先将 Base64 码表 BASE64_table_404200 赋值给了 v6 和 v56
由于每一步 if else 都会执行 sub_401960() 函数,跟进:
该函数的作用是在堆上分配一段新的内存空间,并将原始数据从源地址复制到新分配的内存中
即:该函数可用于扩展内存空间并复制数据
根据 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
后面这一段代码进行了字符串的反转:
v37[] 存储的是 =onQzpXUjFldhl3YWpnVyEWe6Jke,v49[] 是一个新的数组,v18 为 v37[] 的长度,v20 为索引位置,v49[v20 - 2] = *(v22 + v18 - v20 - 1) 将 v37[] 反向复制到 v49[]
于是使用 在线字符串反转 反向并进行 Base64 解密发现解密成功:zBzya2VzVcyavQcQzsBz
但是 ISCC{zBzya2VzVcyavQcQzsBz} 结果不对
发现在前面还有一段代码:
其中:
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;
      }设置了两个指针 v13 和 v14,用于寻找两个相同且相邻的元素
如果遇到两个相同且相邻的元素,就累加重复次数 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}


















