成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

針對常見混淆技術的反制措施

安全 應用安全
在這篇文章中,我們將仔細研究兩種常見的混淆技術,以了解它們是如何工作的,并弄清楚如何去混淆。

現代軟件經常將混淆技術作為其反篡改策略的一部分,以防止黑客逆向分析軟件的關鍵組件。他們經常使用多種混淆技術來抵御黑客的攻擊,這有點像滾雪球:隨著雪層的增多,軟件規模也隨之變大,使其逆向分析難度隨之提高。

在這篇文章中,我們將仔細研究兩種常見的混淆技術,以了解它們是如何工作的,并弄清楚如何去混淆。

概述

這里,我們將研究以下混淆技術:

基于IAT導入表的混淆技術

基于控制流的混淆技術

基于IAT導入表的混淆技術

在深入介紹基于IAT導入表的混淆方法之前,先讓我解釋一下導入表到底是什么。

什么是導入函數?

當進行逆向分析時,需要弄清楚的第一件事,就是它如何調用操作系統的函數。在我們的例子中,我們將重點關注Windows 10系統,因為大多數視頻游戲只能在Windows系統上運行。無論如何,對于那些還不知道的人來說,Windows提供了一系列重要的動態鏈接庫(DLL)文件,幾乎每個Windows可執行文件都會用到這些庫文件。這些DLL文件中保存了許多函數,可以供Windows可執行文件“導入”,使其可以加載和執行給定DLL中的函數。

它們為何如此重要?

例如,Ntdll.dll庫負責幾乎所有與內存有關的功能,如打開一個進程的句柄(NtOpenProcess),分配一個內存頁(NtVirtualAlloc,NtVirtualAllocEx),查詢內存頁(NtVirtualQuery,NtVirtualQueryEx),等等。

另一個重要的DLL庫是ws2_32.dll,它通過以下函數處理各種網絡活動:

Socket
Connect / WSAConnect
Send / WSASend
SendTo / WSASendTo
Recv / WSARecv
RecvFrom / WSARecvFrom

現在讀者可能會問,知道這些有什么意義呢?好吧,如果您把一個二進制文件扔到像IDA這樣的反匯編器中(我通常會做的第一件事),就是檢查所有導入的函數,以便對二進制文件的功能有一個大致的了解。例如,當ws2_32.dll存在于導入表中時,表明該二進制文件可能會連接到Internet。

現在,我們可能想要進行更深入的研究,并考察使用了哪些ws2_32.dll函數。如果我們使用Socket函數并找出它的調用位置,我們就可以檢查它的參數,這樣,我們就可以通過搜索引擎查找相應的函數名,從而輕松地找出它所使用的協議和類型。

注意:IDA已自動向反匯編代碼中添加了注釋。

經過混淆處理的導入表

無論如何,這些Windows函數能提供相當多的信息,因為它們是有據可查的函數。因此,攻擊者希望能夠把這些函數藏起來,以掩蓋正在發生的事情。

我們在反匯編器中看到的所有這些導入函數都是從導入地址表(IAT)加載的,該表在可執行文件的PE頭文件中的某個地方被引用。一些惡意軟件/游戲通常試圖通過不直接指向DLL函數來隱藏這些導入地址。相反,他們可能會使用一個蹦床或迂回函數。

考察我們的示例

在這個例子中,我們使用的是一種蹦床式混淆技術,具體如下所示:

下面的地址0x7FF7D7F9B000引用了我們的函數0x19AA1040FE1,盡管看起來完全不是這么回事。您可能認為這是垃圾代碼,但仔細看看,您會發現并非如此。

請仔細查看前兩個指令:前面的指令是mov rax, FFFF8000056C10A1,后面的指令是jmp 19AA1040738,后面的都是垃圾指令。不管怎樣,讓我們跟隨跳轉指令,看看它會跳到哪里:

看,又是4個有效的指令,這次是一個異或指令和兩個加法指令,然后是另一個跳轉指令。讓我們把這個過程再重復幾遍...

最后,我們來到jmp rax指令!需要注意的是,所有的XOR、SUB和ADD指令都是在Rax寄存器上執行的,這意味著它可能包含導入函數的實際指針。下面,讓我們算算看。

實際上,在經過數學運算之后,我們得到了指向advapi32.regopenkeyexa的指針!

現在,我們所要做的就是重復幾百次運算,從而徹底消除針對IAT導入表的混淆處理。

