• 【黑客免杀攻防】读书笔记10


    0x1 switch-case分支

    switch-case其实就是if-else语句的另一种体现形式。但大于3之后的switchc-case。编译器会对代码进行优化。

    1.1 简单switch-case分支识别技巧

    C源代码:

    int _tmain(int argc, _TCHAR* argv[])
    {
    	int nNum = 2;
    	switch (nNum)
    	{
    	case 0:
    		printf("nNum=0", nNum);
    		break;
    	case 1:
    		printf("nNum=%d", nNum);
    		break;
    	case 2:
    		printf("nNum=%d", nNum);
    		break;
    	default:
    		printf("nNum=%d,error!", nNum);
    	}
    
    	return 0;
    }
    

    Debug版反汇编代码

    Debug版的反汇编代码与源代码是很相似的。并不会剪掉不可达分支。

    • 以常量、变量为判断条件的优化效果与if-else有多大区别?
    011A70C0 push ebp
    011A70C1 mov ebp,esp
    011A70C3 sub esp,0xD0
    011A70C9 push ebx
    011A70CA push esi
    011A70CB push edi
    011A70CC lea edi,[local.52]
    011A70D2 mov ecx,0x34
    011A70D7 mov eax,0xCCCCCCCC
    011A70DC rep stos dword ptr es:[edi]
    011A70DE mov [local.2],0x2          ;  将2赋值到变量 nNum = 2
    011A70E5 mov eax,[local.2]          ;  将nNum变量赋值到eax
    011A70E8 mov [local.52],eax         ;  赋值到局部变量中
    011A70EE cmp [local.52],0x0         ;  对比0的情况
    011A70F5 je X9-25.011A710B
    011A70F7 cmp [local.52],0x1         ;  对比1的情况
    011A70FE je X9-25.011A711E
    011A7100 cmp [local.52],0x2         ;  对比2的情况
    011A7107 je X9-25.011A7131
    011A7109 jmp X9-25.011A7144         ;  默认情况就无条件跳转到最后一个分支处
    011A710B mov eax,[local.2]
    011A710E push eax
    011A710F push 9-25.01235E50         ;  nNum=%d
    011A7114 call 9-25.011A3CE7
    011A7119 add esp,0x8
    011A711C jmp X9-25.011A7155         ;  执行完就无条件跳转到结束的位置
    011A711E mov eax,[local.2]
    011A7121 push eax
    011A7122 push 9-25.01235E50         ;  nNum=%d
    011A7127 call 9-25.011A3CE7
    011A712C add esp,0x8
    011A712F jmp X9-25.011A7155         ;  执行完就无条件跳转到结束的位置
    011A7131 mov eax,[local.2]
    011A7134 push eax
    011A7135 push 9-25.01235E50         ;  nNum=%d
    011A713A call 9-25.011A3CE7
    011A713F add esp,0x8
    011A7142 jmp X9-25.011A7155         ;  执行完就无条件跳转到结束的位置
    011A7144 mov eax,[local.2]
    011A7147 push eax
    011A7148 push 9-25.01235E5C         ;  nNum=%d,error!
    011A714D call 9-25.011A3CE7
    011A7152 add esp,0x8
    011A7155 xor eax,eax
    011A7157 pop edi
    011A7158 pop esi                    ;  9-25.<ModuleEntryPoint>
    011A7159 pop ebx
    011A715A add esp,0xD0
    011A7160 cmp ebp,esp
    011A7162 call 9-25.011A231A
    011A7167 mov esp,ebp
    011A7169 pop ebp
    011A716A retn
    
    

    通过Debug反汇编代码,总结以下特点:

        cmp xxx,xxx
        jXX CASE1_TAG
        cmp xxx,xxx
        jXX CASE2_TAG
        cmp xxx,xxx
        jXX CASE3_TAG
        JMP SWITCH_END_TAG
    CASE1_TAG:
    .....分支1执行代码
        JMP SWITCH_END_TAG
    CASE2_TAG:
    .....分支2执行代码
        JMP SWITCH_END_TAG
    CASE3_TAG:
    .....分支3执行代码
        JMP SWITCH_END_TAG
    DEFAULT:
        ..DEFAULT代码执行代码
    SWITCH_END_TAG
    

    Release版反汇编代码:

    Release版反汇编则是把代码进行了优化,由此可见switch-case语句与if-else语句一样,不可达分支都会被编译器优化掉。

    001B1000  push 0x2                 0x2
    001B1002  push 9-25.001C6448       nNum=%d
    001B1007  call 9-25.printf         printf
    001B100C  add esp,0x8
    001B100F  xor eax,eax
    001B1011  retn
    

    那么尝试以变量为判断条件的优化效果与if-else有多大区别?

    C源代码:

    int _tmain(int argc, _TCHAR* argv[])
    {
    	switch (argc)
    	{
    	case 0:
    		printf("argc=0",argc);
    		break;
    	case 1:
    		printf("argc=%d",argc);
    		break;
    	case 2:
    		printf("argc=%d",argc);
    		break;
    	default:
    		printf("argc=%d,error!",argc);
    	}
    
    	return 0;
    }
    
    

    按照if-else的优化逻辑,case 1与case 2 会指向一处。

    00B11000 push ebp
    00B11001 mov ebp,esp
    00B11003 mov ecx,dword ptr ss:[ebp+0x8];argc传参给ecx寄存器中
    00B11006 mov eax,ecx                   ;ecx寄存器又把值传给eax
    00B11008 sub eax,0x0                   ;switch()开始的地方,对比0
    00B1100B je X9-25.00B1104F
    00B1100D sub eax,0x1                   ;注意这里用的是减法,非常巧妙
    00B11010 je X9-25.00B1103C             ;当值等于0的时候跳转
    00B11012 sub eax,0x1
    00B11015 je X9-25.00B11029
    00B11017 push ecx                      ;Default case
    00B11018 push 9-25.00B26458            ;argc=%d,error!
    00B11022 add esp,0x8
    00B11025 xor eax,eax
    00B11027 pop ebp
    00B11028 retn
    00B11029 push 0x2                      ;argc为2的情况
    00B1102B push 9-25.00B26450            ;argc=%d
    00B11030 call 9-25.printf              ;printf
    00B11035 add esp,0x8
    00B11038 xor eax,eax
    00B1103A pop ebp
    00B1103B retn
    00B1103C push 0x1                      ;argc为1的情况
    00B1103E push 9-25.00B26450            ;argc=%d
    00B11043 call 9-25.printf              ;printf
    00B11048 add esp,0x8
    00B1104B xor eax,eax
    00B1104D pop ebp
    00B1104E retn
    00B1104F push 0x0                      ;argc为0的情况
    00B11051 push 9-25.00B26448            ;argc=0
    00B11056 call 9-25.printf              ;printf
    00B1105B add esp,0x8
    00B1105E xor eax,eax
    00B11060 pop ebp
    00B11061 retn
    

    可以发现release在条件跳转前用的不再是cmp,而是sub。通过阅读这块代码可知,程序先将main()函数的参数1传递给EAX,然后减0。je的跳转条件是ZF=1,因此当EAX为0时,那么将其减0肯定会使用ZF位置1,而其实就是一个变形的CMP指令。

    假设EAX等于2,因此按照上面代码的流程走会先将其减0,此时ZF位不变;

    接着下面又对EAX减1,此时ZF位仍然没变化;

    而当走到第三步时,此时EAX的值为1,又将其减1后肯定就等于0了,ZF位置为1,后面的JZ跳转生效。

    小结:上诉操作就是做了一连串的减法,到哪等于0后,就证明这个值原先为多少。

    1.2 复杂分支的switch-case识别

    C源代码:

    int _tmain(int argc, _TCHAR* argv[])
    {
    	switch (argc)
    	{
    	case 0:
    		printf("argc=0",argc);
    		break;
    	case 1:
    		printf("argc=%d",argc);
    		break;
    	case 6:
    		printf("argc=%d",argc);
    		break;
    	case 7:
    		printf("argc=%d",argc);
    		break;
    	case 9:       
    		printf("argc=%d",argc);
    		break;
    	default:
    		printf("nNum=%d,error!",argc);
    	}
    
    	return 0;
    }
    
    

    Release版反汇编代码:

    00D71000 push ebp
    00D71001 mov ebp,esp
    00D71003 mov eax,[arg.1]                          ;  取得局部变量后传递给EAX
    00D71006 cmp eax,0x9                              ;  Switch (cases 0..9)
    00D71009 ja short 9-29.00D71071                   ;  如果大于最大值9,直接跳转到Default处
    00D7100B jmp dword ptr ds:[eax*4+0xD71084]        ;  这里采用了一个比较特殊的方式
    00D71012 push 0x0                                 ;  Case 0 of switch 00D71006
    00D71014 push 9-29.00D86448                       ;  argc=0
    00D71019 call 9-29.printf_initialize_sse2_sse2_fr>
    00D7101E add esp,0x8                              ;  argc=0
    00D71021 xor eax,eax
    00D71023 pop ebp                                  ;  kernel32.770B62C4
    00D71024 retn
    00D71025 push 0x1                                 ;  Case 1 of switch 00D71006
    00D71027 push 9-29.00D86450                       ;  argc=%d
    00D7102C call 9-29.printf_initialize_sse2_sse2_fr>
    00D71031 add esp,0x8                              ;  argc=%d
    00D71034 xor eax,eax
    00D71036 pop ebp                                  ;  kernel32.770B62C4
    00D71037 retn
    00D71038 push 0x6                                 ;  Case 6 of switch 00D71006
    00D7103A push 9-29.00D86450                       ;  argc=%d
    00D7103F call 9-29.printf_initialize_sse2_sse2_fr>
    00D71044 add esp,0x8                              ;  argc=%d
    00D71047 xor eax,eax
    00D71049 pop ebp                                  ;  kernel32.770B62C4
    00D7104A retn
    00D7104B push 0x7                                 ;  Case 7 of switch 00D71006
    00D7104D push 9-29.00D86450                       ;  argc=%d
    00D71052 call 9-29.printf_initialize_sse2_sse2_fr>
    00D71057 add esp,0x8                              ;  argc=%d
    00D7105A xor eax,eax
    00D7105C pop ebp                                  ;  kernel32.770B62C4
    00D7105D retn
    00D7105E push 0x9                                 ;  Case 9 of switch 00D71006
    00D71060 push 9-29.00D86450                       ;  argc=%d
    00D71065 call 9-29.printf_initialize_sse2_sse2_fr>
    00D7106A add esp,0x8
    00D7106D xor eax,eax                              ;  argc=%d
    00D7106F pop ebp                                  ;  kernel32.770B62C4
    00D71070 retn
    00D71071 push eax                                 ;  Default case of switch 00D71006
    00D71072 push 9-29.00D86458                       ;  nNum=%d,error!
    00D71077 call 9-29.printf_initialize_sse2_sse2_fr>
    00D7107C add esp,0x8
    00D7107F xor eax,eax                              ;  nNum=%d,error!
    00D71081 pop ebp                                  ;  kernel32.770B62C4
    00D71082 retn
    
    
    

    位于第4行的jmp指令以及前几条反汇编指令的含义:

    
    mov     eax, [ebp+arg_0] ; 取得局部变量后传递给EAX
    cmp     eax, 9          ; 与9比较,我们通过源代码可知这是switch-case分支的最大值
                            ; 因此如果传入的值大于9就肯定会执行Default处代码
    ja      short loc_401071 ; jumptable 0040100B default case
    jmp     ds:off_401084[eax*4] ; EAX此时保存的是输入的值,将其乘以4后再加上一个地址,这其实就是一个典型的数组寻址。
                            ; 是一个读取int型数组的操作,数组里保存的是地址指针
    
    

    OD上-数据窗口中跟随-地址常量。就可以看懂0xD71084存储的到底是啥了。里面保存的内容就是各个case块的地址,我们将之称为“跳转表”。跳转表的作用就是用数组的寻址运算代替复杂的if-else分支,可以大大提高程序的执行效率。

    跳转表的基本原理是建立一张表格,里面保存着从case1到caseN的所有分支应该到达的地址。从数据窗口中可以看到代码中的case1到caseN地址都保存在了跳转表里。

    00D71084  12 10 D7 00 25 10 D7 00 71 10 D7 00 71 10 D7 00  ဒ×ဥ×ၱ×ၱ×
    00D71094  71 10 D7 00 71 10 D7 00 38 10 D7 00 4B 10 D7 00  ၱ×ၱ×း×။×
    00D710A4  71 10 D7 00 5E 10 D7 00                          ၱ×ၞ×
    

    case2至case5里保存的地址都是Default分支的地址,这就证明这几个case在程序的源代码中是属于未处理的状态。


    图:跳转表

    1.3 switch-case分支结构与稀疏矩阵

    Switch-case除了转成if-else与利用跳转表两种优化模式外。当switch-case分支两个数之差大于50甚至更多的时候,编译器会采用更加优化的方式进行处理。由此引出稀疏矩阵的知识点。

    1)什么是稀疏矩阵?

    稀疏矩阵就是零元素或同一元素占全部元素的百分比很小(例如5%以下)的矩阵

    2)什么时候应用稀疏矩阵?

    由于每个编译器所使用的策略不一样,因此其“体积-效率比”权值的设定也不尽相同。如果使用跳转表生成的代码的体积大于使用稀疏矩阵的体积,那么一般情况下编译器就会选择使用稀疏矩阵来优化此段代码。

    C源代码:

    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	switch (argc)
    	{
    	case 0:
    		printf("argc=0",argc);
    		break;
    	case 1:
    		printf("argc=%d",argc);
    		break;
    	case 6:
    		printf("argc=%d",argc);
    		break;
    	case 7:
    		printf("argc=%d",argc);
    		break;
    	case 199:        // 注意这里,想想如果还使用跳转表的话会是什么后果……
    		printf("argc=%d",argc);
    		break;
    	default:
    		printf("nNum=%d,error!",argc);
    	}
    
    	return 0;
    }
    
    

    以上代码编译之后,拖进IDA里,旁边的函数名caseN是我改掉的。在VS系列编译器中,针对switch-case分支结构的稀疏矩阵(间接表)都是用1字节表示的,因此其最小索引值与最大索引值之差不得大于256,否则此优化方法便不再适用。

    其次这个稀疏矩阵,又可称为间接表的数组需要与跳转表相呼应才能保证程序流程最终执行到正确的地方。IDA解析后的反汇编代码如下:

    .text:00401000 sub_401000      proc near               
    .text:00401000
    .text:00401000 arg_0 = dword ptr  8
    .text:00401000
    .text:00401000       push    ebp
    .text:00401001       mov     ebp, esp
    .text:00401003       mov     ecx, [ebp+arg_0]
    .text:00401006       cmp     ecx, 0C7h       ; switch 200 cases
    .text:0040100C       ja      short caseDefault ; jumptable 00401015 default case
    .text:0040100E       movzx   eax, ds:byte_4010A8[ecx]
    .text:00401015       jmp     ds:off_401090[eax*4] ; switch jump
    .text:0040101C ; -----------------------------------------------------------------
    .text:0040101C
    .text:0040101C case0:                        
    .text:0040101C       push    0               ; jumptable 00401015 case 0
    .text:0040101E       push    offset aArgc0   ; "argc=0"
    .text:00401023       call    sub_401180
    .text:00401028       add     esp, 8
    .text:0040102B       xor     eax, eax
    .text:0040102D       pop     ebp
    .text:0040102E       retn
    .text:0040102F ; ---------------------------------------------------------------------------
    .text:0040102F
    .text:0040102F case1:                        
    .text:0040102F       push    1               ; jumptable 00401015 case 1
    .text:00401031       push    offset aArgcD   ; "argc=%d"
    .text:00401036       call    sub_401180
    .text:0040103B       add     esp, 8
    .text:0040103E       xor     eax, eax
    .text:00401040       pop     ebp
    .text:00401041       retn
    .text:00401042 ; ---------------------------------------------------------------------------
    .text:00401042
    .text:00401042 case6:                        
    .text:00401042       push    6               ; jumptable 00401015 case 6
    .text:00401044       push    offset aArgcD   ; "argc=%d"
    .text:00401049       call    sub_401180
    .text:0040104E       add     esp, 8
    .text:00401051       xor     eax, eax
    .text:00401053       pop     ebp
    .text:00401054       retn
    .text:00401055 ; ---------------------------------------------------------------------------
    .text:00401055
    .text:00401055 case7:                        
    .text:00401055       push    7               ; jumptable 00401015 case 7
    .text:00401057       push    offset aArgcD   ; "argc=%d"
    .text:0040105C       call    sub_401180
    .text:00401061       add     esp, 8
    .text:00401064       xor     eax, eax
    .text:00401066       pop     ebp
    .text:00401067       retn
    .text:00401068 ; ---------------------------------------------------------------------------
    .text:00401068
    .text:00401068 case199:                        
    .text:00401068         push    0C7h            ; jumptable 00401015 case 199
    .text:0040106D         push    offset aArgcD   ; "argc=%d"
    .text:00401072         call    sub_401180
    .text:00401077         add     esp, 8
    .text:0040107A         xor     eax, eax
    .text:0040107C         pop     ebp
    .text:0040107D         retn
    .text:0040107E ; ---------------------------------------------------------------------------
    .text:0040107E
    .text:0040107E caseDefault:                        
    .text:0040107E             push    ecx             ; jumptable 00401015 default case
    .text:0040107F             push    offset aNnumDError ; "nNum=%d,error!"
    .text:00401084             call    sub_401180
    .text:00401089             add     esp, 8
    .text:0040108C             xor     eax, eax
    .text:0040108E             pop     ebp
    .text:0040108F             retn
    .text:0040108F sub_401000  endp
    

    通过上面的代码分析出主要是以下两句代码在控制其流程:

    .text:0040100E  movzx   eax, ds:byte_4010A8[ecx] ; Move with Zero-Extend
    .text:00401015  jmp     ds:off_401090[eax*4] ; switch jump
    

    由于代码中的switch-case分支结构拥有6个分支,因此间接表里保存的索引内容都是在0-5之间的,然后根据索引来确定跳转到第几个分支上去。


    图:间接表(稀疏矩阵)

    1.4 switch-case分支结构与平衡二叉树

    间接表的应用只限于小于等于256的情况,如果超过256这种与跳转表相配合的间接表就会失效,取而代之的便是著名的二叉树了。

     二叉树查找法我们也可以暂时将其理解为折半查找法。
     
    C源代码:

    int _tmain(int argc, _TCHAR* argv[])
    {
    	switch (argc)
    	{
    	case 1:
    		printf("argc1=0",argc);
    		break;
    	case 92:
    		printf("argc12=%d",argc);
    		break;
    	case 262:
    		printf("argc1=%d",argc);
    		break;
    	case 118:
    		printf("argc118=%d",argc);
    		break;
    	case 25:
    		printf("argc25=%d",argc);
    		break;
    	case 456:
    		printf("argc456=%d",argc);
    		break;
    	case 588:
    		printf("argc588=%d",argc);
    		break;
    	case 896:
    		printf("argc896=0",argc);
    		break;
    	case 1000:
    		printf("argc1000=%d",argc);
    		break;
    	case 1090:
    		printf("argc1090=%d",argc);
    		break;
    	case 2100:
    		printf("argc2100=%d",argc);
    		break;
    	default:
    		printf("default nNum=%d,error!",argc);
    	}
    	return 0;
    }
    
    

    Release版本反汇编代码:

    .text:00401000 _wmain          proc near               ; CODE XREF: ___tmainCRTStartup+F5p
    .text:00401000
    .text:00401000 arg_0           = dword ptr  4
    .text:00401000
    .text:00401000                 mov     eax, [esp+arg_0]
    .text:00401004                 cmp     eax, 1C8h       ; 1C8H == 456
    .text:00401009                 jg      loc_4010AA      ;大于456时转移
    .text:0040100F                 jz      loc_401095      ; zf == 0 也就是刚好是456时也跳转
    .text:00401015                 cmp     eax, 5Ch        ; 5CH == 92
    .text:00401018                 jg      short loc_401062
    .text:0040101A                 jz      short loc_401050
    .text:0040101C                 mov     ecx, eax
    .text:0040101E                 sub     ecx, 1
    .text:00401021                 jz      short loc_40103E
    .text:00401023                 sub     ecx, 18h        ; 
    .text:00401026                 jnz     loc_40110A
    .text:0040102C                 push    19h             ; 
    .text:0040102E                 push    offset aArgc25D ; "argc25=%d"
    .text:00401033                 call    _printf
    .text:00401038                 add     esp, 8
    .text:0040103B                 xor     eax, eax
    .text:0040103D                 retn
    .text:0040103E ; ---------------------------------------------------------------------------
    .text:0040103E
    .text:0040103E loc_40103E:                             ; CODE XREF: _wmain+21j
    .text:0040103E                 push    1
    .text:00401040                 push    offset aArgc10  ; "argc1=0"
    .text:00401045                 call    _printf
    .text:0040104A                 add     esp, 8
    .text:0040104D                 xor     eax, eax
    .text:0040104F                 retn
    .text:00401050 ; ---------------------------------------------------------------------------
    .text:00401050
    .text:00401050 loc_401050:                             ; CODE XREF: _wmain+1Aj
    .text:00401050                 push    5Ch             ; 5CH == 92
    .text:00401052                 push    offset aArgc12D ; "argc12=%d"
    .text:00401057                 call    _printf
    .text:0040105C                 add     esp, 8
    .text:0040105F                 xor     eax, eax
    .text:00401061                 retn
    .text:00401062 ; ---------------------------------------------------------------------------
    .text:00401062
    .text:00401062 loc_401062:                             ; CODE XREF: _wmain+18j
    .text:00401062                 cmp     eax, 76h        ; 76h == 118
    .text:00401065                 jz      short loc_401083
    .text:00401067                 cmp     eax, 106h       ; 106h == 262
    .text:0040106C                 jnz     loc_40110A
    .text:00401072                 push    eax
    .text:00401073                 push    offset aArgc1D  ; "argc1=%d"
    .text:00401078                 call    _printf
    .text:0040107D                 add     esp, 8
    .text:00401080                 xor     eax, eax
    .text:00401082                 retn
    .text:00401083 ; ---------------------------------------------------------------------------
    .text:00401083
    .text:00401083 loc_401083:                             ; CODE XREF: _wmain+65j
    .text:00401083                 push    76h
    .text:00401085                 push    offset aArgc118D ; "argc118=%d"
    .text:0040108A                 call    _printf
    .text:0040108F                 add     esp, 8
    .text:00401092                 xor     eax, eax
    .text:00401094                 retn
    .text:00401095 ; ---------------------------------------------------------------------------
    .text:00401095
    .text:00401095 loc_401095:                             ; CODE XREF: _wmain+Fj
    .text:00401095                 push    1C8h             ; 1C8h == 456
    .text:0040109A                 push    offset aArgc456D ; "argc456=%d"
    .text:0040109F                 call    _printf
    .text:004010A4                 add     esp, 8
    .text:004010A7                 xor     eax, eax
    .text:004010A9                 retn
    .text:004010AA ; ---------------------------------------------------------------------------
    .text:004010AA
    .text:004010AA loc_4010AA:                             ; CODE XREF: _wmain+9j
    .text:004010AA                 cmp     eax, 3E8h       ; 3E8h == 1000
    .text:004010AF                 jg      short loc_4010FC
    .text:004010B1                 jz      short loc_4010E7
    .text:004010B3                 cmp     eax, 24Ch       ; 24Ch == 588
    .text:004010B8                 jz      short loc_4010D2
    .text:004010BA                 cmp     eax, 380h       ; 389h == 896
    .text:004010BF                 jnz     short loc_40110A
    .text:004010C1                 push    eax
    .text:004010C2                 push    offset aArgc8960 ; "argc896=0"
    .text:004010C7                 call    _printf
    .text:004010CC                 add     esp, 8
    .text:004010CF                 xor     eax, eax
    .text:004010D1                 retn
    .text:004010D2 ; ---------------------------------------------------------------------------
    .text:004010D2
    .text:004010D2 loc_4010D2:                             ; CODE XREF: _wmain+B8j
    .text:004010D2                 push    24Ch
    .text:004010D7                 push    offset aArgc588D ; "argc588=%d"
    .text:004010DC                 call    _printf
    .text:004010E1                 add     esp, 8
    .text:004010E4                 xor     eax, eax
    .text:004010E6                 retn
    .text:004010E7 ; ---------------------------------------------------------------------------
    .text:004010E7
    .text:004010E7 loc_4010E7:                             ; CODE XREF: _wmain+B1j
    .text:004010E7                 push    3E8h
    .text:004010EC                 push    offset aArgc1000D ; "argc1000=%d"
    .text:004010F1                 call    _printf
    .text:004010F6                 add     esp, 8
    .text:004010F9                 xor     eax, eax
    .text:004010FB                 retn
    .text:004010FC ; ---------------------------------------------------------------------------
    .text:004010FC
    .text:004010FC loc_4010FC:                             ; CODE XREF: _wmain+AFj
    .text:004010FC                 cmp     eax, 442h       ; 442h == 1090
    .text:00401101                 jz      short loc_401130
    .text:00401103                 cmp     eax, 834h       ; 834h == 2100
    .text:00401108                 jz      short loc_40111B
    .text:0040110A
    .text:0040110A loc_40110A:                             ; CODE XREF: _wmain+26j
    .text:0040110A                                         ; _wmain+6Cj ...
    .text:0040110A                 push    eax
    .text:0040110B                 push    offset aDefaultNnumDEr ; "default nNum=%d,error!"
    .text:00401110                 call    _printf
    .text:00401115                 add     esp, 8
    .text:00401118                 xor     eax, eax
    .text:0040111A                 retn
    .text:0040111B ; ---------------------------------------------------------------------------
    .text:0040111B
    .text:0040111B loc_40111B:                             ; CODE XREF: _wmain+108j
    .text:0040111B                 push    834h
    .text:00401120                 push    offset aArgc2100D ; "argc2100=%d"
    .text:00401125                 call    _printf
    .text:0040112A                 add     esp, 8
    .text:0040112D                 xor     eax, eax
    .text:0040112F                 retn
    .text:00401130 ; ---------------------------------------------------------------------------
    .text:00401130
    .text:00401130 loc_401130:                             ; CODE XREF: _wmain+101j
    .text:00401130                 push    442h
    .text:00401135                 push    offset aArgc1090D ; "argc1090=%d"
    .text:0040113A                 call    _printf
    .text:0040113F                 add     esp, 8
    .text:00401142                 xor     eax, eax
    .text:00401144                 retn
    .text:00401144 _wmain          endp
    .text:00401144
    

    对于二叉树结构的识别,如果其每次跳转所对比的值都是其后面分支跳转的中间值之一,那么这就有可能是一个二叉树。

    .text:00401004                 cmp     eax, 1C8h       ; 1C8H == 456
    .text:00401009                 jg      loc_4010AA      ; 大于456时转移
    .text:00401015                 cmp     eax, 5Ch         ; 5CH == 92
    .text:00401018                 jg      short loc_401062 ; 分支1
    .text:0040101A                 jz      short loc_401050
    ... ...
    .text:004010AA loc_4010AA:                             ; CODE XREF: _wmain+9j
    .text:004010AA                 cmp     eax, 3E8h       ; 3E8h == 1000
    .text:004010AF                 jg      short loc_4010FC
    .text:004010B1                 jz      short loc_4010E7
    .text:004010B3                 cmp     eax, 24Ch       ; 24Ch == 588
    

    图:二叉树结构

    0x2 参考:

    -【专题策划】《恶意样本分析手册》合辑
      http://blog.nsfocus.net/malware-sample-analysis-summary/?from=timeline&isappinstalled=0

  • 相关阅读:
    基础笔记8(二)(容器-引用类型的排序)
    OO的五大原则是指SRP、OCP、LSP、DIP、ISP。
    基础笔记9(泛型)
    insert statements will fail to restore data from temporary table. you must correct statements preceded by a warning comment in spcript.
    sqlserver 官方文档
    sqlserver 时间格式转换汇总:日、周、月、年、小时、分钟、秒 等。
    sqlserver查询一天中时间最大的那行数据
    PowerDesigner设计表时显示注释列Comment(转载)
    无法访问网页:多是域名或ip地址错误。
    远程报错,本地完全没问题,多是配置文件错误!
  • 原文地址:https://www.cnblogs.com/17bdw/p/7712764.html
Copyright © 2020-2023  润新知