国产成人毛片毛片久久网_国产午夜激无码av毛片不_国产乱对白精彩在线播放_av资源站中文字幕_亚洲男人的天堂网站_国产成 人 综合 亚洲网_中国国产激情一区_少妇一级淫片免费放_亚洲一本大道av久在线播放_免费观看美女裸体网站

行業(yè)動態(tài)

防御吧作為15年知名老牌域名服務(wù)商,CNNIC和CANN雙認(rèn)證域名注冊商,已經(jīng)
持續(xù)為500多萬個域名提供服務(wù),包括智能DNS/自由轉(zhuǎn)移/隱私保護(hù)等服務(wù)!
編譯與反編譯原理實(shí)戰(zhàn)之dad反編譯器淺析
2022-02-08 14:46:21 【

編譯與反編譯相關(guān)的知識是比較枯燥的,本文試圖結(jié)合對androguard(https://github.com/androguard/androguard)的內(nèi)置dad反編譯器來進(jìn)行講解。那么學(xué)習(xí)這一部分知識有什么用處呢?就拿android應(yīng)用來說,VMP/java2c等等加固混淆的實(shí)現(xiàn)和對抗都和編譯與反編譯的原理息息相關(guān),理解這一部分的知識無論是對于正向開發(fā)還是逆向分析都是有很大幫助的。比如github上開源的一個java2c方案dcc(https://github.com/amimo/dcc)也用了dad的代碼。java2c可以理解成是一個比較特殊的編譯的過程,正常情況是把java代碼編譯成字節(jié)碼交給虛擬機(jī)解釋執(zhí)行,java2c是把java代碼”編譯”成c代碼。本文首先會介紹一些編譯與反編譯相關(guān)的基本理論,然后會讓讀者大致了解一下androguard,最后會對androguard的內(nèi)置dad反編譯器中的一些關(guān)鍵的代碼進(jìn)行講解。



基本理論

支配(dominates):若從進(jìn)入程式塊到達(dá)基本塊N的所有路徑,都會在到達(dá)基本塊N之前先到達(dá)基本塊M,則基本塊M是基本塊N的支配點(diǎn)。由支配點(diǎn)構(gòu)成的樹就是支配樹。


前進(jìn)邊(advancing edge):是指向的基本塊是在圖的深度優(yōu)先搜索中沒有走過的基本塊。


倒退邊(back edge):是指向的基本塊是在圖的深度優(yōu)先搜索中已經(jīng)走過的基本塊。倒退邊多半表示有循環(huán)。


區(qū)間圖(interval graph):是指給定一個節(jié)點(diǎn)h,其區(qū)間圖是h為入口節(jié)點(diǎn),并且其中所有閉合路徑都包含h的最大的單入口子圖。h被稱為區(qū)間頭節(jié)點(diǎn)或簡稱頭節(jié)點(diǎn)。


對于一個CFG,可以把它區(qū)間圖稱為一階圖。二階圖將每個區(qū)間圖作為一個節(jié)點(diǎn),以此類推。如果最終能夠把CFG轉(zhuǎn)化為單個節(jié)點(diǎn),就稱該CFG是可規(guī)約的(reducible)。


拓?fù)渑判?topological sorting):拓?fù)渑判蚴且粋有向無環(huán)圖的所有頂點(diǎn)的線性序列。且該序列必須滿足下面兩個條件:


每個頂點(diǎn)出現(xiàn)且只出現(xiàn)一次。

若存在一條從頂點(diǎn) A 到頂點(diǎn) B 的路徑,那么在序列中頂點(diǎn) A 出現(xiàn)在頂點(diǎn) B 的前面。

前序/后序/逆后序:


深度優(yōu)先搜索在遍歷圖的過程中,可以記錄如下順序:


前序:即在遞歸調(diào)用之前將頂點(diǎn)加入隊列,代表深度優(yōu)先搜索訪問頂點(diǎn)的順序。


后序:即在遞歸調(diào)用之后將頂點(diǎn)加入隊列,代表深度優(yōu)先搜索頂點(diǎn)遍歷完成的順序。