基于IAT的自動去混淆處理

我想,沒有人喜歡用計算器手工重復上述過程,做一次已經很煩了。從現在開始,我們將使用C#實現自動計算。正如您可能已經看到的,我們只需要處理在同一個寄存器上執行的ADD、SUB和XOR操作。原因是Rax被用作返回地址,而諸如Rcx、Rdx、R8、R9和其他寄存器對于被調用方來說是不安全的,并且可能與調用約定沖突。這意味著,我們甚至不需要使用反匯編器,因為我們可以很輕松地區分這些指令,這要歸功于涉及的寄存器和操作碼寥寥無幾。

到此為止,我們已經詳細解釋了混淆處理技術。接下來,大家不妨以Unsnowman項目中的importfix.cs為例,來了解與去混淆處理相關的代碼。

基于控制流的混淆技術

在逆向分析二進制文件時,另一個有價值的信息來源是匯編指令本身。對于人類來說,它們可能難以理解,但對于像IDA這樣的反編譯器來說,我們只需按下F5鍵,IDA就會生成我們人類可以理解的偽代碼。

混淆實際指令的一個簡單方法,是組合使用垃圾代碼和不透明分支(即該分支條件總是為不成立,也就是說,該分支用于也不會被執行)。這意味著:把垃圾代碼放在一個分支指令之后。訣竅在于,我們可以使用條件轉移,但是,要確保條件永遠為真,這樣分支就會一直被執行。反匯編器不知道的是,條件跳轉在運行時總是為真,這使得它相信條件跳轉的兩個分支都可以在運行時到達。

好吧,如果還不太明白的話,可以借助下面的截圖來加深理解。第一張截圖顯示的是落到另一條指令中的jbe。

注意:用紅色標記的字節是垃圾代碼。

現在仔細看看下面的第二張圖片,我在這里所做的只是NOP最后一條指令的兩個字節,以便讓IDA顯示隱藏在and [rdx+24448B48h], bh指令后面的指令。

我們也可以用無條件跳轉來修補條件跳轉,以確保IDA不會再次上當。

在我們繼續之前,我想展示最后一個例子,因為前面的例子太簡單了。當我們將這些實現混淆處理的跳轉鏈接起來時,事情就變得復雜起來,具體如下圖所示。

然而,這張圖只顯示了它在控制流方面造成的混亂,但想象一下,當IDA竭盡全力根據垃圾指令創建這張圖時,我的CPU是多么的痛苦。

現在,您可能想知道去混淆后的函數到底是什么樣子的,別急,請看下圖!

看到我在左邊畫的那個藍色小箭頭了嗎?右邊顯示的就是這部分內容的放大版本。現在看一下右邊,在函數的一小部分中就有七個去混淆的跳轉。想象一下,以手動或半自動方式去混淆得需要多少時間。實際上,就算用IDA腳本手工完成這個過程,也花了我40分鐘……這還只是處理了一個函數。設想一下,為了找到真正要找的東西,還得需要處理多少其他的函數呢?!

基于控制流的自動去混淆技術

好了,現在我們已經考察了基于控制流的去混淆原理,接下來,我們將對這個過程實現自動化。正如我之前提到的,我們曾經用IDA腳本來修補無條件跳轉指令,并將垃圾指令替換為NOP指令。

然而,這個去混淆過程還是花了我40分鐘,因為識別不透明的分支非常費勁。那么,我們該如何解決這個問題呢?大家可能認為應該檢查每一個條件跳轉指令,并檢查它是否是不透明的,如果是的話,就用NOP替換它,然后重復上述過程,對吧?錯了!

讓我告訴你一個秘密,我們并不關心什么是不透明的,或諸如此類的事情。我真正關心的是,當我按下F5鍵時,IDA能否返回反編譯好的代碼——只要這些經過混淆的跳轉指令導致垃圾指令與實際的匯編指令發生沖突,這種情況就不會發生。

但這是否意味著我們需要弄清楚一個條件跳轉是否是不透明的呢?不,我們只需檢查跳轉操作是否與現有的指令相沖突,如果是的話,就對這個指令進行相應的修改,就像我們第一個例子中看到的那樣。

DeFlow去混淆算法

現在,我們知道了如何解決這個問題,下面,我們開始深入研究本人想出的算法,以便對用這種混淆技術處理的內容進行去混淆。

  var disasm = new Disassembler(buffer, address - base); // NOTE: base = BaseAddress + .text offset

