花指令简析
几种花指令简析
- 一、花指令1
- (一)反汇编代码比对与分析
- 总结经验:
- (二)去花
- 1.反汇编器中手动改字节
- 2.IDC去花
- 3. 010 Editor改PE文件
- 二、花指令2
- (一)反汇编代码比对与分析
- 1、call指令
- (1)call + label
- (2)call far ptr + label
- (3)call + reg16
- (4)call word ptr + 内存单元地址
- (5)call dword ptr + 内存单元地址
- 2、ret指令
- (二)去花
- 1.反汇编器手动改字节
- 2.IDC去花
- 3. 010 Editor改PE文件
- 三、花指令3
- (一)反汇编代码比对与分析
- (二)去花
- 1.反汇编器手动改字节
- 2.IDC去花
- 3. 010 Editor改PE文件
- 四、花指令4
- 五、花指令5
- 六、花指令6
- (一)反汇编代码比对与分析
- (二)去花
- 1.反汇编器手动改字节
- 2.IDC去花
- 3. 010 Editor改PE文件
- 七、花指令7
- 八、花指令8(配合裸函数)
- (一)反汇编代码比对与分析
- 裸函数
- (1)无参数无返回值的函数框架
- (2)有参数有返回值的函数框架
- (3)带局部变量的函数框架
- (二)去花
- 1.反汇编器手动改字节
- 2.IDC去花
- 3.010 Editor修改
本篇博客我们将给用下面源代码编译出的程序加不同的花指令来分析,并学习如何去除花指令
//实现求n的m次方 #include<stdio.h> #include<windows.h> int main() {MessageBox(NULL, "未加花指令", "YQC", 0);int n, m, result=1;printf("请输入n和m:\n");scanf_s("%d%d", &n, &m);for (int i = 0; i < m; i++)result *= n;printf("结果为%d", result);return 0; }
附
IDA
静态反汇编出的汇编.text:0045E3A0 ; int __cdecl main_0(int argc, const char **argv, const char **envp) .text:0045E3A0 _main_0 proc near ; CODE XREF: _main↑j .text:0045E3A0 .text:0045E3A0 var_F4 = byte ptr -0F4h .text:0045E3A0 var_30 = dword ptr -30h .text:0045E3A0 var_24 = dword ptr -24h .text:0045E3A0 var_18 = dword ptr -18h .text:0045E3A0 var_C = dword ptr -0Ch .text:0045E3A0 var_4 = dword ptr -4 .text:0045E3A0 argc = dword ptr 8 .text:0045E3A0 argv = dword ptr 0Ch .text:0045E3A0 envp = dword ptr 10h .text:0045E3A0 .text:0045E3A0 push ebp .text:0045E3A1 mov ebp, esp .text:0045E3A3 sub esp, 0F4h .text:0045E3A9 push ebx .text:0045E3AA push esi .text:0045E3AB push edi .text:0045E3AC lea edi, [ebp+var_F4] .text:0045E3B2 mov ecx, 3Dh .text:0045E3B7 mov eax, 0CCCCCCCCh .text:0045E3BC rep stosd .text:0045E3BE mov eax, ___security_cookie .text:0045E3C3 xor eax, ebp .text:0045E3C5 mov [ebp+var_4], eax .text:0045E3C8 mov ecx, offset unk_53B00F .text:0045E3CD call j_@__CheckForDebuggerJustMyCode@4 ; __CheckForDebuggerJustMyCode(x) .text:0045E3D2 mov esi, esp .text:0045E3D4 push 0 ; uType .text:0045E3D6 push offset Caption ; "YQC" .text:0045E3DB push offset Text ; "未加花指令" .text:0045E3E0 push 0 ; hWnd .text:0045E3E2 call ds:MessageBoxA .text:0045E3E8 cmp esi, esp .text:0045E3EA call j___RTC_CheckEsp .text:0045E3EF mov [ebp+var_24], 1 .text:0045E3F6 push offset aNM ; "请输入n和m:\n" .text:0045E3FB call sub_45748D .text:0045E400 add esp, 4 .text:0045E403 lea eax, [ebp+var_18] .text:0045E406 push eax .text:0045E407 lea ecx, [ebp+var_C] .text:0045E40A push ecx .text:0045E40B push offset aDD ; "%d%d" .text:0045E410 call sub_4584E6 .text:0045E415 add esp, 0Ch .text:0045E418 mov [ebp+var_30], 0 .text:0045E41F jmp short loc_45E42A .text:0045E421 ; --------------------------------------------------------------------------- .text:0045E421 .text:0045E421 loc_45E421: ; CODE XREF: _main_0+9C↓j .text:0045E421 mov eax, [ebp+var_30] .text:0045E424 add eax, 1 .text:0045E427 mov [ebp+var_30], eax .text:0045E42A .text:0045E42A loc_45E42A: ; CODE XREF: _main_0+7F↑j .text:0045E42A mov eax, [ebp+var_30] .text:0045E42D cmp eax, [ebp+var_18] .text:0045E430 jge short loc_45E43E .text:0045E432 mov eax, [ebp+var_24] .text:0045E435 imul eax, [ebp+var_C] .text:0045E439 mov [ebp+var_24], eax .text:0045E43C jmp short loc_45E421 .text:0045E43E ; --------------------------------------------------------------------------- .text:0045E43E .text:0045E43E loc_45E43E: ; CODE XREF: _main_0+90↑j .text:0045E43E mov eax, [ebp+var_24] .text:0045E441 push eax .text:0045E442 push offset aD ; "\n%d" .text:0045E447 call sub_45748D .text:0045E44C add esp, 8 .text:0045E44F xor eax, eax .text:0045E451 push edx .text:0045E452 mov ecx, ebp ; Esp .text:0045E454 push eax .text:0045E455 lea edx, Fd ; Fd .text:0045E45B call j_@_RTC_CheckStackVars@8 ; _RTC_CheckStackVars(x,x) .text:0045E460 pop eax .text:0045E461 pop edx .text:0045E462 pop edi .text:0045E463 pop esi .text:0045E464 pop ebx .text:0045E465 mov ecx, [ebp+var_4] .text:0045E468 xor ecx, ebp ; StackCookie .text:0045E46A call j_@__security_check_cookie@4 ; __security_check_cookie(x) .text:0045E46F add esp, 0F4h .text:0045E475 cmp ebp, esp .text:0045E477 call j___RTC_CheckEsp .text:0045E47C mov esp, ebp .text:0045E47E pop ebp .text:0045E47F retn .text:0045E47F _main_0 endp
一、花指令1
__asm {xor eax, eaxtest eax, eax //产生确定标志位ZFje LABEL1jne LABEL2 //je和jnz指令形成互补跳转LABEL2 :_emit 0x5e //与pop si机器码相同and eax, ebx_emit 0x50 //与push ax机器码相同xor eax, ebx_emit 0x74 //与汇编助记符 jz 机器码相同add eax, edxLABEL1 :}
加花指令1后的程序源码
#include<stdio.h>
#include<windows.h>
int a = 0;
int main()
{MessageBox(NULL, "加花指令1", "YQC", 0);//MessageBox(NULL, "未加花指令", "YQC", 0);int n, m, result=1;printf("请输入n和m:\n");scanf_s("%d%d", &n, &m);__asm {xor eax, eaxtest eax, eaxje LABEL1jne LABEL2LABEL2 :_emit 0x5e //与pop si机器码相同and eax, ebx_emit 0x50 //与push ax机器码相同xor eax, ebx_emit 0x74 //与汇编助记符 jz 机器码相同add eax, edxLABEL1 :}for (int i = 0; i < m; i++)result *= n;printf("结果为%d", result);return 0;
}
(一)反汇编代码比对与分析
下面给出用IDA
静态分析加花的程序
的主函数部分生成的反汇编代码,此时因为花指令,main函数已经不能F5了。
.text:0045E3A0 ; int __cdecl main_0(int argc, const char **argv, const char **envp)
.text:0045E3A0 _main_0: ; CODE XREF: _main↑j
.text:0045E3A0 push ebp
.text:0045E3A1 mov ebp, esp
.text:0045E3A3 sub esp, 0F4h
.text:0045E3A9 push ebx
.text:0045E3AA push esi
.text:0045E3AB push edi
.text:0045E3AC lea edi, [ebp-0F4h]
.text:0045E3B2 mov ecx, 3Dh ; '='
.text:0045E3B7 mov eax, 0CCCCCCCCh
.text:0045E3BC rep stosd
.text:0045E3BE mov eax, ___security_cookie
.text:0045E3C3 xor eax, ebp
.text:0045E3C5 mov [ebp-4], eax
.text:0045E3C8 mov esi, esp
.text:0045E3CA push 0
.text:0045E3CC push offset unk_510E50
.text:0045E3D1 push offset a1 ; "加花指令1"
.text:0045E3D6 push 0
.text:0045E3D8 call ds:MessageBoxA
.text:0045E3DE cmp esi, esp
.text:0045E3E0 call j___RTC_CheckEsp
.text:0045E3E5 mov dword ptr [ebp-24h], 1
.text:0045E3EC push offset aNM ; "请输入n和m:\n"
.text:0045E3F1 call sub_45748D
.text:0045E3F6 add esp, 4
.text:0045E3F9 lea eax, [ebp-18h]
.text:0045E3FC push eax
.text:0045E3FD lea ecx, [ebp-0Ch]
.text:0045E400 push ecx
.text:0045E401 push offset aDD ; "%d%d"
.text:0045E406 call sub_4584E6
.text:0045E40B add esp, 0Ch
.text:0045E40E xor eax, eax
.text:0045E410 test eax, eax
.text:0045E412 jz short near ptr locret_45E41E+1
.text:0045E414 jnz short $+2
.text:0045E416
.text:0045E416 loc_45E416: ; CODE XREF: .text:0045E414↑j
.text:0045E416 pop esi
.text:0045E417 and eax, ebx
.text:0045E419 push eax
.text:0045E41A xor eax, ebx
.text:0045E41C jz short loc_45E421
.text:0045E41E
.text:0045E41E locret_45E41E: ; CODE XREF: .text:0045E412↑j
.text:0045E41E retn 45C7h
.text:0045E421 ; ---------------------------------------------------------------------------
.text:0045E421
.text:0045E421 loc_45E421: ; CODE XREF: .text:0045E41C↑j
.text:0045E421 rol byte ptr [eax], 1
.text:0045E421 ; ---------------------------------------------------------------------------
.text:0045E423 db 0
.text:0045E424 dd 9EB0000h, 83D0458Bh, 458901C0h, 0D0458BD0h, 7DE8453Bh
.text:0045E424 dd 0DC458B0Ch, 0F445AF0Fh, 0EBDC4589h, 0DC458BE3h
.text:0045E448 ; ---------------------------------------------------------------------------
.text:0045E448 push eax
.text:0045E449 push offset aD_0 ; "结果为%d"
.text:0045E44E call sub_45748D
.text:0045E453 add esp, 8
.text:0045E456 xor eax, eax
.text:0045E458 push edx
.text:0045E459 mov ecx, ebp
.text:0045E45B push eax
.text:0045E45C lea edx, dword_45E488
.text:0045E462 call j_@_RTC_CheckStackVars@8 ; _RTC_CheckStackVars(x,x)
.text:0045E467 pop eax
.text:0045E468 pop edx
.text:0045E469 pop edi
.text:0045E46A pop esi
.text:0045E46B pop ebx
.text:0045E46C mov ecx, [ebp-4]
.text:0045E46F xor ecx, ebp
.text:0045E471 call j_@__security_check_cookie@4 ; __security_check_cookie(x)
.text:0045E476 add esp, 0F4h
.text:0045E47C cmp ebp, esp
.text:0045E47E call j___RTC_CheckEsp
.text:0045E483 mov esp, ebp
.text:0045E485 pop ebp
.text:0045E486 retn
对比一下不同的区域,很明显看出加了花指令1
的程序因为花指令的存在产生了未被反汇编出的部分
细看可以注意到,下图三个框框中是源码中没有加入的花指令,只是加了对应的机器码,显然ida把那几个机器码当作汇编指令来翻译了(0x5e
与pop si
机器码相同,0x50
与push ax
机器码相同,0x74
与汇编助记符 jz
机器码相同),
但程序运行过程中是不会受到影响的,这是因为有下面指令的存在,仔细看下面注释你能发现程序运行这个花指令的时候产生确定标志位(ZF
),进而出现确定跳转(必定执行je LABEL1
跳转指令 ),所以肯定是会绕过干扰代码
的,
xor eax, eax //eax寄存器置零test eax, eax //ZF=1,是确定标志位【test不会将结果放在寄存器上,它只影响ZF的状态,如果EAX == 0,那么ZF = 1】je LABEL1 //ZF=1时触发跳转到正常指令处LABEL1 jne LABEL2 //ZF=0时触发跳转到干扰项处LABEL2,与上面的指令形成互补跳转
LABEL2 :/*干扰项所在*/
LABEL1:/*正常代码*/
总结经验:
现在许多的花指令采用生成确定标志位并搭配两个互补的条件跳转指令替代一个强制跳转指令(如
jmp
)以增加汇编难度的方式。
其实如果一个程序出现下面互补跳转代码,而且跳转代码前某一个标志位一定是确定值,同时指令下方还出现报错的情况,基本可以断定是花指令(可以作为花指令起始或者存在的标志,是充分不必要条件
)。
而且运行过程中必然不发生跳转的那一个指令的目标地址或者标号是干扰项。xor eax, eax test eax, eax //产生确定标志位je LABEL1 jne LABEL2 LABEL2 :/*干扰项所在*/ LABEL1:/*正常代码*/
那么回到当前花指令分析,这个花指令最主要起作用的地方是让IDA将机器码0x74
翻译成了jz
指令,致使这个指令的下一个本该翻译成指令(即add eax, edx)的机器码却被翻译成了跳转的地址,进而出现了后面的指令不正常反汇编的情况。
(二)去花
以IDA为例(OD也可),
既然这个花指令的关键是那个0x74
字节码,那么只要从那个字节码下手,使之无效(改成nop)即可,至于花指令中另外两个字节码0x5e
和0x50
,它们并未影响ida的反汇编,这里可以不处理。
去花操作这里给出三种方法:
1.反汇编器中手动改字节
光标移到jz short loc_45E421
指令处,按下图所示修改字节码
直接把jz
对应的字节码0x74
改成0x90
,即nop
助记符,点击确认
然后按D
把nop
指令下面的不正常指令变成数据,
然后按C
强制把数据反汇编成代码
对比后效果如下。
要想F5的话,还差一步,需要手动申明成函数,直接从ret
指令到main
函数开头之间的代码部分选中,按P
一键生成函数。
F5效果如下,去花成功
2.IDC去花
针对花指令较多的情况,建议采用idc脚本去花,
先获取jz short loc_45E421
的字节码如下图,我们只要取74 03
即可,因为0x74
代表jz助记符,而add reg16,reg16
的字节码刚好为 0x03
,对应了add eax,eax
根据idc的API
和语法写出如下idc脚本
#include<idc.idc>
static main()
{auto StartVa, StopVa, Size, i;StartVa=0x0045E3A0;StopVa=0x0045E486;Size=StopVa-StartVa;for (i=0; i<Size; i++){if (Byte(StartVa)==0x74){if(Byte(StartVa+1)==0x03){PatchByte(StartVa, 0x90);MakeCode(StartVa);StartVa++;Message("Find FakeJmp Opcode!!\n");continue;}}StartVa++;}Message("Clear FakeJmp Opcode Ok\n");
}
按下图所示运行一下,或者放到txt文件里面改后缀为.idc
,然后file->Script file
导入该文件
修改成功
但不足的是这个脚本没有实现自动反汇编成汇编代码,运行了该脚本之后依旧有一些没有反汇编成正常汇编代码的部分。依旧需要手动改成数据后再改成代码,最后申明函数
(参考第一种方法——IDA中手动改字节)
3. 010 Editor改PE文件
上面两种方法都是在反汇编好的基础上做修改,其实还可以直接用16进制编辑器,比如010 Editor
,直接改字节码,改完后再反汇编,就省得再手动操作。
以010 Editor
为例,查找字节码74 03 C2 C7
(查74 03
可能有多个结果)。
如下:
ctrl+s
保存一下,再放到ida,现在已经可以F5了。
二、花指令2
__asm {push eax;xor eax, eax;test eax, eax;jnz LABEL1;jz LABEL2;LABEL1:_emit 0xE8; //与call助记符的机器码相同LABEL2:mov byte ptr[a], 0;call LABEL3;_emit 0xFF; //与adc助记符的字节码相同LABEL3:add dword ptr ss : [esp], 8;ret;__emit 0x11;mov byte ptr[a], 2;pop eax;}
加花指令2后的程序源码
//实现求n^{m}
#include<stdio.h>
#include<windows.h>
int a = 0;
int main()
{MessageBox(NULL, "加花指令2", "YQC", 0);//MessageBox(NULL, "未加花指令", "YQC", 0);int n, m, result=1;printf("请输入n和m:\n");scanf_s("%d%d", &n, &m);__asm {push eax;xor eax, eax;test eax, eax;jnz LABEL1;jz LABEL2;LABEL1:_emit 0xE8;LABEL2:mov byte ptr[a], 0;call LABEL3;_emit 0xFF;LABEL3:add dword ptr ss : [esp], 8;ret;__emit 0x11;mov byte ptr[a], 2;pop eax;}for (int i = 0; i < m; i++)result *= n;printf("结果为%d", result);return 0;
}
(一)反汇编代码比对与分析
给出用IDA
静态分析加花的程序
的主函数部分而生成的反汇编代码,
.text:0045E3A0 ; int __cdecl main_0(int argc, const char **argv, const char **envp)
.text:0045E3A0 _main_0: ; CODE XREF: _main↑j
.text:0045E3A0 push ebp
.text:0045E3A1 mov ebp, esp
.text:0045E3A3 sub esp, 0F4h
.text:0045E3A9 push ebx
.text:0045E3AA push esi
.text:0045E3AB push edi
.text:0045E3AC lea edi, [ebp-0F4h]
.text:0045E3B2 mov ecx, 3Dh ; '='
.text:0045E3B7 mov eax, 0CCCCCCCCh
.text:0045E3BC rep stosd
.text:0045E3BE mov eax, ___security_cookie
.text:0045E3C3 xor eax, ebp
.text:0045E3C5 mov [ebp-4], eax
.text:0045E3C8 mov esi, esp
.text:0045E3CA push 0
.text:0045E3CC push offset unk_510E50
.text:0045E3D1 push offset a2 ; "加花指令2"
.text:0045E3D6 push 0
.text:0045E3D8 call ds:MessageBoxA
.text:0045E3DE cmp esi, esp
.text:0045E3E0 call j___RTC_CheckEsp
.text:0045E3E5 mov dword ptr [ebp-24h], 1
.text:0045E3EC push offset aNM ; "请输入n和m:\n"
.text:0045E3F1 call sub_45748D
.text:0045E3F6 add esp, 4
.text:0045E3F9 lea eax, [ebp-18h]
.text:0045E3FC push eax
.text:0045E3FD lea ecx, [ebp-0Ch]
.text:0045E400 push ecx
.text:0045E401 push offset aDD ; "%d%d"
.text:0045E406 call sub_4584E6
.text:0045E40B add esp, 0Ch
.text:0045E40E push eax
.text:0045E40F xor eax, eax
.text:0045E411 test eax, eax
.text:0045E413 jnz short loc_45E417
.text:0045E415 jz short near ptr loc_45E417+1
.text:0045E417
.text:0045E417 loc_45E417: ; CODE XREF: .text:0045E413↑j
.text:0045E417 ; .text:0045E415↑j
.text:0045E417 call near ptr 7E81E9E2h
.text:0045E41C push ebx
.text:0045E41C ; ---------------------------------------------------------------------------
.text:0045E41D db 2 dup(0), 0E8h
.text:0045E420 dd 1, 48336FFh, 11C30824h
.text:0045E42C ; ---------------------------------------------------------------------------
.text:0045E42C mov byte_537E3C, 2
.text:0045E433 pop eax
.text:0045E434 mov dword ptr [ebp-30h], 0
.text:0045E43B jmp short loc_45E446
.text:0045E43D ; ---------------------------------------------------------------------------
.text:0045E43D
.text:0045E43D loc_45E43D: ; CODE XREF: .text:0045E458↓j
.text:0045E43D mov eax, [ebp-30h]
.text:0045E440 add eax, 1
.text:0045E443 mov [ebp-30h], eax
.text:0045E446
.text:0045E446 loc_45E446: ; CODE XREF: .text:0045E43B↑j
.text:0045E446 mov eax, [ebp-30h]
.text:0045E449 cmp eax, [ebp-18h]
.text:0045E44C jge short loc_45E45A
.text:0045E44E mov eax, [ebp-24h]
.text:0045E451 imul eax, [ebp-0Ch]
.text:0045E455 mov [ebp-24h], eax
.text:0045E458 jmp short loc_45E43D
.text:0045E45A ; ---------------------------------------------------------------------------
.text:0045E45A
.text:0045E45A loc_45E45A: ; CODE XREF: .text:0045E44C↑j
.text:0045E45A mov eax, [ebp-24h]
.text:0045E45D push eax
.text:0045E45E push offset aD_0 ; "结果为%d"
.text:0045E463 call sub_45748D
.text:0045E468 add esp, 8
.text:0045E46B xor eax, eax
.text:0045E46D push edx
.text:0045E46E mov ecx, ebp
.text:0045E470 push eax
.text:0045E471 lea edx, dword_45E49C
.text:0045E477 call j_@_RTC_CheckStackVars@8 ; _RTC_CheckStackVars(x,x)
.text:0045E47C pop eax
.text:0045E47D pop edx
.text:0045E47E pop edi
.text:0045E47F pop esi
.text:0045E480 pop ebx
.text:0045E481 mov ecx, [ebp-4]
.text:0045E484 xor ecx, ebp
.text:0045E486 call j_@__security_check_cookie@4 ; __security_check_cookie(x)
.text:0045E48B add esp, 0F4h
.text:0045E491 cmp ebp, esp
.text:0045E493 call j___RTC_CheckEsp
.text:0045E498 mov esp, ebp
.text:0045E49A pop ebp
.text:0045E49B retn
比对一下两者前后的区别
分析这类汇编,需要先了解一下一些汇编转移指令call
和ret
的原理
1、call指令
(1)call + label
这个指令是先将call + 标号的下一条语句的IP放入栈中,然后使当前的IP+16位位移,相当于
push IP jmp near ptr 标号
(2)call far ptr + label
这个指令是先将call指令的下一个指令的代码段地址入栈,再把call下一条指令的偏移地址入栈,然后使其跳到标号所在处,相当于
push CS push IP jmp far ptr 标号
(3)call + reg16
这个指令先将call的下一条指令的IP入栈,然后再以寄存器的值为IP的代码处
push IP jmp reg16
(4)call word ptr + 内存单元地址
这个指令的是先将call指令的下一条指令的IP入栈,然后跳到以内存单元地址为IP的代码处
push IP jum word ptr 内存单元地址
(5)call dword ptr + 内存单元地址
这个指令先将call指令的下一条指令的CS入栈,再将call指令的下一条指令的IP入栈,然后跳到以内存单元的高位为CS,低位为IP的代码处
push CS push IP jmp dword ptr 内存单元地址
2、ret指令
与上面的call指令对应把由call指令入栈的地址数据都出栈给(CS和)IP,相当于
(pop CS) pop IP
现在分析这个花指令,
LABEL1
标号的是干扰项,跟1
号花指令效果一样,不再赘述。
直接从LABEL2
开始分析,调用了函数LABEL3
,把_emit 0xFF;
的指令地址入栈(push IP
),此时栈顶指针sp
指向的是call
指令的下一条指令的地址,即0xFF
这个干扰项的地址,但执行add dword ptr ss : [esp], 8;
给这个地址值加了8
,指向了mov byte ptr[a], 2
的地址,巧妙地修改了这个地址,所以接下来的ret指令
将该指令的地址出栈到IP
寄存器(pop IP
),以至于在调用完LABEL3
函数之后,下一个指令变成了mov byte ptr[a], 2
,这样程序运行不会受到干扰项的影响。
__asm {push eax;xor eax, eax;test eax, eax;jnz LABEL1;jz LABEL2;LABEL1:_emit 0xE8; //与call助记符的字节码相同LABEL2:mov byte ptr[a], 0;call LABEL3; //相当于push IP;jmp near ptr LABEL3_emit 0xFF; //干扰项,与adc助记符的字节码相同LABEL3:add dword ptr ss : [esp], 8; //sp寄存器(栈顶寄存器)的值(IP)自增8ret;__emit 0x11; //干扰项mov byte ptr[a], 2;pop eax;}
(二)去花
在分析1号花指令的时候,我们得出结论:就这个程序而言,下面的代码基本可以看作是花指令存在的标志。
xor eax, eax
test eax, eax
je LABEL1
jne LABEL2
但要为了去花,需要确定花指令的具体位置,就当前这个花指令而言,我们注意到有一个push eax
,然后紧跟着垃圾指令,所以推断最后势必有个pop eax
,所以完全可以以push eax
(0x50)为起始,pop eax
(0x58)为中止,中间的代码全部nop,从而达到去花的目的。
1.反汇编器手动改字节
反汇编出如下情形,基于以上思想,可以在IDA中patch byte如下:
直接把push eax
(0x50)到pop eax
(0x58)都pop即可
申明成函数后,一键F5反编译成功。
2.IDC去花
确定nop的范围(以push eax
(0x50)为起始,pop eax
(0x58)为中止),运行下面脚本就可以去花了。
#include <idc.idc>
/*匹配字符串的函数*/
static matchBytes(StartAddr, Match)
{ auto Len, i, PatSub, SrcSub; Len = strlen(Match);while (i < Len) { PatSub = substr(Match, i, i+1); SrcSub = form("%02X", Byte(StartAddr)); SrcSub = substr(SrcSub, i % 2, (i % 2) + 1); if (PatSub != "?" && PatSub != SrcSub) //以问号作为匹配函数中止条件return 0; if (i % 2 == 1) StartAddr++; i++; }return 1;
}
static main()
{ auto Addr, Start, End, Condition, i;Start = 0x45E3A0; //起始地址End = 0x45E49B; //中止地址Condition = "5033C085C07502????"; //目标字符串for (Addr = Start; Addr < End; Addr++) //遍历区域内字节码{ if (matchBytes(Addr, Condition)) { Message("Find FakeJmp Opcode!!\n"); for (i = 1; Byte(Addr+i)!=0x58; i++) //出现pop eax则停止patch{PatchByte(Addr+i, 0x90); //nop填充MakeCode(Addr+i); //反汇编转代码} } }AnalyzeArea(Start, End); Message("Clear FakeJmp Opcode Ok ");
}
如下图所示,最后还要再申明一下函数就可以F5。
3. 010 Editor改PE文件
关于16进制编辑器的修改操作也是直接把那部分花指令的区域全部nop,这边不再赘述。
三、花指令3
__asm {push ebx;xor ebx, ebx; test ebx, ebx;jnz LABEL5;jz LABEL6;LABEL5:_emit 0x21; //与and助记符的机器码相同LABEL6:pop ebx;}
加花指令3后的程序源码
//实现求n^{m}
#include<stdio.h>
#include<windows.h>
int a = 0;
int main()
{MessageBox(NULL, "加花指令3", "YQC", 0);//MessageBox(NULL, "未加花指令", "YQC", 0);int n, m, result=1;printf("请输入n和m:\n");scanf_s("%d%d", &n, &m);__asm {push ebx;xor ebx, ebx;test ebx, ebx;jnz LABEL5;jz LABEL6;LABEL5:_emit 0x21;LABEL6:pop ebx;}for (int i = 0; i < m; i++)result *= n;printf("结果为%d", result);return 0;
}
(一)反汇编代码比对与分析
同样给出用IDA
静态分析加花的程序
的主函数部分而生成的反汇编代码。
.text:0045E3A0 ; int __cdecl main_0(int argc, const char **argv, const char **envp)
.text:0045E3A0 _main_0: ; CODE XREF: _main↑j
.text:0045E3A0 push ebp
.text:0045E3A1 mov ebp, esp
.text:0045E3A3 sub esp, 0F4h
.text:0045E3A9 push ebx
.text:0045E3AA push esi
.text:0045E3AB push edi
.text:0045E3AC lea edi, [ebp-0F4h]
.text:0045E3B2 mov ecx, 3Dh ; '='
.text:0045E3B7 mov eax, 0CCCCCCCCh
.text:0045E3BC rep stosd
.text:0045E3BE mov eax, ___security_cookie
.text:0045E3C3 xor eax, ebp
.text:0045E3C5 mov [ebp-4], eax
.text:0045E3C8 mov esi, esp
.text:0045E3CA push 0
.text:0045E3CC push offset unk_510E50
.text:0045E3D1 push offset a3 ; "加花指令3"
.text:0045E3D6 push 0
.text:0045E3D8 call ds:MessageBoxA
.text:0045E3DE cmp esi, esp
.text:0045E3E0 call j___RTC_CheckEsp
.text:0045E3E5 mov dword ptr [ebp-24h], 1
.text:0045E3EC push offset aNM ; "请输入n和m:\n"
.text:0045E3F1 call sub_45748D
.text:0045E3F6 add esp, 4
.text:0045E3F9 lea eax, [ebp-18h]
.text:0045E3FC push eax
.text:0045E3FD lea ecx, [ebp-0Ch]
.text:0045E400 push ecx
.text:0045E401 push offset aDD ; "%d%d"
.text:0045E406 call sub_4584E6
.text:0045E40B add esp, 0Ch
.text:0045E40E push ebx
.text:0045E40F xor ebx, ebx
.text:0045E411 test ebx, ebx
.text:0045E413 jnz short loc_45E417
.text:0045E415 jz short near ptr loc_45E417+1
.text:0045E417
.text:0045E417 loc_45E417: ; CODE XREF: .text:0045E413↑j
.text:0045E417 ; .text:0045E415↑j
.text:0045E417 and [ebx-39h], ebx
.text:0045E41A inc ebp
.text:0045E41B rol byte ptr [eax], 1
.text:0045E41B ; ---------------------------------------------------------------------------
.text:0045E41D db 3 dup(0)
.text:0045E420 ; ---------------------------------------------------------------------------
.text:0045E420 jmp short loc_45E42B
.text:0045E422 ; ---------------------------------------------------------------------------
.text:0045E422
.text:0045E422 loc_45E422: ; CODE XREF: .text:0045E43D↓j
.text:0045E422 mov eax, [ebp-30h]
.text:0045E425 add eax, 1
.text:0045E428 mov [ebp-30h], eax
.text:0045E42B
.text:0045E42B loc_45E42B: ; CODE XREF: .text:0045E420↑j
.text:0045E42B mov eax, [ebp-30h]
.text:0045E42E cmp eax, [ebp-18h]
.text:0045E431 jge short loc_45E43F
.text:0045E433 mov eax, [ebp-24h]
.text:0045E436 imul eax, [ebp-0Ch]
.text:0045E43A mov [ebp-24h], eax
.text:0045E43D jmp short loc_45E422
.text:0045E43F ; ---------------------------------------------------------------------------
.text:0045E43F
.text:0045E43F loc_45E43F: ; CODE XREF: .text:0045E431↑j
.text:0045E43F mov eax, [ebp-24h]
.text:0045E442 push eax
.text:0045E443 push offset aD_0 ; "结果为%d"
.text:0045E448 call sub_45748D
.text:0045E44D add esp, 8
.text:0045E450 xor eax, eax
.text:0045E452 push edx
.text:0045E453 mov ecx, ebp
.text:0045E455 push eax
.text:0045E456 lea edx, dword_45E484
.text:0045E45C call j_@_RTC_CheckStackVars@8 ; _RTC_CheckStackVars(x,x)
.text:0045E461 pop eax
.text:0045E462 pop edx
.text:0045E463 pop edi
.text:0045E464 pop esi
.text:0045E465 pop ebx
.text:0045E466 mov ecx, [ebp-4]
.text:0045E469 xor ecx, ebp
.text:0045E46B call j_@__security_check_cookie@4 ; __security_check_cookie(x)
.text:0045E470 add esp, 0F4h
.text:0045E476 cmp ebp, esp
.text:0045E478 call j___RTC_CheckEsp
.text:0045E47D mov esp, ebp
.text:0045E47F pop ebp
.text:0045E480 retn
给出用IDA
静态分析加花的程序
的主函数部分而生成的反汇编代码,
0x21
对应操作码是and
与操作,所以被ida当成了指令被反汇编成了指令and [ebx-39h], ebx
,原理跟1号花指令异曲同工,所以直接进入去花阶段。
(二)去花
1.反汇编器手动改字节
再把后面的指令转化成数据再转化成代码,整理如下。
申明成函数后就可以F5了。
2.IDC去花
先确定nop的范围,我们注意到代码xor ebx, ebx;test ebx, ebx; jnz short loc_45E417; jz short near ptr loc_45E417+1
前面有个 push ebx
,那就以pop ebx
为中止,中间代码为花指令全部nop。
#include <idc.idc>
static matchBytes(StartAddr, Match)
{ auto Len, i, PatSub, SrcSub; Len = strlen(Match);while (i < Len) { PatSub = substr(Match, i, i+1); SrcSub = form("%02X", Byte(StartAddr)); SrcSub = substr(SrcSub, i % 2, (i % 2) + 1); if (PatSub != "?" && PatSub != SrcSub) return 0; if (i % 2 == 1) StartAddr++; i++; }return 1;
}
static main()
{ auto Addr, Start, End, Condition, junk_len, i;Start = 0x0045E3A0; End = 0x0045E480;//"xor ebx, ebx;test ebx, ebx; jnz short loc_45E417;jz short loc_45E418"的字节码作为识别花指令的标识。Condition = "33DB85DB7502740121????";for (Addr = Start; Addr < End; Addr++) { if (matchBytes(Addr, Condition)) { Message("Find FakeJmp Opcode!!\n");Message(Addr);for (i = 1; Byte(Addr+i)!=0x21; i++);PatchByte(Addr+i, 0x90); MakeCode(Addr+i); } }AnalyzeArea(Start, End); Message("Clear Fake-Jmp Opcode Ok ");
}
3. 010 Editor改PE文件
查找33DB85DB7502740121
,然后改0x21
为0x90即可
四、花指令4
跟3号和1号异曲同工,不再赘述。
void example4()
{__asm {push ebx;xor ebx, ebx;test ebx, ebx;jnz LABEL7;jz LABEL8;LABEL7:_emit 0xC7;LABEL8:pop ebx;}a = 4;
}
五、花指令5
__asm {call LABEL9;_emit 0x83;LABEL9:add dword ptr ss : [esp], 8;ret;__emit 0xF3;}
六、花指令6
LoadLibrary("./hhhh"); //函数返回值存储于eax中_asm {cmp eax, 0;jc LABEL6_1; //cf=1触发跳转jnc LABEL6_2; //cf=0触发跳转LABEL6_1:_emit 0xE8; //与call汇编指令对应LABEL6_2:}
加花指令6后的程序源码
//实现求n^{m}
#include<stdio.h>
#include<windows.h>
int a = 0;
int main()
{MessageBox(NULL, "加花指令6", "YQC", 0);//MessageBox(NULL, "未加花指令", "YQC", 0);int n, m, result=1;printf("请输入n和m:\n");scanf_s("%d%d", &n, &m);LoadLibrary("./hhhh");//函数返回值存储于eax中_asm {cmp eax, 0;jc LABEL6_1;jnc LABEL6_2;LABEL6_1:_emit 0xE8;LABEL6_2:}for (int i = 0; i < m; i++)result *= n;printf("结果为%d", result);return 0;
}
(一)反汇编代码比对与分析
给出用IDA
静态分析加花的程序
的主函数部分而生成的反汇编代码。
.text:0045E3A0 ; int __cdecl main_0(int argc, const char **argv, const char **envp)
.text:0045E3A0 _main_0: ; CODE XREF: _main↑j
.text:0045E3A0 push ebp
.text:0045E3A1 mov ebp, esp
.text:0045E3A3 sub esp, 0F4h
.text:0045E3A9 push ebx
.text:0045E3AA push esi
.text:0045E3AB push edi
.text:0045E3AC lea edi, [ebp-0F4h]
.text:0045E3B2 mov ecx, 3Dh ; '='
.text:0045E3B7 mov eax, 0CCCCCCCCh
.text:0045E3BC rep stosd
.text:0045E3BE mov eax, ___security_cookie
.text:0045E3C3 xor eax, ebp
.text:0045E3C5 mov [ebp-4], eax
.text:0045E3C8 mov esi, esp
.text:0045E3CA push 0
.text:0045E3CC push offset unk_510E50
.text:0045E3D1 push offset a6 ; "加花指令6"
.text:0045E3D6 push 0
.text:0045E3D8 call ds:MessageBoxA
.text:0045E3DE cmp esi, esp
.text:0045E3E0 call j___RTC_CheckEsp
.text:0045E3E5 mov dword ptr [ebp-24h], 1
.text:0045E3EC push offset aNM ; "请输入n和m:\n"
.text:0045E3F1 call sub_45748D
.text:0045E3F6 add esp, 4
.text:0045E3F9 lea eax, [ebp-18h]
.text:0045E3FC push eax
.text:0045E3FD lea ecx, [ebp-0Ch]
.text:0045E400 push ecx
.text:0045E401 push offset aDD ; "%d%d"
.text:0045E406 call sub_4584E6
.text:0045E40B add esp, 0Ch
.text:0045E40E mov esi, esp
.text:0045E410 push offset aHhhh ; "./hhhh"
.text:0045E415 call ds:LoadLibraryA
.text:0045E41B cmp esi, esp
.text:0045E41D call j___RTC_CheckEsp
.text:0045E422 cmp eax, 0
.text:0045E425 jb short loc_45E429
.text:0045E427 jnb short near ptr loc_45E429+1
.text:0045E429
.text:0045E429 loc_45E429: ; CODE XREF: .text:0045E425↑j
.text:0045E429 ; .text:0045E427↑j
.text:0045E429 call near ptr 11629F5h
.text:0045E429 ; ---------------------------------------------------------------------------
.text:0045E42E dw 0
.text:0045E430 dd 8B09EB00h, 0C083D045h, 0D0458901h, 3BD0458Bh, 0C7DE845h
.text:0045E430 dd 0FDC458Bh, 89F445AFh, 0E3EBDC45h, 50DC458Bh
.text:0045E454 ; ---------------------------------------------------------------------------
.text:0045E454 push offset aD_0 ; "结果为%d"
.text:0045E459 call sub_45748D
.text:0045E45E add esp, 8
.text:0045E461 xor eax, eax
.text:0045E463 push edx
.text:0045E464 mov ecx, ebp
.text:0045E466 push eax
.text:0045E467 lea edx, dword_45E494
.text:0045E46D call j_@_RTC_CheckStackVars@8 ; _RTC_CheckStackVars(x,x)
.text:0045E472 pop eax
.text:0045E473 pop edx
.text:0045E474 pop edi
.text:0045E475 pop esi
.text:0045E476 pop ebx
.text:0045E477 mov ecx, [ebp-4]
.text:0045E47A xor ecx, ebp
.text:0045E47C call j_@__security_check_cookie@4 ; __security_check_cookie(x)
.text:0045E481 add esp, 0F4h
.text:0045E487 cmp ebp, esp
.text:0045E489 call j___RTC_CheckEsp
.text:0045E48E mov esp, ebp
.text:0045E490 pop ebp
.text:0045E491 retn
这个花指令属于利用API返回确定值,配合互补跳转语句和添加操作码作为干扰项实现反汇编的效果。
调用函数,返回值是放在eax中的
,这个花指令调用了LoadLibrary
函数,加载了一个实际不存在的名为hhhh
的库,查询API档案
如下,可以看出,这个函数必定返回0
,且存入eax
,也就是说cmp指令
使进位标志寄存器cf=0
,产生了确定值,即确定跳转。
(二)去花
1.反汇编器手动改字节
同样,改0xE8
即可
申明成函数后然后可以F5
2.IDC去花
确定要nop
的范围,直接cmp
指令起始,到0xE8
干扰项结束,即83 F8 00 72 02 73 01 E8
#include <idc.idc>
static matchBytes(StartAddr, Match)
{ auto Len, i, PatSub, SrcSub; Len = strlen(Match);while (i < Len) { PatSub = substr(Match, i, i+1); SrcSub = form("%02X", Byte(StartAddr)); SrcSub = substr(SrcSub, i % 2, (i % 2) + 1); if (PatSub != "?" && PatSub != SrcSub) return 0; if (i % 2 == 1) StartAddr++; i++; }return 1;
}
static main()
{ auto Addr, Start, End, Condition, junk_len, i;Start = 0x0045E3A0; End = 0x0045E491;Condition = "83F80072027301E8????";for (Addr = Start; Addr < End; Addr++) { if (matchBytes(Addr, Condition)) { Message("Find FakeJmp Opcode!!\n");Message(Addr);for (i = 0;Byte(Addr+i)!=0xE8; i++){PatchByte(Addr+i, 0x90); MakeCode(Addr+i); }PatchByte(Addr+i, 0x90); MakeCode(Addr+i); } }AnalyzeArea(Start, End); Message("Clear Fake-Jmp Opcode Ok ");
}
3. 010 Editor改PE文件
先定位到花指令,然后跟ida中一样修改0xE8为0x90,保存后直接ida打开可以反汇编与反编译
七、花指令7
同样,确定标志位产生确定跳转,配合干扰项,不再赘述。
if (a > 0)return 1;elsereturn 0;_asm {cmp eax, 0;jc LABEL7_1;jz LABEL7_2;LABEL7_1:_emit 0xE8;LABEL7_2:}
八、花指令8(配合裸函数)
void __declspec(naked)__cdecl cnuF(int* a)//裸函数,开辟和释放堆栈由我们自己写。
{//55 8b ec 83__asm{/*保留栈底*/push ebp/*开辟栈空间*/mov ebp, espsub esp, 0x40//0x40是缓冲区大小/*保留现场(寄存器状态)*/push ebxpush esipush edi/*缓冲区写入数据*/mov eax, 0xCCCCCCCC //0xCCCC在gb2312中是'烫'字mov ecx, 0x10 //cx为下面填'烫'操作计数lea edi, dword ptr ds : [ebp - 0x40]rep stos dword ptr es : [edi]}/*执行的操作*/*a = 1;//花指令_asm {call LABEL9;_emit 0xE8;_emit 0x01;_emit 0x00;_emit 0x00;_emit 0x00;LABEL9:push eax;push ebx;lea eax, dword ptr ds : [ebp - 0x0]add dword ptr ss : [eax - 0x50], 26;pop eax;pop ebx;pop eax;jmp eax;__emit 0xE8;_emit 0x03;_emit 0x00;_emit 0x00;_emit 0x00;mov eax, dword ptr ss : [esp - 8];}__asm{/*恢复现场*/pop edipop esipop ebx/*释放栈空间*/mov esp, ebppop ebpret}
}
在无函数返回值的裸函数
中加花后的源程序源码如下
//实现求n^{m}
#include<stdio.h>
#include<windows.h>
int a = 0;
void __declspec(naked)__cdecl cnuF(int* a)//裸函数,开辟和释放堆栈由我们自己写。
{//55 8b ec 83__asm{/*保留栈底*/push ebp/*开辟栈空间*/mov ebp, espsub esp, 0x40 //0x40是缓冲区大小/*保留现场(寄存器状态)*/push ebxpush esipush edi/*缓冲区写入数据*/mov eax, 0xCCCCCCCC //0xCCCC在gb2312中是'烫'字mov ecx, 0x10 //cx为下面填'烫'操作计数lea edi, dword ptr ds : [ebp - 0x40]rep stos dword ptr es : [edi] //用烫填充}/*执行的操作*/*a = 1;MessageBox(NULL, "加花指令8", "YQC", 0);//MessageBox(NULL, "未加花指令", "YQC", 0);int n, m, result, i;printf("请输入n和m:\n");scanf_s("%d%d", &n, &m);/*花指令*/_asm{call LABEL9;_emit 0xE8; //垃圾指令_emit 0x01;_emit 0x00;_emit 0x00;_emit 0x00;LABEL9:push eax;push ebx;lea eax, dword ptr ds : [ebp - 0x0]add dword ptr ss : [eax - 0x50], 26;pop eax;pop ebx;pop eax;jmp eax;__emit 0xE8;_emit 0x03;_emit 0x00;_emit 0x00;_emit 0x00;mov eax, dword ptr ss : [esp - 8];}for (result = 1, i = 0; i < m; i++)result *= n;printf("结果为%d", result);__asm{/*恢复现场*/pop edipop esipop ebx/*释放栈空间*/mov esp, ebppop ebpret}}
int main()
{cnuF(&a);return 0;
}
(一)反汇编代码比对与分析
给出用加花的程序
的关键函数部分的反汇编代码。
.text:004613F0 sub_4613F0 proc near ; CODE XREF: sub_45A8EF↑j
.text:004613F0
.text:004613F0 var_14 = byte ptr -14h
.text:004613F0 var_8 = byte ptr -8
.text:004613F0 arg_0 = dword ptr 8
.text:004613F0
.text:004613F0 push ebp
.text:004613F1 mov ebp, esp
.text:004613F3 sub esp, 40h
.text:004613F6 push ebx
.text:004613F7 push esi
.text:004613F8 push edi
.text:004613F9 mov eax, 0CCCCCCCCh
.text:004613FE mov ecx, 10h
.text:00461403 db 3Eh
.text:00461403 lea edi, [ebp-40h]
.text:00461407 rep stosd
.text:00461409 mov eax, [ebp+arg_0]
.text:0046140C mov dword ptr [eax], 1
.text:00461412 mov esi, esp
.text:00461414 push 0 ; uType
.text:00461416 push offset Caption ; "YQC"
.text:0046141B push offset Text ; "加花指令8"
.text:00461420 push 0 ; hWnd
.text:00461422 call ds:MessageBoxA
.text:00461428 cmp esi, esp
.text:0046142A call j___RTC_CheckEsp
.text:0046142F push offset aNM ; "请输入n和m:\n"
.text:00461434 call sub_45748D
.text:00461439 add esp, 4
.text:0046143C lea eax, [ebp+var_14]
.text:0046143F push eax
.text:00461440 lea ecx, [ebp+var_8]
.text:00461443 push ecx
.text:00461444 push offset aDD ; "%d%d"
.text:00461449 call sub_4584E6
.text:0046144E add esp, 0Ch
.text:00461451 call sub_46145B
.text:00461456 call sub_46145C
.text:00461456 sub_4613F0 endp ; sp-analysis failed
.text:00461456
.text:0046145B
.text:0046145B ; =============== S U B R O U T I N E =======================================
.text:0046145B
.text:0046145B
.text:0046145B sub_46145B proc near ; CODE XREF: sub_4613F0+61↑p
.text:0046145B push eax
.text:0046145B sub_46145B endp ; sp-analysis failed
.text:0046145B
.text:0046145C
.text:0046145C ; =============== S U B R O U T I N E =======================================
.text:0046145C
.text:0046145C
.text:0046145C sub_46145C proc near ; CODE XREF: sub_4613F0+66↑p
.text:0046145C push ebx
.text:0046145D db 3Eh
.text:0046145D lea eax, [ebp+0]
.text:00461461 add dword ptr ss:[eax-50h], 1Ah
.text:00461466 pop eax
.text:00461467 pop ebx
.text:00461468 pop eax
.text:00461469 jmp eax
.text:00461469 sub_46145C endp ; sp-analysis failed
.text:00461469
.text:0046146B ; ---------------------------------------------------------------------------
.text:0046146B call loc_461473
.text:0046146B ; ---------------------------------------------------------------------------
.text:00461470 db 36h, 8Bh, 44h
.text:00461473 ; ---------------------------------------------------------------------------
.text:00461473
.text:00461473 loc_461473: ; CODE XREF: .text:0046146B↑j
.text:00461473 and al, 0F8h
.text:00461475 mov dword ptr [ebp-20h], 1
.text:0046147C mov dword ptr [ebp-2Ch], 0
.text:00461483 jmp short loc_46148E
.text:00461485 ; ---------------------------------------------------------------------------
.text:00461485
.text:00461485 loc_461485: ; CODE XREF: .text:004614A0↓j
.text:00461485 mov eax, [ebp-2Ch]
.text:00461488 add eax, 1
.text:0046148B mov [ebp-2Ch], eax
.text:0046148E
.text:0046148E loc_46148E: ; CODE XREF: .text:00461483↑j
.text:0046148E mov eax, [ebp-2Ch]
.text:00461491 cmp eax, [ebp-14h]
.text:00461494 jge short loc_4614A2
.text:00461496 mov eax, [ebp-20h]
.text:00461499 imul eax, [ebp-8]
.text:0046149D mov [ebp-20h], eax
.text:004614A0 jmp short loc_461485
.text:004614A2 ; ---------------------------------------------------------------------------
.text:004614A2
.text:004614A2 loc_4614A2: ; CODE XREF: .text:00461494↑j
.text:004614A2 mov eax, [ebp-20h]
.text:004614A5 push eax
.text:004614A6 push offset aD_0 ; "结果为%d"
.text:004614AB call sub_45748D
.text:004614B0 add esp, 8
.text:004614B3 pop edi
.text:004614B4 pop esi
.text:004614B5 pop ebx
.text:004614B6 mov esp, ebp
.text:004614B8 pop ebp
.text:004614B9 retn
这个程序的花指令是放到裸函数中的方式,先简单了解一下裸函数
裸函数
对于一个裸函数而言,就是编译器不会为这个函数生成代码,如
开辟和释放栈空间
还有ret
,这些指令在裸函数中都需要我们自己写,且最后一定不能缺少ret
指令。
一般在函数名前面加上__deplspec(naked)
,此时这个函数便是裸函数,同时编译器对裸函数也不会进行任何处理。
下面以实现两个传入参数相加的功能为例给出不同裸函数的基本框架(如果对这些指令不是很理解可以参考
堆栈图):(1)无参数无返回值的函数框架
void __declspec(naked) Fun(){__asm{//提升堆栈push ebpmov ebp,espsub ebp,0x40//保护现场push ebxpush esipush edi//向缓冲区填充数据lea edi,dword ptr ds:[ebp-0x40]mov eax,0xCCCCCCCCmov ecx,0x10rep stosd ;rep stos dword ptr es:[edi]//恢复现场pop edipop esipop ebx//降低堆栈mov esp,ebppop ebp//返回函数调用前的下一行地址ret} }
(2)有参数有返回值的函数框架
int __declspec(naked) plus(int x, int y) {__asm{//提升堆栈push ebpmov ebp,espsub esp,0x40//保护现场push ebxpush esipush edi//向缓冲区填充数据lea edi,dword ptr ds:[ebp-0x40]mov eax,0xCCCCCCCCmov ecx,0x10rep stos dword ptr es:[edi]//函数核心功能块mov eax,dword ptr ds:[ebp+0x8]add eax,dword ptr ds:[ebp+0xC]//恢复现场pop edipop esipop ebx//降低堆栈mov esp,ebppop ebp//返回函数调用前的下一行地址ret} }
(3)带局部变量的函数框架
int __declspec(naked) plus(int x, int y) {__asm{//提升堆栈push ebpmov ebp,espsub esp,0x40//保护现场push ebxpush esipush edi//向缓冲区填充数据lea edi,dword ptr ds:[ebp-0x40]mov eax,0xCCCCCCCCmov ecx,0x10rep stos dword ptr es:[edi]//局部变量入栈mov dword ptr ds:[ebp-0x4]mov dword ptr ds:[ebp-0x8]//函数核心功能块mov eax,dword ptr ds:[ebp+0x8]add eax,dword ptr ds:[ebp+0xC]//恢复现场pop edipop esipop ebx//降低堆栈mov esp,ebppop ebp//返回函数调用前的下一行地址ret} }
下面用OD分析花指令,从call LABEL9
处单步执行,注意观察,如下图。
对call入栈的指令地址进行了修改
修改之后变成mov eax, dword ptr ss : [esp - 8];
(在OD中没有反汇编成功)的地址
一共三次出栈操作,第三次是将修改后的指令地址出栈到eax,下面有个jmp无条件跳转语句,程序自动跳到mov eax, dword ptr ss : [esp - 8];
,绕过垃圾指令
(二)去花
1.反汇编器手动改字节
然后再把位于jmp eax
下面的call
语句nop
掉,
这个函数依旧会报红,直接把这个函数全选,然后按u
重新变成数据,最后再c
和p
,变成代码并申明函数即可。
效果如下,
但光这样会出现问题就是因为jmp eax
的存在,反汇编引擎
不知道跳转的位置,所以部分代码会丢失,被分隔开,产生如下情况;
所以还需要把jmp eax
用nop
填充掉,这样就可以F5了
2.IDC去花
确定要nop的范围:从call LABEL9开始到第二个垃圾指令全部nop
#include <idc.idc>
static matchBytes(StartAddr, Match)
{ auto Len, i, PatSub, SrcSub; Len = strlen(Match);while (i < Len) { PatSub = substr(Match, i, i+1); SrcSub = form("%02X", Byte(StartAddr)); SrcSub = substr(SrcSub, i % 2, (i % 2) + 1); if (PatSub != "?" && PatSub != SrcSub) return 0; if (i % 2 == 1) StartAddr++; i++; }return 1;
}
static main()
{ auto Addr, Start, End, Condition, junk_len, i;Start = 0x004613F0; End = 0x004614B9;Condition = "E805000000E801000000????";for (Addr = Start; Addr < End; Addr++) { if (matchBytes(Addr, Condition)) { Message("Find FakeJmp Opcode!!\n");for (i = 0;!matchBytes(Addr+i,"368B44????"); i++){PatchByte(Addr+i, 0x90); MakeCode(Addr+i); }} }AnalyzeArea(Start, End); Message("Clear Fake-Jmp Opcode Ok ");
}
最后再重新反汇编处理一下
3.010 Editor修改
按照反汇编器中修改的方式修改即可