远程线程注入

0x00 前言

远程线程注入是一种经典的DLL注入技术。其实就是指一个新进程中另一个进程中创建线程的技术。

0x01 介绍

1.远程线程注入原理

画了一个图大致理解了下远程线程注入dll的原理。

如果是实现注入dll的话,流程大致就是:

通过OpenProcess获取目标进程句柄。

通过VirtualAllocEx在目标进程空间中申请内存,通过WriteProcessMemory放入需要载入的dll的路径。

通过GetModuleHandleA获取诸如kernel32.dll这类系统dll的模块句柄,进而获取LoadLibraryA这类载入动态链接库的函数地址(固定)

通过CreateRemoteThread的参数传入目标进程对象句柄、写入到目标进程空间的dll路径、LoadLibraryA函数地址,实现中目标中创建多线程加载dll。

2.函数介绍

OpenProcess 函数

打开现有的本地进程对象。

函数声明

HANDLE WINAPI OpenProcess(     _In_ DWORD dwDesiredAccess,     _In_ BOOL  bInheritHandle,     _In_ DWORD dwProcessId);

参数

  • dwDesiredAccess [in]

    访问进程对象。此访问权限针对进程的安全描述符进行检查。此参数可以是一个或多个进程访问权限。如果调用该函数的进程启用了SeDebugPrivilege权限,则无论安全描述符的内容如何,都会授予所请求的访问权限。
  • bInheritHandle [in]

    若此值为TRUE,则此进程创建的进程将继承该句柄。否则,进程不会继承此句柄。
  • dwProcessId [in]

    要打开的本地进程的标识符。

    如果指定的进程是系统进程(0x00000000),则该函数失败,最后一个错误代码为ERROR_INVALID_PARAMETER。如果指定的进程是空闲进程或CSRSS进程之一,则此功能将失败,并且最后一个错误代码为ERROR_ACCESS_DENIED,因为它们的访问限制会阻止用户级代码打开它们。

    如果您使用GetCurrentProcessId作为此函数的参数,请考虑使用GetCurrentProcess而不是OpenProcess,以提高性能。

返回值

  • 如果函数成功,则返回值是指定进程的打开句柄。
  • 如果函数失败,返回值为NULL。 要获取扩展错误信息,请调用GetLastError。

VirtualAllocEx 函数

在指定进程的虚拟地址空间内保留,提交或更改内存区域的状态。 该函数初始化其分配给零的内存。

函数声明

LPVOID WINAPI VirtualAllocEx(    _In_     HANDLE hProcess,    _In_opt_ LPVOID lpAddress,    _In_     SIZE_T dwSize,    _In_     DWORD  flAllocationType,    _In_     DWORD  flProtect);

参数

  • hProcess [in]

    过程的句柄。该函数在该进程的虚拟地址空间内分配内存。

    句柄必须具有PROCESS_VM_OPERATION权限。有关更多信息,请参阅流程安全和访问权限。
  • lpAddress [in]

    指定要分配的页面的所需起始地址的指针。

    如果您正在保留内存,则该函数会将该地址舍入到分配粒度的最接近的倍数。

    如果您提交已经保留的内存,该功能会将该地址舍入到最接近的页面边界。要SESSION 0 隔离页面的大小和主机上的分配粒度,请使用GetSystemInfo函数。

    如果lpAddress为NULL,则该函数确定在哪里分配该区域。
  • dwSize [in]

    要分配的内存大小,以字节为单位。

    如果lpAddress为NULL,则函数将dwSize循环到下一个页面边界。

    如果lpAddress不为NULL,则该函数将从lpAddress到lpAddress + dwSize的范围内分配包含一个或多个字节的所有页面。这意味着,例如,跨越页面边界的2字节范围会导致功能分配两个页面。
  • flAllocationType [in]

    内存分配类型。此参数必须包含以下值之一:
