撰写本系列文章目的在於提升资讯安全之实务能力,
并透过实作体悟到资讯安全领域的重要性,
本系列所有文章之内容皆有一定技术水平,
不得从事非法行为、恶意攻击等非法活动,
「一切不合法规之行为皆受法律所约束」,
为了避免造成公司、厂商或玩家之间困扰,
所有实作不会拿已上市产品、Online Game 等等来作范例学习,
且部分具有深度、价值之内容,将会提升一定阅读门槛(不对该技术做分析、解说),
请勿透过本系列文章所学,从事任何非法活动,请不要以身试法!!!
首先开始前要先说一下,
小弟我目前还属於菜鸟阶段,正不断努力学习中,
若有发现错误或不妥之处还请不吝赐教。
欢迎大家多多留言,互相交流交流。
然後这篇文章不是从零开始讲,所以有些东西需要某些基本知识才能完全掌握。
研究过或是写过游戏外挂的人,都应该会知道这个知名的「Blackbone」Library 吧?
Blackbone 是一个 Windows memory hacking library,
程序码写得挺漂亮的,可以直接拿来学习、使用(前提要先看懂,因为只是 library),
这个 Library 有非常多实用的功能,今天只有要讲一个小功能:INJECT_DLL
首先,先来说一下从 Kernel mode(R0) 注入 DLL 的好处:
再来,根据我的经验告诉我一件事,DLL Injection 不是一项 「等待被解决的问题」
What is 不是一项 「等待被解决的问题」 ???
意思是说 DLL Injection 这件事情本身就不是问题,
换句话说就是 DLL Injection 太容易了,手法非常非常多,
就技术面来看,基本上保护得再好都有方法能绕过,然後注入,
所以你/你还在为 DLL Injection 这件事烦恼吗?
接着,讲一下这个 Project 所采用的三种注入技术:
(今天只讲第一种,在写下去内容太多,而且小弟我时间不多 >,<)
(有时间再来写一篇)
首先,程序码位於 Inject.c 的 BBInjectDll() 中,
一开始可以看到:
透过 PsLookupProcessByProcessId 拿 Process(注入目标) 的 EProcess
status = PsLookupProcessByProcessId( (HANDLE)pData->pid, &pProcess );
What is EProcess?
这个 EProcess Structure 长怎样?要怎麽看?
!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
dt _eprocess ffffc182c08835c0
ffffc182c08835c0
就是 notepad.exe 的 _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
);
继续往下看会看到:
// 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 方法,
隐藏的方法非常多,之後会举几个有趣的例子。
最後整理了一下大致流程:
编译与执行:
额外补充:
大家若有发现哪里写得不好或错误的地方,都留个言讨论一下吧 XD
那我们下期见 o( ̄▽ ̄)ブ
<<: Day 13:Python基本介绍06 | 函数、读写档案、引用
>>: Day 5 [Python ML] 欠拟合(Underfitting)和过拟合(Overfitting)
我们昨天已经测试了一个Json的API, 那我们今天将测试方法改成这样 public functio...
大家好,我是西瓜,你现在看到的是 2021 iThome 铁人赛『如何在网页中绘制 3D 场景?从 ...
"我想要在React上实现同一页在menu上点击,就滑到对应的区块,该怎麽做呢?"...
前言 昨天知道一些背景的属性後,是不是觉得有点单调呀,所以我们今天就来谈谈渐层 基本语法 backg...
Dependency Injection Dependency Injection中文翻译为依赖注入...