逆后序:即在遞歸調(diào)用之后將頂點(diǎn)壓入棧,代表著頂點(diǎn)的拓?fù)渑判颉?/p>



androguard簡介

本文分析的androguard源代碼版本為3.3.5。


decompiler:提供反編譯功能,主要有兩種方式實(shí)現(xiàn),一種是使用jadx,一種是使用內(nèi)置的名為dad的反編譯器,(還有一些其他的第三方反編譯器均已廢棄)。decompiler/dad目錄即為dad反編譯器的實(shí)現(xiàn)了。


我們運(yùn)行一下decompile.py試試,apk就直接用提供的測試apk:androguard-3.3.5/examples/android/TestsAndroguard/bin/TestActivity.apk。


% python3 decompile.py

INFO: ========================

INFO: Classes:

......

INFO:  Ltests/androguard/TestActivity;

......

INFO: ========================

Choose a class (* for all classes): TestActivity

INFO: ======================

......

INFO: 9: foo

......

INFO: ======================

Method (* for all methods): 9

INFO: Source:

INFO: ===========================

package tests.androguard;

public class TestActivity extends android.app.Activity {

   private static final int test2 = 20;

   public int[] tab;

   private int test;

   public int test3;

   public int value;

   public int value2;


   public int foo(int p3, int p4)

   {

       int v0 = p4;

       while(true) {

           int v4_1;

           if (p3 < v0) {

               v4_1 = (v0 + 1);

               try {

                   p3 = (v0 / p3);

                   v0 = v4_1;

               } catch (RuntimeException v1) {

                   p3 = 10;

               }

           } else {

               if (p3 == 0) {

                   break;

               }

               v4_1 = v0;

           }

           v0 = v4_1;

       }

       return v0;

   }

}

同時在/tmp/dad/blocks,/tmp/dad/pre-structured和/tmp/dad/structured可以看到生成的CFG圖。非條件節(jié)點(diǎn)的邊是藍(lán)色的,條件節(jié)點(diǎn)為true的邊是綠色的,條件節(jié)點(diǎn)為false的邊是紅色的。try catch的邊是黑色的虛線。


/tmp/dad/pre-structured:




/tmp/dad/structured:




這一部分之后還會詳細(xì)解釋,下面對dad目錄下的文件做個簡介。



源碼分析

網(wǎng)上找的一張圖。按照反編譯技術(shù)實(shí)施的順序劃分,則可以分為7個階段,它們是:句法分析、語義分析、中間代碼生成、控制流圖生成、控制流分析、代碼生成。




當(dāng)我們運(yùn)行decompile.py的時候調(diào)用了DvMachine處理提供的apk(dex/odex)文件,通過androguard/core/analysis/analysis.py中的Analysis類對其進(jìn)行分析。對于每個class通過ClassAnalysis進(jìn)行分析;對于每個method通過MethodAnalysis進(jìn)行分析。


       self.vms.append(vm)

       for current_class in vm.get_classes():

           self.classes[current_class.get_name()] = ClassAnalysis(current_class)


       for method in vm.get_methods():

           self.methods[method] = MethodAnalysis(vm, method)

在MethodAnalysis中,h表示分支指令和對應(yīng)的目的地址,l表示所有的目的地址,例如對于下面這個method:


(0),v1, +5,length:4,if-lez

(4),v0, v1, 2,length:4,mul-int/lit8

(8),v0,length:2,return

(10) v0, v1, 2,length:4,add-int/lit8

(14) -3,length:2,goto

h:

{0: [4, 10], 8: [-1], 14: [8]}

l:

[4, 10, -1, 8]

所有的分支指令:


throw

throw.

if.

goto

goto.

return

return.

packed-switch$

sparse-switch$

據(jù)此創(chuàng)建DVMBasicBlock并通過BasicBlocks列表進(jìn)行管理,接下來主要的處理是在process函數(shù)中。


下面就重點(diǎn)講一下process函數(shù)中涉及到的控制流圖生成(graph.py),數(shù)據(jù)流分析(dataflow.py)和控制流分析(control_flow.py)。


opcode_ins.py

所有的opcode。


instruction.py

