ISCC 2022 RE

ISCC 2022 RE

练武题

Amy's Code

v9=[0]*20
v9[0] = 149
v9[1] = 169
v9[2] = 137
v9[3] = 134
v9[4] = 212
v9[5] = 188
v9[6] = 177
v9[7] = 184
v9[8] = 177
v9[9] = 197
v9[10] = 192
v9[11] = 179
v9[12] = 153
v9[13] = 129
v9[14] = 150
v9[15] = 131
v9[16] = 196
v9[17] = 111
v9[18] = 166
v9[19] = 184

v6='LWHFUENGDJGEFHYDHIGJ'

flag=''
for i in range(len(v6)):
    flag+=chr((v9[i]-ord(v6[i]))^i)
print(flag)

ISCC{reverse_430l7M}

Bob's Code

main函数

image

子函数分析

sub_D916E0(Str1, 2);

image

sub_D91023(v8, Str1, 46, 22);  # 在 a4 位置,插入 a3

image

int __cdecl sub_D92D10(int a1, int a2, char a3, int a4)
{
  int result; // eax
  int j; // [esp+D0h] [ebp-14h]
  int i; // [esp+DCh] [ebp-8h]

  for ( i = 0; *(_BYTE *)(i + a1); ++i )        // 将 a1 赋值给 a2
    *(_BYTE *)(i + a2) = *(_BYTE *)(i + a1);
  *(_BYTE *)(a4 + a2) = a3;                     // a2[a4] 替换为 a3
  for ( j = a4; ; ++j )                         // 相当于 在a4 位置插入a3
  {
    result = j + a2;
    if ( !*(_BYTE *)(j + a2) )                  // /0 时跳出 循环
      break;
    *(_BYTE *)(j + a2 + 1) = *(_BYTE *)(j + a1);// 将 a2[j+1] 替换为 a1[j]
  }
  return result;
}

解密py

import base64

base64_table1=list('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_')
base64_table1_=base64_table1
base64_table2='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

text='.W1BqthGbeblqdVh0WVDxp.ipRoXRKtG4aAWtYX1pVoVpWtGWbYMe1dhkboF0.'

# func1 大小写转化
text1=''
for i in range(len(text)):
    if text[i]>='A' and text[i]<='Z':    # 大写字母左移2
        text1+=chr((ord(text[i])-65-2+26)%26+65)
    elif text[i]>='a' and text[i]<='z':
        text1+= chr((ord(text[i]) - 97 - 2 + 26) % 26+97)
    else:
        text1 += text[i]
print(text1) # .U1ZorfEzczjobTf0UTBvn.gnPmVPIrE4yYUrWV1nTmTnUrEUzWKc1bfizmD0.

text2='U1ZorfEzczjobTf0UTBvngnPmVPIrE4yYUrWV1nTmTnUrEUzWKc1bfizmD0='
# func2  替换

# 生成变表
for i in range(5,19):
    v12 = base64_table1[i]
    base64_table1[i] = base64_table1_[i + 26]
    base64_table1[i + 26] = v12

new_table=''.join(base64_table1)
print(new_table)
text3=base64.b64decode(text2.translate(str.maketrans(new_table,base64_table2)))

# flag
print(base64.b64decode(text3.decode())) # ISCC{66mmCJ8r-Yl3vh5VI-wL17dneG}

ISCC{66mmCJ8r-Yl3vh5VI-wL17dneG}

GetTheTable

ida 打开发现,不是exe文件,是elf 文件,删除后缀重新打开

image

分析发现是base58 编码

import base58

text='ERaQux2nUagUfGv41eBeGKfoUx'

print(base58.b58decode(text.encode()))  # ISCC{ZhiEdJQA3QNnm}

ISCC{ZhiEdJQA3QNnm}

Sad Code

关键 函数分析

image

直接z3库 开始求解v14

#coding=utf-8
from z3 import *   # z3库求v14

