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

針對(duì)常見混淆技術(shù)的反制措施

安全 應(yīng)用安全
在這篇文章中,我們將仔細(xì)研究?jī)煞N常見的混淆技術(shù),以了解它們是如何工作的,并弄清楚如何去混淆。

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

在這篇文章中,我們將仔細(xì)研究?jī)煞N常見的混淆技術(shù),以了解它們是如何工作的,并弄清楚如何去混淆。

概述

這里,我們將研究以下混淆技術(shù):

基于IAT導(dǎo)入表的混淆技術(shù)

基于控制流的混淆技術(shù)

基于IAT導(dǎo)入表的混淆技術(shù)

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

什么是導(dǎo)入函數(shù)?

當(dāng)進(jìn)行逆向分析時(shí),需要弄清楚的第一件事,就是它如何調(diào)用操作系統(tǒng)的函數(shù)。在我們的例子中,我們將重點(diǎn)關(guān)注Windows 10系統(tǒng),因?yàn)榇蠖鄶?shù)視頻游戲只能在Windows系統(tǒng)上運(yùn)行。無論如何,對(duì)于那些還不知道的人來說,Windows提供了一系列重要的動(dòng)態(tài)鏈接庫(kù)(DLL)文件,幾乎每個(gè)Windows可執(zhí)行文件都會(huì)用到這些庫(kù)文件。這些DLL文件中保存了許多函數(shù),可以供Windows可執(zhí)行文件“導(dǎo)入”,使其可以加載和執(zhí)行給定DLL中的函數(shù)。

它們?yōu)楹稳绱酥匾?

例如,Ntdll.dll庫(kù)負(fù)責(zé)幾乎所有與內(nèi)存有關(guān)的功能,如打開一個(gè)進(jìn)程的句柄(NtOpenProcess),分配一個(gè)內(nèi)存頁(yè)(NtVirtualAlloc,NtVirtualAllocEx),查詢內(nèi)存頁(yè)(NtVirtualQuery,NtVirtualQueryEx),等等。

另一個(gè)重要的DLL庫(kù)是ws2_32.dll,它通過以下函數(shù)處理各種網(wǎng)絡(luò)活動(dòng):

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

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

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

注意:IDA已自動(dòng)向反匯編代碼中添加了注釋。

經(jīng)過混淆處理的導(dǎo)入表

無論如何,這些Windows函數(shù)能提供相當(dāng)多的信息,因?yàn)樗鼈兪怯袚?jù)可查的函數(shù)。因此,攻擊者希望能夠把這些函數(shù)藏起來,以掩蓋正在發(fā)生的事情。

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

考察我們的示例

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

下面的地址0x7FF7D7F9B000引用了我們的函數(shù)0x19AA1040FE1,盡管看起來完全不是這么回事。您可能認(rèn)為這是垃圾代碼,但仔細(xì)看看,您會(huì)發(fā)現(xiàn)并非如此。

請(qǐng)仔細(xì)查看前兩個(gè)指令:前面的指令是mov rax, FFFF8000056C10A1,后面的指令是jmp 19AA1040738,后面的都是垃圾指令。不管怎樣,讓我們跟隨跳轉(zhuǎn)指令,看看它會(huì)跳到哪里:

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

最后,我們來到j(luò)mp rax指令!需要注意的是,所有的XOR、SUB和ADD指令都是在Rax寄存器上執(zhí)行的,這意味著它可能包含導(dǎo)入函數(shù)的實(shí)際指針。下面,讓我們算算看。

實(shí)際上,在經(jīng)過數(shù)學(xué)運(yùn)算之后,我們得到了指向advapi32.regopenkeyexa的指針!

現(xiàn)在,我們所要做的就是重復(fù)幾百次運(yùn)算,從而徹底消除針對(duì)IAT導(dǎo)入表的混淆處理。

基于IAT的自動(dòng)去混淆處理

