ISCTF 2022
Re
SigninReverse
ida 64 位 打开程序,即可获得flag
ISCTF{27413241-9eab-41e2-aca1-88fe8b525956}
ezbase
# coding=UTF-8
import base64
# 动态调试获得
print(base64.b64decode('ZmxhZ3szNHB4bzlVc1ZrRFdSTnU1WFRDWFd1THEzQlFFbTFrd3pvfQ=='.encode()))
flag{34pxo9UsVkDWRNu5XTCXWuLq3BQEm1kwzo}
坤坤的csgo邀请
0x01 upx 脱壳
010 edit vmp为 UPX,使用upx工具进行脱壳
0x02 程序分析
F12 发现像flag的字符串
ISCTF{Kun._Ku..n_loves_you_.s0_m.uc.h.}
.s0_m.uc.h.}ISCTF{Kun._Ku..n_loves_you_
ISCTF{Kun.Ku..n_loves_you.s0_m.uc.h.}
base64
这题离了大普
看花了眼,以为魔改了base64,分析发现并没有魔改!!!
change_table='ABCDEFGHIJKLMN0PQRSTUVWXYZabcdofghijk1mnepqrstuvwxyzOl23456789+/'
table= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
str1='MTU5ZTMOMjIOYzAwZWFkMmQzYTAz0WQzMjA4NGV1NTA='
print(base64.b64decode(str1.translate(str.maketrans(change_table,table))))
最后,找了又找看了又看,才发现真的是base64 变表【e o 交换位置,最后发现的时候傻眼了】
ISCTF{159e34224c00ead2d3a039d32084ee50}
easyVM
0x01 VM opcode 翻译
简单VM 翻译一下
先提取 opcode
import idaapi
start_address=0xEFFC88
data_length=16*25 // 略微多提取16字节,一定要提取正确,不然后面很痛苦
data = idaapi.dbg_read_memory(start_address, data_length)
fp = open(r'F:\CTF_\CTF练习\2022_ctf\ISCTF2022\Re\easyVM\dump', 'wb')
fp.write(data)
fp.close()
转化为 伪汇编
# coding=UTF-8
with open(r"F:\CTF_\CTF练习\2022_ctf\ISCTF2022\Re\easyVM\dump",'rb') as f:
opcode=f.read()
opcode=list(opcode)
print(opcode)
print(len(opcode))
pos=1
while True:
while True:
while opcode[pos-1]==1:
print("flag[",opcode[pos],"]+=",opcode[pos+1])
pos+=3
if opcode[pos-1]!=4:
break
print("flag[", opcode[pos], "]-=",opcode[pos + 1])
pos += 3
if opcode[pos-1]!=5:
break
print("flag[", opcode[pos], "]^=",opcode[pos + 1])
pos += 3
# coding=UTF-8
with open(r"F:\CTF_\CTF练习\2022_ctf\ISCTF2022\Re\easyVM\dump",'rb') as f:
opcode=f.read()
opcode=list(opcode)
print(opcode)
print(len(opcode))
pos=1
while True:
while True:
while opcode[pos-1]==1:
print("flag[",opcode[pos],"]=","(flag[",opcode[pos],"]+",opcode[pos+1],")&0xff")
pos+=3
if opcode[pos-1]!=4:
break
print("flag[", opcode[pos], "]=","(flag[",opcode[pos],"]-",opcode[pos + 1],")&0xff")
pos += 3
if opcode[pos-1]!=5:
break
print("flag[", opcode[pos], "]=","(flag[",opcode[pos],"]^",opcode[pos + 1],")")
pos += 3
0x02 z3 自动求解
# coding=UTF-8
from z3 import *
s = Solver()
flag = [BitVec(f"flag[{i}]", 8) for i in range(32)]
enc = [0x65, 0xE2, 0x57, 0x60, 0xCE, 0x1E, 0xE1, 0x5C, 0x4B, 0x4B,
0x23, 0x6D, 0x8C, 0xC2, 0xBC, 0x58, 0x84, 0x92, 0x7E, 0x8C,
0x43, 0xDB, 0x15, 0x71, 0x97, 0x4A, 0xE3, 0xC4, 0x1F, 0x7C,
0xC2, 0xFD]
flag[ 15 ]= (flag[ 15 ]- 49 )&0xff
flag[ 3 ]= (flag[ 3 ]^ 149 )
flag[ 2 ]= (flag[ 2 ]- 89 )&0xff
flag[ 22 ]= (flag[ 22 ]^ 245 )
flag[ 21 ]= (flag[ 21 ]+ 34 )&0xff
flag[ 0 ]= (flag[ 0 ]+ 139 )&0xff
flag[ 20 ]= (flag[ 20 ]^ 64 )
flag[ 15 ]= (flag[ 15 ]+ 247 )&0xff
flag[ 0 ]= (flag[ 0 ]- 146 )&0xff
flag[ 14 ]= (flag[ 14 ]^ 124 )
flag[ 8 ]= (flag[ 8 ]- 33 )&0xff
flag[ 2 ]= (flag[ 2 ]^ 210 )
flag[ 30 ]= (flag[ 30 ]+ 61 )&0xff
flag[ 27 ]= (flag[ 27 ]+ 235 )&0xff
flag[ 10 ]= (flag[ 10 ]- 39 )&0xff
flag[ 20 ]= (flag[ 20 ]+ 4 )&0xff
flag[ 8 ]= (flag[ 8 ]+ 16 )&0xff
flag[ 16 ]= (flag[ 16 ]- 146 )&0xff
flag[ 20 ]= (flag[ 20 ]- 89 )&0xff
flag[ 0 ]= (flag[ 0 ]- 236 )&0xff
flag[ 22 ]= (flag[ 22 ]+ 80 )&0xff
flag[ 21 ]= (flag[ 21 ]- 195 )&0xff
flag[ 6 ]= (flag[ 6 ]- 228 )&0xff
flag[ 3 ]= (flag[ 3 ]^ 198 )
flag[ 0 ]= (flag[ 0 ]+ 5 )&0xff
flag[ 4 ]= (flag[ 4 ]^ 3 )
flag[ 4 ]= (flag[ 4 ]- 169 )&0xff
flag[ 5 ]= (flag[ 5 ]^ 230 )
flag[ 23 ]= (flag[ 23 ]^ 173 )
flag[ 18 ]= (flag[ 18 ]- 228 )&0xff
flag[ 21 ]= (flag[ 21 ]^ 10 )
flag[ 10 ]= (flag[ 10 ]^ 51 )
flag[ 2 ]= (flag[ 2 ]+ 30 )&0xff
flag[ 27 ]= (flag[ 27 ]^ 34 )
flag[ 25 ]= (flag[ 25 ]+ 240 )&0xff
flag[ 25 ]= (flag[ 25 ]+ 231 )&0xff
flag[ 29 ]= (flag[ 29 ]- 186 )&0xff
flag[ 13 ]= (flag[ 13 ]^ 1 )
flag[ 11 ]= (flag[ 11 ]^ 32 )
flag[ 11 ]= (flag[ 11 ]^ 132 )
flag[ 16 ]= (flag[ 16 ]+ 217 )&0xff
flag[ 23 ]= (flag[ 23 ]+ 229 )&0xff
flag[ 23 ]= (flag[ 23 ]+ 190 )&0xff
flag[ 24 ]= (flag[ 24 ]- 25 )&0xff
flag[ 13 ]= (flag[ 13 ]- 114 )&0xff
flag[ 19 ]= (flag[ 19 ]- 50 )&0xff
flag[ 30 ]= (flag[ 30 ]^ 33 )
flag[ 30 ]= (flag[ 30 ]- 182 )&0xff
flag[ 12 ]= (flag[ 12 ]- 12 )&0xff
flag[ 16 ]= (flag[ 16 ]+ 101 )&0xff
flag[ 8 ]= (flag[ 8 ]^ 217 )
flag[ 19 ]= (flag[ 19 ]- 45 )&0xff
flag[ 10 ]= (flag[ 10 ]+ 228 )&0xff
flag[ 4 ]= (flag[ 4 ]+ 115 )&0xff
flag[ 9 ]= (flag[ 9 ]^ 254 )
flag[ 13 ]= (flag[ 13 ]^ 10 )
flag[ 31 ]= (flag[ 31 ]- 95 )&0xff
flag[ 17 ]= (flag[ 17 ]+ 97 )&0xff
flag[ 24 ]= (flag[ 24 ]+ 187 )&0xff
flag[ 14 ]= (flag[ 14 ]- 212 )&0xff
flag[ 31 ]= (flag[ 31 ]- 178 )&0xff
flag[ 23 ]= (flag[ 23 ]+ 36 )&0xff
flag[ 12 ]= (flag[ 12 ]+ 25 )&0xff
flag[ 21 ]= (flag[ 21 ]- 132 )&0xff
flag[ 21 ]= (flag[ 21 ]^ 206 )
flag[ 26 ]= (flag[ 26 ]+ 187 )&0xff
flag[ 24 ]= (flag[ 24 ]- 15 )&0xff
flag[ 2 ]= (flag[ 2 ]^ 69 )
flag[ 27 ]= (flag[ 27 ]- 145 )&0xff
flag[ 20 ]= (flag[ 20 ]- 137 )&0xff
flag[ 8 ]= (flag[ 8 ]+ 202 )&0xff
flag[ 9 ]= (flag[ 9 ]^ 110 )
flag[ 28 ]= (flag[ 28 ]^ 43 )
flag[ 30 ]= (flag[ 30 ]+ 247 )&0xff
flag[ 29 ]= (flag[ 29 ]^ 57 )
flag[ 26 ]= (flag[ 26 ]+ 188 )&0xff
flag[ 14 ]= (flag[ 14 ]^ 205 )
flag[ 26 ]= (flag[ 26 ]^ 74 )
flag[ 6 ]= (flag[ 6 ]- 220 )&0xff
flag[ 16 ]= (flag[ 16 ]^ 211 )
flag[ 11 ]= (flag[ 11 ]- 152 )&0xff
flag[ 4 ]= (flag[ 4 ]+ 193 )&0xff
flag[ 0 ]= (flag[ 0 ]+ 206 )&0xff
flag[ 24 ]= (flag[ 24 ]+ 247 )&0xff
flag[ 15 ]= (flag[ 15 ]- 196 )&0xff
flag[ 25 ]= (flag[ 25 ]^ 242 )
flag[ 15 ]= (flag[ 15 ]- 15 )&0xff
flag[ 4 ]= (flag[ 4 ]+ 6 )&0xff
flag[ 23 ]= (flag[ 23 ]+ 15 )&0xff
flag[ 7 ]= (flag[ 7 ]^ 101 )
flag[ 29 ]= (flag[ 29 ]^ 22 )
flag[ 16 ]= (flag[ 16 ]- 185 )&0xff
flag[ 24 ]= (flag[ 24 ]^ 104 )
flag[ 23 ]= (flag[ 23 ]+ 113 )&0xff
flag[ 19 ]= (flag[ 19 ]+ 150 )&0xff
flag[ 19 ]= (flag[ 19 ]+ 27 )&0xff
flag[ 29 ]= (flag[ 29 ]- 220 )&0xff
flag[ 24 ]= (flag[ 24 ]- 87 )&0xff
flag[ 23 ]= (flag[ 23 ]+ 143 )&0xff
flag[ 9 ]= (flag[ 9 ]^ 73 )
flag[ 6 ]= (flag[ 6 ]- 192 )&0xff
flag[ 13 ]= (flag[ 13 ]+ 156 )&0xff
flag[ 5 ]= (flag[ 5 ]+ 151 )&0xff
flag[ 0 ]= (flag[ 0 ]- 107 )&0xff
flag[ 30 ]= (flag[ 30 ]+ 31 )&0xff
flag[ 25 ]= (flag[ 25 ]- 171 )&0xff
flag[ 16 ]= (flag[ 16 ]- 106 )&0xff
flag[ 1 ]= (flag[ 1 ]^ 241 )
flag[ 27 ]= (flag[ 27 ]^ 101 )
flag[ 9 ]= (flag[ 9 ]^ 170 )
flag[ 27 ]= (flag[ 27 ]+ 176 )&0xff
flag[ 1 ]= (flag[ 1 ]- 18 )&0xff
flag[ 28 ]= (flag[ 28 ]- 76 )&0xff
flag[ 2 ]= (flag[ 2 ]+ 155 )&0xff
flag[ 19 ]= (flag[ 19 ]^ 14 )
flag[ 8 ]= (flag[ 8 ]+ 130 )&0xff
flag[ 30 ]= (flag[ 30 ]+ 7 )&0xff
flag[ 12 ]= (flag[ 12 ]- 183 )&0xff
flag[ 16 ]= (flag[ 16 ]^ 111 )
flag[ 0 ]= (flag[ 0 ]- 114 )&0xff
flag[ 31 ]= (flag[ 31 ]+ 173 )&0xff
flag[ 30 ]= (flag[ 30 ]+ 7 )&0xff
flag[ 13 ]= (flag[ 13 ]^ 146 )
flag[ 1 ]= (flag[ 1 ]- 205 )&0xff
flag[ 24 ]= (flag[ 24 ]- 232 )&0xff
flag[ 11 ]= (flag[ 11 ]+ 67 )&0xff
flag[ 4 ]= (flag[ 4 ]+ 215 )&0xff
flag[ 28 ]= (flag[ 28 ]- 176 )&0xff
for i in range(32):
s.add(enc[i] == flag[i])
if s.check() == sat:
model = s.model()
print(model)
else:
print('fail:[-]')
成功求解!
# coding=UTF-8
flag=[0]*32
flag[5] = 97
flag[11] = 102
flag[8] = 55
flag[16] = 49
flag[21] = 52
flag[27] = 53
flag[28] = 48
flag[30] = 56
flag[4] = 101
flag[3] = 51
flag[10] = 51
flag[14] = 57
flag[23] = 54
flag[7] = 57
flag[26] = 50
flag[24] = 52
flag[29] = 49
flag[1] = 48
flag[20] = 97
flag[13] = 49
flag[19] = 48
flag[2] = 98
flag[9] = 56
flag[22] = 48
flag[25] = 48
flag[31] = 97
flag[18] = 98
flag[17] = 49
flag[15] = 101
flag[12] = 54
flag[6] = 97
flag[0] = 98
print('ISCTF{',end='')
for i in range(len(flag)):
print(chr(flag[i]),end='')
print('}')
ISCTF{b0b3eaa9783f619e11b0a4064025018a}
5121-babyre
加密逻辑很简单,输入十进制数,取出各位值,进行验证。
直接爆破!
# coding=UTF-8
v12 = [0]*10
v12[0] = 1
v12[1] = 1000
v12[2] = 1111
v12[3] = 111
v12[4] = 1110
v12[5] = 10
v12[6] = 1100
v12[7] = 11
v12[8] = 0
v12[9] = 1101
flag=''
v3=[0]*4
for i in range(len(v12)):
for k in range(0,10):
v7=k
for j in range(3,-1,-1):
v3[j]=(v7>>j)&1
if v12[i]==(v3[0] & v3[1] & (v3[3] == 0) |v3[0] & (v3[2] == 0 and v3[3] == 0) | v3[0] & (v3[1] == 0) & (v3[2] == 0) & v3[3] | (v3[0] == 0) & (v3[1] == 0) & v3[2] & (v3[3] == 0))+ 10*(v3[1] & (v3[2] == 0 and v3[3] == 0) | (v3[1] == 0) & (v3[2] == 0) &
v3[3] | (v3[0] == 0) & (v3[1] == 0) & v3[2] & (v3[3] == 0)) + 100 *(v3[1] &
v3[2] & (v3[3] == 0) | (v3[1] == 0) & (v3[2] == 0) &
v3[3] | (v3[0] == 0) & (v3[1] == 0) &
v3[2] & (v3[3] == 0)) + 1000*(v3[1] & v3[2] & (v3[3] == 0) | v3[0] & v3[2] & (v3[3] == 0) | (v3[1] == 0) & (v3[2] == 0) & v3[3]):
flag+=str(k)
break
print(flag) # 1594826307
ISCTF{1594826307}
babyopcode
0x01 VM opcode 分析
做过类似题 [GWCTF 2019]babyvm,但简单的多
0x02 opcode 转化为伪汇编
opcode = [0xF5, 0xF1, 0xE1, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4,
0x20, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x01, 0x00, 0x00, 0x00,
0xF2, 0xF1, 0xE4, 0x21, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x02,
0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x22, 0x00, 0x00, 0x00,
0xF1, 0xE1, 0x03, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x23,
0x00, 0x00, 0x00, 0xF1, 0xE1, 0x04, 0x00, 0x00, 0x00, 0xF2,
0xF1, 0xE4, 0x24, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x05, 0x00,
0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x25, 0x00, 0x00, 0x00, 0xF1,
0xE1, 0x06, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x26, 0x00,
0x00, 0x00, 0xF1, 0xE1, 0x07, 0x00, 0x00, 0x00, 0xF2, 0xF1,
0xE4, 0x27, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x08, 0x00, 0x00,
0x00, 0xF2, 0xF1, 0xE4, 0x28, 0x00, 0x00, 0x00, 0xF1, 0xE1,
0x09, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x29, 0x00, 0x00,
0x00, 0xF1, 0xE1, 0x0A, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4,
0x2A, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0B, 0x00, 0x00, 0x00,
0xF2, 0xF1, 0xE4, 0x2B, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0C,
0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x2C, 0x00, 0x00, 0x00,
0xF4]
for i in range(len(opcode)):
if opcode[i] == 0xF1:
print('mov ', end='')
if opcode[i + 1] == 0xE1:
print('eax ' + 'flag[' + str(opcode[i + 2]) + ']')
elif opcode[i + 1] == 0xE2:
print('ebx ' + 'flag[' + str(opcode[i + 2]) + ']')
elif opcode[i + 1] == 0xE3:
print('ecx ' + 'flag[' + str(opcode[i + 2]) + ']')
elif opcode[i + 1] == 0xE4:
print('flag[' + str(opcode[i + 2]) + '] ' + 'eax')
i += 6
elif opcode[i] == 0xf2:
print('xor eax ebx^0x12')
elif opcode[i] == 0xf5:
print('read flag')
伪汇编代码
read flag
mov eax flag[0]
xor eax ebx^0x12
mov flag[32] eax
mov eax flag[1]
xor eax ebx^0x12
mov flag[33] eax
mov eax flag[2]
xor eax ebx^0x12
mov flag[34] eax
mov eax flag[3]
xor eax ebx^0x12
mov flag[35] eax
mov eax flag[4]
xor eax ebx^0x12
mov flag[36] eax
mov eax flag[5]
xor eax ebx^0x12
mov flag[37] eax
mov eax flag[6]
xor eax ebx^0x12
mov flag[38] eax
mov eax flag[7]
xor eax ebx^0x12
mov flag[39] eax
mov eax flag[8]
xor eax ebx^0x12
mov flag[40] eax
mov eax flag[9]
xor eax ebx^0x12
mov flag[41] eax
mov eax flag[10]
xor eax ebx^0x12
mov flag[42] eax
mov eax flag[11]
xor eax ebx^0x12
mov flag[43] eax
mov eax flag[12]
xor eax ebx^0x12
mov flag[44] eax
其实就是,将明文 xor 0x12 【EBX 为0】
# coding=UTF-8
enc = [0xBB, 0x80, 0xB2, 0x9E, 0x82, 0xDC, 0x9E, 0xB2, 0xDE, 0x8C,
0x9E, 0x94]
enc = [((~i)^0x12)&0xff & 0xff for i in enc]
for i in range(len(enc)):
print(chr(enc[i]),end='')
ISCTF{Vm_so1s_3asy}
开摆re
v8 = "isctfajzu"
Str2 = "123456789"
for i in range(len(v8)):
print(chr(ord(v8[i])^ord(Str2[i])),end='')
ISCTF{XAP@SW]BL}
请送我一个绿茶
0x01 python exe 逆向转pyc
python pyinstxtractor.py green_tea.exe
根据 struct 内容,010修补后在线转python 文件
但是,打开struct 前面字节也是空的。
根据 [*] Python version: 310,重新conda 安装python 3.10 环境尝试
conda create --name test python=3.10
可以看到警告消失了,打开发现结构依旧有误!
GitHub - extremecoders-re/pyinstxtractor: PyInstaller Extractor
重新下载 pyinstxtractor.py 【正常】 同时反编译出了直接就是.pyc 文件,很强大
在线网站反编译python反编译 - 在线工具 (tool.lu) 得到python 文件。
0x02 程序分析
#!/usr/bin/env python
# visit https://tool.lu/pyc/ for more information
# Version: Python 3.10
from ctypes import *
def MX(z, y, total, key, p, e):
temp1 = (z.value >> 5 ^ y.value << 2) + (y.value >> 3 ^ z.value << 4)
temp2 = (total.value ^ y.value) + (key[p & 3 ^ e.value] ^ z.value)
return c_uint32(temp1 ^ temp2)
def encrypt(n, flag, key):
delta = 0x9E3779B9L
rounds = 6 + 52 // n
total = c_uint32(0)
z = c_uint32(flag[n - 1])
e = c_uint32(0)
if rounds > 0:
total.value += delta
e.value = total.value >> 2 & 3
for p in range(n - 1):
y = c_uint32(flag[p + 1])
flag[p] = c_uint32(flag[p] + MX(z, y, total, key, p, e).value).value
z.value = flag[p]
y = c_uint32(flag[0])
flag[n - 1] = c_uint32(flag[n - 1] + MX(z, y, total, key, n - 1, e).value).value
z.value = flag[n - 1]
rounds -= 1
if not rounds > 0:
return flag
if __name__ == '__main__':
print('please input the key1:')
key1 = int(input())
print('please input the key2:')
key2 = int(input())
flag = [
key1,
key2]
k = [
5,
2,
1,
0]
n = 2
res = encrypt(n, flag, k)
print('Is that the answer you want? : ', hex(res[0]), hex(res[1]))
print('Input 0 to exit')
stop = input()
return None
分析一下没啥特别的,就是简单的XXTEA 加密,没有任何变化之处
0x03 py解密
解密即可
# coding=utf-8
from ctypes import *
_DELTA = 0x9E3779B9
def MX(z, y, total, key, p, e):
temp1 = (z.value >> 5 ^ y.value << 2) + (y.value >> 3 ^ z.value << 4)
# temp2 = (total.value ^ y.value) + (key[p & 3^ e.value] ^ z.value) # p^e.value&3 二者无差别
temp2 = (total.value ^ y.value) + (key[(p ^ e.value) & 3] ^ z.value)
return c_uint32(temp1 ^ temp2)
def encrypt(n, v, key):
delta = 0x9e3779b9
rounds = 6 + 52 // n
total = c_uint32(0)
z = c_uint32(v[n - 1])
e = c_uint32(0)
while rounds > 0:
total.value += delta
e.value = (total.value >> 2) & 3
for p in range(n - 1):
y = c_uint32(v[p + 1])
v[p] = c_uint32(v[p] + MX(z, y, total, key, p, e).value).value
z.value = v[p]
y = c_uint32(v[0])
v[n - 1] = c_uint32(v[n - 1] + MX(z, y, total, key, n - 1, e).value).value
z.value = v[n - 1]
rounds -= 1
return v
def decrypt(n, v, key):
delta = 0x9e3779b9
rounds = 6 + 52 // n
total = c_uint32(rounds * delta)
y = c_uint32(v[0])
e = c_uint32(0)
while rounds > 0:
e.value = (total.value >> 2) & 3
for p in range(n - 1, 0, -1):
z = c_uint32(v[p - 1])
v[p] = c_uint32((v[p] - MX(z, y, total, key, p, e).value)).value
y.value = v[p]
z = c_uint32(v[n - 1])
v[0] = c_uint32(v[0] - MX(z, y, total, key, 0, e).value).value
y.value = v[0]
total.value -= delta
rounds -= 1
return v
# test
if __name__ == "__main__":
key1 = 0x7e855dd5
key2 = 0x5416bd8c
v = [key1, key2]
k = [5, 2, 1, 0]
n = 2
res = decrypt(n, v, k)
print(res)
ISCTF{4444444488888888}
final
0x01 程序流程patch
easy:004061B8 81 EC C8 00 00 00 sub esp, 0C8h
easy:004061BE 68 D4 68 40 00 push offset loc_4068D4
easy:004061C3 C3 retn
patch idc 脚本
#include <idc.idc>
static main()
{
auto i,j,from,size;
from=0x00406000; //起始地址
size=0x00406959-0x00406000;//扫描数据块大小
for ( i=0; i < size;i++ ) {
//查找 68 ? ? ? ? C3 ,替换 EB ? ? ? ? C3
if ((Byte(from)==0x68)&&(Byte(from+5)==0xC3))
{
PatchByte(from,0xE9);
PatchByte(from+5,0x90);
from=from+6;
continue;
}
from++;
}
Message("\n" + "OK\n");
}
上述patch 是无效的
push xxxx // 跳转地址
retn
jmp xxxx // 这里是偏移地址
call xxxx // 这里是偏移地址
显然上述的patch 操作,就很大问题了!程序里是绝对地址
汇编 JMP 详解 - 纯白、色 - 博客园 (cnblogs.com)
发现 Far Jmp 可以实现我们想要的效果。
JMP 的 3 种类型
短跳转(Short Jmp,只能跳转到256字节的范围内),对应机器码:EB
近跳转(Near Jmp,可跳至同一段范围内的地址),对应机器码:E9
远跳转(Far Jmp,可跳至任意地址),对应机器码: EA
#include <idc.idc>
static main()
{
auto i,j,from,size;
from=0x00406000; //起始地址
size=0x00406959-0x00406000;//扫描数据块大小
for ( i=0; i < size;i++ ) {
//查找 68 ? ? ? ? C3 ,替换 EB ? ? ? ? C3
if ((Byte(from)==0x68)&&(Byte(from+5)==0xC3))
{
PatchByte(from,0xEA);
PatchByte(from+5,0x00);// 这里不能改为0x90, jmp far 会解析紊乱
from=from+6;
continue;
}
from++;
}
Message("\n" + "OK\n");
}
patch 后得到正常 F5 反汇编流程
0x02 算法加密分析
0x03 py解密
order 可动态获取
# coding=utf-8
enc = [0x75, 0xB9, 0x3B, 0x34, 0x41, 0x6C, 0xA4, 0x4A, 0x9B, 0x46,
0x73, 0x7C, 0x84, 0x9D, 0x4B, 0x92, 0x38, 0x37, 0x8A, 0x73,
0x96, 0x31, 0x75, 0x63, 0x2F, 0x29, 0x6A, 0x28, 0x7C, 0x34,
0x37, 0x27, 0x2D]
order = [0x14, 0x6, 0xc, 0x4, 0x1b, 0x1a, 0x1c, 0x12, 0x3, 0x1f, 0xb, 0x19, 0x18, 0xa, 0x1, 0x5, 0x17, 0x16, 0x8, 0x13,
0x11, 0x7, 0xf, 0x1d, 0x1e, 0xd, 0x15, 0x9, 0x2, 0x1f, 0xe, 0x10]
for i in range(len(order)-1, -1, -1):
tmp = enc[i]
enc[i] = enc[order[i] - 1] - i
enc[order[i]-1] = tmp
for i in range(len(enc) - 2):
enc[i] ^= order[i]
print(chr(enc[i]),end='')
print()
print(enc)
print('x' * 32)
ISCTF{cc10cffe04x1b3f72e0f50e4ba28a09c}
提交发现不对,程序也跑不同!
动态调试 input cc10cffe04x1b3f72e0f50e4ba28a09c
可以看到,加密后的密文,只有第四位不同,其余31 位都是相同的,这就很奇怪了
分析知道,问题出现在order上:
order = [0x14, 0x6, 0xc, 0x4, 0x1b, 0x1a, 0x1c, 0x12, 0x3, 0x1f, 0xb, 0x19, 0x18, 0xa, 0x1, 0x5, 0x17, 0x16, 0x8, 0x13,
0x11, 0x7, 0xf, 0x1d, 0x1e, 0xd, 0x15, 0x9, 0x2, 0x1f, 0xe, 0x10]
# 可以看到第四位,正好是 0x4 ,即相当于值不交换,直接减3
# 做如下更改
for i in range(len(enc) - 2, -1, -1):
if i == order[i] - 1:
enc[i] -= i
continue
tmp = enc[i]
enc[i] = (enc[order[i] - 1] - i) & 0xff
enc[order[i] - 1] = tmp
# 得到flag
ISCTF{cc15cffe04b1b3f72e0f50e4ba28a09c}
easyopcode
0x01 VM opcode 翻译
显然是babyopcode 的加强版 [GWCTF 2019]babyvm - Moominn - 博客园 (cnblogs.com)
直接翻译成伪代码!
# coding=utf-8
opcode = [0xF5, 0xF1, 0xE1, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4,
0x20, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x01, 0x00, 0x00, 0x00,
0xF7, 0xF1, 0xE4, 0x21, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x00,
0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x20, 0x00, 0x00, 0x00,
0xF1, 0xE1, 0x0E, 0x00, 0x00, 0x00, 0xF6, 0xF1, 0xE4, 0x2E,
0x00, 0x00, 0x00, 0xF1, 0xE1, 0x02, 0x00, 0x00, 0x00, 0xFB,
0xF1, 0xE4, 0x22, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0E, 0x00,
0x00, 0x00, 0xF6, 0xF1, 0xE4, 0x2E, 0x00, 0x00, 0x00, 0xF1,
0xE1, 0x03, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x23, 0x00,
0x00, 0x00, 0xF1, 0xE1, 0x0E, 0x00, 0x00, 0x00, 0xF6, 0xF1,
0xE4, 0x2E, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x00, 0x00, 0x00,
0x00, 0xF2, 0xF1, 0xE4, 0x20, 0x00, 0x00, 0x00, 0xF1, 0xE1,
0x01, 0x00, 0x00, 0x00, 0xF7, 0xF1, 0xE4, 0x21, 0x00, 0x00,
0x00, 0xF1, 0xE1, 0x04, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4,
0x24, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x05, 0x00, 0x00, 0x00,
0xF2, 0xF1, 0xE4, 0x25, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x01,
0x00, 0x00, 0x00, 0xF7, 0xF1, 0xE4, 0x21, 0x00, 0x00, 0x00,
0xF1, 0xE1, 0x06, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x26,
0x00, 0x00, 0x00, 0xF1, 0xE1, 0x00, 0x00, 0x00, 0x00, 0xF2,
0xF1, 0xE4, 0x20, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x01, 0x00,
0x00, 0x00, 0xF7, 0xF1, 0xE4, 0x21, 0x00, 0x00, 0x00, 0xF1,
0xE1, 0x07, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x27, 0x00,
0x00, 0x00, 0xF1, 0xE1, 0x08, 0x00, 0x00, 0x00, 0xF2, 0xF1,
0xE4, 0x28, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x09, 0x00, 0x00,
0x00, 0xF6, 0xF1, 0xE4, 0x29, 0x00, 0x00, 0x00, 0xF1, 0xE1,
0x00, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x20, 0x00, 0x00,
0x00, 0xF1, 0xE1, 0x0A, 0x00, 0x00, 0x00, 0xF6, 0xF1, 0xE4,
0x2A, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0B, 0x00, 0x00, 0x00,
0xF6, 0xF1, 0xE4, 0x2B, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0C,
0x00, 0x00, 0x00, 0xF6, 0xF1, 0xE4, 0x2C, 0x00, 0x00, 0x00,
0xF1, 0xE1, 0x00, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x20,
0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0D, 0x00, 0x00, 0x00, 0xF6,
0xF1, 0xE4, 0x2D, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0E, 0x00,
0x00, 0x00, 0xF6, 0xF1, 0xE4, 0x2E, 0x00, 0x00, 0x00, 0xF1,
0xE1, 0x0F, 0x00, 0x00, 0x00, 0xF6, 0xF1, 0xE4, 0x2F, 0x00,
0x00, 0x00, 0xF1, 0xE1, 0x10, 0x00, 0x00, 0x00, 0xF6, 0xF1,
0xE4, 0x30, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x11, 0x00, 0x00,
0x00, 0xF6, 0xF1, 0xE4, 0x31, 0x00, 0x00, 0x00, 0xF1, 0xE1,
0x00, 0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x20, 0x00, 0x00,
0x00, 0xF1, 0xE1, 0x12, 0x00, 0x00, 0x00, 0xF6, 0xF1, 0xE4,
0x32, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x13, 0x00, 0x00, 0x00,
0xF6, 0xF1, 0xE4, 0x33, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x00,
0x00, 0x00, 0x00, 0xF2, 0xF1, 0xE4, 0x20, 0x00, 0x00, 0x00,
0xF4]
i = 0
for i in range(len(opcode)):
if (opcode[i] == 0xF1):
print('mov ', end='')
if (opcode[i + 1] == 0xE1):
print('eax ' + 'flag[' + str(opcode[i + 2]) + ']')
elif (opcode[i + 1] == 0xE2):
print('ebx ' + 'flag[' + str(opcode[i + 2]) + ']')
elif (opcode[i + 1] == 0xE3):
print('ecx ' + 'flag[' + str(opcode[i + 2]) + ']')
elif (opcode[i + 1] == 0xE4):
print('flag[' + str(opcode[i + 2]) + '] ' + 'eax')
i += 6
elif (opcode[i] == 0xF2):
print('xor eax 0x43')
i += 1
elif (opcode[i] == 0xF6):
print('xor eax 0x34')
i += 1
elif (opcode[i] == 0xF7):
print('xor eax 0x32')
i += 1
elif (opcode[i] == 0xF8):
print('xor eax 0x23')
i += 1
elif (opcode[i] == 0xF9):
print('xor eax 0x17')
i += 1
elif (opcode[i] == 0xFA):
print('xor eax 0x71')
i += 1
elif (opcode[i] == 0xFB):
print('xor eax 0x66')
i += 1
elif (opcode[i] == 0xF5):
print('read')
i += 1
伪代码!
read
mov eax flag[0]
xor eax 0x43
mov flag[32] eax
mov eax flag[1]
xor eax 0x32
mov flag[33] eax
mov eax flag[0]
xor eax 0x43
mov flag[32] eax
mov eax flag[14]
xor eax 0x34
mov flag[46] eax
mov eax flag[2]
xor eax 0x66
mov flag[34] eax
mov eax flag[14]
xor eax 0x34
mov flag[46] eax
mov eax flag[3]
xor eax 0x43
mov flag[35] eax
mov eax flag[14]
xor eax 0x34
mov flag[46] eax
mov eax flag[0]
xor eax 0x43
mov flag[32] eax
mov eax flag[1]
xor eax 0x32
mov flag[33] eax
mov eax flag[4]
xor eax 0x43
mov flag[36] eax
mov eax flag[5]
xor eax 0x43
mov flag[37] eax
mov eax flag[1]
xor eax 0x32
mov flag[33] eax
mov eax flag[6]
xor eax 0x43
mov flag[38] eax
mov eax flag[0]
xor eax 0x43
mov flag[32] eax
mov eax flag[1]
xor eax 0x32
mov flag[33] eax
mov eax flag[7]
xor eax 0x43
mov flag[39] eax
mov eax flag[8]
xor eax 0x43
mov flag[40] eax
mov eax flag[9]
xor eax 0x34
mov flag[41] eax
mov eax flag[0]
xor eax 0x43
mov flag[32] eax
mov eax flag[10]
xor eax 0x34
mov flag[42] eax
mov eax flag[11]
xor eax 0x34
mov flag[43] eax
mov eax flag[12]
xor eax 0x34
mov flag[44] eax
mov eax flag[0]
xor eax 0x43
mov flag[32] eax
mov eax flag[13]
xor eax 0x34
mov flag[45] eax
mov eax flag[14]
xor eax 0x34
mov flag[46] eax
mov eax flag[15]
xor eax 0x34
mov flag[47] eax
mov eax flag[16]
xor eax 0x34
mov flag[48] eax
mov eax flag[17]
xor eax 0x34
mov flag[49] eax
mov eax flag[0]
xor eax 0x43
mov flag[32] eax
mov eax flag[18]
xor eax 0x34
mov flag[50] eax
mov eax flag[19]
xor eax 0x34
mov flag[51] eax
mov eax flag[0]
xor eax 0x43
mov flag[32] eax
整理一下:
read
mov eax flag[2]
xor eax 0x66
mov flag[34] eax
mov eax flag[3]
xor eax 0x43
mov flag[35] eax
mov eax flag[4]
xor eax 0x43
mov flag[36] eax
mov eax flag[5]
xor eax 0x43
mov flag[37] eax
mov eax flag[6]
xor eax 0x43
mov flag[38] eax
mov eax flag[1]
xor eax 0x32
mov flag[33] eax
mov eax flag[7]
xor eax 0x43
mov flag[39] eax
mov eax flag[8]
xor eax 0x43
mov flag[40] eax
mov eax flag[9]
xor eax 0x34
mov flag[41] eax
mov eax flag[10]
xor eax 0x34
mov flag[42] eax
mov eax flag[11]
xor eax 0x34
mov flag[43] eax
mov eax flag[12]
xor eax 0x34
mov flag[44] eax
mov eax flag[13]
xor eax 0x34
mov flag[45] eax
mov eax flag[14]
xor eax 0x34
mov flag[46] eax
mov eax flag[15]
xor eax 0x34
mov flag[47] eax
mov eax flag[16]
xor eax 0x34
mov flag[48] eax
mov eax flag[17]
xor eax 0x34
mov flag[49] eax
mov eax flag[18]
xor eax 0x34
mov flag[50] eax
mov eax flag[19]
xor eax 0x34
mov flag[51] eax
mov eax flag[0]
xor eax 0x43
mov flag[32] eax
0x02 解密
# coding=utf-8
enc = [0x35, 0x5F, 0x39, 0x11, 0x70, 0x35, 0x26, 0x31, 0x30, 0x07,
0x6B, 0x5D, 0x01, 0x6B, 0x72, 0x61, 0x5A, 0x5A, 0x4D, 0x15]
enc[0]^=0x43
enc[1]^=0x32
enc[2]^=0x66
for i in range(3,9):
enc[i]^=0x43
for i in range(9,20):
enc[i]^=0x34
for i in range(len(enc)):
print(chr(enc[i]),end='') #vm_R3vers3_i5_FUnny!
ISCTF{vm_R3vers3_i5_FUnny!}
青春re手不会梦到密码学学姐
???随机数
等Wp吧!
simple_flower
0x01 花指令去除
call $+5
$是intel汇编格式中的一个预定义符号,表示当前指令所在的地址偏移
所以可知"call $+5"操作的含义就是:
假设call $+5指令的地址为A,那此指令可以翻译为call A+5
而call指令一般为5个字节,所以此指令完成的操作即为:
将下一条指令的地址push到栈中,然后跳转到下一条指令。
举例:下面组合操作,即:
call $+5
pop ebp
1、call将"pop ebp"指令地址push到栈中
2、跳转到"pop ebp"执行
3、"pop ebp"将之前call push进栈的值弹出到ebp中
4、ebp中的值为"pop ebp"指令的地址
版本一:
#include <idc.idc>
static main()
{
auto i,j,from,size;
from=0x00407000; //起始地址
size=0x0040F201-0x00407000;//扫描数据块大小
for ( i=0; i < size;i++ ) {
//查找 E8 00 00 00 00 call $+5,替换90
if ((Byte(from)==0xe8)&&(Byte(from+1)==0x00)&&(Byte(from+2)==0x00)&&(Byte(from+3)==0x00)&&(Byte(from+4)==0x00))
{
for(j=0;j<5;j++)
{
PatchByte(from,0x90);
from++;
}
continue;
}
if ((Byte(from)==0xe9))
{
PatchByte(from,0x90);
continue;
}
//查找 CD 03 int 3 中断 ,替换90
if ((Byte(from)==0xCD)&&(Byte(from+1)==0x03))
{
for(j=0;j<2;j++)
{
PatchByte(from,0x90);
from++;
}
continue;
}
from++;
}
Message("\n" + "OK\n");
}
版本二:
#include <idc.idc>
static main()
{
auto i,j,from,size;
from=0x00407000; //起始地址
size=0x0040F201-0x00407000;//扫描数据块大小
for ( i=0; i < size;i++ ) {
//查找 EB 03 C3 CD 03 ,替换90
if ((Byte(from)==0xeB)&&(Byte(from+1)==0x03)&&(Byte(from+2)==0xC3)&&(Byte(from+3)==0xCD)&&(Byte(from+4)==0x03))
{
for(j=0;j<5;j++)
{
PatchByte(from,0x90);
from++;
}
continue;
}
//查找 E8 01 00 00 00 E9 ,替换90
if ((Byte(from)==0xe8)&&(Byte(from+1)==0x01)&&(Byte(from+2)==0x00)&&(Byte(from+3)==0x00)&&(Byte(from+4)==0x00)&&(Byte(from+5)==0xE9))
{
for(j=0;j<6;j++)
{
PatchByte(from,0x90);
from++;
}
continue;
}
//查找 E8 00 00 00 00 call $+5,替换90
if ((Byte(from)==0xe8)&&(Byte(from+1)==0x00)&&(Byte(from+2)==0x00)&&(Byte(from+3)==0x00)&&(Byte(from+4)==0x00))
{
for(j=0;j<5;j++)
{
PatchByte(from,0x90);
from++;
}
continue;
}
//查找 C3,替换90
if ((Byte(from)==0xC3))
{
for(j=0;j<1;j++)
{
PatchByte(from,0x90);
from++;
}
continue;
}
from++;
}
Message("\n" + "OK\n");
}
发现上述去除方法都没有作用,关键在于 call $+5 那三行的作用
下面详细分析一下
veryEasy:00407027 E8 00 00 00 00 call $+5
veryEasy:00407027
veryEasy:0040702C 3E 87 04 24 xchg eax, ds:[esp]
veryEasy:00407030 8D 40 0C lea eax, [eax+0Ch]
veryEasy:00407033 3E 87 04 24 xchg eax, ds:[esp]
veryEasy:00407037 C3 retn
veryEasy:00407037
veryEasy:00407038 ; ---------------------------------------------------------------------------
veryEasy:00407038 E8 00 00 00 00 call $+5 // 更新esp为下条指令的地址
veryEasy:00407038
//下面三条指令,相当将esp栈顶地址对应值更新为【:esp】+0xc,eax 不变
//实现 retn 后,栈顶【:esp】+0xc,即retn 不会跳出函数
veryEasy:0040703D 3E 87 04 24 xchg eax, ds:[esp]
veryEasy:00407041 8D 40 0C lea eax, [eax+0Ch]
veryEasy:00407044 3E 87 04 24 xchg eax, ds:[esp]
veryEasy:00407048 C3 retn
xchg 这个条指令 可以把两个寄存器的内容互换
还有 call loc_407226【功效与上面类似】
veryEasy:004071B2 9D popf
veryEasy:004071B3 50 push eax
veryEasy:004071B4 B8 02 02 00 00 mov eax, 202h
veryEasy:004071B9 9C pushf
veryEasy:004071BA C1 E8 06 shr eax, 6
veryEasy:004071BD 83 F0 01 xor eax, 1
veryEasy:004071C0 83 C8 03 or eax, 3
veryEasy:004071C3 58 pop eax
veryEasy:004071C4 87 04 24 xchg eax, [esp]
veryEasy:004071C7 9D popf
veryEasy:004071C8 E8 00 00 00 00 call $+5
veryEasy:004071C8
veryEasy:004071CD 3E 87 04 24 xchg eax, ds:[esp]
veryEasy:004071D1 8D 40 0C lea eax, [eax+0Ch]
veryEasy:004071D4 3E 87 04 24 xchg eax, ds:[esp]
veryEasy:004071D8 C3 retn
veryEasy:004071D8
veryEasy:004071D9 ; ---------------------------------------------------------------------------
veryEasy:004071D9 9C pushf
现在分析清楚了,那么怎么处理,能达到去花之恋的效果,能F5 反汇编成功呢?
思索了一下,由于 eax 值不变,变的只有esp 值,及其esp值地址对应的指令位置在改变,实现的功能就是跳转。这么一想,如果全部nop掉,似乎对函数功能没影响??
尝试一下!版本三:
#include <idc.idc>
static main()
{
auto i,j,from,size;
from=0x00407000; //起始地址
size=0x0040F201-0x00407000;//扫描数据块大小
for ( i=0; i < size;i++ ) {
//查找 EB 03 C3 CD 03 ,替换90
if ((Byte(from)==0xeB)&&(Byte(from+1)==0x03)&&(Byte(from+2)==0xC3)&&(Byte(from+3)==0xCD)&&(Byte(from+4)==0x03))
{
for(j=0;j<5;j++)
{
PatchByte(from,0x90);
from++;
}
continue;
}
//查找 E8 01 00 00 00 E9 ,替换90
if ((Byte(from)==0xe8)&&(Byte(from+1)==0x01)&&(Byte(from+2)==0x00)&&(Byte(from+3)==0x00)&&(Byte(from+4)==0x00)&&(Byte(from+5)==0xE9))
{
for(j=0;j<12;j++)
{
PatchByte(from,0x90);
from++;
}
continue;
}
//查找 E8 00 00 00 00 call $+5,替换90
if ((Byte(from)==0xe8)&&(Byte(from+1)==0x00)&&(Byte(from+2)==0x00)&&(Byte(from+3)==0x00)&&(Byte(from+4)==0x00))
{
for(j=0;j<17;j++)
{
PatchByte(from,0x90);
from++;
}
continue;
}
from++;
}
Message("\n" + "OK\n");
}
IDC patch 后,效果还不错
0x02 去除冗余指令
nterlockedExchange(a,b) 能以原子操作的方式交换俩个参数a, b,并返回a以前的值
__writeeflags() 将指定的值写入程序状态和控件 (EFLAGS) 寄存器。
__readeflags 读取程序状态和控件 (EFLAGS) 寄存器。
# 其实就是 popf pushf 等组合造成,也导致程序代码很长!
查看对应汇编知道,存在大量冗余指令!
veryEasy:00407000 50 push eax
veryEasy:00407001 51 push ecx
veryEasy:00407002 58 pop eax
veryEasy:00407003 59 pop ecx
veryEasy:00407004 50 push eax
veryEasy:00407005 51 push ecx
veryEasy:00407006 58 pop eax
veryEasy:00407007 59 pop ecx
veryEasy:00407008 50 push eax
veryEasy:00407009 51 push ecx
veryEasy:0040700A 58 pop eax
veryEasy:0040700B 59 pop ecx
veryEasy:0040700C 50 push eax
veryEasy:0040700D 51 push ecx
veryEasy:0040700E 58 pop eax
veryEasy:0040700F 59 pop ecx
veryEasy:00407010 51 push ecx
veryEasy:00407011 59 pop ecx
veryEasy:00407012 50 push eax
veryEasy:00407013 B8 02 02 00 00 mov eax, 202h
veryEasy:00407018 9C pushf
暂时不好写算法nop,先进行分析吧!
nterlockedExchange(a,b) 能以原子操作的方式交换俩个参数a, b,并返回a以前的值
__writeeflags() 将指定的值写入程序状态和控件 (EFLAGS) 寄存器。
__readeflags 读取程序状态和控件 (EFLAGS) 寄存器。
其实就是通过原子操作,实现一定的函数功能![这里做题时分析的不准确,其实还是冗余干扰指令]
通过动态调试获得密文!
enc='1937D1D45CC0436889B488B3C01C8CA09A8E1924EA39A5D23DEDBA41A3A5AE57739E298D'
# 转为小端序
enc = [0x0D4D13719, 0x6843C05C, 0x0B388B489, 0x0A08C1CC0, 0x24198E9A, 0x0D2A539EA, 0x41BAED3D, 0x57AEA5A3, 0x8D299E73]
可以看到,整体运行流程!但加密细节还需要分析,根据密文 确定输入 36 个字符
python deflat_1.py -f test-patch1.exe --addr 0x00407000 ### 无效
分析到这里,程序逻辑,依然杂乱,必须继续去除不必要干扰!
继续分析汇编代码,去除冗余!
veryEasy:004094C4 51 push ecx
veryEasy:004094C5 B9 01 00 00 00 mov ecx, 1
veryEasy:004094CA 9C pushf
veryEasy:004094CB C1 E9 06 shr ecx, 6
veryEasy:004094CE 83 F1 01 xor ecx, 1
veryEasy:004094D1 83 C9 08 or ecx, 8
veryEasy:004094D4 59 pop ecx
veryEasy:004094D5 87 0C 24 xchg ecx, [esp+0E0h+pos]
veryEasy:004094D8 9D popf
veryEasy:004094D9 51 push ecx
veryEasy:004094DA B9 01 00 00 00 mov ecx, 1
veryEasy:004094DF 9C pushf
veryEasy:004094E0 C1 E9 06 shr ecx, 6
veryEasy:004094E3 83 F1 01 xor ecx, 1
veryEasy:004094E6 83 C9 08 or ecx, 8
veryEasy:004094E9 59 pop ecx
veryEasy:004094EA 87 0C 24 xchg ecx, [esp+0E0h+pos]
veryEasy:004094ED 9D popf
veryEasy:00407012 50 push eax
veryEasy:00407013 B8 02 02 00 00 mov eax, 202h
veryEasy:00407018 9C pushf
veryEasy:00407019 C1 E8 06 shr eax, 6
veryEasy:0040701C 83 F0 01 xor eax, 1
veryEasy:0040701F 83 C8 03 or eax, 3
veryEasy:00407022 58 pop eax
veryEasy:00407023 87 04 24 xchg eax, [esp+4+var_4]
veryEasy:00407026 9D
v778 = __readeflags();
__writeeflags(v778);
veryEasy:0040D9B7 9C pushf
veryEasy:0040D9B8 51 push ecx
veryEasy:0040D9B9 33 C1 xor eax, ecx
veryEasy:0040D9BB 33 C8 xor ecx, eax
veryEasy:0040D9BD 33 C1 xor eax, ecx
veryEasy:0040D9BF 91 xchg eax, ecx
veryEasy:0040D9C0 59 pop ecx
veryEasy:0040D9C1 9D popf
veryEasy:0040D9C2 9C pushf
veryEasy:0040D9C3 51 push ecx
veryEasy:0040D9C4 33 C1 xor eax, ecx
veryEasy:0040D9C6 33 C8 xor ecx, eax
veryEasy:0040D9C8 33 C1 xor eax, ecx
veryEasy:0040D9CA 91 xchg eax, ecx
veryEasy:0040D9CB 59 pop ecx
veryEasy:0040D9CC 9D popf
veryEasy:0040D9CD 9C pushf
veryEasy:0040B98D 9D popf
veryEasy:0040B98E 50 push eax
veryEasy:0040B98F 58 pop eax
veryEasy:0040B990 52 push edx
veryEasy:0040B991 5A pop edx
veryEasy:0040B992 9C pushf
veryEasy:0040BADE 53 push ebx
veryEasy:0040BADF 5B pop ebx
veryEasy:0040BAE0 31 C0 xor eax, eax
veryEasy:0040BAE2 51 push ecx
veryEasy:0040BAE3 59 pop ecx
veryEasy:004082AC 50 push eax
veryEasy:004082AD 51 push ecx
veryEasy:004082AE 58 pop eax
veryEasy:004082AF 59 pop ecx
veryEasy:004082B0 50 push eax
veryEasy:004082B1 51 push ecx
veryEasy:004082B2 58 pop eax
veryEasy:004082B3 59 pop ecx
分析知道,上面的指令无作用,全部去除!
版本四:【实现完全简化】
#include <idc.idc>
static main()
{
auto i,j,from,size;
from=0x00407000; //起始地址
size=0x0040F201-0x00407000;//扫描数据块大小
for ( i=0; i < size;i++ ) {
//查找 EB 03 C3 CD 03 ,替换90
if ((Byte(from)==0xeB)&&(Byte(from+1)==0x03)&&(Byte(from+2)==0xC3)&&(Byte(from+3)==0xCD)&&(Byte(from+4)==0x03))
{
for(j=0;j<5;j++)
{
PatchByte(from,0x90);
from++;
}
continue;
}
//查找 E8 01 00 00 00 E9 ,替换90
if ((Byte(from)==0xe8)&&(Byte(from+1)==0x01)&&(Byte(from+2)==0x00)&&(Byte(from+3)==0x00)&&(Byte(from+4)==0x00)&&(Byte(from+5)==0xE9))
{
for(j=0;j<12;j++)
{
PatchByte(from,0x90);
from++;
}
continue;
}
//查找 E8 00 00 00 00 call $+5,替换90
if ((Byte(from)==0xe8)&&(Byte(from+1)==0x00)&&(Byte(from+2)==0x00)&&(Byte(from+3)==0x00)&&(Byte(from+4)==0x00))
{
for(j=0;j<17;j++)
{
PatchByte(from,0x90);
from++;
}
continue;
}
//查找 51 b9 01 00 00 00 9c call $+5,替换90
if ((Byte(from)==0x51)&&(Byte(from+1)==0xb9)&&(Byte(from+2)==0x01)&&(Byte(from+3)==0x00)&&(Byte(from+4)==0x00)&&(Byte(from+5)==0x00)&&(Byte(from+6)==0x9c))
{
for(j=0;j<21;j++)
{
PatchByte(from,0x90);
from++;
}
continue;
}
if ((Byte(from)==0x50)&&(Byte(from+1)==0xb8)&&(Byte(from+2)==0x02)&&(Byte(from+3)==0x02)&&(Byte(from+4)==0x00)&&(Byte(from+5)==0x00)&&(Byte(from+6)==0x9c))
{
for(j=0;j<21;j++)
{
PatchByte(from,0x90);
from++;
}
continue;
}
if ((Byte(from)==0x9c)&&(Byte(from+1)==0x51)&&(Byte(from+2)==0x33)&&(Byte(from+3)==0xc1)&&(Byte(from+4)==0x33)&&(Byte(from+5)==0xc8)&&(Byte(from+6)==0x33))
{
for(j=0;j<11;j++)
{
PatchByte(from,0x90);
from++;
}
continue;
}
if ((Byte(from)==0x9d)&&(Byte(from+1)==0x50)&&(Byte(from+2)==0x58)&&(Byte(from+3)==0x52)&&(Byte(from+4)==0x5a)&&(Byte(from+5)==0x9c))
{
for(j=0;j<6;j++)
{
PatchByte(from,0x90);
from++;
}
continue;
}
if ((Byte(from)==0x53)&&(Byte(from+1)==0x5b)&&(Byte(from+2)==0x31)&&(Byte(from+3)==0xc0)&&(Byte(from+4)==0x51)&&(Byte(from+5)==0x59))
{
for(j=0;j<6;j++)
{
PatchByte(from,0x90);
from++;
}
continue;
}
// 去除冗余指令
if ((Byte(from)==0x51)&&(Byte(from+1)==0x59))
{
for(j=0;j<2;j++)
{
PatchByte(from,0x90);
from++;
}
continue;
}
// 去除冗余指令
if ((Byte(from)==0x53)&&(Byte(from+1)==0x5b))
{
for(j=0;j<2;j++)
{
PatchByte(from,0x90);
from++;
}
continue;
}
// 去除冗余指令
if ((Byte(from)==0x52)&&(Byte(from+1)==0x5a))
{
for(j=0;j<2;j++)
{
PatchByte(from,0x90);
from++;
}
continue;
}
// 去除冗余指令
if ((Byte(from)==0x50)&&(Byte(from+1)==0x58))
{
for(j=0;j<2;j++)
{
PatchByte(from,0x90);
from++;
}
continue;
}
// 去除冗余指令
if ((Byte(from)==0x50)&&(Byte(from+1)==0x51)&&(Byte(from+2)==0x58)&&(Byte(from+3)==0x59))
{
for(j=0;j<4;j++)
{
PatchByte(from,0x90);
from++;
}
continue;
}
// 去除pushf popf
if ((Byte(from)==0x9c)||(Byte(from)==0x9d))
{
for(j=0;j<1;j++)
{
PatchByte(from,0x90);
from++;
}
continue;
}
from++;
}
Message("\n" + "OK\n");
}
最终得到好分析的代码!【idc代码写得比较冗余,但先就这样吧】
0x03 程序分析
如图分析清楚流程后,进行解密
0x04 解密py
# coding=UTF-8
print('flag{'+'x'*30+'}')
enc = [0x0D4D13719, 0x6843C05C, 0x0B388B489, 0x0A08C1CC0, 0x24198E9A, 0x0D2A539EA, 0x41BAED3D, 0x57AEA5A3, 0x8D299E73]
enc='1937D1D45CC0436889B488B3C01C8CA09A8E1924EA39A5D23DEDBA41A3A5AE57739E298D'
input=list(bytes.fromhex(enc))
print(input)
for i in range(31,-1,-1):
# input[1] ^= input[6]
# input[2] ^= input[7]
# input[3] ^= input[8]
# input[4] ^= input[5] ^ input[0]
# input[0] ^= input[5]
input[0] ^= input[5]
input[4] ^= input[5] ^ input[0]
input[3] ^= input[8]
input[2] ^= input[7]
input[1] ^= input[6]
# 获取前四个字符 2进制位
# bin_=''
# for k in range(0,4):
# bin_+=str(bin(input[i]))[2:].zfill(8)
#
# bin_=list(bin_)
# print(bin_)
# 这里根据 加密逻辑直接生成
bin_ = ['0'] * 32
k=0
pos=0
pos1=0
while True:
tmp=input[k]
if tmp>=2:
tmp1=tmp>>1
while True:
bin_[pos] = str(tmp%2)
tmp=tmp1
pos+=1
tmp1=tmp1//2
if tmp1==0:
break
pos=pos1
pos1 += 4
pos =pos1
k+=1
if k==4:
break
print(bin_)
# 奇数轮取反二进制位
if i%2==1:
for j in range(len(bin_)):
if bin_[j]=='0':
bin_[j]='1'
else:
bin_[j] = '0'
for j in range(30,-1,-1):
if bin_[j+1]=='1' and bin_[j]=='1':
input[j + 5] -= input[j + 4]
elif bin_[j+1]=='1' and bin_[j]=='0':
input[j + 5] ^= input[j + 4]
elif bin_[j+1]=='0' and bin_[j]=='0':
input[j + 5] += input[j + 4]
input[j + 5] &=0xff
for i in range(len(input)):
print(chr(input[i]),end='')
ISCTF{Easyc4ca4238a0b923820dcc509a6f75849b}
Block
hint:
小蓝鲨参加了114514年的ISCC,他遇到了一道Mobile题,你能帮他拿到flag吗?密码学博士李华最近在研究一种算法,你能破解这个算法吗? flag格式:将ISCC改为ISCTF
0x01 jeb 附加调试
存在反调试,查看 Mainifest
android:extractNativeLibs="false"
APK瘦身属性——android:extractNativeLibs - xiaxueliang - 博客园 (cnblogs.com)
尝试使用,如下指令运行程序并调试!
adb shell am start -d -n tk.mcsog.isccgetflag56/tk.mcsog.isccgetflag56.MainActivity
也debug 了!
最终jadx 调通后,发现 题目给的apk文件无法运行,手机上也不能安装!
0x02 so 文件动静分析
程序无法跑通,光静态太分析,直接劝退!
0x03 等wp吧!
总结
这次比赛 simple_flower、final两题收获很大!对去除干扰冗余混淆指令有了更多理解,也试着更多的在汇编语言层面对程序进行分析,去解决问题。
同时在Block 题中,虽然未对程序加密流程做实质分析,但是借助此次做题机会,解决了我本机环境,jeb、jadx 等安卓逆向工具无法结合模拟器进行动态调试的问题【之前零零总总花了很多时间,之后有时间或许会整理一个系列:ida so文件、jeb、jadx 等工具动态调试技巧】。
青春re手不会梦到密码学学姐这题,分析得懵懵懂懂、窥门而不入。随机种子生成的值,让我都不知道该从哪分析!
Block、青春re手不会梦到密码学学姐 ,这两题都是零解题目,不知道官方会不会放官方WP,还是希望有机会能学习一下!