v14=['']*20
for i in range(8):
    v14[i]=Int('v14['+str(i)+']')

s=Solver()
s.add(v14[2] + 7 * v14[1] - 4 * v14[0] - 2 * v14[3] == 0x1F6B7856C)
s.add( 5 * v14[3] + 3 * v14[2] - v14[1] - 2 * v14[0] == 0x10BE1E7C7)
s.add(2 * v14[1] + 8 * v14[3] + 10 * v14[0] - 5 * v14[2] == 0x49CB05E09)
s.add(7 * v14[0] + 15 * v14[1] - 3 * v14[3] - 2 * v14[2] == 0x7EA87530B)
s.add(15 * v14[4] + 35 * v14[7] - v14[5] - v14[6] == 0xCABA96AC9)
s.add(38 * v14[6] + v14[4] + v14[7] - 24 * v14[5] == 0x51198C284)
s.add(38 * v14[5] + 32 * v14[4] - v14[6] - v14[7] == 0x14C896AD3E)
s.add(v14[4] + 41 * v14[6] - v14[5] - 25 * v14[7] == 0x6FAA694D1)

if s.check()==sat:
    m=s.model()
    print (m)

v14_=[1230193475,2068531009,1129337427,1127045457,1112032329,1481321808,1448625733,1161973629]
for i in range(len(v14_)):
    print (hex(v14_[i])),
# 0x49534343 0x7b4b4741 0x43505253 0x432d5951 0x42484449 0x584b2d50 0x56584645 0x45424f7d

func_1
image

func_2
image

分析知道这两个函数的作用就是将输入的flag,每四个字符一组转化为(对应ascll 码值)16进制,并组合成数字,将其两两转化为对应字符即得到flag

# v14 = 0x49534343 0x7b4b4741 0x43505253 0x432d5951 0x42484449 0x584b2d50 0x56584645 0x45424f7d

Destination='495343437b4b474143505253432d595142484449584b2d505658464545424f7d' #观察发现

最终py

# coding=utf-8
import binascii
from z3 import *  # z3库求v14

v14 = [''] * 20
for i in range(8):
    v14[i] = Int('v14[' + str(i) + ']')

s = Solver()
s.add(v14[2] + 7 * v14[1] - 4 * v14[0] - 2 * v14[3] == 0x1F6B7856C)
s.add(5 * v14[3] + 3 * v14[2] - v14[1] - 2 * v14[0] == 0x10BE1E7C7)
s.add(2 * v14[1] + 8 * v14[3] + 10 * v14[0] - 5 * v14[2] == 0x49CB05E09)
s.add(7 * v14[0] + 15 * v14[1] - 3 * v14[3] - 2 * v14[2] == 0x7EA87530B)
s.add(15 * v14[4] + 35 * v14[7] - v14[5] - v14[6] == 0xCABA96AC9)
s.add(38 * v14[6] + v14[4] + v14[7] - 24 * v14[5] == 0x51198C284)
s.add(38 * v14[5] + 32 * v14[4] - v14[6] - v14[7] == 0x14C896AD3E)
s.add(v14[4] + 41 * v14[6] - v14[5] - 25 * v14[7] == 0x6FAA694D1)

if s.check() == sat:
    m = s.model()
    print (m)

v14_ = [1230193475, 2068531009, 1129337427, 1127045457, 1112032329, 1481321808, 1448625733, 1161973629]
for i in range(len(v14_)):
    print (hex(v14_[i])),

# v14 = 0x49534343 0x7b4b4741 0x43505253 0x432d5951 0x42484449 0x584b2d50 0x56584645 0x45424f7d
Destination = '495343437b4b474143505253432d595142484449584b2d505658464545424f7d'  # 观察发现
print (binascii.unhexlify(Destination))   # ISCC{KGACPRSC-YQBHDIXK-PVXFEEBO}

ISCC{KGACPRSC-YQBHDIXK-PVXFEEBO}

擂台题

easyre

去除花指令

