ISCTF 2022

Re

SigninReverse

ida 64 位 打开程序,即可获得flag

image

ISCTF{27413241-9eab-41e2-aca1-88fe8b525956}

ezbase

# coding=UTF-8

import base64

# 动态调试获得
print(base64.b64decode('ZmxhZ3szNHB4bzlVc1ZrRFdSTnU1WFRDWFd1THEzQlFFbTFrd3pvfQ=='.encode()))

flag{34pxo9UsVkDWRNu5XTCXWuLq3BQEm1kwzo}

坤坤的csgo邀请

0x01 upx 脱壳

image

image

010 edit vmp为 UPX,使用upx工具进行脱壳

image

0x02 程序分析

image

F12 发现像flag的字符串

image

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 翻译

image

简单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

image

image

加密逻辑很简单,输入十进制数,取出各位值,进行验证。

直接爆破!

# 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 分析

image

image

image

image

image

image

image

做过类似题 [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

image

image

根据 struct 内容,010修补后在线转python 文件

但是,打开struct 前面字节也是空的。

根据 [*] Python version: 310,重新conda 安装python 3.10 环境尝试

conda create --name test python=3.10

image

可以看到警告消失了,打开发现结构依旧有误!

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

image

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 算法加密分析

image

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

image

可以看到,加密后的密文,只有第四位不同,其余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 翻译

image

image

显然是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 等组合造成,也导致程序代码很长!

image

查看对应汇编知道,存在大量冗余指令!

image

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]

image

image

image

可以看到,整体运行流程!但加密细节还需要分析,根据密文 确定输入 36 个字符

python deflat_1.py -f test-patch1.exe --addr 0x00407000   ### 无效

image

分析到这里,程序逻辑,依然杂乱,必须继续去除不必要干扰!
继续分析汇编代码,去除冗余!

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 程序分析

image

如图分析清楚流程后,进行解密

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 附加调试

image

image

存在反调试,查看 Mainifest

image

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 文件动静分析

image

程序无法跑通,光静态太分析,直接劝退!

0x03 等wp吧!

总结

这次比赛 simple_flower、final两题收获很大!对去除干扰冗余混淆指令有了更多理解,也试着更多的在汇编语言层面对程序进行分析,去解决问题。

同时在Block 题中,虽然未对程序加密流程做实质分析,但是借助此次做题机会,解决了我本机环境,jeb、jadx 等安卓逆向工具无法结合模拟器进行动态调试的问题【之前零零总总花了很多时间,之后有时间或许会整理一个系列:ida so文件、jeb、jadx 等工具动态调试技巧】。

青春re手不会梦到密码学学姐这题,分析得懵懵懂懂、窥门而不入。随机种子生成的值,让我都不知道该从哪分析!

Block、青春re手不会梦到密码学学姐 ,这两题都是零解题目,不知道官方会不会放官方WP,还是希望有机会能学习一下!

posted @ 2022-11-05 00:16  Only-xiaoxiao  阅读(899)  评论(0编辑  收藏  举报