【Day 03】- 打针!打针!从 R0 注入的那件事!

Agenda

  • 资安宣言
  • 测试环境与工具
  • 学习目标
  • 技术原理与程序码
  • References
  • 下期预告

资安宣言


撰写本系列文章目的在於提升资讯安全之实务能力,
并透过实作体悟到资讯安全领域的重要性,
本系列所有文章之内容皆有一定技术水平,
不得从事非法行为、恶意攻击等非法活动,
「一切不合法规之行为皆受法律所约束」,
为了避免造成公司、厂商或玩家之间困扰,
所有实作不会拿已上市产品、Online Game 等等来作范例学习,
且部分具有深度、价值之内容,将会提升一定阅读门槛(不对该技术做分析、解说),
请勿透过本系列文章所学,从事任何非法活动,请不要以身试法!!!


测试环境与工具

学习目标

  • 1.能将 DLL 注入到 Process 中
  • 2.能将 DLL 注入到受保护的 Process 中

技术原理与程序码

  • 今天要讲的是一个公开的 Project:Blackbone
  • 在阅读这篇文章前,你/你应该先了解一下 DLL_injection

首先开始前要先说一下,
小弟我目前还属於菜鸟阶段,正不断努力学习中,
若有发现错误或不妥之处还请不吝赐教。
欢迎大家多多留言,互相交流交流。

然後这篇文章不是从零开始讲,所以有些东西需要某些基本知识才能完全掌握。

研究过或是写过游戏外挂的人,都应该会知道这个知名的「Blackbone」Library 吧?
Blackbone 是一个 Windows memory hacking library,
程序码写得挺漂亮的,可以直接拿来学习、使用(前提要先看懂,因为只是 library),
这个 Library 有非常多实用的功能,今天只有要讲一个小功能:INJECT_DLL

首先,先来说一下从 Kernel mode(R0) 注入 DLL 的好处:

  1. 拥有系统级别的「操作权」
  2. 能任意、轻松的存取/修改 Memory(要有能力)
  3. 假设你/你有「能力」的话,想做「任何事情」都不是问题

再来,根据我的经验告诉我一件事,DLL Injection 不是一项 「等待被解决的问题」

What is 不是一项 「等待被解决的问题」 ???
意思是说 DLL Injection 这件事情本身就不是问题,
换句话说就是 DLL Injection 太容易了,手法非常非常多,
就技术面来看,基本上保护得再好都有方法能绕过,然後注入,
所以你/你还在为 DLL Injection 这件事烦恼吗?
/images/emoticon/emoticon47.gif

接着,讲一下这个 Project 所采用的三种注入技术:
(今天只讲第一种,在写下去内容太多,而且小弟我时间不多 >,<)
(有时间再来写一篇)

  1. LdrLoadDll + ZwCreateThreadEx
  2. LdrLoadDll + APC
  3. Manual map

首先,程序码位於 Inject.c 的 BBInjectDll() 中,

一开始可以看到:
透过 PsLookupProcessByProcessId 拿 Process(注入目标) 的 EProcess

status = PsLookupProcessByProcessId( (HANDLE)pData->pid, &pProcess );

What is EProcess?

  • 在 Kernel 还有个东西叫做「KPROCESS」,这边就不讲,直接讲「EProcess」
  • 用一句话做简单、简洁有力的说明就是:储存着 Process 的各种资讯的一个 Structure
  • EProcess 的「E」代表 Execute 的意思

这个 EProcess Structure 长怎样?要怎麽看?

  1. 可以使用 Administrator 权限打开 WinDbg
  2. 选 Kernel Debug
    • (可能需要打开测试模式,自己看跳出来的说明罗!)
  3. 选 Local 按下确定
    • (可能需要打开测试模式,自己看跳出来的说明罗!)
  4. 输入 !process 0 0 查看目前打开的所有 Process
    • 内容大概长这样:
    lkd> !process 0 0
    **** NT ACTIVE PROCESS DUMP ****
    PROCESS ffffc182be847040
        SessionId: none  Cid: 0004    Peb: 00000000  ParentCid: 0000
        DirBase: 001ab000  ObjectTable: ffff848ddf014040  HandleCount: 2192.
        Image: System
    
    PROCESS ffffc182bff3a5c0
        SessionId: none  Cid: 0144    Peb: 775efe7000  ParentCid: 0004
        DirBase: 011b8000  ObjectTable: ffff848ddf5ab500  HandleCount:  52.
        Image: smss.exe
    
    --- --- --- --- --- ---
    --- --- --- --- --- ---
    --- --- --- --- --- ---
    
    PROCESS ffffc182be948080
        SessionId: 1  Cid: 0888    Peb: 9a982f9000  ParentCid: 11c0
        DirBase: 10b2f000  ObjectTable: ffff848debb41600  HandleCount: 345.
        Image: windbg.exe
    
    PROCESS ffffc182c08835c0
        SessionId: 1  Cid: 1528    Peb: d2ccd46000  ParentCid: 11c0
        DirBase: 109a1000  ObjectTable: ffff848debc60800  HandleCount: 262.
        Image: notepad.exe
    
  5. 假设要看 notepad.exe 的 EProcess 输入 dt _eprocess ffffc182c08835c0
    • ffffc182c08835c0 就是 notepad.exe 的 _EPROCESS 地址
    • 今天不讲 EProcess 哦,只是带过一下,後面几天就会来详细谈谈 EProcess ^^
    • 然後就会看到:
    lkd> dt _eprocess ffffc182c08835c0
    nt!_EPROCESS
       +0x000 Pcb              : _KPROCESS ? //刚刚说到的 KPROCESS
       +0x2d8 ProcessLock      : _EX_PUSH_LOCK
       +0x2e0 UniqueProcessId  : 0x00000000`00001528 Void
       +0x2e8 ActiveProcessLinks : _LIST_ENTRY [ xxx - xxx ]
       +0x2f8 RundownProtect   : _EX_RUNDOWN_REF
    
        --- --- --- --- --- ---
        --- --- --- --- --- ---
        --- --- --- --- --- ---
    
       +0x82c MitigationFlags2 : 0
       +0x82c MitigationFlags2Values : <unnamed-tag>
       +0x830 PartitionObject  : 0xffffc182`be8489f0 Void
    
       //end
    