image

一番艰难的去花指令后得到main函数

main 函数分析

image

    sub_401005((int)Str, (int)Destination);
    sub_40100A(Str, Destination);
    sub_40100F(Str, Destination);

    # 上述三个函数 将input 分别异或 + 异或 enc!@#key  三位一次
    # 然后将值 与 ^<L^<LX:LX.MJ.MJ9PJ9VF$VF$T@$T]; 比较

解密py

temp='^<L^<LX:LX.MJ.MJ9PJ9VF$VF$T@$T];'
key='enc!@#key'

flag=[ord(temp[i]) for i in range(len(temp))]
print(flag)

for j in range(len(flag)):
    flag[j]^=ord(key[6+j%3])
for j in range(len(flag)):
    flag[j]-=ord(key[3+j%3])
for j in range(len(flag)):
    flag[j]^=ord(key[j%3])

for i in range(len(flag)):
    print(chr(flag[i]),end='')

ISCC{qwqqwqwqqwereerereeroiooioiooipp}

Encode

关键函数梳理

image

加密逻辑:
1. 先对data 简单 xor 处理
2. 获取密钥
3. 模运算

image

解密py

#coding=utf-8
def getInv(a,mod):
    y,x,d=0,0,0
    d = exgcd(a, mod, x, y)
    if d == 1:
        return (x % mod + mod) % mod
    else:
        return -1

def exgcd(a,b,x,y):
    result=0
    if b:
        result = exgcd(b, a % b, y, x)
        y -= a / b * x
    else:
        x = 1
        y = 0
        return a
    return result

key =[0]*10
key[0],key[1],key[2] =[0x7,0xb,0xd]
key[3] = key[1] * key[0]
key[4] = (key[1] - 1) * (key[0] - 1)
key[5] = getInv(key[2], key[4])

# 求key
print (key)

# 爆破求模同余前data_
data=[0]*24
#text='0x363F061D2B074A230x0602390607052B360x02392D2D1A4B2138'
text=[0x23,0x4A,0x7,0x2B,0x1D,0x6,0x3F,0x36,0x36,0x2B,0x5,0x7,0x6,0x39,0x2,0x6,0x38,0x21,0x4B,0x1A,0x2D,0x2D,0x39,0x2]

for i in range(24):
    for j in range(1,1000):
        if text[i]==pow(j,key[2],key[3]):
            data[i] = j
            break

print (data)

# 求flag
for i in range(len(data)):
    data[i]+=70
    data[i]^=0x3f

len_=len(data)
for i in range((len(data)%2+len(data))/2,-1,-1):
    data[len_-i-1]^=data[i]
    data[i]^=data[len_-i-1]
    data[len_-i-1]^=data[i]

flag=''
for i in range(len_):
    flag+=chr(data[i]^0xf)

print (flag)  #ISCC{PWN_ISR_EALLY_HARD}  有细微偏差,修正可得

ISCC{PWN_IS_REALLY_HARD}

Self-Reverse

简单分析

ida 打开,发现部分字节码,花指令和堆栈不平衡,艰难的处理后发现些提示

Id: MFX 3.95 Copyright (C) 1996-2018 the MFX Team. All Rights Re   但没查到什么东西

image

没进展,linux 运行看程序的输入和输出

运行结果

Welcome To Self-Reverse Program
The self-reverse tutorial start
Please input a: 
Please input b: 
-2076682664
Starting Reverse...
Analysing Input...
Input is 0 and -2076682664
Analysing Output...
Output is -2076682664
Analysing Logic...
Logical is a + b
Analysing Code...
Code is 
#include <iostream>
using namespace std;
int main()
{
    int a,b;
    cout << "Please input a: " << endl;
    cin >> a;
    cout << "Please input b: " << endl;
    cin >> b;
    cout << a + b << endl;
    return 0;                                                                
}                                                                            
Self-Reverse Finished!
Now, you have already learned the self-reverse program
Strat your Reverse
Please input flag: 
Wrong flag!
Starting Reverse...
Analysing Input...
Input is 
Analysing Output...
Output is Wrong flag!
Analysing Logic...
Logical is Wrong flag!
Analysing Code...
Code is 
#include <iostream>                                                          
#include <string>                                                            
using namespace std;                                                         
int main()                                                                   
{                                                                            
    string flag;                                                             
    cout << "Please input flag: " << endl;                                   
    cin >> flag;                                                             
    cout << "Wrong flag!" << endl;                                           
    return 0;                                                                
}                                                                            
Self-Reverse Finished!