我想,沒有人喜歡用計(jì)算器手工重復(fù)上述過程,做一次已經(jīng)很煩了。從現(xiàn)在開始,我們將使用C#實(shí)現(xiàn)自動(dòng)計(jì)算。正如您可能已經(jīng)看到的,我們只需要處理在同一個(gè)寄存器上執(zhí)行的ADD、SUB和XOR操作。原因是Rax被用作返回地址,而諸如Rcx、Rdx、R8、R9和其他寄存器對(duì)于被調(diào)用方來說是不安全的,并且可能與調(diào)用約定沖突。這意味著,我們甚至不需要使用反匯編器,因?yàn)槲覀兛梢院茌p松地區(qū)分這些指令,這要?dú)w功于涉及的寄存器和操作碼寥寥無幾。

到此為止,我們已經(jīng)詳細(xì)解釋了混淆處理技術(shù)。接下來,大家不妨以Unsnowman項(xiàng)目中的importfix.cs為例,來了解與去混淆處理相關(guān)的代碼。

基于控制流的混淆技術(shù)

在逆向分析二進(jìn)制文件時(shí),另一個(gè)有價(jià)值的信息來源是匯編指令本身。對(duì)于人類來說,它們可能難以理解,但對(duì)于像IDA這樣的反編譯器來說,我們只需按下F5鍵,IDA就會(huì)生成我們?nèi)祟惪梢岳斫獾膫未a。

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

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

注意:用紅色標(biāo)記的字節(jié)是垃圾代碼。

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

我們也可以用無條件跳轉(zhuǎn)來修補(bǔ)條件跳轉(zhuǎn),以確保IDA不會(huì)再次上當(dāng)。

在我們繼續(xù)之前,我想展示最后一個(gè)例子,因?yàn)榍懊娴睦犹?jiǎn)單了。當(dāng)我們將這些實(shí)現(xiàn)混淆處理的跳轉(zhuǎn)鏈接起來時(shí),事情就變得復(fù)雜起來,具體如下圖所示。

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

現(xiàn)在,您可能想知道去混淆后的函數(shù)到底是什么樣子的,別急,請(qǐng)看下圖!

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

基于控制流的自動(dòng)去混淆技術(shù)

好了,現(xiàn)在我們已經(jīng)考察了基于控制流的去混淆原理,接下來,我們將對(duì)這個(gè)過程實(shí)現(xiàn)自動(dòng)化。正如我之前提到的,我們?cè)?jīng)用IDA腳本來修補(bǔ)無條件跳轉(zhuǎn)指令,并將垃圾指令替換為NOP指令。

然而,這個(gè)去混淆過程還是花了我40分鐘,因?yàn)樽R(shí)別不透明的分支非常費(fèi)勁。那么,我們?cè)撊绾谓鉀Q這個(gè)問題呢?大家可能認(rèn)為應(yīng)該檢查每一個(gè)條件跳轉(zhuǎn)指令,并檢查它是否是不透明的,如果是的話,就用NOP替換它,然后重復(fù)上述過程,對(duì)吧?錯(cuò)了!

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

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

DeFlow去混淆算法

現(xiàn)在,我們知道了如何解決這個(gè)問題,下面,我們開始深入研究本人想出的算法,以便對(duì)用這種混淆技術(shù)處理的內(nèi)容進(jìn)行去混淆。

  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;

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

代碼很長(zhǎng),是吧?同時(shí),它比基于IAT導(dǎo)入表的去混淆處理更難理解,因?yàn)槲覀兪褂昧艘粋€(gè)實(shí)際的反匯編庫(kù)來獲得每個(gè)指令的大小和助記符。使用反匯編器幾乎是必須的,因?yàn)槲覀冞€必須弄清楚一條指令是否與其他指令相沖突。

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

關(guān)于DeFlow算法的深入解釋

主函數(shù)在遞歸調(diào)用DeflowChunk進(jìn)行線性反匯編時(shí),會(huì)跟蹤已經(jīng)發(fā)現(xiàn)的塊。對(duì)新發(fā)現(xiàn)的塊的跟蹤是通過列表和循環(huán)完成的:由于在一個(gè)塊中可能執(zhí)行大量的分支指令,所以可能觸發(fā)StackOverflow。

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

  • Ret——如果沒有設(shè)置lastTarget,則停止
  • Invalid——如果沒有設(shè)置lastTarget,則停止
  • ConditionalJump——如果在我們的緩沖區(qū)范圍內(nèi),則計(jì)算目標(biāo)地址并跟蹤
  • UnconditionalJump——如果在我們的緩沖區(qū)范圍內(nèi),則計(jì)算目標(biāo)地址并保存以供進(jìn)一步分析
  • Call——如果在我們的緩沖區(qū)范圍內(nèi),計(jì)算目標(biāo)地址并保存以供進(jìn)一步分析