好的,现在讲回来程序码,还记得讲到哪里?... 刚拿到注入目标的 EProcess 而已。

继续往下看会看到正在判断 pData->type 是什麽类型,
第一个可以看到是 pData->type == IT_MMap(不是这次要讲的)

继续往下看会看到正在拿 Ntdll.dll 的 Base

pNtdll = BBGetUserModule( pProcess, &ustrNtdll, isWow64 );

这边有判断目标是不是 Wow64,什麽?你/你要问什麽是 Wow64
这个不是本章重点,就略过去罗~

继续往下看会看到正在拿 LdrLoadDll address(实作方法就不说惹)

LdrLoadDll = BBGetModuleExport( pNtdll, "LdrLoadDll", pProcess, NULL );

继续往下看会看到厉害的东西:

if (PsIsProtectedProcess( pProcess ))
{
    prot.pid         = pData->pid;
    prot.protection  = Policy_Disable;
    prot.dynamicCode = Policy_Disable;
    prot.signature   = Policy_Disable;
    BBSetProtection( &prot );
}

这段 CODE 的用意就是要把 Process 保护拿掉,就是要把以下的值清空,
(这边不带大家看 CODE 了,有兴趣的可以自己看、自己找)
在 Win10 16299 的 EProcess 中,
有个位置叫做:

+0x6ca Protection       : _PS_PROTECTION

Protection 里面有三样东西:

[+0x000] Level                    : 0x0 [Type: unsigned char]
[+0x000 ( 2: 0)] Type             : 0x0 [Type: unsigned char]
[+0x000 ( 3: 3)] Audit            : 0x0 [Type: unsigned char]
[+0x000 ( 7: 4)] Signer           : 0x0 [Type: unsigned char]

所以这些代表什麽? o(≧∀≦)o
好了,我都提到这边了,请自行谷歌~(>_<。)\,
这个部分涉及部分技术,而且也不是本章重点就不深入讨论罗~

继续往下看会看到正在判断 if (pData->type == IT_Thread)
耶!终於到了本章重点,LdrLoadDll + ZwCreateThreadEx

xxx pUserBuf = isWow64 ? xxxFunc( xxx ) : BBGetNativeCode( LdrLoadDll, &ustrPath );

