垃圾代码 + 反调试 + 壳
程序壳段只有几个函数, 每个函数由于加了大量的垃圾代码, 导致直接看代码并不是太直观
用ApiMonitor看下用了哪些API及调用栈,
用IDA导出函数的asm, 根据调用栈定位到调用API处, 文本搜索相关的变量引用
反调试API
NtSetInformationThread -> ThreadHideFromDebugger
NtQueryInformationProcess -> DebugPort / DebugObjectHandle / DebugFlags
CheckRemoteDebuggerPresent
IsDebuggerPresent
创建线程
CreateThread
NtCreateThreadEx
解压缩
cabinet.dll
Decompress // win8+
壳
T0_EntryPoint()
{
CreateThread(T1);
CreateThread(T2);
NtSetInformationThread();
NtCreateThreadEx(T3);
LOOP_FOREVER();
}
T1_004DF2B0()
{
while(1)
{
Sleep(100);
GetThreadContext(GetCurrentThread(), &ctx);
ctx.Dr0(...);
ctx.Dr1(...);
ctx.Dr2(...);
ctx.Dr3(...);
}
}
T2_004EAA70(hT1)
{
while(1)
{
SuspendThread(hT1);
GetThreadContext(hT1, &ctx);
ctx.Dr0 = GetProcAddress;
ctx.Dr1 = GetThreadContext;
ctx.Dr2 = GetStartupInfoA;
ctx.Dr3 = GetProcessHeap;
SetThreadContext(hT1, &ctx);
Sleep(100);
}
}
// T4_004B6580 => SetWaitableTimer(T4X)
// T4X_004C9960 => 反调试
T3_00464960()
{
ZwCreateThreadEx(T4);
DecompressBlocks();
// CALL [fn];
// fn=stub;
// stub: mov eax, imm1
// stub: xor eax, imm2
// stub: jmp eax
PrepareIAT();
NtHeaders.OptionalHeader.AddressOfEntryPoint = 0x004C9950;
jmp [Base + NtHeaders.FileHeader.TimeDateStamp] // RealOEP
}
主程序
T1_004010E0(); // CheckCrcOfMain
T2_004016F0(); // CheckForDebugger
// 这里反调试相关的代码有个 32位 -> 64位 -> 32位 的过程
fn()
{
__asm
{
; 32bit code
; ...
; 32 -> 64
push 33h
call $+5
add dword ptr [esp], 5
retf
; 64bit code
; ...
; 64 -> 32
call $+5
mov dword ptr [rsp+4], 23h
add dword ptr [rsp], 0Dh
retf
; 32bit code
; ...
}
}
// 有限正整数集合中任何两个数的差均不相同,求该集合最大元素的最小值
// hxxps://bbs.csdn.net/topics/10424232
// [0, 2, 6, 24, 29, 40, 43, 55, 68, 75, 76, 85]
Main_004012B0()
{
// ...
__asm
{
; 如果是脱过壳的, 需要注意下这里
mov ecx, g_ImageBase
mov eax, [ecx+_IMAGE_DOS_HEADER.e_lfanew]
mov eax, [eax+ecx+28h] ; OptionalHeader.AddressOfEntryPoint
mov eax, [eax]
}
// ..
}
整数集合前面链接里面有, 再转换一下即可得到flag
def print_sn(choices):
rnds = [
[
0x5E67, 0x12E1, 0x475B, 0x7BD4, 0x304E, 0x64C8, 0x1942, 0x4DBC, 0x0236, 0x36B0,
0x6B29, 0x1FA3, 0x541D, 0x0897, 0x3D11, 0x718B, 0x2604, 0x5A7E, 0x0EF8, 0x4372,
0x77EC, 0x2C66, 0x60E0, 0x1559, 0x49D3, 0x7E4D, 0x32C7, 0x6741, 0x1BBB, 0x5034,
0x04AE, 0x3928, 0x6DA2, 0x221C, 0x5696, 0x0B10, 0x3F89, 0x7403, 0x287D, 0x5CF7,
0x1171, 0x45EB, 0x7A64, 0x2EDE, 0x6358, 0x17D2, 0x4C4C, 0x00C6, 0x3540, 0x69B9,
0x1E33, 0x52AD, 0x0727, 0x3BA1, 0x701B, 0x2494, 0x590E, 0x0D88, 0x4202, 0x767C,
0x2AF6, 0x5F70, 0x13E9, 0x4863, 0x7CDD, 0x3157, 0x65D1, 0x1A4B, 0x4EC4, 0x033E,
0x37B8, 0x6C32, 0x20AC, 0x5526, 0x09A0, 0x3E19, 0x7293, 0x270D, 0x5B87, 0x1001,
0x447B, 0x78F4, 0x2D6E, 0x61E8, 0x1662, 0x4ADC, 0x7F56, 0x33D0, 0x6849, 0x1CC3,
],
[
0x5E67, 0x027A, 0x4E00, 0x1986, 0x650D, 0x3093, 0x7C19, 0x479F, 0x1325, 0x5EAB,
0x2A31, 0x75B8, 0x413E, 0x0CC4, 0x584A, 0x23D0, 0x6F56, 0x3ADD, 0x0663, 0x51E9,
0x1D6F, 0x68F5, 0x347B, 0x0001, 0x4B88, 0x170E, 0x6294, 0x2E1A, 0x79A0, 0x4526,
0x10AD, 0x5C33, 0x27B9, 0x733F, 0x3EC5, 0x0A4B, 0x55D1, 0x2158, 0x6CDE, 0x3864,
0x03EA, 0x4F70, 0x1AF6, 0x667D, 0x3203, 0x7D89, 0x490F, 0x1495, 0x601B, 0x2BA1,
0x7728, 0x42AE, 0x0E34, 0x59BA, 0x2540, 0x70C6, 0x3C4D, 0x07D3, 0x5359, 0x1EDF,
0x6A65, 0x35EB, 0x0171, 0x4CF8, 0x187E, 0x6404, 0x2F8A, 0x7B10, 0x4696, 0x121D,
0x5DA3, 0x2929, 0x74AF, 0x4035, 0x0BBB, 0x5741, 0x22C8, 0x6E4E, 0x39D4, 0x055A,
0x50E0, 0x1C66, 0x67ED, 0x3373, 0x7EF9, 0x4A7F, 0x1605, 0x618B, 0x2D11, 0x7898,
],
]
s = ''
mx = choices[-1] + 1
for i in range(mx):
if i in choices:
rnd = rnds[0][i]
else:
rnd = rnds[1][i]
s += '%02X' % (rnd & 0xFF)
s += '%02X' % ((rnd >> 8) & 0xFF)
print s
return
def test():
print_sn([0, 2, 6, 24, 29, 40, 43, 55, 68, 75, 76, 85])
return
test()
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法
最后于 2020-11-21 08:01
被风间仁编辑
,原因: