锦鲤安全的技术文章仅供参考,此文所提供的信息仅供网络安全人员学习和参考,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失,均由使用者本人负责。如有侵权烦请告知,我们会立即删除并致歉。本文所提供的工具仅用于学习,禁止用于其他,请在24小时内删除工具文件!谢谢!
Windows 控制台程序在启动时会出现一个黑(dos)窗口,一般我们想要隐藏有两种方式,一是转成窗口(Windows GUI)程序,二是使用Windows API 隐藏,这里对不同种方式隐藏黑窗口的效果和优缺点进行了总结。
优点:最常用的方式。
缺点:窗口程序某些时候容易引起杀软报毒,尤其是 x86 应用程序,报毒最为明显。
使用如下预处理指令将程序从控制台程序切换为 GUI 程序, GUI 程序不会显示 dos 窗口:
#pragma comment(linker, "/subsystem:\"Windows\" /entry:\"mainCRTStartup\"")
这条指令的意思是使用子系统(subsystem)为窗口(Windows)模式,入口(entry)点指定为 mainCRTStartup 函数。
C 控制台程序并不会直接调用main函数,而是先调用 mainCRTStartup 执行一些 C 程序处理之后再调用main函数。
这里之所有要指定入口点指定为 mainCRTStartup 函数是因为使用子系统为窗口模式后默认的 C 窗口程序入口点是 WinMainCRTStartup 函数,由 WinMainCRTStartup 函数间接调用 WinMain 函数,并不会调用我们常见的 main 函数,为了让程序转为 C 窗口程序后还能调用 main 函数则必须指定入口点为 mainCRTStartup。
使用预处理指令等同于 VS 进行如下设置:
通过PE编辑器直接修改 PE 文件的 Optional Header,将 Subsystem 由 3 修改为 2,即将控制台程序修改为窗口程序:
这种方式与前两种操作方式效果是一样的。
优点:相比于直接将控制台程序转成窗口程序隐藏窗口,通过 API 方式隐藏更隐蔽,同时报毒也更低
缺点:使用 API 隐藏的行为特征也会更为明显,且不同的 API 隐藏方式的行为特征也不同。
执行如下代码即可,使用起来简单粗暴:
FreeConsole();
使用这个 API 隐藏窗口最为快速,dos 窗口一闪而过,之后就被隐藏了。但是杀软也对该API的使用最为敏感,使用该 API 可能直接被杀软行为检测然后报毒。
使用该 API 组合必须在 dos 窗口完全显示出来之后才能隐藏,否则调用无效:
HWND hwnd = GetForegroundWindow();
ShowWindow(hwnd, SW_HIDE);
也就是像下面这样子,Sleep 延迟1秒,等待窗口完全显示出来,然后再调用 API 隐藏窗口:
使用该 API 组合效果比起 FreeConsole 函数,隐藏效果要慢很多,而且容易出现隐藏到错误的窗口。
GetForegroundWindow() 函数效果是用户当前正在使用的窗口,如何用户操作比较快的话,就可能获取到其它程序的窗口,导致隐藏到错误的窗口,因此不建议使用该 API 组合。
这种方式是 API 隐藏最为推荐的方式,也是很多木马使用的隐藏方式。
使用如下代码创建子进程隐藏窗口,首先获取当前路径,加上 go 参数,调用 CreateProcessA() 函数创建子进程,设置 CREATE_NO_WINDOW 标志隐藏子进程的窗口,然后在前面加上判断,判断启动参数是否为 go 如果是则表明这是子进程则不需要再创建子进程隐藏窗口,执行其它操作:
#include <iostream>
#include <Windows.h>
int main(int argc, char* argv[])
{
// 判断启动参数是否为 go,如果是则表明这是子进程,窗口已隐藏
if (argc == 2 && strcmp(argv[1], "go") == 0) {
MessageBoxA(0, "隐藏窗口", "隐藏窗口", 0);
return 0;
}
// 获取当前程序完整路径
char path[MAX_PATH * 4];
GetModuleFileNameA(NULL, path, MAX_PATH * 4);
// 路径加上参数 go
strcat_s(path, " go");
STARTUPINFOA StartInfo;
PROCESS_INFORMATION pinfo;
memset(&StartInfo, 0, sizeof(STARTUPINFO));
StartInfo.cb = sizeof(STARTUPINFO);
//StartInfo.dwFlags = STARTF_USESHOWWINDOW;
//StartInfo.wShowWindow = SW_HIDE;
BOOL result = CreateProcessA(NULL, path, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &StartInfo, &pinfo);
if (!result) {
printf("创建子进程失败:%d\n", GetLastError());
Sleep(10000);
}
return 0;
}
使用该方式隐藏窗口效果等同于 FreeConsole 函数,启动窗口一闪而过,但是隐蔽方式比 FreeConsole 函数要好,但是在某些杀软的复杂环境下可能会阻止进程创建子进程从而导致程序执行失败。
一般场景下推款将程序转为窗口程序隐藏黑窗口,特殊场景如果不得不使用控制程序,较为推款使用最后一种方式创建子进程隐藏黑窗口。