表示opcode的類,對應(yīng)于上圖中的中間代碼生成,比如if-eq,if-ne等等這些opcode都用ConditionalExpression類表示。


writer.py

輸出源代碼。


dast.py

輸出AST。


basic_blocks.py

提供build_node_from_block函數(shù),根據(jù)提供的block的最后一條指令返回對應(yīng)的block類型。返回的類型包括:


ReturnBlock,SwitchBlock,CondBlock,ThrowBlock和StatementBlock。


graph.py

compute_rpo函數(shù)


得到CFG的逆后序。


post_order函數(shù)


得到CFG的后序。


split_if_nodes函數(shù)


將CondBlock拆分為StatementBlock和新的CondBlock,StatementBlock是頭節(jié)點(diǎn),新的CondBlock僅由跳轉(zhuǎn)條件組成。


simplify函數(shù)


通過合并/刪除StatementBlock來簡化CFG:如果StatementBlock B跟在StatementBlock A后面,并且StatementBlock B除了StatementBlock A沒有其它的前任節(jié)點(diǎn),那么我們可以將A和B合并成一個新的StatementBlock。


還刪除除了重定向控制流之外什么都不做的節(jié)點(diǎn)(只包含goto的節(jié)點(diǎn))。


dom_lt函數(shù)/immediate_dominators函數(shù)


通過Lengauer-Tarjan算法構(gòu)造支配樹。dom[i]表示支配i的節(jié)點(diǎn)。


bfs函數(shù)


廣度優(yōu)先搜索。


make_node函數(shù)


通過block(DVMBasicBlock)得到node(basic_blocks.py中定義的block類型)。


首先檢查該block是否有對應(yīng)的node,沒有就創(chuàng)建出來。


如果說這是一個try block,則把catch node也創(chuàng)建出來,加到catch_edges(try node,即CFG中黑色虛線箭尾)和reverse_catch_edges(catch node,即CFG中黑色虛線箭頭)。


對于該block的所有child如果沒有對應(yīng)的node就創(chuàng)建出來,添加該node到child node的edge。如果這個node是SwitchBlock,則將child node添加到node的cases中;如果這個node是CondBlock,則將node的true或false設(shè)置為child node。


construct函數(shù)


構(gòu)造CFG。


調(diào)用bfs函數(shù)得到DVMBasicBlock序列,對于每個DVMBasicBlock調(diào)用make_node函數(shù)然后加到圖中,設(shè)置graph的entry和exit。


control_flow.py

控制流分析。


intervals函數(shù)


給定一個控制流圖,計算該圖區(qū)間圖(interval graph)的集合。


對于最前面運(yùn)行decompile.py舉的例子,如果大家去看/tmp/dad/pre-structured中生成的CFG圖,該圖有兩個區(qū)間圖,一個是1-Statement(foo-BB@0x0)這一個節(jié)點(diǎn)組成的區(qū)間圖,另外一個就是其他節(jié)點(diǎn)組成的區(qū)間圖。


算法:


將圖的入口加入頭節(jié)點(diǎn)列表,遍歷所有頭節(jié)點(diǎn):取出頭節(jié)點(diǎn)并標(biāo)記,將其加入一個區(qū)間圖,如果存在一個節(jié)點(diǎn),其所有前驅(qū)節(jié)點(diǎn)都在當(dāng)前區(qū)間圖中就將該節(jié)點(diǎn)加入當(dāng)前區(qū)間圖,重復(fù)直到所有可能的節(jié)點(diǎn)都被加入當(dāng)前區(qū)間圖; 此時如果存在一個節(jié)點(diǎn)不在區(qū)間圖中但是它的一個前驅(qū)節(jié)點(diǎn)在區(qū)間圖中,那么這個節(jié)點(diǎn)就是另一個區(qū)間圖的頭節(jié)點(diǎn),將該節(jié)點(diǎn)加入頭節(jié)點(diǎn)列表,重復(fù)直到所有可能的節(jié)點(diǎn)都被加入頭節(jié)點(diǎn)列表。


derived_sequence函數(shù)