foreach(var insn in disasm.Disassemble())
ulong target = 0;
ulong lastAddrStart
bool isJmp = true;

switch(insn.Mnemonic)
// Stop analysing when we encounter a invalid or return instruction while we have no lastTarget
case ud_mnemonic_code.Invalid:
case ud_mnemonic_code.Ret:
if(lastTarget == 0)
return newChunks; // Only accept when no lastTarget as we may be looking at junk code
break;
case ud_mnemonic_code.ConditionalJump: // all conditional jumps
if(lastTarget == 0)
target = calcTargetJump(insn); // Helper to extract jump location from instruction

if(!isInRange(target)) // Helper to see if target address is located in our Buffer
isJmp = false;
break;

// Check if instruction is bigger then 2, if so it wont be obfuscated but we
// do want to analyse the target location
if(insn.Length > 2)
isJmp = false;
newChunks.Add(target);
break;
else
isJmp = false; // Do not this conditional jump accept while we already
// have a target (might be looking at junk code)
break;
case ud_mnemonic_code.UnconditionalJump:
case ud_mnemonic_code.Call:
if(lastTarget == 0)
ulong newAddress = calcTargetJump(insn); // Helper to extract jump location from instruction

if(!isInRange(newAddress))
isJmp = false;
break;

// Add target and next instruction IF not JMP (CALL does return, JMP not)
if(insn.Mnemonic == ud_mnemonic_code.Call)
newChunks.Add(address + insn.PC);

// Add instruction target for further analyses
newChunks.Add(newAddress);
return newChunks;
break;


// quick mafs
ulong location = (address+insn.Offset);
stepsLeft = (int)(lastTarget - location); // Only valid if we have a lastTarget!

// Setup a new target if current instruction is conditional jump while there is no lastTarget
if(lastTarget == 0 && isJmp)
lastBranch = loction;
lastBranchSize = insn.Length;
lastTarget = target;
else if (stepsLeft <= 0 && lastTarget != 0)
// if stepsLeft isn't zero then our lastTarget is located slighlt above us,
// meaning that we are partly located inside the previous instruction and thus we are hidden (obfuscated)
if(stepsLeft != 0)
int count = lastTarget = lastBranch; // calculate how much bytes we are in the next instruction
if(count > 0)
// making sure we are a positive jump
int bufferOffset = lastBranch - base; // subtract base from out address so we can write to our local buffer

// NOP slide everything except our own instruction
if(int i = 0; i < count - lastBranchSize; i++)
buffer[bufferOffset + lastBranchSize + i] = isNegative ? 0x90 : 0xCC; // We use NOP for negative jumps
// and int3 for positive

if(!isNegative)
buffer[bufferOffset] = 0xEB; // Force unconditional Jump

// add next instruction for analyses and exit current analysis
newChunks.Add(lastTarget);
return newChunks;
else
// we are a negative jump, set 63th bit to indicate negative jump
lastTarget = |= 1 << 63;

// add target to analyser and exit current analysis
newChunks.Add(lastTarget);
return newChunks;
else
// stepsLeft was zero, meaning there is no collision
// add both target address and next instruction address so we can exit current analysis
newChunks.Add(lastBranch + lastBranchSize);
newChunks.Add(lastTarget);
return newChunks;

return newChunks;

注意:這里顯示是偽代碼,并且我知道它無法正常運行! (真的)

代碼很長,是吧?同時,它比基于IAT導入表的去混淆處理更難理解,因為我們使用了一個實際的反匯編庫來獲得每個指令的大小和助記符。使用反匯編器幾乎是必須的,因為我們還必須弄清楚一條指令是否與其他指令相沖突。

偽代碼中提供了大量的注釋,可以幫助大家理解其中的工作原理。

關于DeFlow算法的深入解釋

主函數在遞歸調用DeflowChunk進行線性反匯編時,會跟蹤已經發現的塊。對新發現的塊的跟蹤是通過列表和循環完成的:由于在一個塊中可能執行大量的分支指令,所以可能觸發StackOverflow。

DeflowChunk將首先檢查是否遇到給定的分支指令,如果是的話,則執行以下操作之一:

  • Ret——如果沒有設置lastTarget,則停止
  • Invalid——如果沒有設置lastTarget,則停止
  • ConditionalJump——如果在我們的緩沖區范圍內,則計算目標地址并跟蹤
  • UnconditionalJump——如果在我們的緩沖區范圍內,則計算目標地址并保存以供進一步分析
  • Call——如果在我們的緩沖區范圍內,計算目標地址并保存以供進一步分析

