hgame 2022-2024 week4 汇总
hgame 2022-2024 week4 汇总
在该处下断点动调的时候,先输入32个字符,然后F9步过,然后出现下图情况
下面的链接即为当比较成功时的跳转地,上方的则为当比较数据不同时的Never Gonna Give You UP(其实没啥用)
在If里面直接对两个数据进行比较,如果一样就输出win,所以我们可以得知两个数据相同
可以看出在48行前的加密和比较后进行的加密是对称加密,则我们可以选择将Buf2的1数据调整成条件判断里面unk_974D40这个地方的数据,然后通过动调得到flag(即v10)
双击buf2,右键选择paste data,将unk_974D40的数据给Buf2,注意不是动调的时候的输入的数据所存的位置
然后不用保存文件直接F9,在输入的位置的得到被还原的flag的数据
hgame{WOWOW_h@ppy_n3w_ye4r_2022}
ezvm
典型vm
已知为vm题,故可以将变量名改成寄存器,好看一点
下图可以看到关键步骤
双击可以获取r的地址,为0x49F020,已知偏移量为0x6d,所以实际opcode的地址为0x49F020+0x6d*4=0x49f364
可以脚本获取地址
我们最开始动调,发现程序无输入,获取r[0]的初值为0
通过分析发现case18里面在进行数据的压栈操作,动调可知此时v0=0,由此我们可知data的数据地址为0x49F024+209*4,即为0x49f364
前往0x49f364获取data
故
1 |
|
1 |
|
通过脚本跑出结果
xor_keys = [94, 70, 97, 67, 14, 83, 73, 31, 81, 94, 54, 55, 41, 65, 99, 59, 100, 59, 21, 24, 91, 62, 34, 80, 70, 94, 53, 78, 67, 35, 96, 59] plain_text = [] cipher = [142, 136, 163, 153, 196, 165, 195, 221, 25, 236, 108, 155, 243, 27, 139, 91, 62, 155, 241, 134, 243, 244, 164, 248, 248, 152, 171, 134, 137, 97, 34, 193] for i in range(0,32): plain_text.append(chr((cipher[i]^xor_keys[i])//2)) print(plain_text) print(‘’.join(plain_text))
hgame{Ea$Y-Vm-t0-PrOTeCT_cOde!!}
Server
在main_main函数里面找到
进入main_HttpHandleFunc,找到main_encrypt
在main_encrypt函数中,可以找到math_big_ptr_Int_SetString函数,经了解可知该题目若使用 IDA7.6 及以上版本打开时,会直接加载嵌入的符号表,可减轻逆向的难度,如果打开后部分的函数仍然分析不出函数参数时,可以自行针对math_big__ptr_Int_SetString函数进行数据的补齐
参考格式:
__int64 __usercall math_big___ptr_Int__SetString@(char *str@, __int64 a2@, int a3@, int a4@)
关键加密逻辑
根据前面的代码我们可以猜测是RSA,在main_encrypt函数里面可以获得数据
其中p=92582184765240663364795767694262273105045150785272129481762171937885924776597
q=107310528658039985708896636559112400334262005367649176746429531274300859498993
e=950501
浅浅浅去看了一下RSA…………………………
exp:
import gmpy2
from Crypto.Util.number import bytes_to_long,long_to_bytes
p=92582184765240663364795767694262273105045150785272129481762171937885924776597
q=107310528658039985708896636559112400334262005367649176746429531274300859498993
n=p*q
e=950501
ccc=[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, 102, 94]
tmp=0x66
for i in range(1,153,2):
ccc[i] ^= tmp
tmp = ccc[i]
#print(i, chr(tmp))
print(hex(tmp))
for j in range(0x30,0x3a):
tmp=j
tmp ^= 0x66
cct=[i for i in ccc]
for i in range(0,153,2):
cct[i] ^= tmp
tmp = cct[i]
a=bytes(cct)
print(a)
c=135005562109829034199059149474896341566307600227148289525068532297727897409776873250963225670468340868270979975367474527115512003915945795967599087720024
#c=34015463119928024098049048464997351467317701237049299424078433287626887508766972240862235771478241878371969874377575537014502102905844785866589186730125
d=gmpy2.invert(e,(p-1)*(q-1))
m=pow(c,d,n)
print(long_to_bytes(m))
hgame{g0_and_g0_http_5erv3r_nb}
hardasm
了解一下
指令
vpermd:这条指令用于按照索引重新排列一个256位的寄存器中的32位元素。它会根据指定的索引重新排列元素,将元素重新排列后存储到目标寄存器中。
vpxor:这个指令执行两个向量寄存器之间的逻辑异或操作。即将两个向量寄存器中对应位置的元素进行逻辑异或运算,并将结果存储到目标寄存器中。
vpaddb:这个指令用于在字节级别对两个向量寄存器中的元素进行相加操作。它会将两个向量寄存器中对应位置的字节元素相加,并将结果存储到目标寄存器中。
vpsubb:这个指令用于在字节级别对两个向量寄存器中的元素进行相减操作。它会将两个向量寄存器中对应位置的字节元素相减,并将结果存储到目标寄存器中。
vpshufb:这个指令执行按字节的逐元素选择和重组操作。根据掩码寄存器中的值,在源寄存器中选择元素,并将这些元素按照特定的顺序重新排列后存储到目标寄存器中。
vpcmpeqb:将两个操作数进行逐字节比较,如果两个操作数的对应字节相等,则将目标寄存器中的对应字节设置为全部1(0xFF)
以上这些指令主要用于在向量级别上执行数据处理和计算操作
在 AVX 指令集中,__m256
数据类型表示一个包含 8 个单精度浮点数的 256 位向量。这意味着它可以同时对这 8 个单精度浮点数执行相同的操作,从而实现数据的并行处理。
上图中可以看出数据被存入ymm0-ymm7中
这里的判断语句,切换到汇编界面如下
我们可以看到下面的代码一直在进行逐个字节比较的操作,直到遇到一个字节为0或者比较完所有字节。如果所有字节都不为0,则跳转到标签”loc_140008055”,如果遇到字节为0,则直接跳转到标签”loc_14000804E”,即错误,所以当所有字节不为0时,即为正确
从这一行我们可以看出比较是否相等的数据的结果被存放在ymm1寄存器里面
因为在判断条件的比较里面有8个m256类型的单精度浮点数的向量,每个单精度浮点数会进行4次字节比较,所以会进行32次字节比较的操作,故flag长度为32字节
经过检验发现Flag的格式
检验过程:1.准备除了固定的hgame{ }以外的25的字符(里面随便啥都行)
- 动调,发现前面数据为0FFh,即可以说明该格式正确
所以尝试进行爆破,对程序进行patch,将[rsp+70h+var_50]处的内容传递给rcx,然后再打印出来
要使得字符让程序返回0xFFh
在file–>script command里面添加脚本
等待……
exp:
1 |
|
hgame{right_your_asm_is_good!!}
shellcode
打开找到main_main函数,进入
通过分析这段代码,发现主要功能——从指定目录中读取文件列表,对特定的字符串进行Base64解码操作,然后将解码后的结果存储到新文件中。
关键步骤:
- 使用
io_ioutil_ReadDir
函数从 “inputdir” 目录中读取文件列表,并将结果存储在变量Dir
中。 - 对特定的字符串进行 Base64 解码操作,结果存储在变量
v81
中。 - 调用
main_VirtualAlloc
函数分配内存,并将解码后的字符串拷贝到新分配的内存中。 - 使用
main_RtlMoveMemory
函数将数据从一个地址复制到另一个地址。 - 循环遍历Dir中的文件列表,依次读取每个文件内容,并进行处理:
- 构造输入文件路径,使用
os_ReadFile
读取文件内容。 - 将文件内容拷贝到新的字节切片中,并执行一系列操作。
- 将处理后的数据写入到输出文件中。
- 构造输入文件路径,使用
- 最后通过循环控制,实现对文件列表的遍历和处理,直到处理完所有文件或达到指定条件退出循环。
开始做题
先在这几个关键地方下断点,然后开始动调,发现没有输入环节
这个地方表示对字符串进行base64的解密,且字符串长度为284字节,双击进入获取进行解密的字符串数据,得到
注意还有下面的那一部分
获取得到数据:
VUiD7FBIjWwkIEiJTUBIi0VAiwCJRQC4BAAAAEgDRUCLAIlFBMdFCAAAAADHRQwj782rx0UQFgAAAMdFFCEAAADHRRgsAAAAx0UcNwAAAMdFIAAAAACLRSCD+CBzWotFDANFCIlFCItFBMHgBANFEItVCANVBDPCi1UEweoFA1UUM8IDRQCJRQCLRQDB4AQDRRiLVQgDVQAzwotVAMHqBQNVHDPCA0UEiUUEuAEAAAADRSCJRSDrnkiLRUCLVQCJELgEAAAASANFQItVBIkQSI1lMF3D
然后在线解码好像不太行,选择使用脚本一键解密后直接写入文件
脚本:
import base64
with open(‘ans’, ‘wb’) as f:
f.write(base64.b64decode(‘VUiD7FBIjWwkIEiJTUBIi0VAiwCJRQC4BAAAAEgDRUCLAIlFBMdFC AAAAADHRQwj782rx0UQFgAAAMdFFCEAAADHRRgsAAAAx0UcNwAAAMdFIAAAAACLRSCD+CBzWotFDANFCIlFCItFBMHgBANFEItVCANVBDPCi1UEweoFA1UUM8IDRQCJRQCLRQDB4AQDRRiLVQgDVQAzwotVAMHqBQNVHDPCA0UEiUUEuAEAAAADRSCJRSDrnkiLRUCLVQCJELgEAAAASANFQItVBIkQSI1lMF3D’))
将脚本运行得到生成的文件,将文件拖入IDA
p创建函数然后F5,得到加密逻辑
发现一个规整的tea加密
由下图可知enc文件时加密后的数据
用winhex打开flag.enc
得到数据
\x20\x69\xB3\xE4\xD0\x24\x69\x93\x44\xD1\x16\xA8\xF5\xD5\x82\xAA\xDA\xF0\x79\x36\x06\xFD\x32\x7F\xD3\xC0\x60\x34\x39\x49\x21\xB7\xA2\x69\x72\xE5\xFA\x51\x6A\x83
exp:
#include <stdio.h>
#include <stdint.h>
void decrypt(uint32_t* v, int32_t* k) {
uint32_t v0 = v[0], v1 = v[1], i;
int32_t sum = 0xABCDEF23 * 32;
uint32_t delta = 0xABCDEF23;
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
for (i = 0; i<32; i++) {
v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
sum -= delta;
}
v[0] = v0; v[1] = v1;
}
int main(){
uint32_t key[4] = {22,33,44,55};
uint32_t enc[10] = {0xE4B36920, 0x936924D0, 0xA816D144, 0xAA82D5F5, 0x3679F0DA, 0x7F32FD06, 0x3460C0D3, 0xB7214939,
0xE57269A2, 0x836A51FA};
for(int i = 0;i<9;i+=2){
decrypt(&enc[i],key);
}
char* p = enc;
for(int i = 0;i<40;i++){
printf("%c",p[i]);
}
}
hgame{th1s_1s_th3_tutu’s_h0mew0rk}
vm
根据判断,遇到0xFF停止
修改变量名
提取出opcode
这个为vm的主要逻辑
找到数据
分析所模拟的指令
概览:
使用脚本打印出符合条件的指令
opcode = [0x00, 0x03, 0x02, 0x00, 0x03, 0x00, 0x02, 0x03, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x03, 0x02, 0x32,
0x03, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00,
0x01, 0x00, 0x00, 0x03, 0x02, 0x64, 0x03, 0x00, 0x02, 0x03,
0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x01, 0x00, 0x00, 0x03,
0x00, 0x08, 0x00, 0x02, 0x02, 0x01, 0x03, 0x04, 0x01, 0x00,
0x03, 0x05, 0x02, 0x00, 0x03, 0x00, 0x01, 0x02, 0x00, 0x02,
0x00, 0x01, 0x01, 0x00, 0x00, 0x03, 0x00, 0x01, 0x03, 0x00,
0x03, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x03, 0x01, 0x28,
0x04, 0x06, 0x5F, 0x05, 0x00, 0x00, 0x03, 0x03, 0x00, 0x02,
0x01, 0x00, 0x03, 0x02, 0x96, 0x03, 0x00, 0x02, 0x03, 0x00,
0x00, 0x00, 0x00, 0x04, 0x07, 0x88, 0x00, 0x03, 0x00, 0x01,
0x03, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x03,
0x01, 0x28, 0x04, 0x07, 0x63, 0xFF, 0xFF]
input1 = []
i = 0
while opcode[i] != 0xFF:
match opcode[i]:
case 0x00:
print(f’{i}’, end=’ ‘)
o = i + 1
if opcode[o]:
match opcode[o]:
case 0x01:
print(“mov input[reg[2]], reg[0]”)
case 0x02:
print(“mov reg[%d], reg[%d]” % (opcode[i+2],opcode[i+3]))
case 0x03:
print(“mov reg[%d], %d” % (opcode[i+2], opcode[i+3]))
else:
print(“mov reg[0], input[reg[2]]”)
i += 4
case 0x01:
print(f’{i}’, end=’ ‘)
o = i + 1
if opcode[o]:
match opcode[o]:
case 0x01:
print(“push reg[0]”)
case 0x02:
print(“push reg[2]”)
case 0x03:
print(“push reg[3]”)
else:
print(“push reg[0]”)
i += 2
case 0x02:
print(f’{i}’, end=’ ‘)
o = i + 1
if opcode[o]:
match opcode[o]:
case 0x01:
print(“pop reg[1]”)
case 0x02:
print(“pop reg[2]”)
case 0x03:
print(“pop reg[3]”)
else:
print(“pop reg[0]”)
i += 2
case 0x03:
print(f’{i}’, end=’ ‘)
o = i + 1
match opcode[o]:
case 0:
print(“add reg[%d],reg[%d]” % (opcode[i + 2], opcode[i + 3]))
case 1:
print(“sup reg[%d],reg[%d]” % (opcode[i + 2], opcode[i + 3]))
case 2:
print(“mul reg[%d],reg[%d]” % (opcode[i + 2], opcode[i + 3]))
case 3:
print(“xor reg[%d],reg[%d]” % (opcode[i + 2], opcode[i + 3]))
case 4:
print(“shl reg[%d],reg[%d]” % (opcode[i + 2], opcode[i + 3]))
case 5:
print(“shr reg[%d],reg[%d]” % (opcode[i + 2], opcode[i + 3]))
i += 4
case 0x04:
print(f’{i} cmp reg[0], reg[1]’)
i += 1
case 0x05:
print(f’{i} jmp %d ‘ % (opcode[i+1]))
i += 2
case 0x06:
print(f’{i} je %d ‘ % (opcode[i+1]))
i += 2
case 0x07:
print(f’{i} jne %d ‘ % (opcode[i+1]))
i += 2
得到汇编结果
mov reg[2],0
add reg[2],reg[3]
mov reg[0],data[reg[2]]
mov reg[1],reg[0]
mov reg[2],50
add reg[2],reg[3]
mov reg[0],data[reg[2]]
add reg[1],reg[0]
mov reg[2],100
add reg[2],reg[3]
mov reg[0],data[reg[2]]
xor reg[1],reg[0]
mov reg[0],8
mov reg[2],reg[1]
shl reg[1],reg[0]
shr reg[2],reg[0]
add reg[1],reg[2]
mov reg[0],reg[1]
push reg[0]
mov reg[0],1
add reg[3],reg[0]
mov reg[0],reg[3]
mov reg[1],40
cmp reg[0],reg[1]
jne 95
jmp 0
mov reg[3],0
pop reg[0]
mov reg[2],150
add reg[2],reg[3]
mov reg[0],data[reg[2]]
cmp reg[0],reg[1]
je 136
mov reg[0],1
add reg[3],reg[0]
mov reg[0],reg[3]
mov reg[1],40
cmp reg[0],reg[1]
je 99
然后再读取汇编将汇编代码转换成反汇编
a = [0x9b, 0xa8, 0x2, 0xbc, 0xac, 0x9c, 0xce, 0xfa, 0x2, 0xb9, 0xff, 0x3a, 0x74, 0x48, 0x19, 0x69, 0xe8, 0x3, 0xcb,
0xc9, 0xff, 0xfc, 0x80, 0xd6, 0x8d, 0xd7, 0x72, 0x0, 0xa7, 0x1d, 0x3d, 0x99, 0x88, 0x99, 0xbf, 0xe8, 0x96, 0x2e,
0x5d, 0x57, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]
b = [0xc9, 0xa9, 0xbd, 0x8b, 0x17, 0xc2, 0x6e, 0xf8, 0xf5, 0x6e, 0x63, 0x63, 0xd5, 0x46, 0x5d, 0x16, 0x98, 0x38, 0x30,
0x73, 0x38, 0xc1, 0x5e, 0xed, 0xb0, 0x29, 0x5a, 0x18, 0x40, 0xa7, 0xfd, 0xa, 0x1e, 0x78, 0x8b, 0x62, 0xdb, 0xf,
0x8f, 0x9c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]
c = [0x4800, 0xf100, 0x4000, 0x2100, 0x3501, 0x6400, 0x7801, 0xf900, 0x1801, 0x5200, 0x2500, 0x5d01, 0x4700, 0xfd00,
0x6901, 0x5c00, 0xaf01, 0xb200, 0xec01, 0x5201, 0x4f01, 0x1a01, 0x5000, 0x8501, 0xcd00, 0x2300, 0xf800, 0xc00,
0xcf00, 0x3d01, 0x4501, 0x8200, 0xd201, 0x2901, 0xd501, 0x601, 0xa201, 0xde00, 0xa601, 0xca01, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0]
flag = ‘0123456789012345678901234567890123456789’
res = [0 for i in range(40)]
for i in range(40): x = a[i] + ord(flag[i])
y = x ^ b[i]
z = (y << 8) & 0xff00
m = y >> 8
n = z + m
最后,逆回去就行了……
a = [0x9b, 0xa8, 0x2, 0xbc, 0xac, 0x9c, 0xce, 0xfa, 0x2, 0xb9, 0xff, 0x3a, 0x74, 0x48, 0x19, 0x69, 0xe8, 0x3, 0xcb,
0xc9, 0xff, 0xfc, 0x80, 0xd6, 0x8d, 0xd7, 0x72, 0x0, 0xa7, 0x1d, 0x3d, 0x99, 0x88, 0x99, 0xbf, 0xe8, 0x96, 0x2e,
0x5d, 0x57]
b = [0xc9, 0xa9, 0xbd, 0x8b, 0x17, 0xc2, 0x6e, 0xf8, 0xf5, 0x6e, 0x63, 0x63, 0xd5, 0x46, 0x5d, 0x16, 0x98, 0x38, 0x30,
0x73, 0x38, 0xc1, 0x5e, 0xed, 0xb0, 0x29, 0x5a, 0x18, 0x40, 0xa7, 0xfd, 0xa, 0x1e, 0x78, 0x8b, 0x62, 0xdb, 0xf,
0x8f, 0x9c]
c = [0x4800, 0xf100, 0x4000, 0x2100, 0x3501, 0x6400, 0x7801, 0xf900, 0x1801, 0x5200, 0x2500, 0x5d01, 0x4700, 0xfd00,
0x6901, 0x5c00, 0xaf01, 0xb200, 0xec01, 0x5201, 0x4f01, 0x1a01, 0x5000, 0x8501, 0xcd00, 0x2300, 0xf800, 0xc00,
0xcf00, 0x3d01, 0x4501, 0x8200, 0xd201, 0x2901, 0xd501, 0x601, 0xa201, 0xde00, 0xa601, 0xca01]
c = [i for i in reversed(c)]
flag = ‘’
for i in range(40):
m = c[i] >> 8
n = (c[i] & 0xff) << 8
y = m + n
y = y ^ b[i]
y = y - a[i]
flag += chr(y)
print(flag)
hgame{y0ur_rever5e_sk1ll_i5_very_g0od!!}
hgame week4 re wp
change
点开进入程序可以发现主要逻辑集中在中间那块
exp:
cmp=[0x13, 0x0A, 0x5D, 0x1C, 0x0E, 0x08, 0x23, 0x06, 0x0B, 0x4B,
0x38, 0x22, 0x0D, 0x1C, 0x48, 0x0C, 0x66, 0x15, 0x48, 0x1B,
0x0D, 0x0E, 0x10, 0x4F]
s=’am2qasl’
flag=’’
for i in range(len(cmp)):
if i%2:
flag+=chr(cmp[i]^ord(s[i%len(s)]))
else:
flag+=chr((cmp[i]-10)^ord(s[i%len(s)]))
print(flag)
1 |
|
again
首先使用pyinstxtractor把bin1.exe进行解包
在首个文件夹中找到pyc文件,并且使用命令.\pycdc 文件名进行反编译(注意不是./)
上面两个不太行
直接pycdas查看字节码
得到字节码如下:
D:\题目\hgame\again!\bin1.exe_extracted>pycdas.exe bin1.pyc
bin1.pyc (Python 3.12) //文件信息
[Code] //Code对象信息
File Name: bin1.py
Object Name:
Qualified Name:
Arg Count: 0
Pos Only Arg Count: 0
KW Only Arg Count: 0
Stack Size: 10
Flags: 0x00000000
[Names] //Names 包含了代码中使用到的变量名和模块名,例如:’hashlib’, ‘print’, ‘bytearray’ 等
‘hashlib’
‘print’
‘bytearray’
‘s’
‘open’
‘read’
‘f’
‘t’
‘range’
‘i’
‘ord’
‘len’
‘append’
‘md5’
‘bytes’
‘hexdigest’
‘md5_hash’
[Locals+Names] //Locals+Names提供具体的本地变量信息
[Constants] //Constants包含了代码中使用到的常量例如整数、字符串,一些常量示例
0
None
‘you should use this execute file to decrypt “bin2”‘
‘hint:md5’
‘bin1.pyc’
‘rb’
‘jkasnwojasd’
15
6
256
[Disassembly]
0 RESUME 0
2 LOAD_CONST 0: 0
4 LOAD_CONST 1: None
6 IMPORT_NAME 0: hashlib
8 STORE_NAME 0: hashlib
10 PUSH_NULL
12 LOAD_NAME 1: print
14 LOAD_CONST 2: ‘you should use this execute file to decrypt “bin2”‘
16
20 CALL 1
30 POP_TOP
32 PUSH_NULL
34 LOAD_NAME 1: print
36 LOAD_CONST 3: ‘hint:md5’
38
42 CALL 1
52 POP_TOP
54 PUSH_NULL
56 LOAD_NAME 2: bytearray
58
62 CALL 0
72 STORE_NAME 3: s
74 PUSH_NULL
76 LOAD_NAME 2: bytearray
78 PUSH_NULL
80 LOAD_NAME 4: open
82 LOAD_CONST 4: ‘bin1.pyc’
84 LOAD_CONST 5: ‘rb’
86
90 CALL 2
100
122
126 CALL 0
136
140 CALL 1
150 STORE_NAME 6: f
152 LOAD_CONST 6: ‘jkasnwojasd’
154 STORE_NAME 7: t
156 PUSH_NULL
158 LOAD_NAME 8: range
160 LOAD_CONST 0: 0
162 LOAD_CONST 7: 15
164
168 CALL 2
178 GET_ITER
180 FOR_ITER 106 (to 394)
182 STORE_NAME 9: i
184 LOAD_NAME 6: f
186 LOAD_NAME 9: i
188 BINARY_SUBSCR
198 LOAD_NAME 6: f
200 LOAD_NAME 9: i
202 LOAD_CONST 8: 6
204 BINARY_OP 6 (%)
208 BINARY_SUBSCR
218 BINARY_OP 0 (+)
222 PUSH_NULL
224 LOAD_NAME 10: ord
226 LOAD_NAME 7: t
228 LOAD_NAME 9: i
230 LOAD_CONST 8: 6
232 BINARY_OP 6 (%)
236 BINARY_SUBSCR
246
250 CALL 1
260 PUSH_NULL
262 LOAD_NAME 10: ord
264 LOAD_NAME 7: t
266 LOAD_NAME 9: i
268 PUSH_NULL
270 LOAD_NAME 11: len
272 LOAD_NAME 7: t
274
278 CALL 1
288 BINARY_OP 6 (%)
292 BINARY_SUBSCR
302
306 CALL 1
316 BINARY_OP 0 (+)
320 BINARY_OP 12 (^)
324 LOAD_CONST 9: 256
326 BINARY_OP 6 (%)
330 LOAD_NAME 6: f
332 LOAD_NAME 9: i
334 STORE_SUBSCR
338 LOAD_NAME 3: s
340
362 LOAD_NAME 6: f
364 LOAD_NAME 9: i
366 BINARY_SUBSCR
376
380 CALL 1
390 POP_TOP
392 JUMP_BACKWARD 107 (to 180)
394 PUSH_NULL
396 LOAD_NAME 1: print
398 LOAD_NAME 3: s
400
404 CALL 1
414 POP_TOP
416 PUSH_NULL
418 LOAD_NAME 0: hashlib
420 LOAD_ATTR 13: md5
430 PUSH_NULL
432 LOAD_NAME 14: bytes
434 LOAD_NAME 3: s
436
440 CALL 1
450
454 CALL 1
464
486
490 CALL 0
500 STORE_NAME 16: md5_hash
502 LOAD_CONST 1: None
504 RETURN_VALUE
手撕一下,写成py代码(把py文件和pyc放同一个路径下)
import hashlib
print(‘you should use this execute file to decrypt “bin2”‘)
print(‘hint:md5’)
s=bytearray()
f=bytearray(open(‘./bin1.pyc’,’rb’).read())
t=’jkasnwojasd’
for i in range(15):
f[i]=((f[i]+f[i%6]) ^ (ord(t[i%6])+ord(t[i%len(t)]))) % 256
s.append(f[i])
print(s)
md5_hash=hashlib.md5(bytes(s)).hexdigest()
print(md5_hash)
#a405b5d321e446459d8f9169d027bd92
(不知道为什么我用之前的Pyinstxtractor最后解包解出来的pyc最后得到的结果和wp不一样,于是又重新搞了一个Pyinstxtractor)
用winhex打开bin2,搜索刚刚得到的字符串发现如上
因为文件不可执行,盲猜和文件数据进行异或,刚好在头部尝试进行异或以后发现MZ,exe标志,则印证了猜想
代码:
file=open(‘bin2’,’rb’).read() #读出来是b’xxxx’的形式
key1 = (“a405b5d321e446459d8f9169d027bd92”).encode() #key1进行encode后就是b’xxxx’
arr=[key1[i%len(key1)]^file[i] for i in range(len(file))]
open(‘bin2.exe’,’wb’).write(bytes(arr))
print(‘Done!’)
还是在同一路径下运行得到最终的exe
直接使用IDA打开
exp:
#include <stdio.h>
#include <stdint.h>
#include <stdio.h>
void decrypt1(uint32_t* v, uint32_t* k) {
uint32_t v5 = 0;
uint32_t data;
uint32_t z;
uint32_t z2;
uint32_t v7;
uint32_t y;
int v9;
int v19 = 12;
int i;
do {
v5 += 0x7937B99E;
v5 &= 0xffffffff;
} while (–v19);
v19 = 12;
y = v[0];
do
{
v7 = (v5 >> 2) & 3;
for (i = 7; i > 0; –i)
{
z = v[i - 1];
y = v[i] -= ((v5 ^ y) + (z ^ k[v7 ^ i & 3])) ^ (((16 * z) ^ (y >> 3)) + ((z >> 5) ^ (4 * y)));
}
z = v[7];
y = v[0] -= (((v5 ^ y) + (z ^ k[v7 ^ i & 3])) ^ (((16 * z) ^ (y >> 3)) + ((z >> 5) ^ (4 * y))));
v5 += 0x7937B99E;
v5 &= 0xffffffff;
} while (--v19);
}
int main()
{
uint32_t key[4] = {
0x1234, 0x2341, 0x3412, 0x4123
};
uint32_t array[8] = { 0x506fb5c3, 0xb9358f45, 0xc91ae8c7, 0x3820e280, 0xd13aba83,
0x975cf554, 0x4352036b, 0x1cd20447 };
uint32_t temp[2] = { 0,0 };
int i = 0;
decrypt1(array, key);
for (int i = 0; i < 8; i++) {
printf(“0x%x,”, array[i]);
}
return 0;
}
hgame{btea_is_a_hard_encryption}
crackme2
打开最开始是一个没有经过修改的base64换表题
在线解码
发现不对
退回到main函数的界面,发现unwind
发现异常
将上方的main函数u掉,然后对except函数按p创建函数然后反汇编
将NtQueryInformationProcess给nop掉,然后保存一下
得到下图
该程序通过VirtualProtect将原来的base的地址的函数进行异或操作
即为SMC程序,在执行时进行自解密
在第二个virtualprotect函数处下断点进行动调(注意最后进入动调输入环节结束后需要手动按F9执行到断点所在位置处)
然后F5即可得到反汇编代码,双击此时的Address,查看具体值,发现没有进行识别,手动全选然后c(force),再P创建函数可以得到下图反汇编代码
直接把上面条件一条一条搬下来使用z3求解
exp:
1 |
|
hgame{SMC_4nd_s0lv1ng_equ4t1Ons}