1.WOW:
首先用die查看一下,选择32位的ida打开:
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[64]; // [esp+0h] [ebp-168h] BYREF
char v5[64]; // [esp+40h] [ebp-128h] BYREF
int v6[17]; // [esp+80h] [ebp-E8h] BYREF
char v7[64]; // [esp+C4h] [ebp-A4h] BYREF
char v8[16]; // [esp+104h] [ebp-64h] BYREF
_QWORD Buf2[5]; // [esp+114h] [ebp-54h] BYREF
char v10[4]; // [esp+13Ch] [ebp-2Ch] BYREF
int v11; // [esp+140h] [ebp-28h]
int v12; // [esp+144h] [ebp-24h]
int v13; // [esp+148h] [ebp-20h]
int v14; // [esp+14Ch] [ebp-1Ch]
int v15; // [esp+150h] [ebp-18h]
int v16; // [esp+154h] [ebp-14h]
int v17; // [esp+158h] [ebp-10h]
int v18; // [esp+15Ch] [ebp-Ch]
int v19; // [esp+160h] [ebp-8h]
int i; // [esp+164h] [ebp-4h]
memset(v6, 0, sizeof(v6));
v6[0] = 68;
memset(v8, 0, sizeof(v8));
*(_DWORD *)v10 = 0;
v11 = 0;
v12 = 0;
v13 = 0;
v14 = 0;
v15 = 0;
v16 = 0;
v17 = 0;
v18 = 0;
v19 = 0;
memset(Buf2, 0, sizeof(Buf2));
sub_401940(&unk_404D60);
sub_401850(&unk_404D60, &unk_4051A0);
sub_4012B0();
sub_401A50(Format);
sub_401AC0(a40s, (char)v10);
for ( i = 0; i < 4; ++i )
{
sub_401850(&v10[8 * i], v5);
sub_401410(v5, v7);
sub_4018D0(v7, &Buf2[i]);
}
sub_4019B0((char *)&loc_401C73 + 51);
sub_4019B0(&loc_401C73);
if ( !memcmp(byte_404D40, Buf2, 0x20u) )
sub_401A50(aYouWin);
else
sub_401A50(aError);
memset(v10, 0, 0x28u);
for ( i = 0; i < 4; ++i )
{
sub_401850(&Buf2[i], v7);
sub_401630(v7, v4);
sub_4018D0(v4, &v10[8 * i]);
}
return 0;
}
分析可知,是一段对称加密,所以我们只需要把加密的数据在调试的时候放进对比的位置就能让程序执行加载flag的步骤,并且把flag放在内存里面。
2.ezvm
由题目名字可知,这是一道VM的题,先有64位ida打开程序看一下:
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
sub_40B110(argc, argv, envp);
sub_497E40(&unk_4A1E00, "input your flag:", 16i64);
sub_4993C0(&unk_4A1E00);
while ( 1 )
{
v3 = dword_49F020[dword_49F020[0] + 109];
if ( v3 == -1 )
return 0;
switch ( v3 )
{
case 0:
sub_401770(&dword_49F020[3], &dword_49F020[2]);
break;
case 1:
sub_401775(&dword_49F020[2]);
break;
case 2:
sub_401779(&dword_49F020[2]);
break;
case 3:
sub_40178C(&dword_49F020[3], &dword_49F020[7]);
break;
case 4:
sub_401798();
break;
case 5:
sub_4017BB();
break;
case 6:
sub_4017DE();
break;
case 7:
sub_401825();
break;
case 8:
sub_40183B();
break;
case 9:
sub_401851();
break;
case 10:
sub_401867();
break;
case 11:
sub_40187D();
break;
case 12:
sub_4018BE((unsigned int)dword_49F020[6]);
break;
case 13:
sub_4018CE((unsigned int)dword_49F020[6]);
break;
case 14:
sub_401791((unsigned int)dword_49F020[2]);
break;
case 15:
sub_401893((unsigned int)dword_49F020[3], (unsigned int)dword_49F020[5]);
break;
case 16:
sub_4018DE(&dword_49F020[3]);
break;
case 17:
sub_4018F3(&dword_49F020[3]);
break;
case 18:
sub_401904();
break;
case 19:
sub_40193A();
break;
case 20:
sub_401953();
break;
case 21:
sub_40177D();
break;
default:
break;
}
++dword_49F020[0];
}
}
对每一个case进行翻译即可
大致逻辑应该是输入每一个字符,左移一位,和一个值异或比较
可以用爆破来做:
# -*- coding:utf-8 -*-
s="hgame{abcdefghijklmnopqrstuvwxy}"
# 对输入的处理逻辑:输入的每个字符,左移1位,和一个值异或,然后比较
print(hex((ord("h")<<1)^0x5e))# 0x8e
print(hex((ord("g")<<1)^0x46))# 0x88
# my_res: 构造的输入s,经过程序处理后的结果
my_res=[0x8e,0x88,0xa3,0x99,0xc4,0xa5,0x8b,0xdb,0x97,0x96,0xfc,0xfb,0xe7,0x91,0xb1,0xef,0xb2,0xe3,0xcf,0xc4,0x85,0xde,0xc0,0xb4,0xa0,0xb6,0xdf,0xa2,0xad,0xd3,0x92,0xc1]
# 逆向对输入的处理逻辑,得到输入的每个字符左移1位后,要去异或的值的集合
xor_box=[]
for i in range(len(s)):
tmp=(ord(s[i])<<1)&0xff
xor_box.append(tmp^my_res[i])
# res: 真正的密文
res=[0x8e,0x88,0xa3,0x99,0xc4,0xa5,0xc3,0xdd,0x19,0xec,0x6c,0x9b,0xf3,0x1b,0x8b,0x5b,0x3e,0x9b,0xf1,0x86,0xf3,0xf4,0xa4,0xf8,0xf8,0x98,0xab,0x86,0x89,0x61,0x22,0xc1]
flag=""
for i in range(len(res)):
for j in range(32,128):
if (j<<1)^xor_box[i]==res[i]:
flag+=chr(j)
print(flag)
# hgame{Ea$Y-Vm-t0-PrOTeCT_cOde!!}
3.server
先用die查一下,是64位的程序,没有壳的保护,并且是用Go语言写的
找到主函数main_main
while ( (unsigned __int64)&retaddr <= *(_QWORD *)(v4 + 16) )
runtime_morestack_noctxt();
这一个循环是检查栈的可用空间,能申请更多的空间
然后我们发现,主函数的回调函数
main_HttpHandleFuncvoid __fastcall net_http__ptr_Server_ListenAndServe()
{
__int64 v0; // rax
__int64 v1; // r14
__int64 v2; // rcx
__int64 v3; // [rsp-20h] [rbp-28h]
void *retaddr; // [rsp+8h] [rbp+0h] BYREF
__int64 v6; // [rsp+10h] [rbp+8h]
while ( (unsigned __int64)&retaddr <= *(_QWORD *)(v1 + 16) )
{
v6 = v0;
runtime_morestack_noctxt();
v0 = v6;
}
if ( !*(_DWORD *)(v0 + 120) )
{
v3 = net_Listen();
if ( !v2 )
net_http__ptr_Server_Serve(v3);
}
}
我们用8.3的ida运行一下试试,看来8.3还是比7.7聪明一点:
__int64 __golang main_HttpHandleFunc(__int64 a1, int a2, http_Request *a3, int a4, int a5)
{
__int64 v5; // r14
char *ptr; // rax
signed __int64 len; // rbx
int v8; // r8d
int v9; // r9d
int v10; // r10d
int v11; // r11d
string v12; // kr00_16
__int64 v13; // rcx
unsigned __int64 v14; // rdx
unsigned __int64 v15; // rsi
int v16; // eax
int v17; // r10d
int v18; // r11d
__int64 v20; // rbx
int v21; // r8d
int v22; // r9d
int v23; // r10d
int v24; // r11d
__int64 v25; // rax
int v26; // r8d
int v27; // r9d
int v28; // r10d
int v29; // r11d
int v30; // eax
int v31; // r10d
int v32; // r11d
int v33; // eax
int v34; // r10d
int v35; // r11d
__int64 v36; // [rsp+0h] [rbp-1368h]
__int64 v37; // [rsp+0h] [rbp-1368h]
__int64 v38; // [rsp+0h] [rbp-1368h]
__int64 v39[154]; // [rsp+8h] [rbp-1360h] BYREF
size_t v40; // [rsp+4D8h] [rbp-E90h]
__int64 v41; // [rsp+4E0h] [rbp-E88h]
unsigned __int64 v42; // [rsp+4E8h] [rbp-E80h]
__int64 v43; // [rsp+4F0h] [rbp-E78h]
char v44[1216]; // [rsp+4F8h] [rbp-E70h] BYREF
__int64 v45; // [rsp+9B8h] [rbp-9B0h] BYREF
char v46[1216]; // [rsp+9C0h] [rbp-9A8h] BYREF
__int64 v47; // [rsp+E80h] [rbp-4E8h] BYREF
char v48[1216]; // [rsp+E88h] [rbp-4E0h] BYREF
char *v49; // [rsp+1348h] [rbp-20h]
__int64 v50; // [rsp+1350h] [rbp-18h]
_ptr_http_Request v51; // [rsp+1358h] [rbp-10h]
void *retaddr; // [rsp+1368h] [rbp+0h] BYREF
int v54; // [rsp+1370h] [rbp+8h]
__int64 v55; // [rsp+1370h] [rbp+8h]
_ptr_http_Request v59; // [rsp+1380h] [rbp+18h]
string v60; // 0:rbx.8,8:rcx.8
while ( (unsigned __int64)&retaddr < 0x12E8 || (unsigned __int64)&v39[15] <= *(_QWORD *)(v5 + 16) )
{
v55 = a1;
v59 = a3;
runtime_morestack_noctxt();
a1 = v55;
a3 = v59;
}
v54 = a1;
v51 = a3;
net_http__ptr_Request_ParseForm(a3);
v60.ptr = "flag";
v60.len = 4LL;
v12 = net_http__ptr_Request_FormValue(v51, v60);
len = v12.len;
ptr = v12.ptr;
if ( v12.len )
{
v40 = v12.len;
v49 = v12.ptr;
v13 = 0LL;
v14 = 0LL;
v15 = 0LL;
while ( len > v13 )
{
v50 = v15;
v41 = v13;
v42 = v14;
a4 = strconv_FormatInt((unsigned __int8)ptr[v13], 16, v13, a4, v15, v8, v9, v10, v11, v36, v39[0]);
v20 = v50;
v25 = runtime_concatstring2(0, v50, v42, a4, 16, v21, v22, v23, v24, v37, v39[0], v39[1], v39[2]);
v13 = v41 + 1;
v14 = v20;
v15 = v25;
ptr = v49;
len = v40;
}
v43 = 99LL;
qmemcpy(v44, &unk_69B9F0, sizeof(v44));
main_encrypt(v15, v14, 0LL, (__int64)&v45, (int)&unk_69B9F0 + 1216, v8, v9, v10, v11, v36, v39[0]);
v47 = v38;
qmemcpy(v48, v39, sizeof(v48));
v45 = v43;
qmemcpy(v46, v44, sizeof(v46));
if ( (unsigned __int8)runtime_memequal(&v47, &v45, 1224LL) )
{
v30 = runtime_convI2I(
(unsigned int)&RTYPE_io_Writer,
v54,
a2,
(unsigned int)&v47,
(unsigned int)&v45,
v26,
v27,
v28,
v29);
return fmt_Fprintf(v30, v54, (unsigned int)"Congradulation!", 15, 0, 0, 0, v31, v32);
}
else
{
v33 = runtime_convI2I(
(unsigned int)&RTYPE_io_Writer,
v54,
a2,
(unsigned int)&v47,
(unsigned int)&v45,
v26,
v27,
v28,
v29);
return fmt_Fprintf(v33, v54, (unsigned int)"Wrong!", 6, 0, 0, 0, v34, v35);
}
}
else
{
v16 = runtime_convI2I((unsigned int)&RTYPE_io_Writer, v54, a2, a4, a5, v8, v9, v10, v11);
return fmt_Fprintf(v16, v54, (unsigned int)"Please input flag!", 18, 0, 0, 0, v17, v18);
}
}
根据大佬的做法,我们通过分析可以得到加密过程为RSA和异或运算,使用爆破来解决:
from Crypto.Util.number import *
#pip install pycryptodome -i https://pypi.tuna.tsinghua.edu.cn/simple
import gmpy2
#pip install gmpy2
a = [99,85,4,3,5,5,5,3,7,7,2,8,8,11,1,2,10,4,2,13,8,9,12,9,4,13,8,0,14,0,15,13,14,10,2, 2,1,7,3,5,6,4,6,7,6,2,2,5,3,3,9,6,0,11,13,11,0,2,3,8,3,11,7,1,11,5,14,5,0,10,14,15, 13,7,13,7,14,1,15,1,11,5,6,2,12,6,10,4,1,7,4,2,6,3,6,12,5,12,3,12,6,0,4,15,2,14,7,0 ,14,14,12,4,3,4,2,0,0,2,6,2,3,6,4,4,4,7,1,2,3,9,2,12,8,1,12,3,12,2,0,3,14,3,14,12,9 ,1,7,15,5,7,2,2,4]
for j in range(256): # 爆破
num = j
a = [99,85,4,3,5,5,5,3,7,7,2,8,8,11,1,2,10,4,2,13,8,9,12,9,4,13,8,0,14,0,15,13,14,10,2, 2,1,7,3,5,6,4,6,7,6,2,2,5,3,3,9,6,0,11,13,11,0,2,3,8,3,11,7,1,11,5,14,5,0,10,14,15, 13,7,13,7,14,1,15,1,11,5,6,2,12,6,10,4,1,7,4,2,6,3,6,12,5,12,3,12,6,0,4,15,2,14,7,0 ,14,14,12,4,3,4,2,0,0,2,6,2,3,6,4,4,4,7,1,2,3,9,2,12,8,1,12,3,12,2,0,3,14,3,14,12,9 ,1,7,15,5,7,2,2,4]
for i in range(len(a)-1,-1,-1):
num ^= a[i]
a[i] = a[i] ^ num
for i in range(len(a)-1,-1,-1):
num ^= a[i]
a[i] = a[i] ^ num
try: # 将无法转换的情况直接丢弃,说明该情况必然不是flag
enc = int(“”.join(map(chr,a)))
except ValueError:
continue
p = 92582184765240663364795767694262273105045150785272129481762171937885924776597
q = 107310528658039985708896636559112400334262005367649176746429531274300859498993
e = 950501
r = (p-1)*(q-1)
d = gmpy2.invert(e,r)
m = pow(enc,d,p*q)
print(long_to_bytes(m))
4.hardasm
64位的程序,用ida打开来看一下main函数:
int __cdecl main(int argc, const char **argv, const char **envp)
{
char *v6684; // rcx
int result; // eax
__m256 v6688; // [rsp+20h] [rbp-50h] BYREF
__asm
{
vmovaps [rbp+var_10], xmm7
vmovaps [rbp+var_20], xmm6
vstmxcsr dword ptr [rsp+70h+var_50]
}
LODWORD(v6688.m256_f32[0]) |= 0x8040u;
__asm
{
vldmxcsr dword ptr [rsp+70h+var_50]
vxorps xmm0, xmm0, xmm0
vmovaps ymmword ptr [rsp+70h+var_50], ymm0
vzeroupper
}
sub_140008070(argc, &v6688, envp);
__asm
{
vmovdqa ymm0, ymmword ptr [rsp+70h+var_50]
vmovdqa ymm1, cs:ymmword_14000A040
vmovdqa ymm2, cs:ymmword_14000A060
vmovdqa ymm3, cs:ymmword_14000A080
vmovdqa ymm4, cs:ymmword_14000A0A0
vmovdqa ymm5, cs:ymmword_14000A0C0
vmovdqa ymm6, cs:ymmword_14000A0E0
vmovdqa ymm7, cs:ymmword_14000A100
vpermd ymm4, ymm7, ymm4
........
统计一下可以看到:
里面出现了这几指令:vpsubb,vpermd,vpshufb,vpaddb,vpxor
其实会一些汇编的能看出来这几个指令的意思;
第一个执行两个字节向量之间的逐元素减法运算
第二个用于对两个双字向量进行按位排列重组
第三个用于按位重新排列字节向量中的数据
第四个逐元素加法运算
第五个就是异或运算
然后就可以使用idapython解码
import numpy
def vpaddb(a,b):
global num
for i in range(32):
num[a][i]+=num[b][i]
def vpsubb(a,b):
global num
for i in range(32):
num[a][i]-=num[b][i]
def vpxor(a,b):
global num
for i in range(32):
num[a][i]^=num[b][i]
def vpshufb(a,b):
global num
c=[0]*32
for i in range(16):
if num[b][i]&0x80:
c[i]=0
else:
c[i]=num[a][num[b][i]&0xF]
if num[b][i+16]&0x80:
c[i+16]=0
else:
c[i+16]=num[a][16+(num[b][i+16]&0xF)]
num[a]=c
def invvpshufb(a,b):
global num
c=[0]*32
for i in range(16):
if num[b][i]&0x80:
c[i]=0
else:
c[num[b][i]&0xF]=num[a][i]
if num[b][i+16]&0x80:
c[i+16]=0
else:
c[16+(num[b][i+16]&0xF)]=num[a][i+16]
num[a]=c
def vpermd(a,b):
global num
c=[0]*32
for i in range(8):
c[i*4]=num[a][num[b][i*4]*4]
c[i*4+1]=num[a][num[b][i*4]*4+1]
c[i*4+2]=num[a][num[b][i*4]*4+2]
c[i*4+3]=num[a][num[b][i*4]*4+3]
num[a]=c
def invvpermd(a,b):
global num
c=[0]*32
for i in range(8):
c[num[b][i*4]*4]=num[a][i*4]
c[num[b][i*4]*4+1]=num[a][i*4+1]
c[num[b][i*4]*4+2]=num[a][i*4+2]
c[num[b][i*4]*4+3]=num[a][i*4+3]
num[a]=c
op=[]
flag=[147,203,231,147,169,129,13,182,216,221,156,127,192,77,205,240,0,160,159,34,137,239
,84,93,239,0,141,254,94,76,208,236]
num=numpy.uint8([[104,103,97,109,101,123,114,105,103,104,116,95,121,111,117,114,95,
97,115,109,95,105,115,95,103,111,111,100,33,33,125,0],
[
240,255,100,38,242,143,64,238,238,39,7,239,136,10,33,20,195,252,112,229,168,243,245
,26,212,60,177,12,229,188,185,27],
[
13,192,132,197,14,128,80,255,40,26,128,72,29,193,227,29,52,81,155,53,188,213,244,195,196,64,144,7,42,192,45,144],
[
137,161,62,192,229,20,95,197,95,20,176,208,37,31,232,245,176,52,54,194,199,160,178,
60,94,126,156,164,152,232,84,11],
[
51,95,98,104,13,100,168,255,143,153,167,148,158,154,41,52,39,54,214,130,194,109,232
,170,150,74,101,192,12,55,25,201],
[
143,33,168,55,67,9,7,51,166,135,76,74,161,116,75,230,85,19,91,63,28,215,185,158,57,
96,29,198,145,138,54,139],
[ 0,1,8,9,10,2,3,4,5,12,13,14,6,7,11,15, 0,6,7,8,9,10,2,3,4,5,13,14,11,12,1,15],
[ 0,0,0,0,4,0,0,0,5,0,0,0,1,0,0,0,2,0,0,0,3,0,0,0,6,0,0,0,7,0,0,0]])#八个寄存器
ea=0x140001081
while True : #运行一遍汇编,验证加密过程是否正确,并得到ymm1~ymm7的数据
if ea >= 0x0140007F17:
break
opcode=print_insn_mnem(ea) # 得到汇编助记符
opnum1=int(print_operand(ea,0)[-1]) #得到第一个操作数的序号(即ymm0取0)
opnum2=int(print_operand(ea,1)[-1])
opnum3=int(print_operand(ea,2)[-1])
op.append([opcode,opnum1,opnum2,opnum3])
if opcode=='vpaddb':
vpaddb(opnum1,opnum3)
elif opcode=='vpsubb':
vpsubb(opnum1,opnum3)
elif opcode=='vpxor':
vpxor(opnum1,opnum3)
elif opcode=='vpshufb':
vpshufb(opnum1,opnum3)
elif opcode=='vpermd':
vpermd(opnum1,opnum2)
ea=next_head(ea)
num[0]=flag
for i in op[::-1]: # 倒序解密
if i[0]=='vpaddb':
vpsubb(i[1],i[3])
elif i[0]=='vpsubb':
vpaddb(i[1],i[3])
elif i[0]=='vpxor':
vpxor(i[1],i[3])
elif i[0]=='vpshufb':
invvpshufb(i[1],i[3])
elif i[0]=='vpermd':
invvpermd(i[1],i[2])
for i in num[0]:
try:
print(chr(i),end='')
except UnicodeEncodeError:
print(i,end='')
Comments | NOTHING