可以看到程序是从运行时才逆向出代码,然后执行
同时在字节码中,有关键数据ISCC,感觉有点像 SMC 自修改

image

到这里思路就很明显了,就是动态调试,获取这段字节码的处理逻辑

动调调试

linux 远程调试,有如下报错

image

计算机翻译
Input file is a dynamic library, it cannot be run by itself.Please specify the host application (Debugger, Process options)
输入文件是一个动态库,它本身无法运行。请指定主机应用程序(调试器,处理选项)

采用附加调试

image

发现关键代码,flag 长度 22 位, ISCC{xxxxxxxxxxxxxxxx}

if ( (unsigned __int8)((__int64 (__fastcall *)(__int64, const char *))strcmp)(v0 - 112, "ISCC{") )
  {
    v34 = ((__int64 (__fastcall *)(__int64))strlen)(v0 - 160);
    ((void (__fastcall *)(__int64, __int64, __int64, __int64))strcopy)(v0 - 80, v0 - 160, v34 - 1, -1LL);// 将 中间16 位字符赋值 到v0-80
    v33 = 1;
    if ( (unsigned __int8)((__int64 (__fastcall *)(__int64, void *))strcmp)(v0 - 80, &unk_7F997AAB1381) )// 和 } 比较
      v35 = 1;
  }
  if ( v33 )
    ((void (__fastcall *)(__int64))sub_7F997AAAE050)(v0 - 80);
  ((void (__fastcall *)(__int64))sub_7F997AAAE050)(v0 - 112);
  if ( v35 )
  {
    v36 = ((__int64 (__fastcall *)(__int64))strlen)(v0 - 160);
    ((void (__fastcall *)(__int64, __int64, __int64, __int64))strcopy)(v0 - 320, v0 - 160, 5LL, v36 - 6);// 将flag中间 16 位字符 copy 到v0 -300 
    *(_QWORD *)(v0 - 288) = 0LL;
    *(_QWORD *)(v0 - 280) = 0LL;
    *(_QWORD *)(v0 - 272) = 0LL;
    *(_QWORD *)(v0 - 264) = 0LL;
    *(_QWORD *)(v0 - 256) = 0LL;
    *(_QWORD *)(v0 - 248) = 0LL;
    *(_QWORD *)(v0 - 240) = 0LL;
    *(_QWORD *)(v0 - 232) = 0LL;
    for ( *(_DWORD *)(v0 - 36) = 0; *(int *)(v0 - 36) <= 15; ++*(_DWORD *)(v0 - 36) )// 用下标 i^0xd  对应值 xor flag(i+(0-16))
      *(_DWORD *)(v0 + 4LL * (*(_DWORD *)(v0 - 36) ^ 0xD) - 288) ^= *(char *)((__int64 (__fastcall *)(__int64, _QWORD))unk_7F997AAAE130)(
                                                                               v0 - 320,
                                                                               (*(_DWORD *)(v0 - 36) + 1) % 16);
    for ( *(_DWORD *)(v0 - 40) = 0; *(int *)(v0 - 40) <= 15; ++*(_DWORD *)(v0 - 40) )// 赋值处理
    {
      v37 = 3 * *(_DWORD *)(v0 + 4LL * *(int *)(v0 - 40) - 288) + 1;
      *(_DWORD *)(v0 + 4LL * *(int *)(v0 - 40) - 288) = (unsigned __int8)(HIBYTE(v37)
                                                                        + 3
                                                                        * *(_BYTE *)(v0 + 4LL * *(int *)(v0 - 40) - 288)
                                                                        + 1)
                                                      - HIBYTE(HIDWORD(v37));
    }
    *(_DWORD *)(v0 - 224) = 250;
    *(_DWORD *)(v0 - 220) = 12;
    *(_DWORD *)(v0 - 216) = 229;
    *(_DWORD *)(v0 - 212) = 250;
    *(_DWORD *)(v0 - 208) = 145;
    *(_DWORD *)(v0 - 204) = 157;
    *(_DWORD *)(v0 - 200) = 100;
    *(_DWORD *)(v0 - 196) = 166;
    *(_DWORD *)(v0 - 192) = 108;
    *(_DWORD *)(v0 - 188) = 250;
    *(_DWORD *)(v0 - 184) = 247;
    *(_DWORD *)(v0 - 180) = 145;
    *(_DWORD *)(v0 - 176) = 12;
    *(_DWORD *)(v0 - 172) = 12;
    *(_DWORD *)(v0 - 168) = 99;
    *(_DWORD *)(v0 - 164) = 142;
    for ( *(_DWORD *)(v0 - 44) = 0; *(int *)(v0 - 44) <= 15; ++*(_DWORD *)(v0 - 44) )// 比较结果
    {
      if ( *(_DWORD *)(v0 + 4LL * *(int *)(v0 - 44) - 288) != *(_DWORD *)(v0 + 4LL * *(int *)(v0 - 44) - 224) )

image

加密中间十六位,实际上就是 下标 xor 0xd, 与 **输入flag做映射 **
然后对映射后的数组做如下处理(具体看解密py)

for ( *(_DWORD *)(v0 - 40) = 0; *(int *)(v0 - 40) <= 15; ++*(_DWORD *)(v0 - 40) )// 赋值处理
    {
      v37 = 3 * *(_DWORD *)(v0 + 4LL * *(int *)(v0 - 40) - 288) + 1;
      *(_DWORD *)(v0 + 4LL * *(int *)(v0 - 40) - 288) = (unsigned __int8)(HIBYTE(v37)
                                                                        + 3
                                                                        * *(_BYTE *)(v0 + 4LL * *(int *)(v0 - 40) - 288)
                                                                        + 1)
                                                      - HIBYTE(HIDWORD(v37));
    }

上述代码核心逻辑等价于

        v34=3*j+1   # j 表示 当前处理的数值
        result=(v34>>8)+v34)- (v34>>8)&0xff   

image

比较结果

image

解密py

#define LOWORD(l) ((WORD)((DWORD_PTR)(l) & 0xffff))
#define HIWORD(l) ((WORD)((DWORD_PTR)(l) >> 16))
#HIDWORD是64位开发用的
#define HIDWORD(l) ((DWORD)(((DWORDLONG)(l) >> 32) & 0xFFFFFFFF))

# coding=utf-8

text= [250,12,229,250,145,157,100,166,108,250,247,145,12,12,99,142]

text1=[0]*16
for i in range(16):
    for j in range(0,256):  # 爆破映射后数组,
        v34=3*j+1
        if ((v34>>8)+v34)- (v34>>8)&0xff==text[i]:  # 只取8位 &0xff 要在最外层
            text1[i]=j
            break
        if j==127:
            print (j)
print (text1)
# 打印flag
flag=[0]*16
for i in range(16):    # 映射出flag
    flag[(i+1)%16]=text1[i^0xd]

for i in range(16):
    print(chr(flag[i]),end='')   # ISCC{LYY/vSy0R407!YSS}

ISCC{LYY/vSy0R407!YSS}

image

rerere

定位关键函数

image

发现,定位不了关键函数 数据区全是 .svm_hea
只能朴素的,断点+动态调试

image

image

关键函数分析

sub_1404515F0(v4) # 处理flag 的真正位置

image

在动态调试中,发现一些敏感数据(到后面并没有用上)

密文和比较

发现处理后的最终密文
image

加密逻辑是简单的xor(动态调试得出)【这里的动调需要在该函数中一直跟踪】

image

动态调试,看详细逻辑,可以获得xor文本 和密文,解密获得flag

image

py解密

xor_text=[0x9E, 0x00, 0xE6, 0x00, 0xFB, 0x00, 0x39, 0x00, 0x3C, 0x00,
  0xEA, 0x00, 0x24, 0x00, 0x9C, 0x00, 0x38, 0x00, 0xD0, 0x00,
  0x62, 0x00, 0x55, 0x00, 0x8B, 0x00, 0x33, 0x00, 0x11, 0x00,
  0x43, 0x00, 0x5C, 0x00, 0x40, 0x00, 0x34, 0x00, 0x9C, 0x00,
  0x29, 0x00, 0x28, 0x00, 0xD6, 0x00, 0x27, 0x00, 0xBC, 0x00,
  0x0C, 0x00, 0xD4, 0x00, 0xAB, 0x00, 0x17, 0x00, 0x0D, 0x00,
  0x65, 0x00, 0xE0, 0x00]

chipher=[0xD7, 0x00, 0xB5, 0x00, 0xB8, 0x00, 0x7A, 0x00, 0x47, 0x00,
  0x8B, 0x00, 0x46, 0x00, 0xFF, 0x00, 0x5C, 0x00, 0xB5, 0x00,
  0x04, 0x00, 0x32, 0x00, 0xE3, 0x00, 0x5A, 0x00, 0x7B, 0x00,
  0x28, 0x00, 0x30, 0x00, 0x2D, 0x00, 0x5A, 0x00, 0xF3, 0x00,
  0x59, 0x00, 0x59, 0x00, 0xA4, 0x00, 0x54, 0x00, 0xC8, 0x00,
  0x79, 0x00, 0xA2, 0x00, 0xDC, 0x00, 0x6F, 0x00, 0x74, 0x00,
  0x1F, 0x00, 0x9D]

flag=''

for i in range(0,len(xor_text),2):
    print(chr(xor_text[i]^chipher[i]),end="")  # ISCC{abcdefghijklmnopqrstuvwxyz}

ISCC{abcdefghijklmnopqrstuvwxyz}

总结

第一次参加ISCC,持续时间是真的久!!!

  1. 这次总共做出8道re题,练武题和擂台题各四道。
  2. 总的来说,rerere(练习了动态调试和定位关键函数) 和 self-Reverse(学会了附加调试这种方法) 两道题收获较多,其他做出来的题,都比较常规。
  3. 未做出来的题中:
  • How_decode(是个xxTEA 加密,我觉得所有流程,都分析清楚了,但解密出的数据就是不对,挺无奈的,等其他大佬的wp吧)。
  • Ruststr(elf 损坏,由于不熟悉elf 文件结构,无从下手)。
  • Flower(python 文件逆向,修复flower 文件时,反编译总是失败[被混淆了???])。
  • JoJo 上不了的天堂(函数流程都分析了,但是对flag中间位的处理,静态分析感觉不对,也解密不出flag。尝试动态分析时,发现是创建了新的线程来处理flag。动态调试时,不知如何进入对应的线程[单步跟踪调试时,一不小心就退出了程序,感觉debug了])。
  • baby_pyc(给我的感觉就是道披着逆向皮的RSA密码题,尝试性的做了下就放弃了)。
  • wtf(比赛时间太久,这题就看了下,后面就没做了)

不得不说,ISCC 题量管够,质量好,毕竟我做出来的re题不多(等待大佬的wp)。。。
最后勉励一下,继续加油!!!

posted @ 2022-05-26 18:13  Only-xiaoxiao  阅读(1413)  评论(0编辑  收藏  举报