<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            原文地址:http://drops.wooyun.org/mobile/11326

            0x00 前言


            小弟最近整理之前的資料,偶然發現半年前的混淆對抗研究以及一道CTF練習題目,故分享以作記錄。限于水平,難免會有疏漏或者錯誤之處,望各位讀者批評指正。

            0x01 基本分析


            jeb打開文件,找到方法校驗方法。邏輯很簡單,校驗函數既是Native函數check.

            #!vb
            public native boolean check(String arg1) {
            }
            
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                this.setContentView(2130903040);
                this.inputCode = this.findViewById(2131099648);
                this.btn_submit = this.findViewById(2131099649);
                this.btn_submit.setOnClickListener(new View$OnClickListener() {
                    public void onClick(View v) {
                        if(MainActivity.this.check(MainActivity.this.inputCode.getText().toString())) {
                            MainActivity.this.startActivity(new Intent(MainActivity.this, ResultActivity.class));
                        }
                        else {
                            Toast.makeText(MainActivity.this.getApplicationContext(), "Incorrect Password!", 
                                    0).show();
                        }
                    }
                });
            }
            

            直接使用IDA默認Loader打開直接崩潰,存在畸形ELF文件對抗,使用自定義LOADER加載,也是然并卵的節奏。

            使用Tracer動態打印check函數地址,掛起進程,dump出對應的代碼段加載到IDA,找到check函數。

            #!bash
            seg000:4561E4E8 check
            seg000:4561E4E8                 LDR             PC, =sub_4561E4EC
            seg000:4561E4E8 ; End of function check
            seg000:4561E4E8
            seg000:4561E4EC ; =============== S U B R O U T I N E   seg000:4561E4EC sub_4561E4EC                            ; CODE XREF: checkj
            seg000:4561E4EC                                         ; DATA XREF:    seg000:4561E4EC                 STMFD           SP!, {R0-R12,LR}
            seg000:4561E4F0                 LDR             R0, =6
            seg000:4561E4F4                 B               loc_4561E444
            

            通過分析發現,其實為一個匯編stub,通過此stub跳到真正的check函數。

            #!bash
            seg013:80A0135C sub_80A0135C                            ; DATA XREF: seg013:80A13F98o
            seg013:80A0135C                 B               sub_80A065B8
            seg013:80A0135C ; End of function sub_80A0135C
            seg013:80A01360
            seg013:80A01360 ; =============== S U B R O U T I N E   seg013:80A01360 ; Attributes: thunk
            seg013:80A01360
            seg013:80A01360 sub_80A01360                            ; DATA XREF: sub_80A065C4+Co
            seg013:80A01360                 B               sub_80A065F8
            seg013:80A01360 ; End of function sub_80A01360
            seg013:80A01364 ; =============== S U B R O U T I N E   seg013:80A01364 ; Attributes: thunk
            seg013:80A01364
            seg013:80A01364 sub_80A01364                            ; CODE XREF: sub_80A06620j
            seg013:80A01364                 B               sub_80A0663C
            seg013:80A01364 ; End of function sub_80A01364
            

            以80A0135C(B sub_80A065B8)為例子,跟進sub_80A065B8,可以看到如下指令:

            #!bash
            // 0x80A0135C
            seg013:80A065B8                 BEQ             loc_80A0658C
            seg013:80A065BC                 BNE             loc_80A0658C
            
            seg013:80A0658C                 STMFD           SP!, {R3-R8,R10,LR} 真實指令
            seg013:80A06590                 STMFD           SP!, {R8,LR}
            seg013:80A06594                 LDR             R8, loc_80A065A4
            seg013:80A06598                 LDR             R8, loc_80A065A8
            seg013:80A0659C                 LDR             R8, loc_80A065AC
            seg013:80A065A0                 LDR             R8, locret_80A065B0
            seg013:80A065A4                 LDR             R8, =(sub_80A065C4 - 0x80A065B0)
            seg013:80A065A8                 ADD             R8, PC, R8 ; sub_80A065C4
            seg013:80A065AC                 STR             R8, [SP,#4]
            seg013:80A065B0                 LDMFD           SP!, {R8,PC}
            
            seg013:80A065C4                 STMFD           SP!, {R8,LR}
            seg013:80A065C8                 LDR             R8, =0xFFFFAD25
            seg013:80A065CC                 EOR             R8, R8, #0xAD
            seg013:80A065D0                 ADD             R8, PC, R8 ; loc_80A01360 //返回到80A01360
            seg013:80A065D4                 STR             R8, [SP,#8+var_4]
            seg013:80A065D8                 LDMFD           SP!, {R8,PC}
            

            通過分析可以得到真實指令(STMFD SP!, {R3-R8,R10,LR}),其余指令為混淆指令,最終返回到下一條B即80A01360(B sub_80A065F8)指令。通過分析其他B指令,可以得到類似的混淆指令中夾在一條真實指令,只是存在多種混淆的方式。 至此,我們可以得到此混淆的思路:執行"一個B指令"即一條真實的指令,混淆抽象為:

            不難發現,如果僅僅靠一條一條的尋找真實指令,是非常費時費力的。由于執行前后都存在多種模式的混淆,但總的模式是有限的,那么通過提取指令特征匹配即可以自動化實現去混淆,找出真實指令。

            0x02 基于指令特征匹配對抗混淆


            通過分析找到所有的混淆模式,最后大概幾種。限于篇幅,列舉一些做說明

            #!bash
            // 0x80A0135C
            seg013:80A065B8                 BEQ             loc_80A0658C
            seg013:80A065BC                 BNE             loc_80A0658C
            
            seg013:80A0658C                 STMFD           SP!, {R3-R8,R10,LR} 真實指令
            
            seg013:80A06590                 STMFD           SP!, {R8,LR}
            seg013:80A06594                 LDR             R8, loc_80A065A4
            seg013:80A06598                 LDR             R8, loc_80A065A8
            seg013:80A0659C                 LDR             R8, loc_80A065AC
            seg013:80A065A0                 LDR             R8, locret_80A065B0
            seg013:80A065A4                 LDR             R8, =(sub_80A065C4 - 0x80A065B0)
            seg013:80A065A8                 ADD             R8, PC, R8 ; sub_80A065C4
            seg013:80A065AC                 STR             R8, [SP,#4]
            seg013:80A065B0                 LDMFD           SP!, {R8,PC}
            
            seg013:80A065C4                 STMFD           SP!, {R8,LR}
            seg013:80A065C8                 LDR             R8, =0xFFFFAD25
            seg013:80A065CC                 EOR             R8, R8, #0xAD
            seg013:80A065D0                 ADD             R8, PC, R8 ; loc_80A01360
            seg013:80A065D4                 STR             R8, [SP,#8+var_4]
            seg013:80A065D8                 LDMFD           SP!, {R8,PC}
            

            執行前混淆:B(連續兩條條件完全相反的指令) next_jmp 執行后混淆:這里有兩組STMFD--LDMFD構成的跳轉stub,但其是為一種模式。那如何計算next_jmp呢?這里我采用取巧的方式,通過從LDMFD所在地址反向找到ADD指令,得到";loc_80a01360",再解析出地址80a01360。當然,存在多種prefix,需要作簡單處理獲取地址。

            #!python
            def prefix_match(str):
                pattern = ['sub_', 'loc_', 'unk_', 'locret_']
                for prefix in pattern:
                    if str.find(prefix) > -1:
                        substr = str[str.find(prefix) + len(prefix):]
                        return string.atoi(substr, 16)
                return 0xffffffff;
            

            真實指令:通過解析跳轉遍歷完整個混淆后,通過堆棧平衡原理,提取出真實指令。以上述分析為例,遍歷回到下一條指令80a01360后,對指令進行分組即(B)(STMFD SP!, {R3-R8,R10,LR})(STMFD-LDMFD)(STMFD-LDMFD),非常容易獲取真實指令。實現時,可將分組過程可融入到指令的遍歷即可。

            再接著看另一組混淆,以80A01360(B sub_80A065F8)為例。

            #!bash
            // 0x80A01360
            seg013:80A065F8                 STMFD           SP!, {R0,LR}
            seg013:80A065FC                 LDR             R0, loc_80A0660C
            seg013:80A06600                 LDR             R0, loc_80A06610
            seg013:80A06604                 LDR             R0, loc_80A06614
            seg013:80A06608                 LDR             R0, locret_80A06618
            seg013:80A0660C                 LDR             R0, =(loc_80A065E0 - 0x80A06618)
            seg013:80A06610                 ADD             R0, PC, R0 ; loc_80A065E0
            seg013:80A06614                 STR             R0, [SP,#4]
            seg013:80A06618
            seg013:80A06618                 LDMFD           SP!, {R0,PC}
            
            seg013:80A065E0                 LDR             R3, [R0] //真實指令
            
            seg013:80A065E4                 STMFD           SP!, {R0,LR}
            seg013:80A065E8                 MOV             LR, PC
            seg013:80A065EC                 BL              loc_80A065F0
            seg013:80A065F0
            seg013:80A065F0 loc_80A065F0                            ; CODE XREF: seg013:80A065ECj
            seg013:80A065F0                 LDMFD           SP!, {R0,LR}
            
            seg013:80A065F4                 B               sub_80A06620
            
            seg013:80A06620                 B               sub_80A01364
            

            執行前混淆:STMFD-LDMFD跳轉到loc_80A065E0。獲取next_jmp和上述一致。
            執行后混淆:通過STMFD-LDMFD和兩次B直接跳轉返回到下一條B指令地址sub_80A01364。
            真實指令:和上述類似,遍歷混淆指令時,對指令進行分組(STMFD-LDMFD)、(LDR R3, [R0])、(B)、(B)。易獲取真實指令(LDR R3, [R0])。

            通過上述方法,大概分析20個多有的B指令即可找到所有的混淆模式,總的來說混淆的模式是有限的。

            通過編寫IDAPython腳本,即可實現自動打印真實指令。

            #!bash
            0x80a0135c            PUSH            {r3, r4, r5, r6, r7, r8, sl, lr}
            0x80a01360            LDR             r3, [r0]
            0x80a01364            MOV             r1, r2
            0x80a01368            MOV             r6, r2
            0x80a0136c            LDR             r3, [r3, #0x2a4]
            0x80a01370            MOV             r2, #0
            0x80a01374            MOV             r4, r0
            0x80a01378            BLX             r3
            0x80a0137c            MOV             r7, r0
            

            但存在問題,當IDA并沒有識別出指令時,無法通過GetMnem等API獲取信息。

            p1

            由于混淆對IDA指令識別的影響,致使IDA無法自動將指令反匯編出來。可能已經有讀者意識到,遇到這種情況直接調用MakeCode將數據轉化為指令即可。然而,實際使用MakeCode自動處理時,并不能完成手動按'C'識別指令的功能。那么,是否遇到這種情況后,就手動去完成指令反匯編呢?答案是否定的。由于存在很多這種情況,手動轉化也很費時(測試環境IDA6.8)。

            到這里可以看到,單純依靠簡單的字符串匹配比較的方法,并不能完全滿足自動化提取指令對抗混淆的需求。

            0x03 基于指令解析執行對抗混淆


            通過上述分析,由于IDA存在無法自動反匯編一部分opcode數據,故單純依靠IDAPython是無法滿足指令解析指令的需求的。為了實現對指令的解析,可采用兩種途徑:

            1. 對照arm匯編手冊,編寫常見的opcode解析腳本。以筆者的經驗,這部分內容是比較耗時的。
            2. 引入現有的反匯編引擎,且這種反匯編引擎具備對指令的想盡分析的能力。這里,我選用Capstone。

            Capstone是一款支持多種架構的反匯編引擎,支持對匯編指令粗略和詳細的分析,支持多種語言。當然,Capstone還有很多其他優點,這里就不贅述了。

            3.1 ARM處理器模擬

            可能有讀者馬上會問,模擬arm處理器執行不又是一大工程呢。的確,完全模擬確實包含許多工作量。但結合此混淆的一些特性,整個模擬執行可簡化許多。

            首先,此混淆并不存在流程分支扁平化(與OLLVM相對比)。結合上述分析也可以看到,所有的混淆執行并不會影響條件標志即CPSR寄存器。

            再者,結合堆棧平衡原理,SP寄存器僅僅只需要保存堆棧的變化,比如stmfd僅僅對SP寄存器進行減法操作。

            最后,根據上述找到的混淆模式,可以發現使用的指令其實很少,實際編寫模擬函數工作量也比較小。

            #!python
            def do_emulate(code, base, Rx):
                ret_addr = 0xffffffff
                emu = ARM_emu()
                md = Cs(CS_ARCH_ARM, CS_MODE_ARM)
                md.detail = True
            
                for i in md.disasm(code, base):
                    emu.regs[PC] = i.address + 2 * inst_size
                    dst = i.operands[0]
                    src = i.operands[1]
            
                    if (i.mnemonic).upper() == 'LDR':
                        if dst.type == ARM_OP_REG and src.type == ARM_OP_MEM:
                            Rd = conv_reg(dst.value.reg)    
                            Rs = conv_reg(src.value.mem.base)
                            addr = emu.regs[Rs] + src.value.mem.disp
                            emu.regs[Rd] = Dword(addr & 0xffffffff)
            
                            if Debug:
                                print ('\t LDR %s :\t0x%x' %(i.op_str, emu.regs[Rd]))
            
                    elif (i.mnemonic).upper() == 'ADD':
                        if i.operands[0].type == ARM_OP_REG and i.operands[1].type == ARM_OP_REG and i.operands[2].type == ARM_OP_REG:
                            Rd = conv_reg(i.operands[0].value.reg)
                            R1 = conv_reg(i.operands[1].value.reg)
                            R2 = conv_reg(i.operands[2].value.reg)
                            emu.regs[Rd] = (emu.regs[R1] + emu.regs[R2]) & 0xffffffff
            
                            if Debug:
                                print ('\t ADD %s :\t0x%x' %(i.op_str, emu.regs[Rd]))
                    ...
            

            在模擬執行一條真實指令時,首先將所有寄存器的初始值設置為0,通過主流程中的B指令進入到混淆指令。

            3.2 真實指令提取

            模擬執行時,將混淆中的每條指令都存儲到一個指令堆棧中。結合之前直接字符串模式的思路,來實現對真實指令的提取。

            以80A01364為例子來說明真實指令的提取方法。

            #!bash
            seg013:80A01364 sub_80A01364                            ; CODE XREF: sub_80A06620j
            seg013:80A01364                 B               sub_80A0663C
            
            seg013:80A0663C                 BMI             loc_80A06648
            seg013:80A06640                 BPL             loc_80A06644
            seg013:80A06644
            seg013:80A06644 loc_80A06644                            ; CODE XREF: sub_80A0663C+4j
            seg013:80A06644                                         ; sub_80A0663C:loc_80A06648j
            seg013:80A06644                 B               loc_80A06624
            seg013:80A06648 ; 
            seg013:80A06648
            seg013:80A06648 loc_80A06648                            ; CODE XREF: sub_80A0663Cj
            seg013:80A06648                 B               loc_80A06644
            
            seg013:80A06624 loc_80A06624                            ; CODE XREF: 
            seg013:80A06624                 MOV             R1, R2
            seg013:80A06628                 STMFD           SP!, {R0,LR}
            seg013:80A0662C                 MOV             LR, PC
            seg013:80A06630                 BL              loc_80A06634
            seg013:80A06634 ; 
            seg013:80A06634
            seg013:80A06634 loc_80A06634                            ; CODE XREF: sub_80A0663C-Cj
            seg013:80A06634                 LDMFD           SP!, {R0,LR}
            seg013:80A06638                 B               sub_80A0664C
            
            seg013:80A0664C sub_80A0664C                            ; CODE XREF: sub_80A0663C-4p
            seg013:80A0664C                 B               sub_80A01368
            

            若不在模擬執行中對指令堆棧修正,那么執行完后存儲指令如下所示:

            #!bash
            80A0663C    BMI     loc_80A06648
            80A06648    B       loc_80A06644
            80A06644    B       loc_80A06624
            80A06624    MOV     R1, R2
            80A06628    STMFD   SP!, {R0,LR}
            80A06630    BL      loc_80A06634
            80A06634    LDMFD   SP!, {R0,LR}
            80A06638    B       sub_80A0664C
            80A0664C    B       sub_80A01368
            

            對于BMI,雖然形式上和之前分析的(BEQ loc_80A0658C,BNE loc_80A0658C)直接跳到next_jmp,但檢測下一條指令即可根據條件相反去處。

            對于STM-LDM,當遇到LDM指令時,將STM-LDM及其之間的指令出棧移除。

            剩余(B B MOV B)這些指令,根據上述人工分析的結果可知,因為只存在一條真實指令,那么MOV必定是真實指令。另外,存在這種情況(B B BNE B),產生這種情況的根本原因是混淆前這條指令是if或者循環語句的判定點,直接取出BNE指令即可。

            3.3 函數識別

            不管是基于指令名稱匹配還是解析執行,都需要對函數進行識別。先來看一個函數混淆片段:

            #!bash
            seg013:80A067F0 loc_80A067F0                    ; CODE XREF: seg013:loc_80A06838j
            seg013:80A067F0                 ADR             LR, sub_80A06814
            seg013:80A067F4                 STMFD           SP!, {R8,R9,LR}
            seg013:80A067F8                 LDR             R8, loc_80A067FC
            seg013:80A067FC
            seg013:80A067FC loc_80A067FC                            ; DATA XREF: seg013:80A067F8r
            seg013:80A067FC                 LDR             R9, =0x1A6016A4
            seg013:80A06800                 ADD             R8, R9, R8
            seg013:80A06804                 ADD             R8, PC, R8 ; j_strlen
            seg013:80A06808                 STR             R8, [SP,#8]
            seg013:80A0680C                 LDMFD           SP!, {R8,R9,PC}
            

            對于未混淆的指令,函數通常被編譯為BL或者BLX(指令模式切換)。由于B指令本身的跳轉地址范圍很有限,那么混淆后代碼膨脹必定需要對其指令修正,有點類似InlineHook指令修正。另外,函數的返回地址需要顯式存放到LR寄存器。

            這樣,上述代碼在模擬執行時,當LR寄存器值不為0時,將后續的函數調用轉化為'Call sub_xxx'指令,將PC置為next_jmp(sub_80A06814)接著模擬。

            另外,便于更加清晰的分析,將libc.so加載到和進程一致的基地址,通過IDAPython GetFunctionName獲取函數名稱。

            至此,即可提取出真實指令,check函數流程:

            #!bash
            0x80a0135c            PUSH            {r3, r4, r5, r6, r7, r8, sl, lr}
            0x80a01360            LDR             r3, [r0]
            0x80a01364            MOV             r1, r2
            0x80a01368            MOV             r6, r2
            0x80a0136c            LDR             r3, [r3, #0x2a4]
            0x80a01370            MOV             r2, #0
            0x80a01374            MOV             r4, r0
            0x80a01378            BLX             r3
            0x80a0137c            MOV             r7, r0
            0x80a01380            call  j_strlen
            0x80a01384            ADD             sl, r0, #1
            0x80a01388            MOV             r8, r0
            0x80a0138c            MOV             r0, sl
            0x80a01390            call  j_malloc_0
            0x80a01394            MOV             r1, r7
            0x80a01398            MOV             r2, sl
            0x80a0139c            MOV             r5, r0
            0x80a013a0            call  j_memcpy
            0x80a013a4            LDR             r3, [r4]
            0x80a013a8            MOV             r2, #0
            0x80a013ac            STRB            r2, [r5, r8]
            0x80a013b0            LDR             r3, [r3, #0x2a8]
            0x80a013b4            MOV             r2, r7
            0x80a013b8            MOV             r0, r4
            0x80a013bc            MOV             r1, r6
            0x80a013c0            BLX             r3
            0x80a013c4            LDR             R0, =0x12BC4
            0x80a013c8            MOV             r1, #0x80
            0x80a013cc            LDR             R0, [PC,R0]
            0x80a013d0            call  0x80a01048
            0x80a013d4            ADD             r5, r5, r0
            0x80a013d8            MOV             r0, r5
            0x80a013dc            call  0x80a010c0
            0x80a013e0            MOV             r4, r0
            0x80a013e4            MOV             r0, r5
            0x80a013e8            call  j_free_1
            0x80a013ec            MOV             r0, r4
            0x80a013f0            POP             {r3, r4, r5, r6, r7, r8, sl, pc}
            

            0x04 算法逆向分析


            通過簡單分析check即可看到算法的核心流程在0x80a010c0這個函數,而0x80a01048函數的功能是對指令路徑上的斷點進行檢測,和其他平臺的反調試思路類似,這里把重點放在0x80a010c0的逆向上。自動化分析得到0x80a010c0函數:

            #!bash
            0x80a010c0            PUSH            {r4, r5, r6, r7, r8, sb, sl, lr}
            0x80a010c4            LDR             R7, =0x12EB4
            0x80a010c8            SUB             sp, sp, #0x308
            0x80a010cc            ADD             r6, sp, #4 
            0x80a010d0            LDR             R7, [PC,R7]
            0x80a010d4            LDR             r3, [r7]
            0x80a010d8            MOV             r4, r0
            0x80a010dc            MOV             r1, #0
            0x80a010e0            MOV             r2, #0x100
            0x80a010e4            MOV             r0, r6
            0x80a010e8            ADD             r5, sp, #0x104 
            0x80a010ec            STR             r3, [sp, #0x304]
            0x80a010f0            call  j_memset
            0x80a010f4            MOV             r1, #0
            0x80a010f8            MOV             r2, #0x100
            0x80a010fc            MOV             r0, r5
            0x80a01100            call  j_memset
            0x80a01104            MOV             r0, r4
            0x80a01108            call  j_strlen
            0x80a0110c            SUBS            sb, r0, #0
            0x80a01110            MOVEQ           r0, sb
            0x80a01114            BNE             #0x80a01130
            0x80a01118            LDR             r2, [sp, #0x304]
            0x80a0111c            LDR             r3, [r7]
            0x80a01120            CMP             r2, r3
            0x80a01124            BNE             #0x80a01334
            0x80a01128            ADD             sp, sp, #0x308
            0x80a0112c            POP             {r4, r5, r6, r7, r8, sb, sl, pc}
            
            //獲取代碼段起始256字節作為key
            0x80a011bc            LDR             R0, =0x12DC0   //讀取代碼段起始地址
            0x80a011c0            LDR             LR, =0x66666667
            0x80a011c4            MOV             r4, #0
            0x80a011c8            LDR             R0, [PC,R0]
            0x80a011cc            MOV             r3, r0
            0x80a011d0            SMULL           r2, ip, lr, r4
            0x80a011d4            ASR             r2, r4, #0x1f
            0x80a011d8            LDRB            r1, [r3]
            0x80a011dc            RSB             r2, r2, ip, asr #1
            0x80a011e0            ADD             r2, r2, r2, lsl #2
            0x80a011e4            RSB             r2, r2, r4
            0x80a011e8            STRB            r1, [r6, r4]
            0x80a011ec            ADD             r4, r4, #1
            0x80a011f0            CMP             r4, #0x100
            0x80a011f4            ADD             r3, r3, r2
            0x80a011f8            BNE             #0x80a011d0
            
            //key變換流程
            0x80a01218            MOV             r3, #0
            0x80a0121c            MOV             r0, r3
            0x80a01220            ADD             r4, r4, #1
            0x80a01224            ADD             r6, sp, #0x308
            0x80a01228            AND             r4, r4, #0xff
            0x80a0122c            ADD             r1, r6, r4
            0x80a01230            LDRB            r2, [r1, #-0x304] 
            0x80a01234            LDRB            r8, [r5, r3] 
            0x80a01238            AND             ip, r3, #7 
            0x80a0123c            ADD             r0, r2, r0
            0x80a01240            AND             r0, r0, #0xff
            0x80a01244            ADD             r6, r6, r0
            0x80a01248            LDRB            sl, [r6, #-0x304]
            0x80a0124c            ASR             sb, r8, #5
            0x80a01250            ORR             r8, sb, r8, lsl #3 
            0x80a01254            STRB            sl, [r1, #-0x304] 
            0x80a01258            STRB            r2, [r6, #-0x304]
            0x80a0125c            LDRB            r6, [r1, #-0x304]
            0x80a01260            ADD             sl, sp, #0x308
            0x80a01264            RSB             r1, ip, #8 // 8 - [0, 7]
            0x80a01268            ADD             r2, r2, r6
            0x80a0126c            AND             r2, r2, #0xff
            0x80a01270            ADD             r2, sl, r2
            0x80a01274            LDRB            r2, [r2, #-0x304]
            0x80a01278            EOR             r2, r2, r8
            0x80a0127c            AND             r2, r2, #0xff
            0x80a01280            LSL             r1, r2, r1 
            0x80a01284            ORR             ip, r1, r2, asr ip // 循環左移(8 - i)位
            0x80a01288            STRB            ip, [r5, r3] 
            0x80a0128c            ADD             r3, r3, #1
            0x80a01290            CMP             r3, #0x100
            0x80a01294            BNE             #0x80a01220
            
            for(i = 0; i < 0x100; i++){
                left_rotate(right_rotate(mid_code[i], 5) ^ key_stream[i], 8 - (i % 8));
            ...
            

            限于篇幅,就不在一一分析。其中包括RC4算法。最后得到算法編碼主流程:

            #!cpp
            char gen_mid_code[N];
            char key_stream[N];
            
            for(i = 0; i < N; i++){
                gen_mid_code[i] = left_rotate(str[gen_index(i, strlen(str))], 8 - (i % 8));
            }
            gen_key_stream(ori_key, key_stream);
            RC4_encrypt(gen_mid_code, key_stream, final_code);
            for(i = 0; i < N; i++){
                if(final_code[i] != check_code[i]){
                        ...
                }
            }
            

            最后,得到flag:Hello Tomorrow!

            至此,此ctf題目大致分析完畢。

            題目下載地址:http://pan.baidu.com/s/1hrqZH9E

            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线