VALUE MEANING
MEM_COMMIT 为指定的预留内存页分配内存费用(从磁盘上的内存和分页文件的总体大小)。 该函数还保证当调用者稍后初次访问存储器时,内容将为零。 除非/直到虚拟地址被实际访问,实际的物理页面才被分配
MEM_RESERVE 保留进程的虚拟地址空间的范围,而不会在内存或磁盘上的分页文件中分配任何实际物理存储
MEM_RESET 表示由lpAddress和dwSize指定的内存范围内的数据不再受关注。 页面不应从页面文件中读取或写入页面文件。 然而,内存块将在以后再次被使用,所以不应该被分解。 该值不能与任何其他值一起使用
MEM_RESET_UNDO 只能在早期成功应用了MEM_RESET的地址范围上调用MEM_RESET_UNDO。 它指示由lpAddress和dwSize指定的指定内存范围内的数据对呼叫者感兴趣,并尝试反转MEM_RESET的影响。 如果功能成功,则表示指定地址范围内的所有数据都是完整的。 如果功能失败,地址范围中的至少一些数据已被替换为零
  • flProtect [in]

    要分配的页面区域的内存保护。 如果页面被提交,您可以指定任何一个内存保护常量。

    如果lpAddress指定了一个地址,flProtect不能是以下值之一:

    PAGE_NOACCESS

    PAGE_GUARD

    PAGE_NOCACHE

    PAGE_WRITECOMBINE

返回值

  • 如果函数成功,则返回值是分配的页面区域的基址。
  • 如果函数失败,返回值为NULL。 要获取扩展错误信息,请调用GetLastError。

WriteProcessMemory 函数

在指定的进程中将数据写入内存区域。 要写入的整个区域必须可访问或操作失败。

函数声明

BOOL WINAPI WriteProcessMemory(     _In_  HANDLE  hProcess,     _In_  LPVOID  lpBaseAddress,     _In_  LPCVOID lpBuffer,     _In_  SIZE_T  nSize,     _Out_ SIZE_T  *lpNumberOfBytesWritten);

参数

  • hProcess [in]

    要修改的进程内存的句柄。 句柄必须具有PROCESS_VM_WRITE和PROCESS_VM_OPERATION访问进程。
  • lpBaseAddress [in]

    指向写入数据的指定进程中的基地址的指针。 在数据传输发生之前,系统会验证指定大小的基地址和内存中的所有数据是否可以进行写入访问,如果不可访问,则该函数将失败。
  • lpBuffer [in]

    指向缓冲区的指针,其中包含要写入指定进程的地址空间的数据。
  • nSize [in]

    要写入指定进程的字节数。
  • lpNumberOfBytesWritten [out]

    指向变量的指针,该变量接收传输到指定进程的字节数。 此参数是可选的。 如果lpNumberOfBytesWritten为NULL,则忽略该参数。

返回值

  • 如果函数成功,则返回值不为零。
  • 如果函数失败,返回值为0(零)。 要获取扩展错误信息,请调用GetLastError。

CreateRemoteThread 函数

创建在另一个进程的虚拟地址空间中运行的线程。

使用CreateRemoteThreadEx函数创建在另一个进程的虚拟地址空间中运行的线程,并可选地指定扩展属性。

函数声明

HANDLE WINAPI CreateRemoteThread(   _In_  HANDLE                 hProcess,   _In_  LPSECURITY_ATTRIBUTES  lpThreadAttributes,   _In_  SIZE_T                 dwStackSize,   _In_  LPTHREAD_START_ROUTINE lpStartAddress,   _In_  LPVOID                 lpParameter,   _In_  DWORD                  dwCreationFlags,   _Out_ LPDWORD                lpThreadId);

参数

  • hProcess [in]

    要创建线程的进程的句柄。 句柄必须具有PROCESS_CREATE_THREAD,PROCESS_QUERY_INFORMATION,PROCESS_VM_OPERATION,PROCESS_VM_WRITE和PROCESS_VM_READ访问权限,如果某些平台上没有这些权限,可能会失败。 有关更多信息,请参阅流程安全和访问权限。
  • lpThreadAttributes [in]

    指向SECURITY_ATTRIBUTES结构的指针,该结构指定新线程的安全描述符,并确定子进程是否可以继承返回的句柄。 如果lpThreadAttributes为NULL,则线程将获得默认安全描述符,并且该句柄不能被继承。 线程的默认安全描述符中的访问控制列表(ACL)来自创建者的主令牌。dwStackSize [in]

    堆栈的初始大小,以字节为单位。 系统将此值循环到最近的页面。 如果此参数为0(零),则新线程使用可执行文件的默认大小。 有关更多信息,请参阅线程堆栈大小。
  • lpStartAddress [in]

    指向由线程执行的类型为LPTHREAD_START_ROUTINE的应用程序定义函数的指针,并表示远程进程中线程的起始地址。 该功能必须存在于远程进程中。 有关更多信息,请参阅ThreadProc。
  • lpParameter [in]

    指向要传递给线程函数的变量的指针。
  • dwCreationFlags [in]

    控制线程创建的标志。若是 0,则表示线程在创建后立即运行。
  • lpThreadId [out]

    指向接收线程标识符的变量的指针。

    如果此参数为NULL,则不返回线程标识符。

