困扰我好几天,今天华为的本到货了,安上系统拿它来练手。
这个程序流程很简单,输入64字节的flag然后每4字节会去做个200多行异或移位XXX的加密后生成20字节的密文,与已知密文比较。由于每次只有4字节而且加密过于复杂,所以方向应该是爆破。一直也搜不到wp,还真想给它爆出来。
int __cdecl main(int argc, const char **argv, const char **envp)
{
__m128i si128; // xmm0
__int64 v4; // rdx
__int64 v5; // r8
int *v6; // rcx
__int64 v7; // rax
__int64 v8; // rax
__int64 v9; // rdi
signed __int64 v10; // rbx
int v11; // eax
__int64 v12; // r8
int *v13; // rax
const char *v14; // rdx
_BYTE v16[256]; // [rsp+20h] [rbp-118h] BYREF
si128 = _mm_load_si128((const __m128i *)&xmmword_1400046A0);
dword_140006730 = 2043430169;
dword_140006734 = 2043430169;
dword_140006738 = 2043430169;
dword_14000673C = 2043430169;
dword_140006740 = 2043430169;
dword_140006744 = 2043430169;
dword_140006748 = 2043430169;
dword_14000674C = 2043430169;
dword_140006750 = 2043430169;
dword_140006754 = 2043430169;
dword_140006758 = 2043430169;
dword_14000675C = 2043430169;
dword_140006760 = 2043430169;
dword_140006764 = 2043430169;
dword_140006768 = 2043430169;
dword_14000676C = 2043430169;
stru_140006770[0] = si128;
dword_140006710 = 1937774191;
stru_140006770[1] = si128;
dword_140006714 = 1226093241;
stru_140006770[2] = si128;
dword_140006718 = 388252375;
stru_140006770[3] = si128;
dword_14000671C = -628488704;
stru_140006770[4] = si128;
dword_140006720 = -1452330820;
stru_140006770[5] = si128;
dword_140006724 = 372324522;
stru_140006770[6] = si128;
dword_140006728 = -477237683;
stru_140006770[7] = si128;
dword_14000672C = -1325724082;
stru_140006770[8] = si128;
stru_140006770[9] = si128;
stru_140006770[10] = si128;
stru_140006770[11] = si128;
sub_140001DE0(std::cout, "emm~~~ flag please:\n", envp);
memset(v16, 0, sizeof(v16));
sub_140002080(std::cin, v4, v16);
v6 = (int *)v16;
v7 = -1i64;
do
++v7;
while ( v16[v7] );
if ( v7 == 64 )
{
v9 = 0i64;
v10 = &unk_140004480 - (_UNKNOWN *)&dword_140006710;
while ( 2 )
{
qword_140006864 = 0i64;
*(__int64 *)((char *)&qword_140006864 + 4) = 0i64;
v11 = *(_DWORD *)&v16[v9];
xmmword_140006834 = 0i64;
dword_140006830 = v11;
xmmword_140006844 = 0i64;
LOBYTE(xmmword_140006834) = 0x80;
xmmword_140006854 = 0i64;
dword_14000686C = 0x20000000;
sub_1400011A0(v6);
v13 = &dword_140006710;
do
{
v6 = (int *)*(unsigned int *)((char *)v13 + v10);
if ( *v13 != (_DWORD)v6 )
{
v14 = "hehe~~~\n "; //第2个patch点,把这改为jmp 到开始实现循环
goto LABEL_12;
}
++v13;
}
while ( (__int64)v13 < (__int64)&dword_140006730 );
v9 += 4i64;
v10 += 32i64;
if ( v9 < 64 ) //第1个patch点,每次改到比已知大4,爆破4字节
continue;
break;
}
v14 = "VeryGood!\nNow,\nYou know who is the best houdaode Propositioner le~~~\n ";
LABEL_12:
sub_140001DE0(std::cout, v14, v12);
}
else
{
v8 = sub_140001DE0(std::cout, "hehe~~~\n ", v5);
std::basic_ostream<char,std::char_traits<char>>::operator<<(v8, sub_140001FB0);
}
return 0;
}
但是win程序每次调起太麻烦,所以导致无法爆破。后来终于想到个办法(没机子也没闲着),把程序patch两个地方。
- 第1处在长度比较的位置,每次改为比已爆出长度大4,用来爆下4个字节;
- 第2处在不正确时返回hehe时,这里字节比较长改为jmp 0xfffffd8d 跳到开始1a09,这样程序在这里成一个循环不再退出,时间成百倍的节省。
然后用subprocess调起程序(后来有个问题,写回原程序时会发生无权写入,所以每次新生成一个文件)
from subprocess import *
import string
data = list(open('cppc.exe', 'rb').read())
letter = ''
letter+= '_{}'
letter+= string.ascii_lowercase
letter+= string.ascii_uppercase
#letter+= string.digits
#flag = 'ctfshow{as_we_all_know_AFox_is_the_most_houdaode_Propositioner_}'
flag = ''
def getflag(i):
global flag
for i in letter:
for j in letter:
for k in letter:
for l in letter:
tflag = flag+i+j+k+l+'0'*(64-4-len(flag)) +'\n'
p.stdin.write(tflag)
p.stdin.flush()
retstr = p.stdout.readline()
if 'VeryGood!' in retstr:
flag += i+j+k+l
print(flag)
p.stdout.close()
p.stdin.close()
return True
for i in range(len(flag ),64,4):
data[0x106b] = i+4
open(f'cppc_{i}.exe', 'wb').write(bytes(data))
p = Popen(f'.\cppc_{i}.exe', stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True, shell=True )
p.stdout.readline()
p.stdout.flush()
if getflag(i) :
print(flag)
因为这里大部分flag都是小字字母,所在在不的成功的时候再把大写加进来会省不少时间。64字符多恶心呀。
|