i春秋2022春季赛复现-Re
eazymz
0x1 去控制流平台化
发现就是进行一系列的加密形成一个迷宫地图,然后输入要通过这个迷宫
0x2 动调获取map
0x3 走迷宫
data_map=[1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
for i in range(16):
strmap = ""
for j in range(24):
if data_map[i*24+j] == 1:
strmap += '.'
else:
strmap += '*'
print(strmap)
# .*****...******..*******
# ..***..*.***.**.********
# *.***.**.***....********
# *.***.**.*****.****.****
# *.***.**........***.****
# *..*..**.***.**.**..****
# **.*.***.**..**.**.***.*
# **...***.*..***.**.***.*
# ***.****.******.........
# ***.**.*.***..*.****.***
# ***.*..*.***.**.***..***
# *.*...**........**..****
# *...******.***.****.****
# ***.***....***..***..***
# ***.***.*.*****..*******
# ***.************.*******
# 232222322330030000303322223333333222233333333
print(int("232222322330030000303322223333333222233333333",4))
# 902741462666576198076399615
NONO
0x1 脱壳
https://mp.weixin.qq.com/s/FDy8LqL0aTPf2o7pS6u8Jw
可以观看官方wp脱壳,需要了解一点upx脱壳机原理和elf结构
0x2 去虚假控制流
https://bbs.pediy.com/thread-266005.htm
可以看一下文章里的去除不透明谓词
from keystone import *
# 文件架构
ks = Ks(KS_ARCH_X86, KS_MODE_64)
# 用于混淆的全局变量地址始末
start = 0x604064
end = 0x604090
def my_patch(ea):
new_asm = (GetDisasm(ea).split(',')[0] + ', 0').encode()
patchbytes, count = ks.asm(new_asm)
patchbytes = bytes(patchbytes)
if len(patchbytes) < get_item_size(ea):
nop_len = get_item_size(ea) - len(patchbytes)
ida_bytes.patch_bytes(ea, patchbytes + b'\x90'*nop_len)
print(new_asm, patchbytes)
else:
print("[-]REEOR: more bytes in new ins at 0x" + hex(ea))
for addr in range(start, end+1, 4):
print(hex(addr).center(20,'-'))
ref = ida_xref.get_first_dref_to(addr)
# 获取所有交叉引用
while(ref != ida_idaapi.BADADDR):
my_patch(ref)
ref = ida_xref.get_next_dref_to(addr, ref)
这里做点解释脚本是在干什么,因为虚假控制流会有一个自己的算法把各个寄存器的值和跳转的地址反复做一些多余的操作,导致ida无法f5,这里的start和end的地址就是运行中会用来存储虚假控制流的值,相当于寄存器的作用。
for 循环先获取每一个引用到这些地址的地址,然后ida_idaapi.BADADDR就是判断是否是有效地址,while一直修改和获取下一个引用的地址。
my_patch函数,GetDisasm获取传进来地址的汇编指令,后面的操作如果看了文章就应该知道是把 mov reg, global_var patch成mov reg, 0
0x3 分析代码
可以看见会清晰很多 总共是两个关键函数 sub_400D20 和 sub_401E20
进去可以看见是base64 不过是该表 而且减了43
通过sub_401670 将输入转换成了0和1 然后进行了一种益智游戏数织 红框的位置就是数织的范围规则
以0x15 为划分 最开始的是数织左侧的行 下半方是数织的列
https://cn.puzzle-nonograms.com/?size=3
进入这个网站可以了解一下规则
复现到这里的时候我想,那这样会不会有多解,因为数织这个游戏有可能有多解,但是我只是复现学习,就不深入研究是否真的会不会多解
0x4 解密
import base64
import re
# 填出来的数织答案
v27 = "0000000000000000000000000000000010000000000000000001110000000000000000111110000000000000111111110000000000011111111100000000001111111111100000000000111111100000000000000000000000000111111111111110000011100000000011110000111000000000111111100111111111111111000000000000000000000000000000111111100000000000001111111000000000000111111110000000000001111111000000000000000000000000000000000000000000000000"
# 按照0或1的连续长度进行压缩
tmpl = re.findall(r'0+|1+', v27)
v24 = bytes([len(x) for x in tmpl])
# 拿到base64的新表
unk_403040 = [0x0C, 0xFF, 0xFF, 0xFF, 0x22, 0x2C, 0x25, 0x05, 0x1E, 0x1F, 0x34, 0x1D, 0x39, 0x30, 0x15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2B, 0x14, 0x2D, 0x00, 0x10, 0x02, 0x11, 0x03, 0x37, 0x29, 0x33, 0x3B, 0x2E, 0x24, 0x2F, 0x20, 0x0A, 0x3D, 0x3F, 0x07, 0x08, 0x17, 0x0D, 0x28, 0x27, 0x26, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x35, 0x2A, 0x3A, 0x13, 0x18, 0x3E, 0x31, 0x38, 0x21, 0x01, 0x12, 0x0B, 0x32, 0x04, 0x06, 0x19, 0x09, 0x3C, 0x1B, 0x0E, 0x1C, 0x36, 0x0F, 0x16, 0x1A, 0x23]
mytable = [0 for _ in range(64)]
for x in unk_403040:
if x != 0xFF:
mytable[x] = chr(unk_403040.index(x)+43)
mytable = ''.join(mytable)
# 换表encode
b64table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
s = base64.b64encode(v24)
flag = "flag{" + s.decode().translate(str.maketrans(b64table, mytable)) + "}"
print(flag)
# flag{UDnkDgD2HEPlFEXlF8uyHPBHFEEnD8NTDPr3j85THDP+jKD=}
代码就是wp中的代码
0x5 NONO总结
很感谢官方出了wp,这位师傅题目出的也让人眼前一亮学习到了很多知识,感谢师傅。
RetroRegister.
0x0 前言
来了来了,终于可以复现一道t神出的题了。
之前做的断点都还在,这里是自欺欺人的位置,改变跳转即可注册(滑稽)
0x1 分析
还是之前的下的断点,直接断GetDlgItemTestA,然后往下跟走出函数即可看见判断。
直接通过od里的地址在ida过来,可以看见函数逻辑
第一个函数进来判断了输入的长度和格式要求
动调查看byte里面的数值,相当于范围
最后面的判断位置
这里的算法相当于 输入限制在一定范围 然后v5 就是405028,v5是通过加输入所在的索引 。每次赋值之前需要把V5左移5 乘32相当于左移5位。
然后判断比对的时候就是 把每一次v5的值异或 然后和 算到最后的v5的值的末尾&0x1f 最后5位来判断正确。
所以我们构造只需要让每一次的索引异或起来等于最后一个索引的值即可。
3的索引是1 19个1异或起来就是0 所以最后末尾是2 2的索引是0 构造成功
总结 就是把20个索引异或得到的值 == 最后一个值的索引
然后创建一个reg.dat,把输入的数据都填入里面,405038 和 405028 都是注册码 40503c是通过用户名计算出来的值。user就是用户名的字符串
然后在这些值下断,运行起来叫我们重新登录验证,直接停止ida 然后再直接运行
会断在这里读取我们之前的输入的数据
再运行,可以发现我们断在了一个加密处,wp中写的就是tea魔改
总共3处位置,使用的各个数值就是我们最开始的405038 3c 这些数值
还记得我们当时在第一步校验要重启后下的断点嘛,然后就可以得到这些数值了
wp中T神说程序加了自校验,软件断点会导致数据不一样,但是我这样操作却歪打正着的过了这个自校验
0x2 解密
hash_value=[0x963541E5, 0x80C0F758, 0x0C30C975, 0x9F6D867D]
value = [0 for i in range(5)]
ch = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"
# L5FQH-8QPJB-X95NE-SXKFQ-7W3HR
# chunqiucup
#
def trans(v):
for i in range(5):
t = (v >> ((4-i)*5)) & 31
print(ch[t],end="")
def tea_encrypt(v, k):
v0 = v[0] & 0x1ffffff
v1 = v[1] & 0x1ffffff
x = 0
delta = 0x13c6a7e
k0 = (hash_value[0] & 0xfe000000) >> 25 | (hash_value[2] & 0xfe000000) >> (25 - 7)
k1 = (hash_value[1] & 0xfe000000) >> 25 | (hash_value[3] & 0xfe000000) >> (25 - 7)
k2 = (hash_value[2] & 0x1ffffff)
k3 = (hash_value[3] & 0x1ffffff)
for i in range(32):
x += delta
x = x & 0x1ffffff
v0 += ((v1 << 4) + k0) ^ (v1 + x) ^ ((v1 >> 5) + k1)
v0 = v0 & 0x1ffffff
v1 += ((v0 << 4) + k2) ^ (v0 + x) ^ ((v0 >> 5) + k3)
v1 = v1 & 0x1ffffff
v[0] = v0
v[1] = v1
return v
def tea_decrypt(v, k):
v0 = v[0] & 0x1ffffff
v1 = v[1] & 0x1ffffff
x = (0x13c6a7e * 32) & 0x1ffffff
delta = 0x13c6a7e
k0 = (hash_value[0] & 0xfe000000) >> 25 | (hash_value[2] & 0xfe000000) >> (25 - 7)
k1 = (hash_value[1] & 0xfe000000) >> 25 | (hash_value[3] & 0xfe000000) >> (25 - 7)
k2 = (hash_value[2] & 0x1ffffff)
k3 = (hash_value[3] & 0x1ffffff)
print("0x%.8x,0x%.8x,0x%.8x,0x%.8x"%(k0,k1,k2,k3))
for i in range(32):
v1 -= ((v0 << 4) + k2) ^ (v0 + x) ^ ((v0 >> 5) + k3)
v1 = v1 & 0x1ffffff
v0 -= ((v1 << 4) + k0) ^ (v1 + x) ^ ((v1 >> 5) + k1)
v0 = v0 & 0x1ffffff
x -= delta
x = x & 0x1ffffff
v[0] = v0
v[1] = v1
return v
def check3_1():
value[0] = (hash_value[0] & 0x1ffffff)^ 18151210 #Ada_Lovelace Birth
def hash():
t = 0
t &= 0x1f
t ^= (value[0] & 0x1f00000) >> (4 * 5)
t ^= (value[0] & 0xf8000) >> (3 * 5)
t ^= (value[0] & 0x7c00) >> (2 * 5)
t ^= (value[0] & 0x3e0) >> (1 * 5)
t ^= (value[0] & 0x1f) >> (0 * 5)
print(t)
value[4] |= t << (4 * 5)
t = 0
t &= 0x1f
t ^= (value[1] & 0x1f00000) >> (4 * 5)
t ^= (value[1] & 0xf8000) >> (3 * 5)
t ^= (value[1] & 0x7c00) >> (2 * 5)
t ^= (value[1] & 0x3e0) >> (1 * 5)
t ^= (value[1] & 0x1f) >> (0 * 5)
print(t)
value[4] |= t << (3 * 5)
t = 0
t &= 0x1f
t ^= (value[2] & 0x1f00000) >> (4 * 5)
t ^= (value[2] & 0xf8000) >> (3 * 5)
t ^= (value[2] & 0x7c00) >> (2 * 5)
t ^= (value[2] & 0x3e0) >> (1 * 5)
t ^= (value[2] & 0x1f) >> (0 * 5)
print(t)
value[4] |= t << (2 * 5)
t = 0
t &= 0x1f
t ^= (value[3] & 0x1f00000) >> (4 * 5)
t ^= (value[3] & 0xf8000) >> (3 * 5)
t ^= (value[3] & 0x7c00) >> (2 * 5)
t ^= (value[3] & 0x3e0) >> (1 * 5)
t ^= (value[3] & 0x1f) >> (0 * 5)
print(t)
value[4] |= t << (1 * 5)
t = 0
for i in range(4):
t ^= (value[i] & 0x1f00000) >> (4 * 5)
t ^= (value[i] & 0xf8000) >> (3 * 5)
t ^= (value[i] & 0x7c00) >> (2 * 5)
t ^= (value[i] & 0x3e0) >> (1 * 5)
t ^= (value[i] & 0x1f) >> (0 * 5)
value[4] |= t << (0 * 5)
def solve():
value[0] = (hash_value[0] & 0x1ffffff)^ 18151210 #Ada_Lovelace Birth
value[1] = (hash_value[1] & 0x1ffffff)^ (10*2**20 + 23*2**15 + 8*2**10 + 10*2**5 +17) #CRACK
plain = [0x1852,0x1127]
key = [0xffffffff,0xffffffff,0xffffffff,0xffffffff]
print("0x%.8x,0x%.8x,0x%.8x,0x%.8x"%(hash_value[0],hash_value[1],hash_value[2],hash_value[3]))
decrypted = tea_decrypt(plain, key)
value[2],value[3] = decrypted
hash()
print("0x%.8x,0x%.8x,0x%.8x,0x%.8x,0x%.8x"%(value[0],value[1],value[2],value[3],value[4]))
for i in range(5):
trans(value[i])
print("-",end='')
solve()
解密就没啥好说的了,直接抄wp中t神脚本,美滋滋
总结
只能说很累,很动脑,上来就要先学点新知识,剩下就是理解代码的烧脑的操作。尤其是T神的题目复现中间就想放弃,还好没放弃最终复现出来了。芜湖,T神万岁!!!