看main函数
int __cdecl main(int argc, const char **argv, const char **envp)
{
HMODULE ModuleHandleW; // eax
HMODULE v4; // eax
HMODULE v5; // eax
_BYTE *v7; // [esp+4h] [ebp-28Ch]
HGLOBAL hResData; // [esp+8h] [ebp-288h]
HRSRC hResInfo; // [esp+Ch] [ebp-284h]
_BYTE *lpAddress; // [esp+10h] [ebp-280h]
FILE *Stream; // [esp+1Ch] [ebp-274h]
DWORD dwSize; // [esp+20h] [ebp-270h]
DWORD i; // [esp+28h] [ebp-268h]
struct _PROCESS_INFORMATION ProcessInformation; // [esp+30h] [ebp-260h] BYREF
struct _STARTUPINFOA StartupInfo; // [esp+40h] [ebp-250h] BYREF
CHAR Buffer[260]; // [esp+84h] [ebp-20Ch] BYREF
CHAR TempFileName[260]; // [esp+188h] [ebp-108h] BYREF
ModuleHandleW = GetModuleHandleW(0);
hResInfo = FindResourceW(ModuleHandleW, (LPCWSTR)0x65, L"e_ou");
v4 = GetModuleHandleW(0);
hResData = LoadResource(v4, hResInfo);
v7 = LockResource(hResData);
v5 = GetModuleHandleW(0);
dwSize = SizeofResource(v5, hResInfo);
lpAddress = VirtualAlloc(0, dwSize, 0x1000u, 4u);
if ( !lpAddress )
return 1;
for ( i = 0; i < dwSize; ++i )
{
if ( v7[i] && v7[i] != 120 )
lpAddress[i] = v7[i] ^ 0x78;
else
lpAddress[i] = v7[i];
}
if ( !GetTempPathA(0x104u, Buffer) )
return 1;
if ( !GetTempFileNameA(Buffer, "ou.exe", 0, TempFileName) )
return 1;
Stream = fopen(TempFileName, "wb");
if ( !Stream )
return 1;
if ( fwrite(lpAddress, 1u, dwSize, Stream) == dwSize )
{
fclose(Stream);
VirtualFree(lpAddress, 0, 0x8000u);
memset(&StartupInfo, 0, sizeof(StartupInfo));
memset(&ProcessInformation, 0, sizeof(ProcessInformation));
StartupInfo.cb = 68;
GetStartupInfoA(&StartupInfo);
StartupInfo.wShowWindow = 0;
CreateProcessA(TempFileName, 0, 0, 0, 1, 0, 0, 0, &StartupInfo, &ProcessInformation);
CloseHandle(ProcessInformation.hProcess);
CloseHandle(ProcessInformation.hThread);
DeleteFileA(TempFileName);
return 0;
}
else
{
fclose(Stream);
return 1;
}
}
第一种方法(最简单的):直接动调,在CreateProcessA函数处下断点即可,导出ou.26B6.tmp文件,查壳,拖入IDA分析,逻辑很简单,-30后base64就行
第二种方法:将原来的exe拖入resource hack里导出bin文件,再写脚本进行异或,输出bin文件,再IDA分析即可
下面是bin文件输出方法:
arr = 0x78
with open('C:\\Users\\lenovo\\Desktop\\E_OU101.bin', 'rb') as f:
b = f.read()
b = bytearray(b)
for i in range(len(b)):
if b[i]!=0 & b[i]!=120:
b[i] = b[i] ^ arr
else:
b[i]=b[i]
with open('C:\\Users\\lenovo\\Desktop\\COD_de1', 'wb') as f:
f.write(b)
脚本简单不在此赘述
该题运用chaquopy来调用python代码,根据题目提示我们找到python源码,在app.imy里找到hello.py,代码如下:
from java import jboolean ,jclass #line:1
import struct #line:3
import ctypes #line:4
def MX (O0O00OOO00OO00O00 ,O0OO0O00OO0O000OO ,OO000OO000000O0O0 ,OOO00O00OOO000OOO ,OO0OOO0OOO0OOOO0O ,O0OO000O0000O000O ):#line:7
OOO000O0O0OO00000 =(O0O00OOO00OO00O00 .value >>5 ^O0OO0O00OO0O000OO .value <<2 )+(O0OO0O00OO0O000OO .value >>3 ^O0O00OOO00OO00O00 .value <<4 )#line:8
OOO0OOOOOO0O0OO00 =(OO000OO000000O0O0 .value ^O0OO0O00OO0O000OO .value )+(OOO00O00OOO000OOO [(OO0OOO0OOO0OOOO0O &3 )^O0OO000O0000O000O .value ]^O0O00OOO00OO00O00 .value )#line:9
return ctypes .c_uint32 (OOO000O0O0OO00000 ^OOO0OOOOOO0O0OO00 )#line:11
def encrypt (OO0OO0O0O0O0O0OO0 ,OOO0O0OO0O0OOO000 ,OO0OOOO0OO0OOO0O0 ):#line:14
O0OOO0OO00O0000OO =0x9e3779b9 #line:15
OOOO0OOOO00O0OOOO =6 +52 //OO0OO0O0O0O0O0OO0 #line:16
O00OO00000O0OO00O =ctypes .c_uint32 (0 )#line:18
OO0OOOO0O0O0O0OO0 =ctypes .c_uint32 (OOO0O0OO0O0OOO000 [OO0OO0O0O0O0O0OO0 -1 ])#line:19
OOOOO00000OOOOOOO =ctypes .c_uint32 (0 )#line:20
while OOOO0OOOO00O0OOOO >0 :#line:22
O00OO00000O0OO00O .value +=O0OOO0OO00O0000OO #line:23
OOOOO00000OOOOOOO .value =(O00OO00000O0OO00O .value >>2 )&3 #line:24
for OO0O0OOO000O0000O in range (OO0OO0O0O0O0O0OO0 -1 ):#line:25
OOO0OO00O0OO0O000 =ctypes .c_uint32 (OOO0O0OO0O0OOO000 [OO0O0OOO000O0000O +1 ])#line:26
OOO0O0OO0O0OOO000 [OO0O0OOO000O0000O ]=ctypes .c_uint32 (OOO0O0OO0O0OOO000 [OO0O0OOO000O0000O ]+MX (OO0OOOO0O0O0O0OO0 ,OOO0OO00O0OO0O000 ,O00OO00000O0OO00O ,OO0OOOO0OO0OOO0O0 ,OO0O0OOO000O0000O ,OOOOO00000OOOOOOO ).value ).value #line:27
OO0OOOO0O0O0O0OO0 .value =OOO0O0OO0O0OOO000 [OO0O0OOO000O0000O ]#line:28
OOO0OO00O0OO0O000 =ctypes .c_uint32 (OOO0O0OO0O0OOO000 [0 ])#line:29
OOO0O0OO0O0OOO000 [OO0OO0O0O0O0O0OO0 -1 ]=ctypes .c_uint32 (OOO0O0OO0O0OOO000 [OO0OO0O0O0O0O0OO0 -1 ]+MX (OO0OOOO0O0O0O0OO0 ,OOO0OO00O0OO0O000 ,O00OO00000O0OO00O ,OO0OOOO0OO0OOO0O0 ,OO0OO0O0O0O0O0OO0 -1 ,OOOOO00000OOOOOOO ).value ).value #line:30
OO0OOOO0O0O0O0OO0 .value =OOO0O0OO0O0OOO000 [OO0OO0O0O0O0O0OO0 -1 ]#line:31
OOOO0OOOO00O0OOOO -=1 #line:32
return OOO0O0OO0O0OOO000 #line:34
def check (O0000000000O0O0O0 ):#line:63
print ("checking~~~: "+O0000000000O0O0O0 )#line:64
O0000000000O0O0O0 =str (O0000000000O0O0O0 )#line:65
if len (O0000000000O0O0O0 )!=36 :#line:66
return jboolean (False )#line:67
O00OO00000OO0OOOO =[]#line:69
for O0O0OOOOO0OOO0OOO in range (0 ,36 ,4 ):#line:70
OO0OO0OOO000OO0O0 =O0000000000O0O0O0 [O0O0OOOOO0OOO0OOO :O0O0OOOOO0OOO0OOO +4 ].encode ('latin-1')#line:71
O00OO00000OO0OOOO .append (OO0OO0OOO000OO0O0 )#line:72
_O00OO0OOOOO00O00O =[]#line:73
for O0O0OOOOO0OOO0OOO in O00OO00000OO0OOOO :#line:74
_O00OO0OOOOO00O00O .append (struct .unpack (",O0O0OOOOO0OOO0OOO )[0 ])#line:75
print (_O00OO0OOOOO00O00O )#line:77
OO0OO0OOO000OO0O0 =encrypt (9 ,_O00OO0OOOOO00O00O ,[12345678 ,12398712 ,91283904 ,12378192 ])#line:78
OOOOO0OOO0OO00000 =[689085350 ,626885696 ,1894439255 ,1204672445 ,1869189675 ,475967424 ,1932042439 ,1280104741 ,2808893494 ]#line:85
for O0O0OOOOO0OOO0OOO in range (9 ):#line:86
if OOOOO0OOO0OO00000 [O0O0OOOOO0OOO0OOO ]!=OO0OO0OOO000OO0O0 [O0O0OOOOO0OOO0OOO ]:#line:87
return jboolean (False )#line:88
return jboolean (True )#line:90
def sayHello ():#line:92
print ("hello from py")#line:93
虽然混淆了,但是可以看出来是XXTea加密
后面直接套脚本解密即可
import ctypes
def MX(z, y, total, tea_key, p, e):
temp1 = (z .value >> 5 ^ y .value << 2)+(y .value >> 3 ^ z .value << 4)
temp2 = (total .value ^ y .value)+(tea_key[(p & 3) ^ e .value] ^ z .value)
return ctypes.c_uint32(temp1 ^ temp2)
def decrypt(n, v, key):
delta = 0x9e3779b9
rounds = 6 + 52//n
total = ctypes.c_uint32(rounds * delta)
y = ctypes.c_uint32(v[0])
e = ctypes.c_uint32(0)
while rounds > 0:
e.value = (total.value >> 2) & 3
for p in range(n-1, 0, -1):
z = ctypes.c_uint32(v[p-1])
v[p] = ctypes.c_uint32((v[p] - MX(z,y,total,key,p,e).value)).value
y.value = v[p]
z = ctypes.c_uint32(v[n-1])
v[0] = ctypes.c_uint32(v[0] - MX(z,y,total,key,0,e).value).value
y.value = v[0]
total.value -= delta
rounds -= 1
return v
key_str = [689085350, 626885696, 1894439255, 1204672445, 1869189675, 475967424, 1932042439, 1280104741, 2808893494]
res = encrypt_input = decrypt(9, key_str, [12345678, 12398712, 91283904, 12378192])
for i in res:
print(hex(i)[2:])
手动恢复大端序,然后16进制转字符串即可
flag{c1f8ace6-4b46-4931-b25b-a1010a89c592}
竟然是一道vm题,与最近的研究方向撞上了哈哈
双重vm,大致有两种方法:手搓+脚本还原或是用intel pin进行爆破
首先介绍第一种方法:intel pin爆破
找到程序的输入点,再找到程序的cmp点,有两个case:0x1E21,0x1EC9,通过输入fl,fi等不同字符串发现,两处执行次数不同,用pin去统计执行次数,这样我们可以逐字节爆破。
首先在 source/tools/ManualExamples 路径下 make obj-intel64/inscount0.so TARGET=intel64,编译生成计数器的so文件(即为一种pintool),可以记录程序执行的指令的条数
注:想知道pintool功能可以:$ pin -t your_pintool -h – your_application
同样的,编译32位pintool使用:make obj-ia32/inscount0.so
可以用 make all TAEGET=intel64 make all TAEGET=ia32 来编译所有的pintool
编译完以后生成输出文件 …/…/…/pin -t obj-intel64/inscount0.so -o inscount0.log – 文件路径
这时可以再去写脚本进行爆破,另外的,在编译pintool之前可以把.c源码文件中的一些源码改掉以适应情况
对于此题:
首先我们只想统计指定行代码的执行次数,所以将inscount0.c中:
VOID docount() { icount++; }
改为:
VOID docount(void *ip)
{
if ((long long int)ip == 0x0000555555555E21)
icount++;
}
并将
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_END);
改为
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_INST_PTR, IARG_END);
加入IARG_INST_PTR参数,是为了检测指令的地址,传入ip
注意用
sudo su
echo 0 > /proc/sys/kernel/randomize_va_space
将ALSR保护关掉
改完后在将inscount0进行64位编译,生成输出文件,之后编写脚本如下:
from pwn import *
import subprocess
def run(msg):
cmd = [
"/home/tanggerr/Downloads/pin-3.28-98749-g6643ecee5-gcc-linux/pin",
"-t", "/home/tanggerr/Downloads/pin-3.28-98749-g6643ecee5-gcc-linux/source/tools/ManualExamples/obj-intel64/inscount0.so",
"-o", "/home/tanggerr/Downloads/pin-3.28-98749-g6643ecee5-gcc-linux/source/tools/ManualExamples/inscount0.log",
"--",
"/home/tanggerr/nesting"
]
# 启动进程,并设置 stdin, stdout, stderr 为 PIPE,以便与进程交互
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# 发送数据到进程
p.stdin.write(msg.encode())
p.stdin.flush()
# 从进程接收数据
output = p.stdout.readline()
# print(output)
# 结束进程
p.terminate()
return int(read("/home/tanggerr/Downloads/pin-3.28-98749-g6643ecee5-gcc-linux/source/tools/ManualExamples/inscount0.log").split(" ")[1])
def read(fname):
with open(fname) as f:
return f.read()
# print(run("fl"))
charset = string.printable
l = []
flag = ""
counter = 0
while(True):
max_chr = 0
first_iteration = True # 初始化标志为 True
for chr in charset:
tmp = run(flag + chr)
if first_iteration: # 只在第一次迭代时执行
max_value = tmp
first_iteration = False # 设置标志为 False
if tmp > max_value:
max_chr = chr
max_value = tmp
break
print(max_chr)
flag += str(max_chr)
print(flag)
#flag{2c7c093b-f648-11ed-a716-701ab8caaafe}
等待其爆破出flag{2c7c093b-f648-11ed-a716-701ab8caaafe}
接着看第二种方法:
手搓两层vm,这里我学习了LaoGong战队的解法,请见2023 10.15 香山杯 wp - LaoGong - 飞书云文档 (feishu.cn)
只能说手搓是真的nb(tql,orz