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");
}

jmp指令 - 知乎 (zhihu.com)

说说JMP指令的跳转 - 知乎 (zhihu.com)

上述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"指令的地址

花指令简析_Em0s_Er1t的博客-CSDN博客_花指令

版本一:

#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,还是希望有机会能学习一下!

ISCTF 2022的更多相关文章

  1. CJOJ 2022 【一本通】简单的背包问题(搜索)

    CJOJ 2022 [一本通]简单的背包问题(搜索) Description 设有一个背包可以放入的物品重量为S,现有n件物品,重量分别是w1,w2,w3,-wn. 问能否从这n件物品中选择若干件放入 ...

  2. HDU 2022 海选女主角

    http://acm.hdu.edu.cn/showproblem.php?pid=2022 Problem Description potato老师虽然很喜欢教书,但是迫于生活压力,不得不想办法在业 ...

  3. ural 2022 Riding a Toad

    2022. Riding a Toad Time limit: 1.0 secondMemory limit: 64 MB A tribe of leafmen live in the old for ...

  4. [LOJ 2022]「AHOI / HNOI2017」队长快跑

    [LOJ 2022]「AHOI / HNOI2017」队长快跑 链接 链接 题解 不难看出,除了影响到起点和终点的射线以外,射线的角度没有意义,因为如果一定要从该射线的射出一侧过去,必然会撞到射线 因 ...

  5. [翻译]正式宣布 Visual Studio 2022

    原文: [Visual Studio 2022] 首先,我们要感谢正在阅读这篇文章的你,我们所有的产品开发都始于你也止于你,无论你是在开发者社区上发帖,还是填写了调查问卷,还是向我们发送了反馈意见,或 ...

  6. 它来了!!!有史以来第一个64位Visual Studio(2022)预览版将在今夏发布!

    美国时间2021年4月19日,微软产品研发部一位负责人Amanda Silver在其博客上发布一则<Visual Studio 2022>的消息,表示将在今年(2021年)夏天发布Visu ...

  7. .NET6系列:微软正式宣布Visual Studio 2022

    系列目录     [已更新最新开发文章,点击查看详细] 首先,我们要感谢正在阅读这篇文章的你,我们所有的产品开发都始于你也止于你,无论你是在开发者社区上发帖,还是填写了调查问卷,还是向我们发送了反馈意 ...

  8. .NET6系列:Visual Studio 2022 线路图

    系列目录     [已更新最新开发文章,点击查看详细] 在上一篇博客<Visual Studio 2022>中介绍了VS2022的性能改进与重要功能.本文主要介绍在 Visual Stud ...

  9. 微软发布了Visual Studio 2022 Preview 1 以及.NET 6 Preview 5

    Microsoft 今天宣布了Visual Studio 2022 的第一个预览版,并且同时也发布了.NET 6 Preview 5. https://devblogs.microsoft.com/v ...

  10. Visual Studio 2022 Preview 1 和.NET 6 Preview 5 正式发布

    具有里程碑意义的Visual Studio 2022 Preview 1正式发布,重点是64位,而没有增加新功能,并且同时也发布了.NET 6 Preview 5. https://devblogs. ...

随机推荐

  1. 移动端及pc端适配

    1.rem搭配CSS预处理器使用 这里我就用vue+less来简单操作一下,具体可以封装到底层,这里暂且演示一下原理. 这里推荐一下使用我的自制脚手架 (songyao-cli) 来快速生成一个vue ...

  2. C#中Newtonsoft.Json.dll 的使用

    1.类库说明Newtonsoft.Json.dll是.NET 下开源的json格式序列号和反序列化的类库,利用此类库,可以方便地操作json数据,其中在反序列化时,可以直接将格式化的json数据处理成 ...

  3. postman-error:SyntaxError: Invalid shorthand property initializer

    SyntaxError: Invalid shorthand property initializer 速记属性初始值设定项无效 原因:

  4. LeetCode系列之 (JavaScript) => 53. 最大子数组和

    题目描述: leetcode 题目链接: 53. 最大子数组和 - 力扣(LeetCode) (leetcode-cn.com) 解题思路分析: 题干最终的输出是连续子数组的最大和:1. 贪心算法: ...

  5. web端测试的测试点和注意事项【转载】

    文章来源:作者:simplesally 出处:https://www.cnblogs.com/simple1025/   [转载] 工作中接触了不同类型的web端系统,内容不同,需求不同,测试关注点也 ...

  6. Console对象的实例方法

    1.console.table 将数据以表格的形式显示. 这个方法需要一个必须参数 data,data 必须是一个数组或者是一个对象:还可以使用一个可选参数 columns. // Output an ...

  7. Python 时间日期获取(今天,昨天或者某一段时间)

    日常使用的时间函数: 昨天,或者N天的日期 import time def time_stamp(days): hours = int(days) t = time.strftime("%Y ...

  8. Leetcode457

    A very absurd description for this problem, but people can get the idea by looking at the examples.. ...

  9. 【python】第一模块 步骤五 第一课、内存管理机制

    第一课.内存管理机制 一.课程介绍 1.1 课程概要 课程概要 赋值语句的内存分析 垃圾回收机制 内存管理机制 课程目标 掌握赋值语句内存分析方法 掌握id()和is()的使用 了解python的垃圾 ...

  10. unity shader 描边

    https://zhuanlan.zhihu.com/p/66282034   这个是将整个模型放大 在世界坐标操作 https://blog.csdn.net/ToToTofu/article/de ...