下载是个python文件,打开
先解析代码:
for i in flag:
tmp += str(bin(ord(i)))[2:].zfill(8)
每个字符转ascll码的8位二进制后,去除前缀0b,然后左侧补0至8位
b1 = int(tmp,2)
把temp的字符串当成二进制解析后再转化为整数
int(tmp, 2)是 Python 的内置整数转换函数,第二个参数2表示进制基数,意思是:把第一个参数tmp这个字符串,当成二进制格式的数字来解析,最终转换为十进制的整数。
int(字符串, 基数)是固定用法,基数可以是 2~36 之间的任意整数,用来指定输入字符串的进制:
int('101', 2)→ 把'101'当二进制解析,结果是十进制的5int('A', 16)→ 把'A'当十六进制解析,结果是十进制的10
while b1//31 != 0:
s += s_box[b1%31]
b1 = b1//31
当这个整数>31时,对31求余作为下表,从s_box中取对应字符,再对31整除,相当于转化为31进制
很明显是要写个逆向代码
写解密脚本
importlibnum s_box='qwertyuiopasdfghjkzxcvb123456#$'s='u#k4ggia61egegzjuqz12jhfspfkay'forkinrange(5):b1=kforiins[::-1]:b1=b1*31+s_box.index(i)print(libnum.n2s(int(b1)))这是对应上一道自定义 Base31 加密题的解密脚本,作用是反向还原加密前的明文 flag。
它的核心逻辑是:补回加密时丢失的最高位数字,再通过反向进制转换,把密文字符串还原成大整数,最终转回可读的明文字符串。
二、逐部分逐行解析
1. 导入与基础定义
import libnum s_box = 'qwertyuiopasdfghjkzxcvb123456#$' s = 'u#k4ggia61egegzjuqz12jhfspfkay'libnum:CTF 逆向常用的数值处理库,这里用到的n2s函数可以直接把十进制大整数转换成对应的字节字符串,等价于 “整数转二进制→按 8 位切分→转 ASCII 字符” 的完整流程。s_box:和加密代码完全一致的字符映射表,31 个字符对应 31 进制的 0~30。s:已知的密文字符串,也就是加密后输出的结果。
2. 外层循环:爆破丢失的最高位
for k in range(5): b1 = k- 这是解密的关键:加密时循环条件为
b1//31 != 0,当商小于 31 时循环终止,31 进制的最高位数字没有被存入密文,直接丢失了。 k就是我们补回来的最高位数字。range(5)表示先尝试 0~4 这 5 个小数值(CTF flag 开头格式固定,最高位通常很小),逐个验证哪个能还原出可读的明文。b1 = k:把假设的最高位数字作为初始值,后续逐步拼接低位数字。
3. 内层循环:反向还原大整数
for i in s[::-1]: b1 = b1*31 + s_box.index(i)这是进制转换的反向计算,和加密的「除基取余」完全对称:
s[::-1]:密文倒序
加密时先得到的余数是 31 进制的最低位,后得到的是高位,因此密文字符串是低位在前、高位在后。
解密时要从最高位开始计算,所以先把密文倒序,处理顺序就变成了从高位到低位。s_box.index(i):字符转数字
查找当前密文字符在s_box中的下标,得到该位对应的 31 进制数字值(0~30)。b1 = b1*31 + 当前位数字
这是进制拼接的标准公式,和十进制的123 = ((1)*10 + 2)*10 + 3逻辑完全一致:- 每处理一位,就把当前数值左移一位(乘基数 31)
- 再加上当前位的数字
- 循环结束后,就得到了完整的十进制大整数
b1
4. 整数转字符串输出
print(libnum.n2s(int(b1)))libnum.n2s:number to string,把大整数按大端字节序,每 8 位转成一个字节,最终拼接成完整的明文字符串。- 每一轮 k 对应输出一个结果,其中只有 1 个是可读、符合 CTF flag 格式的,即为正确答案。
得到flag:NSSCTF{WHAt_BASe31}