12 minutes
BUUCTF REVERSE [17~24]_Writeup
BUUCTF-REVERSE-3 writeup
8道练手的逆向题目,可以尝试做一做
0x0 [GWCTF 2019] pyre
应该是python逆向的题目
果然是一个pyc文件,使用pyc的逆向工具进行处理得到python的代码
# uncompyle6 version 3.7.4
# Python bytecode 2.7 (62211)
# Decompiled from: Python 3.8.8 (default, Apr 13 2021, 15:08:03) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: encode.py
# Compiled at: 2019-08-19 21:01:57
print 'Welcome to Re World!'
print 'Your input1 is your flag~'
l = len(input1)
for i in range(l):
num = ((input1[i] + i) % 128 + 128) % 128
code += num
for i in range(l - 1):
code[i] = code[i] ^ code[(i + 1)]
print code
code = ['\x1f', '\x12', '\x1d', '(', '0', '4', '\x01', '\x06', '\x14', '4', ',', '\x1b', 'U', '?', 'o', '6', '*', ':', '\x01', 'D', ';', '%', '\x13']
# okay decompiling .\attachment.pyc
对代码进行审计,发现input1
的数值没有给到,感觉应该是在input1
里面。根据代码进行逆推
code = ['\x1f', '\x12', '\x1d', '(', '0', '4', '\x01', '\x06', '\x14', '4', ',', '\x1b', 'U', '?', 'o', '6', '*', ':', '\x01', 'D', ';', '%', '\x13']
l = len(code)
for i in range(l-2,-1,-1):
code[i] = chr(ord(code[i])^ord(code[i+1]))
flag = ""
for i in range(l):
flag += chr((ord(code[i]) - i)%128)
print(flag)
运行脚本,得到flag
GWHT{Just_Re_1s_Ha66y!}
0x1 rsa
rsa还能出逆向题目:D!Crypto手狂喜
两个文件,一个enc文件和一个key文件,标准的RSA文件
可以使用python脚本来获取n的数值和e的数据
from Crypto.PublicKey import RSA
with open("pub.key",'r') as f:
public_key = RSA.import_key(f.read())
e = public_key.e
n = public_key.n
print(n)
得到n的数值
86934482296048119190666062003494800588905656017203025617216654058378322103517
然后使用yafu工具进行大数分解,得到p和q
P = 304008741604601924494328155975272418463
Q = 285960468890451637935629440372639283459
然后根据后续得到是数据写脚本求解
from Crypto.PublicKey import RSA
from Crypto.Util.number import long_to_bytes,bytes_to_long
from libnum import invmod
with open("pub.key",'r') as f:
public_key = RSA.import_key(f.read())
e = public_key.e
n = public_key.n
# print(n)
with open("flag.enc","rb") as f:
c = bytes_to_long(f.read())
p = 304008741604601924494328155975272418463
q = 285960468890451637935629440372639283459
phi = (p-1)*(q-1)
d = invmod(e,phi)
m = pow(c,d,n)
flag = long_to_bytes(m)
print(flag)
运行脚本得到flag
b'\x02\x9d {zR\x1e\x08\xe4\xe6\x18\x06\x00flag{decrypt_256}\n'
flag即为
flag{decrypt_256}
0x2 [ACTF新生赛] easyre
走下流程,查下壳
发现有壳,需要进行脱壳
使用UPX进行脱壳处理
然后再次查看信息
32位程序,已经成功脱壳,可以丢进ida pro里面玩耍了
int __cdecl main(int argc, const char **argv, const char **envp)
{
_BYTE v4[12]; // [esp+12h] [ebp-2Eh] BYREF
_DWORD v5[3]; // [esp+1Eh] [ebp-22h]
_BYTE v6[5]; // [esp+2Ah] [ebp-16h] BYREF
int v7; // [esp+2Fh] [ebp-11h]
int v8; // [esp+33h] [ebp-Dh]
int v9; // [esp+37h] [ebp-9h]
char v10; // [esp+3Bh] [ebp-5h]
int i; // [esp+3Ch] [ebp-4h]
__main();
qmemcpy(v4, "*F'\"N,\"(I?+@", sizeof(v4));
printf("Please input:");
scanf("%s", v6);
if ( v6[0] != 65 || v6[1] != 67 || v6[2] != 84 || v6[3] != 70 || v6[4] != 123 || v10 != 125 )
return 0;
v5[0] = v7;
v5[1] = v8;
v5[2] = v9;
for ( i = 0; i <= 11; ++i )
{
if ( v4[i] != _data_start__[*((char *)v5 + i) - 1] )
return 0;
}
printf("You are correct!");
return 0;
}
就是非常简单的算法了,进行简单的逆向算法就可以求解,写一个python脚本进行求解
data_list = [42,70,39,34,78,44,34,40,73,63,43,64]
data = r"}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)(" + chr(0x27) + r'&%$# !"'
flag_list = []
for i in data_list:
flag_list.append(data.find(chr(i))+1)
s = [chr(x + 1) for x in pos]
flag = ''.join(s)
print ('flag{'+flag+'}')
运行脚本就可以得到flag:
flag{U9X_1S_W6@T?}
0x3 CrackRTF
先查一下壳
32位无壳的pe文件,使用ida pro打开文件
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
DWORD v3; // eax
DWORD v4; // eax
char Str[260]; // [esp+4Ch] [ebp-310h] BYREF
int v7; // [esp+150h] [ebp-20Ch]
char String1[260]; // [esp+154h] [ebp-208h] BYREF
char Destination[260]; // [esp+258h] [ebp-104h] BYREF
memset(Destination, 0, sizeof(Destination));
memset(String1, 0, sizeof(String1));
v7 = 0;
printf("pls input the first passwd(1): ");
scanf("%s", Destination);
if ( strlen(Destination) != 6 )
{
printf("Must be 6 characters!\n");
ExitProcess(0);
}
v7 = atoi(Destination);
if ( v7 < 100000 )
ExitProcess(0);
strcat(Destination, "@DBApp");
v3 = strlen(Destination);
sub_40100A((BYTE *)Destination, v3, String1);
if ( !_strcmpi(String1, "6E32D0943418C2C33385BC35A1470250DD8923A9") )
{
printf("continue...\n\n");
printf("pls input the first passwd(2): ");
memset(Str, 0, sizeof(Str));
scanf("%s", Str);
if ( strlen(Str) != 6 )
{
printf("Must be 6 characters!\n");
ExitProcess(0);
}
strcat(Str, Destination);
memset(String1, 0, sizeof(String1));
v4 = strlen(Str);
sub_401019((BYTE *)Str, v4, String1);
if ( !_strcmpi("27019e688a4e62a649fd99cadaafdb4e", String1) )
{
if ( !(unsigned __int8)sub_40100F(Str) )
{
printf("Error!!\n");
ExitProcess(0);
}
printf("bye ~~\n");
}
}
return 0;
}
对代码进行审计分析,发现我们需要输入两次密码
首先看下第一次密码输入需要满足什么样的要求:
第一次输入需要进行链接然后使用md5加密然后获取的数值与某些数值相等,也就是:
MD5(xxxxxx@DBAPP) == 6E32D0943418C2C33385BC35A1470250DD8923A9
sub_40100A函数代码:
int __cdecl sub_401230(BYTE *pbData, DWORD dwDataLen, LPSTR lpString1)
{
int result; // eax
DWORD i; // [esp+4Ch] [ebp-28h]
CHAR String2[4]; // [esp+50h] [ebp-24h] BYREF
BYTE v6[20]; // [esp+54h] [ebp-20h] BYREF
DWORD pdwDataLen; // [esp+68h] [ebp-Ch] BYREF
HCRYPTHASH phHash; // [esp+6Ch] [ebp-8h] BYREF
HCRYPTPROV phProv; // [esp+70h] [ebp-4h] BYREF
if ( !CryptAcquireContextA(&phProv, 0, 0, 1u, 0xF0000000) )
return 0;
if ( CryptCreateHash(phProv, 0x8004u, 0, 0, &phHash) )
{
if ( CryptHashData(phHash, pbData, dwDataLen, 0) )
{
CryptGetHashParam(phHash, 2u, v6, &pdwDataLen, 0);
*lpString1 = 0;
for ( i = 0; i < pdwDataLen; ++i )
{
wsprintfA(String2, "%02X", v6[i]);
lstrcatA(lpString1, String2);
}
CryptDestroyHash(phHash);
CryptReleaseContext(phProv, 0);
result = 1;
}
else
{
CryptDestroyHash(phHash);
CryptReleaseContext(phProv, 0);
result = 0;
}
}
else
{
CryptReleaseContext(phProv, 0);
result = 0;
}
return result;
}
程序直接调用WIN32的API函数进行调用来进行MD5的加密
使用在线的MD5工具进行解密获得:
123321@DBApp
下面解决一下第二次加密的问题,第二次加密显然也是一个MD5的比较问题,即:
MD5(xxxxxx123321@DBApp@DBAPP) == 27019e688a4e62a649fd99cadaafdb4e
同样使用上面那个MD5的工具进行求解就可以得到数据:
~!3a@0123321@DBApp
现在得到了所有的密码,是不是可以运行一下程序来得到结果:
运行程序得到了一个rtf文件,打开rtf文件就能得到flag啦:
Flag{N0_M0re_Free_Bugs}
0x4 [2019红帽杯] easyRE
得到一个ELF文件,可以确定是Linux系统的文件,虽然Linux系统的大多数文件是不带壳的,但是为了以防万一还是查下程序信息:
64位程序,果然是没有壳的程序。使用x64 IDA pro打开一下程序,看看程序内部
// positive sp value has been detected, the output may be wrong!
void __fastcall __noreturn start(__int64 a1, __int64 a2, int a3)
{
__int64 v3; // rax
int v4; // esi
__int64 v5; // [rsp-8h] [rbp-8h] BYREF
void *retaddr; // [rsp+0h] [rbp+0h] BYREF
v4 = v5;
v5 = v3;
sub_401BC0(
(unsigned int)sub_4009C6,
v4,
(unsigned int)&retaddr,
(unsigned int)sub_402080,
(unsigned int)sub_402110,
a3,
(__int64)&v5);
}
好像是找不到什么具体的内容,使用字符串检索定位到关键函数的位置
__int64 sub_4009C6()
{
__int64 result; // rax
int i; // [rsp+Ch] [rbp-114h]
__int64 v2; // [rsp+10h] [rbp-110h]
__int64 v3; // [rsp+18h] [rbp-108h]
__int64 v4; // [rsp+20h] [rbp-100h]
__int64 v5; // [rsp+28h] [rbp-F8h]
__int64 v6; // [rsp+30h] [rbp-F0h]
__int64 v7; // [rsp+38h] [rbp-E8h]
__int64 v8; // [rsp+40h] [rbp-E0h]
__int64 v9; // [rsp+48h] [rbp-D8h]
__int64 v10; // [rsp+50h] [rbp-D0h]
__int64 v11; // [rsp+58h] [rbp-C8h]
char v12[13]; // [rsp+60h] [rbp-C0h] BYREF
char v13[4]; // [rsp+6Dh] [rbp-B3h] BYREF
char v14[19]; // [rsp+71h] [rbp-AFh] BYREF
char v15[32]; // [rsp+90h] [rbp-90h] BYREF
int v16; // [rsp+B0h] [rbp-70h]
char v17; // [rsp+B4h] [rbp-6Ch]
char v18[72]; // [rsp+C0h] [rbp-60h] BYREF
unsigned __int64 v19; // [rsp+108h] [rbp-18h]
v19 = __readfsqword(0x28u);
qmemcpy(v12, "Iodl>Qnb(ocy", 12);
v12[12] = 127;
qmemcpy(v13, "y.i", 3);
v13[3] = 127;
qmemcpy(v14, "d`3w}wek9{iy=~yL@EC", sizeof(v14));
memset(v15, 0, sizeof(v15));
v16 = 0;
v17 = 0;
sub_4406E0(0LL, v15, 37LL);
v17 = 0;
if ( sub_424BA0(v15) == 36 )
{
for ( i = 0; i < (unsigned __int64)sub_424BA0(v15); ++i )
{
if ( (unsigned __int8)(v15[i] ^ i) != v12[i] )
{
result = 4294967294LL;
goto LABEL_13;
}
}
sub_410CC0("continue!");
memset(v18, 0, 0x40uLL);
v18[64] = 0;
sub_4406E0(0LL, v18, 64LL);
v18[39] = 0;
if ( sub_424BA0(v18) == 39 )
{
v2 = sub_400E44(v18);
v3 = sub_400E44(v2);
v4 = sub_400E44(v3);
v5 = sub_400E44(v4);
v6 = sub_400E44(v5);
v7 = sub_400E44(v6);
v8 = sub_400E44(v7);
v9 = sub_400E44(v8);
v10 = sub_400E44(v9);
v11 = sub_400E44(v10);
if ( !(unsigned int)sub_400360(v11, off_6CC090) )
{
sub_410CC0("You found me!!!");
sub_410CC0("bye bye~");
}
result = 0LL;
}
else
{
result = 4294967293LL;
}
}
else
{
result = 0xFFFFFFFFLL;
}
LABEL_13:
if ( __readfsqword(0x28u) != v19 )
sub_444020();
return result;
}
找到关键函数,下面就是对关键函数进行分析来寻找线索
简单对代码进行审计,可以发现有很多混淆来防止我们进行正确的逆向分析操作
来,让我们跳进第一个坑:
v19 = __readfsqword(0x28u);
qmemcpy(v12, "Iodl>Qnb(ocy", 12);
v12[12] = 127;
qmemcpy(v13, "y.i", 3);
v13[3] = 127;
qmemcpy(v14, "d`3w}wek9{iy=~yL@EC", sizeof(v14));
memset(v15, 0, sizeof(v15));
v16 = 0;
v17 = 0;
sub_4406E0(0LL, v15, 37LL);
v17 = 0;
if ( sub_424BA0(v15) == 36 )
{
for ( i = 0; i < (unsigned __int64)sub_424BA0(v15); ++i )
{
if ( (unsigned __int8)(v15[i] ^ i) != v12[i] )
{
result = 4294967294LL;
goto LABEL_13;
}
}
sub_410CC0("continue!");
写一个python脚本迭代出假flag:
data = []
v12 = "Iodl>Qnb(ocy"
insert_v = 127;
v13 = "y.i"
v14 = "d`3w}wek9{iy=~yL@EC"
flag = ""
for i in v12:
data.append(ord(i))
data.append(insert_v)
for i in v13:
data.append(ord(i))
data.append(insert_v)
for i in v14:
data.append(ord(i))
for i in range(36):
flag +=chr(data[i]^i)
print(flag)
运行脚本得到fake flag:
Info:The first four chars are `flag`
然后走进下面一个坑:
(”吾愿称之为base64之坑“ XD )
提取数据:
Vm0wd2VHUXhTWGhpUm1SWVYwZDRWVll3Wkc5WFJsbDNXa1pPVlUxV2NIcFhhMk0xVmpKS1NHVkdXbFpOYmtKVVZtcEtTMUl5VGtsaVJtUk9ZV3hhZVZadGVHdFRNVTVYVW01T2FGSnRVbGhhVjNoaFZWWmtWMXBFVWxSTmJFcElWbTAxVDJGV1NuTlhia0pXWWxob1dGUnJXbXRXTVZaeVdrWm9hVlpyV1hwV1IzaGhXVmRHVjFOdVVsWmlhMHBZV1ZSR1lWZEdVbFZTYlhSWFRWWndNRlZ0TVc5VWJGcFZWbXR3VjJKSFVYZFdha1pXWlZaT2NtRkhhRk5pVjJoWVYxZDBhMVV3TlhOalJscFlZbGhTY1ZsclduZGxiR1J5VmxSR1ZXSlZjRWhaTUZKaFZqSktWVkZZYUZkV1JWcFlWV3BHYTFkWFRrZFRiV3hvVFVoQ1dsWXhaRFJpTWtsM1RVaG9hbEpYYUhOVmJUVkRZekZhY1ZKcmRGTk5Wa3A2VjJ0U1ExWlhTbFpqUldoYVRVWndkbFpxUmtwbGJVWklZVVprYUdFeGNHOVhXSEJIWkRGS2RGSnJhR2hTYXpWdlZGVm9RMlJzV25STldHUlZUVlpXTlZadE5VOVdiVXBJVld4c1dtSllUWGhXTUZwell6RmFkRkpzVWxOaVNFSktWa1phVTFFeFduUlRhMlJxVWxad1YxWnRlRXRXTVZaSFVsUnNVVlZVTURrPQ==
进行十次base64解密得到一个博客地址:https://bbs.pediy.com/thread-254172.htm
点开博客看一下,可以看到这段话:
所谓“让对手不要走正确的破解之路”
讲人话 就是“把对手往沟里带”
但对手并不傻 很多时候攻击方比防守方更聪明
要想带对手进沟 防守方必须首先透彻了解攻击方 特别是其人性弱点(知道为什么是马克思主义学院了吧)
Good, 现在知道自己掉坑里面了, 两个坑都掉了一遍所以flag在哪呢?
我们去找下主函数
发现周围存在一个奇奇怪怪的函数,会不会就是这个函数呢
点开看看:
unsigned __int64 sub_400D35()
{
unsigned __int64 result; // rax
unsigned int v1; // [rsp+Ch] [rbp-24h]
int i; // [rsp+10h] [rbp-20h]
int j; // [rsp+14h] [rbp-1Ch]
unsigned int v4; // [rsp+24h] [rbp-Ch]
unsigned __int64 v5; // [rsp+28h] [rbp-8h]
v5 = __readfsqword(0x28u);
v1 = sub_43FD20(0LL) - qword_6CEE38;
for ( i = 0; i <= 1233; ++i )
{
sub_40F790(v1);
sub_40FE60();
sub_40FE60();
v1 = sub_40FE60() ^ 0x98765432;
}
v4 = v1;
if ( ((unsigned __int8)v1 ^ byte_6CC0A0[0]) == 102 && (HIBYTE(v4) ^ (unsigned __int8)byte_6CC0A3) == 103 )
{
for ( j = 0; j <= 24; ++j )
sub_410E90((unsigned __int8)(byte_6CC0A0[j] ^ *((_BYTE *)&v4 + j % 4)));
}
result = __readfsqword(0x28u) ^ v5;
if ( result )
sub_444020();
return result;
}
应该就是简单的异或操作了,需要提取数据来进行异或来得到flag
提取数据并写一个异或脚本就就可以得到flag
data = [
0x40, 0x35, 0x20, 0x56, 0x5D, 0x18, 0x22, 0x45, 0x17, 0x2F,
0x24, 0x6E, 0x62, 0x3C, 0x27, 0x54, 0x48, 0x6C, 0x24, 0x6E,
0x72, 0x3C, 0x32, 0x45, 0x5B
]
s = "flag"
key = ''
flag = ''
for i in range(4):
key += chr(ord(s[i])^data[i])
for i in range(len(data)):
flag += chr(data[i]^ord(key[i%4]))
print(flag)
运行脚本就能得到flag,终于是over了
flag{Act1ve_Defen5e_Test}
0x5 [ACTF新生赛2020] rome
首先,查一下文件的信息
32位程序,没有壳,丢进IDA prio里面看看:
int func()
{
int result; // eax
int v1[4]; // [esp+14h] [ebp-44h]
unsigned __int8 v2; // [esp+24h] [ebp-34h] BYREF
unsigned __int8 v3; // [esp+25h] [ebp-33h]
unsigned __int8 v4; // [esp+26h] [ebp-32h]
unsigned __int8 v5; // [esp+27h] [ebp-31h]
unsigned __int8 v6; // [esp+28h] [ebp-30h]
int v7; // [esp+29h] [ebp-2Fh]
int v8; // [esp+2Dh] [ebp-2Bh]
int v9; // [esp+31h] [ebp-27h]
int v10; // [esp+35h] [ebp-23h]
unsigned __int8 v11; // [esp+39h] [ebp-1Fh]
char v12[29]; // [esp+3Bh] [ebp-1Dh] BYREF
strcpy(v12, "Qsw3sj_lz4_Ujw@l");
printf("Please input:");
scanf("%s", &v2);
result = v2;
if ( v2 == 65 )
{
result = v3;
if ( v3 == 67 )
{
result = v4;
if ( v4 == 84 )
{
result = v5;
if ( v5 == 70 )
{
result = v6;
if ( v6 == 123 )
{
result = v11;
if ( v11 == 125 )
{
v1[0] = v7;
v1[1] = v8;
v1[2] = v9;
v1[3] = v10;
*(_DWORD *)&v12[17] = 0;
while ( *(int *)&v12[17] <= 15 )
{
if ( *((char *)v1 + *(_DWORD *)&v12[17]) > 64 && *((char *)v1 + *(_DWORD *)&v12[17]) <= 90 )
*((_BYTE *)v1 + *(_DWORD *)&v12[17]) = (*((char *)v1 + *(_DWORD *)&v12[17]) - 51) % 26 + 65;
if ( *((char *)v1 + *(_DWORD *)&v12[17]) > 96 && *((char *)v1 + *(_DWORD *)&v12[17]) <= 122 )
*((_BYTE *)v1 + *(_DWORD *)&v12[17]) = (*((char *)v1 + *(_DWORD *)&v12[17]) - 79) % 26 + 97;
++*(_DWORD *)&v12[17];
}
*(_DWORD *)&v12[17] = 0;
while ( *(int *)&v12[17] <= 15 )
{
result = (unsigned __int8)v12[*(_DWORD *)&v12[17]];
if ( *((_BYTE *)v1 + *(_DWORD *)&v12[17]) != (_BYTE)result )
return result;
++*(_DWORD *)&v12[17];
}
result = printf("You are correct!");
}
}
}
}
}
}
return result;
}
对代码进行简单的审计,可以发现,程序的加密算法是类似与凯撒加密的位移方式,可以根据程序的这一特点写个Python脚本进行求解来得到flag的数据:
import string
data = [81,115,119,51,115,106,95,108,122,52,95,85,106,119,64,108]
Lower = string.ascii_lowercase
Upper = string.ascii_uppercase
flag = ""
for i in data:
if i > 64 and i <= 90:
flag += Upper[i-14-65]
elif i > 96 and i <= 122:
flag += Lower[i-18-97]
else:
flag += chr(i)
print ('flag{'+flag+'}')
运行脚本,得到flag:
flag{Cae3ar_th4_Gre@t}
0x6 [FlareOn4] login
发现文件是一个html文件,应该是需要进行源码阅读的题目,然后逆向算法
<!DOCTYPE Html />
<html>
<head>
<title>FLARE On 2017</title>
</head>
<body>
<input type="text" name="flag" id="flag" value="Enter the flag" />
<input type="button" id="prompt" value="Click to check the flag" />
<script type="text/javascript">
document.getElementById("prompt").onclick = function () {
var flag = document.getElementById("flag").value;
var rotFlag = flag.replace(/[a-zA-Z]/g, function (c) {
return String.fromCharCode((c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26);
});
if ("PyvragFvqrYbtvafNerRnfl@syner-ba.pbz" == rotFlag) {
alert("Correct flag!");
} else {
alert("Incorrect flag, rot again");
}
}
</script>
</body>
</html>
源码的核心逻辑是在JavaScript的代码中,看来题目要求我们可以能够对算法进行逆向分析,来得到flag数据
代码进行的操作无非就是±13的操作。
代码会区分大小写分别进行相应的操作来得到flag,如果字母+13小于字母表最后一位字母,字母最终就+13,否则字母最终就-13。逻辑非常简单,写个简单的逆向算法就能进行求解:
cipher = "PyvragFvqrYbtvafNerRnfl@syner-ba.pbz"
m = ""
for i in cipher:
if ord(i) >= 65 and ord(i) <= 90:
if ord(i)-13 < 65:
m += chr(ord(i)+13)
else:
m += chr(ord(i)-13)
elif ord(i) >= 97 and ord(i) <= 122:
if ord(i)-13 < 97:
m += chr(ord(i)+13)
else:
m += chr(ord(i)-13)
else:
m += i
print(m)
运行脚本,就能得到flag:
ClientSideLoginsAreEasy@flare-on.com
唔~题目好像还有个hint可以看一下:
Hint:本题解出相应字符串后请用flag{}包裹,形如:flag{123456@flare-on.com}
所以,这道题目的flag应该就是:
flag{ClientSideLoginsAreEasy@flare-on.com}
0x7 [GUET-CTF2019] re
拿到程序,先看看程序的信息:
发现程序有壳,而且是一个64位的ELF程序
需要进行脱壳处理,程序使用的是最基本的UPX的壳,可以使用UPX进行简单脱壳:
upx -d re
脱壳后,然后使用Exeinfo再次进行查看
壳已经被脱掉,然后使用x64 IDA pro进行查看程序
// positive sp value has been detected, the output may be wrong!
void __fastcall __noreturn start(__int64 a1, __int64 a2, int a3)
{
__int64 v3; // rax
int v4; // esi
__int64 v5; // [rsp-8h] [rbp-8h] BYREF
void *retaddr; // [rsp+0h] [rbp+0h] BYREF
v4 = v5;
v5 = v3;
sub_4016C0(
(unsigned int)sub_400E28,
v4,
(unsigned int)&retaddr,
(unsigned int)sub_401B80,
(unsigned int)sub_401C10,
a3,
(__int64)&v5);
}
发现程序入口并找不到什么东西,应该是将入口函数进行混淆和隐藏了
使用字符串检索来获取相应的信息,来定位到主函数:
__int64 __fastcall sub_400E28(__int64 a1, int a2, int a3, int a4, int a5, int a6)
{
int v6; // edx
int v7; // ecx
int v8; // er8
int v9; // er9
__int64 result; // rax
__int64 v11; // [rsp+0h] [rbp-30h] BYREF
unsigned __int64 v12; // [rsp+28h] [rbp-8h]
v12 = __readfsqword(0x28u);
sub_40F950((unsigned int)"input your flag:", a2, a3, a4, a5, a6, 0LL, 0LL, 0LL, 0LL);
sub_40FA80((unsigned int)"%s", (unsigned int)&v11, v6, v7, v8, v9, v11);
if ( (unsigned int)sub_4009AE(&v11) )
sub_410350("Correct!");
else
sub_410350("Wrong!");
result = 0LL;
if ( __readfsqword(0x28u) != v12 )
sub_443550();
return result;
}
主函数中的一些关键性的函数都被进行了混淆操作,代码进行审计分析
v11是关键输入函数,然后sub_40FA80
是关键性的函数,对sub_40FA80
分析:
_BOOL8 __fastcall sub_4009AE(char *a1)
{
if ( 1629056 * *a1 != 166163712 )
return 0LL;
if ( 6771600 * a1[1] != 731332800 )
return 0LL;
if ( 3682944 * a1[2] != 357245568 )
return 0LL;
if ( 10431000 * a1[3] != 1074393000 )
return 0LL;
if ( 3977328 * a1[4] != 489211344 )
return 0LL;
if ( 5138336 * a1[5] != 518971936 )
return 0LL;
if ( 7532250 * a1[7] != 406741500 )
return 0LL;
if ( 5551632 * a1[8] != 294236496 )
return 0LL;
if ( 3409728 * a1[9] != 177305856 )
return 0LL;
if ( 13013670 * a1[10] != 650683500 )
return 0LL;
if ( 6088797 * a1[11] != 298351053 )
return 0LL;
if ( 7884663 * a1[12] != 386348487 )
return 0LL;
if ( 8944053 * a1[13] != 438258597 )
return 0LL;
if ( 5198490 * a1[14] != 249527520 )
return 0LL;
if ( 4544518 * a1[15] != 445362764 )
return 0LL;
if ( 3645600 * a1[17] != 174988800 )
return 0LL;
if ( 10115280 * a1[16] != 981182160 )
return 0LL;
if ( 9667504 * a1[18] != 493042704 )
return 0LL;
if ( 5364450 * a1[19] != 257493600 )
return 0LL;
if ( 13464540 * a1[20] != 767478780 )
return 0LL;
if ( 5488432 * a1[21] != 312840624 )
return 0LL;
if ( 14479500 * a1[22] != 1404511500 )
return 0LL;
if ( 6451830 * a1[23] != 316139670 )
return 0LL;
if ( 6252576 * a1[24] != 619005024 )
return 0LL;
if ( 7763364 * a1[25] != 372641472 )
return 0LL;
if ( 7327320 * a1[26] != 373693320 )
return 0LL;
if ( 8741520 * a1[27] != 498266640 )
return 0LL;
if ( 8871876 * a1[28] != 452465676 )
return 0LL;
if ( 4086720 * a1[29] != 208422720 )
return 0LL;
if ( 9374400 * a1[30] == 515592000 )
return 5759124 * a1[31] == 719890500;
return 0LL;
}
写个C语言的flag生成器来生成flag
#include <stdio.h>
int main()
{
char a1[30] = {0};
*a1 = 166163712 / 1629056;
a1[1] = 731332800 / 6771600;
a1[2] = 357245568 / 3682944;
a1[3] = 1074393000 / 10431000;
a1[4] = 489211344 / 3977328;
a1[5] = 518971936 / 5138336;
a1[7] = 406741500 / 7532250;
a1[8] = 294236496 / 5551632;
a1[9] = 177305856 / 3409728;
a1[10] = 650683500 / 13013670;
a1[11] = 298351053 / 6088797;
a1[12] = 386348487 / 7884663;
a1[13] = 438258597 / 8944053;
a1[14] = 249527520 / 5198490;
a1[15] = 445362764 / 4544518;
a1[17] = 174988800 / 3645600;
a1[16] = 981182160 / 10115280;
a1[18] = 493042704 / 9667504;
a1[19] = 257493600 / 5364450;
a1[20] = 767478780 / 13464540;
a1[21] = 312840624 / 5488432;
a1[22] = 1404511500 / 14479500;
a1[23] = 316139670 / 6451830;
a1[24] = 619005024 / 6252576;
a1[25] = 372641472 / 7763364;
a1[26] = 373693320 / 7327320;
a1[27] = 498266640 / 8741520;
a1[28] = 452465676 / 8871876;
a1[29] = 208422720 / 4086720;
a1[30] = 515592000 / 9374400;
a1[31] = 719890500 / 5759124;
for(int i=0;i<32;++i)
{
if(a1[i] == 0) printf("*");
else printf("%c",a1[i]);
}
printf("\n");
}
编译并运行来得到flag:
flag{e*65421110ba03099a1c039337}
由于有一位的字符未知,需要进行爆破来得到flag:
flag{e165421110ba03099a1c039337}