返回值

  • 如果函数成功,则返回值是新线程的句柄。
  • 如果函数失败,返回值为NULL。 要获取扩展错误信息,请调用GetLastError。

0x02 编码实现

测试demo,指定进程pid和dll位置。

TestCreatePId.cpp

// TestCreatePId.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
// #include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <iostream> void InjectDLL(DWORD PID, char* Path)
{
DWORD dwSize;
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, PID);
dwSize = strlen(Path) + 1; LPVOID lpParamAddress = VirtualAllocEx(hProcess, 0, dwSize, PARITY_SPACE, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hProcess, lpParamAddress, (PVOID)Path, dwSize, NULL); HMODULE hModule = GetModuleHandleA("kernel32.dll");
LPTHREAD_START_ROUTINE lpStartAddress = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "LoadLibraryA");
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, lpStartAddress, lpParamAddress, 0, NULL);
WaitForSingleObject(hThread, 1000);
CloseHandle(hThread);
} int main(int argc,char *argv[])
{
char* a = argv[2];
//std::cout << atoi(argv[1]) << a <<std::endl;
InjectDLL(int(argv[1]),a);
//InjectDLL(atoi(argv[1]), "c:\\test\\TestDll.dll");
return 0;
}

0x03 实测

经测试对应位数的dll可以加载到对应位数的进程中。

不过遇到个问题,这里也记录下,当想在同一进程载入两个dll,如果先载入的dll有dllmain中,里面的函数没有走完逻辑,那么第二个dll会在线程挂起结束之后载入到进程。比如我载入到第一个dll里面DLL_PROCESS_ATTACH有个MessageBox,我没给他结束窗口,第二个dll没有加载到进程中,而结束了MessageBox窗口才注入到了进程。

0x04 总结

部分进程需要以管理员权限才可注入到进程,具体原因是OpenProcess打开高权限进程会因权限不足无法打开,其实就是权限继承到原因,这里如果以powershell默认启动就会获得SE_DEBUG权限,而进程令牌权限提升准备再挑一篇文章单独总结学习。

另一个问题就是,不能成功注入到一些系统服务的进程,因为系统存在 SESSION 0 隔离。如果想向系统服务进程中注入,需要突破SESSION 0 隔离,这个也准备另启一篇文章做记录。

0x05 参考

https://www.write-bug.com/article/2011.html

https://www.cnblogs.com/LyShark/p/11066063.html