計算CFG的導(dǎo)出序列,也就是如把CFG最終轉(zhuǎn)化為單個節(jié)點(diǎn)。返回兩個參數(shù),第一個參數(shù)Gi是區(qū)間圖的列表,第二個參數(shù)Li是區(qū)間圖的頭節(jié)點(diǎn)的列表。Gi[0]是原始圖,Gi[1]是一階圖,Gi[x]是x階圖;Li[0]是一階圖的頭節(jié)點(diǎn),Li[1]是二階圖的頭節(jié)點(diǎn),Li[x]是x+1階圖的頭節(jié)點(diǎn)。


loop_type函數(shù)


返回循環(huán)的類型:


Pre-test Loop:檢查條件后才執(zhí)行循環(huán);


Post-test Loop:先執(zhí)行循環(huán)再檢查條件;


End-less Loop:無限循環(huán)。


loop_follow函數(shù)


標(biāo)記x.follow['loop'] = y,表示x這個循環(huán)節(jié)點(diǎn)結(jié)束之后應(yīng)該是y節(jié)點(diǎn)。


loop_struct函數(shù)


識別循環(huán)。遍歷derived_sequence函數(shù)返回的兩個列表,如果對于一個頭節(jié)點(diǎn),有一個它的前驅(qū)節(jié)點(diǎn)和它位于同一個區(qū)間圖,那么就標(biāo)記一個循環(huán)。


比如對于下面的代碼:


   public void testWhile() {

       int i = 5;

       int j = 10;

       while (i < j) {

           j = (int) (((double) j) + (((double) i) / 2.0d) + ((double) j));

           i += i * 2;

       }

       f13i = i;

       f14j = j;

   }

derived_sequence函數(shù)返回的結(jié)果:


Gi[0]:(原始圖)


[

1-Statement(testWhile-BB@0x0),

2-If(testWhile-BB@0x6),

4-Statement(testWhile-BB@0xa),

3-Return(testWhile-BB@0x24)

]

Gi[1]:(一階圖)


[

Interval-testWhile-BB@0x0({1-Statement(testWhile-BB@0x0)}),

Interval-testWhile-BB@0x6({2-If(testWhile-BB@0x6), 3-Return(testWhile-BB@0x24), 4-Statement(testWhile-BB@0xa)})

]

Li[0]:(一階圖的頭節(jié)點(diǎn))


{

1-Statement(testWhile-BB@0x0): Interval-testWhile-BB@0x0({1-Statement(testWhile-BB@0x0)}),

2-If(testWhile-BB@0x6): Interval-testWhile-BB@0x6({2-If(testWhile-BB@0x6), 3-Return(testWhile-BB@0x24), 4-Statement(testWhile-BB@0xa)})

}

Li[1]:(二階圖的頭節(jié)點(diǎn))


{

Interval-testWhile-BB@0x0({1-Statement(testWhile-BB@0x0)}): Interval-Interval-testWhile-BB@0x0({Interval-testWhile-BB@0x6({2-If(testWhile-BB@0x6), 3-Return(testWhile-BB@0x24), 4-Statement(testWhile-BB@0xa)}), Interval-testWhile-BB@0x0({1-Statement(testWhile-BB@0x0)})})

}

遍歷Li[0],BB@0x0沒有前驅(qū)節(jié)點(diǎn);BB@0x6的第一個前驅(qū)節(jié)點(diǎn)BB@0x0和它不在同一個區(qū)間圖中;第二個前驅(qū)節(jié)點(diǎn)BB@0xa和它在同一個區(qū)間圖中(BB@0x6為頭節(jié)點(diǎn),包含節(jié)點(diǎn)BB@0x6,BB@0x24,BB@0xa的區(qū)間圖),所以標(biāo)記一個循環(huán)。


if_struct函數(shù)


標(biāo)記x.follow['if'] = y,表示x這個if{…}else{…}結(jié)束之后應(yīng)該是y節(jié)點(diǎn)。


switch_struct函數(shù)


標(biāo)記x.follow['switch'] = y,表示x這個switch結(jié)束之后應(yīng)該是y節(jié)點(diǎn)。


short_circuit_struct函數(shù)


短路求值,將兩個CondBlock合并成一個?偣菜姆N情況。


第一種情況合并前后:





第二種情況合并前后:






第三種情況合并前后:






第四種情況合并前后:






實(shí)際例子:


   public static int testShortCircuit4(int p, int i) {

       if ((p <= 0 || i == 0) && (p == i * 2 || i == p / 3)) {

           return -p;

       }

       return p + 1;

   }

pre-structured:




structured:




while_block_struct函數(shù)


根據(jù)loop_struct函數(shù)識別出的循環(huán)添加一個LoopBlock類型的node并刪除原來的node。


catch_struct函數(shù)


通過reverse_catch_edges遍歷所有catch塊,支配其的節(jié)點(diǎn)即為對應(yīng)的try塊,據(jù)此新建CatchBlock和TryBlock,加入圖中并更新圖。


update_dom函數(shù)


更新支配樹。


identify_structures函數(shù)


主要就是調(diào)用前面提到的這些函數(shù)識別出一些結(jié)構(gòu)。


dataflow.py

數(shù)據(jù)流分析。一些基本知識可以參考下面這幾篇文章。


靜態(tài)分析之?dāng)?shù)據(jù)流分析與 SSA 入門 (一)


靜態(tài)分析之?dāng)?shù)據(jù)流分析與 SSA 入門 (二)


反編譯器C-Decompiler關(guān)鍵技術(shù)的研究和實(shí)現(xiàn)


實(shí)際例子:


   public int test1(int val) {

       return (val + 16) - (this.value * 60);

   }

get_loc_with_ins返回的loc和ins:


0 ASSIGN(VAR_0, CST_16)


1 ASSIGN(VAR_1, (+ PARAM_4 VAR_0))

get_used_vars:0,4


2 ASSIGN(VAR_2, THIS.value)

get_used_vars:3


3 ASSIGN(VAR_2, (* VAR_2 CST_60))

get_used_vars:2


4 ASSIGN(VAR_1, (- VAR_1 VAR_2))

get_used_vars:1,2


5 RETURN(VAR_1)

register_propagation函數(shù)


寄存器傳播。


build_def_use函數(shù)


對于上面的例子構(gòu)建的DU和UD鏈:


use_defs:


(x, y):[z]表示第y行的var x是在第z行定義的


{ (0, 1): [0], (4, 1): [-2], (3, 2): [-1], (2, 3): [2], (1, 4): [1], (2, 4): [3], (1, 5): [4] }


def_uses:


(x, y):[z]表示第y行定義的var x在第z行使用


{ (0, 0): [1], (4, -2): [1], (3, -1): [2], (2, 2): [3], (1, 1): [4], (2, 3): [4], (1, 4): [5]} )


-1表示第一個參數(shù),-2表示第二個參數(shù)。


split_variables函數(shù)


靜態(tài)單賦值SSA,通過分割變量使得每個變量僅有唯一的賦值。


對于上面的例子split_variables之后的DU和UD鏈:


use_defs:


{ (0, 1): [0], (4, 1): [-2], (3, 2): [-1], (5, 4): [1], (6, 5): [4], (7, 3): [2], (8, 4): [3] }


def_uses:


{ (0, 0): [1], (4, -2): [1], (3, -1): [2], (5, 1): [4], (6, 4): [5], (7, 2): [3], (8, 3): [4]} )


dead_code_elimination函數(shù)


清除死代碼并更新DU和UD鏈。


】【打印關(guān)閉】 【返回頂部
分享到QQ空間
分享到: 
上一篇保持安全的八條網(wǎng)絡(luò)安全提示 下一篇SolarWinds黑客依然活躍

立足首都,輻射全球,防御吧專注云防御及云計算服務(wù)15年!

聯(lián)系我們

服務(wù)熱線:13051179500 18910191973
企業(yè)QQ:1245940436
技術(shù)支持:010-56159998
E-Mail:xihedata.com
Copyright ? 2003-2016 fangyuba. 防御吧(完美解決防御與加速) 版權(quán)所有 增值許可:京B2-20140042號
售前咨詢
公司總機(jī):18910191973
24小時電話:010-56159998
投訴電話:18910191973
值班售后/技術(shù)支持
售后服務(wù)/財務(wù)
備案專員
緊急電話:18610088800