如果我們沒有設置lastTarget,將檢查當前指令是否是在緩沖區范圍內跳轉的ConditionalJump(isJmp標志),如果是的話將lastTarget設置為ConditionalJump的目標。

一旦我們獲得了符合條件的lastTarget,就用當前的指令指針減去lastTarget,從而計算出還需要反匯編多少字節(stepsLeft)。

在計算出stepsLeft之后,需要檢查該值是否等于零。如果該值大于零,我們將繼續線性反匯編。

當stepsLeft小于零時,表示匯編代碼與下一條指令發生了沖突。這很可能意味著負責設置lastTarget的最后一個ConditionalJump是一個不透明的條件,這意味著我們當前的塊很可能永遠不會被執行,而是用來掩蓋后面的幾條合法的匯編指令。

我們可以通過將ConditionalJump的第一個字節修改為0xEB,使其成為UnconditionalJump,從而修復該問題。為了進一步掃清障礙,我們還修改了最后一個ConditionalJump和lastTarget之間的所有字節。

然后,對于在線性反匯編過程中發現的每個調用或條件跳轉操作,都進行相應的處理。

小結

不僅是惡意軟件,像視頻游戲這樣的合法軟件也傾向于使用這類混淆技術來盡可能多地隱藏有價值的信息,希望能防止軟件被逆向工程。然而,正如您所看到的,我們已經成功地解開了這兩種技術的神秘面紗,并能夠揭示所有隱藏的信息。

無論如何,我們仍然可以得出結論:這些混淆技術極大地提高了逆向分析的難度,這是一個很好的方法,在一定程度上可以阻止軟件被逆向工程。最重要的是,Deflow算法本身需要幾分鐘/小時(取決于文件大小),就能消除混淆技術對二進制文件的完整控制流造成的影響。

本文翻譯自:https://ferib.dev/blog.php?l=post/Reversing_Common_Obfuscation_Techniques如若轉載,請注明原文地址

責任編輯:武曉燕 來源: 嘶吼網
相關推薦

2023-07-14 16:10:09

惡意軟件

2023-02-20 14:31:11

2018-05-04 10:53:14

2012-04-13 13:24:36

2021-07-01 18:54:04

無人機反制偵測

2021-01-12 09:39:17

算法互聯網技術

2023-08-18 11:29:56

2010-10-14 11:37:24

無線LAN技術

2011-03-15 17:35:45

2022-09-07 14:22:57

勒索軟件企業

2009-06-16 16:17:35

2017-07-14 16:28:21

2023-07-14 14:25:00

Python語言錯誤

2010-01-13 10:22:27

2009-12-28 14:04:44

ADO技術

2022-05-12 13:20:32

GoogleAndroid 應用訂閱

2013-01-29 10:42:57

2022-06-02 16:22:54

穩定幣安全金融

2020-05-18 09:50:17

華為禁令技術

2011-07-27 18:36:16

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日本精品视频一区二区 | 精品日韩一区二区 | 影音先锋欧美资源 | 欧洲av在线 | 成人免费观看男女羞羞视频 | 欧美日韩三级视频 | 成人免费视频 | 中文字幕日韩欧美 | 天天操夜夜爽 | 久久久久亚洲国产| 欧美91| 乱一性一乱一交一视频a∨ 色爱av | 国产精品视频一二三区 | 亚洲精品视频在线播放 | 欧美国产视频 | 亚洲狠狠爱 | 97精品久久 | 欧美一区二区在线免费观看 | 久久精品视频在线免费观看 | 午夜影院网站 | 男女羞羞免费视频 | 亚洲人人舔人人 | 日韩aⅴ在线观看 | 欧美做暖暖视频 | 日韩电影一区 | 欧美福利 | 五月香婷婷 | 亚洲成人久久久 | 日本三级电影在线免费观看 | www,黄色,com | 精品国产18久久久久久二百 | 亚洲成人精品视频 | 欧美炮房 | 99精品国自产在线 | 精品国产一区久久 | 午夜不卡福利视频 | 日韩高清成人 | 日本又色又爽又黄又高潮 | 国产精品久久久久婷婷二区次 | 国产高清精品一区二区三区 | 亚洲欧美aⅴ |