远程线程注入DLL的更多相关文章

  1. 远程线程注入DLL突破session 0 隔离

    远程线程注入DLL突破session 0 隔离 0x00 前言 补充上篇的远程线程注入,突破系统SESSION 0 隔离,向系统服务进程中注入DLL. 0x01 介绍 通过CreateRemoteTh ...

  2. 详细解读:远程线程注入DLL到PC版微信

    一.远程线程注入的原理 1.其基础是在 Windows 系统中,每个 .exe 文件在双击打开时都会加载 kernel32.dll 这个系统模块,该模块中有一个 LoadLibrary() 函数,可以 ...

  3. 远程线程注入dll,突破session 0

    前言 之前已经提到过,远线程注入和内存写入隐藏模块,今天介绍突破session 0的dll注入 其实今天写这个的主要原因就是看到倾旋大佬有篇文章提到:有些反病毒引擎限制从lsass中dump出缓存,可 ...

  4. CreateRemoteThread远程线程注入Dll与Hook

    CreateRemoteThread虽然很容易被检测到,但是在有些场合还是挺有用的.每次想用的时候总想着去找以前的代码,现在在这里记录一下. CreateRemoteThread远程注入 DWORD ...

  5. 【windows核心编程】使用远程线程注入DLL

    前言 该技术是指通过在[目标进程]中创建一个[远程线程]来达到注入的目的. 创建的[远程线程]函数为LoadLibrary, 线程函数的参数为DLL名字, 想要做的工作在DLL中编写.  示意图如下: ...

  6. 微信 电脑版 HOOK(WeChat PC Hook)- 远程线程注入dll原理

    Windows加载dll的特性 1.Windows系统中,每个exe软件运行的时候,会加载系统模块kernel32.dll 2.所有加载进exe软件的系统模块kernel32.dll,内存地址都是一样 ...

  7. 安全之路 —— 借助DLL进行远程线程注入实现穿墙与隐藏进程

    简介        大多数后门或病毒要想初步实现隐藏进程,即不被像任务管理器这样典型的RING3级进程管理器找到过于明显的不明进程,其中比较著名的方法就是通过远程线程注入的方法注入将恶意进程的DLL文 ...

  8. 安全之路 —— 无DLL文件实现远程线程注入

    简介         在之前的章节中,笔者曾介绍过有关于远程线程注入的知识,将后门.dll文件注入explorer.exe中实现绕过防火墙反弹后门.但一个.exe文件总要在注入时捎上一个.dll文件着 ...

  9. Dll注入技术之远程线程注入

    DLL注入技术之远线程注入 DLL注入技术指的是将一个DLL文件强行加载到EXE文件中,并成为EXE文件中的一部分,这样做的目的在于方便我们通过这个DLL读写EXE文件内存数据,(例如 HOOK EX ...

随机推荐

  1. Feign实战技巧篇

    介绍Feign在项目中的正确打开方式 看了上一期Feign远程调用的小伙伴可能会问:阿鉴,你不是说上一期讲的是Feign的99%常用方式吗?怎么今天还有正确打开方式一说呀? 阿鉴:是99%的常用方式, ...

  2. Qt Designer中自定义控件的使用(提升法与插件法)

    准备乱写一点Qt自定义Widget在Designer中的使用.可是又不想重复提升法(promotion)及插件法基本用法,因为Manual中Using Custom Widgets with Qt D ...

  3. VRRP协议原理与配置

    一.VRRP协议概述 1.1.VRRP协议基本概念 局域网中的用户终端通常采用配置一个默认网关的形式访问外部网络,如果此时默认网关设备发生故障,将中断所有用户终端的网络访问,这很可能会给用户带来不可预 ...

  4. Spring的@PropertySource注解使用

    @PropertySource注解是Spring用于加载配置文件,默认支持.properties与.xml两种配置文件.@PropertySource属性如下: name:默认为空,不指定Spring ...

  5. JVM钩子函数的使用

    一.问题引入 背景 在编写一个需要持续在后台运行的程序的时候遇到了这样的场景:我的程序在主函数中创建了一个线程池周期性地执行任务,我希望主线程和线程池都持续运行,但如果收到外部的关闭信号时,主线程和线 ...

  6. weblogicSSRF漏洞复现

    一.关于SSRF 1.1 简介: SSRF(Server-Side Request Forgery)服务端请求伪造,是一种由攻击者构造形成由服务器端发起请求的一个漏洞,一般情况下,SSRF 攻击的目标 ...

  7. 进程之间的通信(multiprocess.Queue)

    一.进程间通信 进程之间的数据是相互隔离的,例如 from multiprocessing import Process def task(): global n # 声明全局变量 n = 999 p ...

  8. Git进行clone的时候,报错:remote: HTTP Basic: Access denied fatal: Authentication failed for ...

    先执行: git config --system --unset credential.helper 原因:用户名或者密码错: 会提示让重新输入用户名和密码,输入正确的用户名和密码即可! 这样以后发现 ...

  9. 数据结构与算法——链表 Linked List(单链表、双向链表、单向环形链表-Josephu 问题)

    链表是有序的列表,但是在内存中存储图下图所示 链表是以 节点 的方式来存储,是 链式存储 每个节点包含 data 域.next 域,指向下一个节点 链表的各个节点 不一定是连续存储,如上图所示 链表还 ...

  10. 【spring 注解驱动开发】spring事务处理原理

    尚学堂spring 注解驱动开发学习笔记之 - 事务处理 事务处理 1.事务处理实现 实现步骤: * 声明式事务: * * 环境搭建: * 1.导入相关依赖 * 数据源.数据库驱动.Spring-jd ...