if (pData->type == IT_Thread)
{
    status = BBExecuteInNewThread( 
                                pUserBuf, 
                                NULL, 
                                THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER, 
                                pData->wait, 
                                &threadStatus 
                                );

先来看一下这一行:

xxx pUserBuf = isWow64 ? xxxFunc( xxx ) : BBGetNativeCode( LdrLoadDll, &ustrPath );

为了节省时间与文章长度,
直接假设程序走的是 BBGetNativeCode( LdrLoadDll, &ustrPath )
其中 ustrPath 是被注入的 DLL Full Path

跟进去可以看到有一段 shellcode,
这段 shellcode 的用意可以在注解中找到:

// <summary>
// Build injection code for native x64 process
// Must be running in target process context
// </summary>
UCHAR code[] =
{
    0x48, 0x83, 0xEC, 0x28,
    // sub rsp, 0x28
    
    0x48, 0x31, 0xC9,
    // xor rcx, rcx
    
    0x48, 0x31, 0xD2,
    // xor rdx, rdx
    
    0x49, 0xB8, 0, 0, 0, 0, 0, 0, 0, 0,
    // mov r8, ModuleFileName   offset +12
    
    0x49, 0xB9, 0, 0, 0, 0, 0, 0, 0, 0,
    // mov r9, ModuleHandle     offset +22
    
    0x48, 0xB8, 0, 0, 0, 0, 0, 0, 0, 0,
    // mov rax, LdrLoadDll      offset +32
    
    0xFF, 0xD0,
    // call rax
    
    0x48, 0xBA, 0, 0, 0, 0, 0, 0, 0, 0,
    // mov rdx, COMPLETE_OFFSET offset +44
    
    0xC7, 0x02, 0x7E, 0x1E, 0x37, 0xC0,
    // mov [rdx], CALL_COMPLETE 
    
    0x48, 0xBA, 0, 0, 0, 0, 0, 0, 0, 0,
    // mov rdx, STATUS_OFFSET   offset +60
    
    0x89, 0x02,
    // mov [rdx], eax
    
    0x48, 0x83, 0xC4, 0x28,
    // add rsp, 0x28
    
    0xC3
    // ret
};

继续往下看会看到正在申请记忆体空间:

status = ZwAllocateVirtualMemory( 
                                ZwCurrentProcess(), 
                                &pBuffer, 
                                0, 
                                &size, 
                                MEM_COMMIT, 
                                PAGE_EXECUTE_READWRITE 
                                );

继续往下看会看到:

  1. 正在将 shellcode 写入刚申请到的记忆体空间中
  2. 正在将 DLL 资讯写进记忆体空间中
  3. (也可以想成是把 DLL 写进 shellcode,再把 shellcode 写进记忆体)
  4. (不过看 CDOE 的话,是先把 shellcode 复制进记忆体,再将 DLL 资讯写进记忆体)
// Copy path
PUNICODE_STRING pUserPath = &pBuffer->path;
pUserPath->Length = 0;
pUserPath->MaximumLength = sizeof(pBuffer->buffer);
pUserPath->Buffer = pBuffer->buffer;

RtlUnicodeStringCopy( pUserPath, pPath );

// Copy code
memcpy( pBuffer, code, sizeof( code ) );

// Fill stubs
*(ULONGLONG*)((PUCHAR)pBuffer + 12) = (ULONGLONG)pUserPath;
*(ULONGLONG*)((PUCHAR)pBuffer + 22) = (ULONGLONG)&pBuffer->module;
*(ULONGLONG*)((PUCHAR)pBuffer + 32) = (ULONGLONG)LdrLoadDll;
*(ULONGLONG*)((PUCHAR)pBuffer + 44) = (ULONGLONG)&pBuffer->complete;
*(ULONGLONG*)((PUCHAR)pBuffer + 60) = (ULONGLONG)&pBuffer->status;

继续往下看就是在 Init routine 了,最後恢复 Process 保护。

实际上到这边 DLL Injection 就结束了,
不过再往下看的话会看到一些有趣的东西,
例如:

// Unlink module
if (pData->unlink)

// Erase header
if (pData->erasePE)

这是之後要讲到的隐藏 Module 方法
隐藏的方法非常多,之後会举几个有趣的例子。

最後整理了一下大致流程:

  1. User mode 向 Driver 传送要注入的相关资讯
  2. Get EProcess
  3. Get ntdll base
  4. Get LdrLoadDll address
  5. 检查是否有保护,如果有移除,注入完成後再恢复
  6. 申请一块记忆体空间,然後传入 DLL(Build injection code for native x64 process)
  7. 启动刚申请好的那块空间
  8. 最後执行(Init routine)

编译与执行:

  • 虾?你/你问怎麽编译、怎麽用?
  • 编译 Driver,然後写个 User mode 程序,将 ioctl 讯号发过去给 Driver。
  • 有学过 Driver 的都会知道怎麽做的!
  • 点我学习

额外补充:

  • 这个 Library 已经出现在网路上有一段时间了
  • 所以基本上有满多东西被挡掉了
  • 所以呢~想拿来开挂应该要碰碰运气罗~

大家若有发现哪里写得不好或错误的地方,都留个言讨论一下吧 XD
那我们下期见 o( ̄▽ ̄)ブ

References

下期预告

  • 【Day 04】- 今天来把 Module 藏起来
  • 如果你/你喜欢我的文章,请记得订阅按赞分享并且打开小铃铛哦
  • 这样就能在第一时间收到通知,也不会错过任何文章啦~~

<<:  Day 13:Python基本介绍06 | 函数、读写档案、引用

>>:  Day 5 [Python ML] 欠拟合(Underfitting)和过拟合(Overfitting)

[Day 22] 针对API的单元测试(二)

我们昨天已经测试了一个Json的API, 那我们今天将测试方法改成这样 public functio...

Multiple objects (上)

大家好,我是西瓜,你现在看到的是 2021 iThome 铁人赛『如何在网页中绘制 3D 场景?从 ...

React-使用useRef跨组件操作DOM

"我想要在React上实现同一页在menu上点击,就滑到对应的区块,该怎麽做呢?"...

铁人赛 Day10 -- 一定要知道的 CSS (七) -- background:linear-gradient渐层背景

前言 昨天知道一些背景的属性後,是不是觉得有点单调呀,所以我们今天就来谈谈渐层 基本语法 backg...

android studio 30天学习笔记 -day 22-Dagger 前言

Dependency Injection Dependency Injection中文翻译为依赖注入...