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函数
子函数分析
sub_D916E0(Str1, 2);
sub_D91023(v8, Str1, 46, 22); # 在 a4 位置,插入 a3
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 文件,删除后缀重新打开
分析发现是base58 编码
import base58
text='ERaQux2nUagUfGv41eBeGKfoUx'
print(base58.b58decode(text.encode())) # ISCC{ZhiEdJQA3QNnm}
ISCC{ZhiEdJQA3QNnm}
Sad Code
关键 函数分析
直接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
func_2
分析知道这两个函数的作用就是将输入的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
去除花指令
一番艰难的去花指令后得到main函数
main 函数分析
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
关键函数梳理
加密逻辑:
1. 先对data 简单 xor 处理
2. 获取密钥
3. 模运算
解密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 但没查到什么东西
没进展,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 自修改
到这里思路就很明显了,就是动态调试,获取这段字节码的处理逻辑
动调调试
linux 远程调试,有如下报错
计算机翻译
Input file is a dynamic library, it cannot be run by itself.Please specify the host application (Debugger, Process options)
输入文件是一个动态库,它本身无法运行。请指定主机应用程序(调试器,处理选项)
采用附加调试
发现关键代码,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) )
加密中间十六位,实际上就是 下标 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
比较结果
解密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}
rerere
定位关键函数
发现,定位不了关键函数 数据区全是 .svm_hea
只能朴素的,断点+动态调试
关键函数分析
sub_1404515F0(v4) # 处理flag 的真正位置
在动态调试中,发现一些敏感数据(到后面并没有用上)
密文和比较
发现处理后的最终密文
加密逻辑是简单的xor(动态调试得出)【这里的动调需要在该函数中一直跟踪】
动态调试,看详细逻辑,可以获得xor文本 和密文,解密获得flag
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,持续时间是真的久!!!
- 这次总共做出8道re题,练武题和擂台题各四道。
- 总的来说,rerere(练习了动态调试和定位关键函数) 和 self-Reverse(学会了附加调试这种方法) 两道题收获较多,其他做出来的题,都比较常规。
- 未做出来的题中:
- How_decode(是个xxTEA 加密,我觉得所有流程,都分析清楚了,但解密出的数据就是不对,挺无奈的,等其他大佬的wp吧)。
- Ruststr(elf 损坏,由于不熟悉elf 文件结构,无从下手)。
- Flower(python 文件逆向,修复flower 文件时,反编译总是失败[被混淆了???])。
- JoJo 上不了的天堂(函数流程都分析了,但是对flag中间位的处理,静态分析感觉不对,也解密不出flag。尝试动态分析时,发现是创建了新的线程来处理flag。动态调试时,不知如何进入对应的线程[单步跟踪调试时,一不小心就退出了程序,感觉debug了])。
- baby_pyc(给我的感觉就是道披着逆向皮的RSA密码题,尝试性的做了下就放弃了)。
- wtf(比赛时间太久,这题就看了下,后面就没做了)
不得不说,ISCC 题量管够,质量好,毕竟我做出来的re题不多(等待大佬的wp)。。。
最后勉励一下,继续加油!!!