如果我們沒有設(shè)置lastTarget,將檢查當(dāng)前指令是否是在緩沖區(qū)范圍內(nèi)跳轉(zhuǎn)的ConditionalJump(isJmp標(biāo)志),如果是的話將lastTarget設(shè)置為ConditionalJump的目標(biāo)。

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

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

當(dāng)stepsLeft小于零時(shí),表示匯編代碼與下一條指令發(fā)生了沖突。這很可能意味著負(fù)責(zé)設(shè)置lastTarget的最后一個(gè)ConditionalJump是一個(gè)不透明的條件,這意味著我們當(dāng)前的塊很可能永遠(yuǎn)不會(huì)被執(zhí)行,而是用來掩蓋后面的幾條合法的匯編指令。

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

然后,對(duì)于在線性反匯編過程中發(fā)現(xiàn)的每個(gè)調(diào)用或條件跳轉(zhuǎn)操作,都進(jìn)行相應(yīng)的處理。

小結(jié)

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

無論如何,我們?nèi)匀豢梢缘贸鼋Y(jié)論:這些混淆技術(shù)極大地提高了逆向分析的難度,這是一個(gè)很好的方法,在一定程度上可以阻止軟件被逆向工程。最重要的是,Deflow算法本身需要幾分鐘/小時(shí)(取決于文件大小),就能消除混淆技術(shù)對(duì)二進(jìn)制文件的完整控制流造成的影響。

本文翻譯自:https://ferib.dev/blog.php?l=post/Reversing_Common_Obfuscation_Techniques如若轉(zhuǎn)載,請(qǐng)注明原文地址

責(zé)任編輯:武曉燕 來源: 嘶吼網(wǎng)
相關(guān)推薦

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

無人機(jī)反制偵測(cè)

2021-01-12 09:39:17

算法互聯(lián)網(wǎng)技術(shù)

2023-08-18 11:29:56

2011-03-15 17:35:45

2022-09-07 14:22:57

勒索軟件企業(yè)

2010-10-14 11:37:24

無線LAN技術(shù)

2009-06-16 16:17:35

2010-01-13 10:22:27

2017-07-14 16:28:21

2023-07-14 14:25:00

Python語(yǔ)言錯(cuò)誤

2009-12-28 14:04:44

ADO技術(shù)

2022-05-12 13:20:32

GoogleAndroid 應(yīng)用訂閱

2020-05-18 09:50:17

華為禁令技術(shù)

2022-06-02 16:22:54

穩(wěn)定幣安全金融

2013-01-29 10:42:57

2011-07-27 18:36:16

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 麻豆精品国产91久久久久久 | 国产精品www | 久久久久久久国产精品影院 | 欧美成人在线免费 | 久久www免费视频 | 在线看av网址 | 国产精品久久久久久av公交车 | 国产美女久久久 | 久久国产精品视频免费看 | 久久精彩视频 | 欧美精品久久久久久 | 国产精品久久久乱弄 | 91pron在线 | 欧美综合在线视频 | 成人一级片在线观看 | 在线看av网址 | 粉嫩高清一区二区三区 | av三级| 国产h视频 | 免费黄色在线观看 | 国产亚洲精品美女久久久久久久久久 | 好姑娘影视在线观看高清 | 亚洲精品国产a久久久久久 午夜影院网站 | 国产成人网| 国产在线中文字幕 | 一区二区三区在线免费观看 | 国产污视频在线 | 91久久国产精品 | 成人在线播放 | 久久aⅴ乱码一区二区三区 亚洲国产成人精品久久久国产成人一区 | 久久天堂 | 国产羞羞视频在线观看 | 毛片一区二区三区 | 一本一道久久a久久精品综合蜜臀 | 免费观看av网站 | 精品一区二区av | 亚洲一区二区三区视频 | 日韩一区二区av | 狠狠亚洲 | 中文字幕在线剧情 | 一区二区三区高清在线观看 |