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

【黑客免杀攻防】读书笔记10 - switch-case分支的更多相关文章

  1. 【黑客免杀攻防】读书笔记5 - PE格式讲解

    0x01 MS-DOS头 MS-DOS头部的字段重点关注e_magic与最后一个e_lfanew是需要关注的. 第一个e_magic字段的值为4D5A,作用是可以作为判断这个文件是否是PE文件. 最后 ...

  2. 【黑客免杀攻防】读书笔记8 - 软件逆向工程基础2(if-else,三目运算符)

    0x1 if-else分支 if-else分支4种状态 1.1 以常量为判断条件的简单if-else分支 C源代码: 单层if-else判断,常量为判断条件 int _tmain(int argc, ...

  3. 【黑客免杀攻防】读书笔记7 - 软件逆向工程基础1(函数调用约定、Main函数查找)

    0x1 准备工作 1.1.准备工具 IDA:交互式反汇编工具 OllyDbg:用户层调试工具 Visual Studio:微软开发工具 1.2.基础知识 C++开发 汇编语言 0x2 查找真正的mai ...

  4. 【黑客免杀攻防】读书笔记6 - PE文件知识在免杀中的应用

    0x1 PE文件与免杀思路 基于PE文件结构知识的免杀技术主要用于对抗启发式扫描. 通过修改PE文件中的一些关键点来达到欺骗反病毒软件的目的. 修改区段名 1.1 移动PE文件头位置免杀 工具:PeC ...

  5. 【黑客免杀攻防】读书笔记18-最终章Anti Rootkit

    1.免杀技巧的遏制 1.1.PE文件 入口点不在第一个区段或在最后一个区段 入口点处代码附近只有一小段代码 入口点在正常范围之外 入口点为一个无效的值,实际入口点为TLS的入口点 区段名重复或者不属于 ...

  6. 【黑客免杀攻防】读书笔记2 - 免杀与特征码、其他免杀技术、PE进阶介绍

    第3章 免杀与特征码 这一章主要讲了一些操作过程.介绍了MyCCL脚本木马免杀的操作,对于定位特征码在FreeBuf也曾发表过类似工具. VirTest5.0特征码定位器 http://www.fre ...

  7. 【黑客免杀攻防】读书笔记15 - 源码免杀、C++壳的编写

    1.源码免杀 1.1 定位产生特征的源码 定位文件特征 1.根据MyCCL的特征码定位工具,定位出有特征的地址 2.根据VS的反汇编窗口,输入有特征的地址得到特征地址与源码的关系 3.插入Messag ...

  8. 【黑客免杀攻防】读书笔记17 - Rootkit基础

    1.构建Rootkit基础环境 1.1.构建开发环境 VS2012+WDK8 1.2.构建基于VS2012的调试环境 将目标机.调试机配置在同一个工作组内 sVS2012配置->DRIVER-& ...

  9. 【黑客免杀攻防】读书笔记14 - 面向对象逆向-虚函数、MFC逆向

    虚函数存在是为了克服类型域解决方案的缺陷,以使程序员可以在基类里声明一些能够在各个派生类里重新定义的函数. 1 识别简单的虚函数 代码示例: #include "stdafx.h" ...

随机推荐

  1. 《linux内核设计与实现》第二章

    第二章 从内核出发 一.获取内核源码 1.使用Git(linux创造的系统) 使用git来获取最新提交到linux版本树的一个副本: $ git clone git://git.kernel.org/ ...

  2. 20135337朱荟潼 Linux第七周学习总结——可执行程序的装载

    朱荟潼 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 第七周 Linu ...

  3. Visual Studio的安装应用及单元测试

    新建项目—Visual C#—类库 一.Visual Studio的安装 这可能是自己安装软件用的的最长时间的一次 ..刚下载完安装的时候灰常的尴尬,因为2013版本和2015的版本都是不支持在win ...

  4. DHCP介绍

    DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)是一个局域网的网络协议,使用UDP协议工作, 主要有两个用途:给内部网络或网络服务供应商自动分配IP ...

  5. Visual Studio(VS)C++单元测试

    版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:Visual Studio(VS)C++单元测试     本文地址:http://techie ...

  6. Docker(二十六)-Docker Compose编排容器

    1. 前言 Docker Compose 是 Docker 容器进行编排的工具,定义和运行多容器的应用,可以一条命令启动多个容器. 使用Compose 基本上分为三步: Dockerfile 定义应用 ...

  7. Zoom 会议系统

    Jfrog的培训过程中 发现ppt的效果很不理想  讲师使用zoom的方式效果很好 首先说一下 zoom的定价体系 官网信息: https://www.zoom.us/profile 好像必须使用 企 ...

  8. Shell命令——文件目录

    Linux只有一个文件系统树,不同的硬件设备可以挂载在不同目录下. 文件或目录有两种表示方式:  - 绝对路径:从根目录”/”开始  - 相对路径:从工作目录开始,使用”..”指向父目录,”.”指向当 ...

  9. HDU 1698 Just a Hook (线段树区间更新入门题)

    Just a Hook Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  10. MT【201】折线计数

    甲乙两人参加竞选,结果甲得n票,乙得m票(n > m) . 则在唱票过程中,甲的累计票数始终超过乙的累计票数的概率是_____________. 答案:$\dfrac{n-m}{n+m}$