0%

简单反调试和花指令

简单反调试和花指令

前言

最近在看到一些反调试和花指令的东西,学习了一波,记录一下学习。

正文

STARTUPINFO反调试

原理:在程序启动后,会有一个STARTUPINFO的结构体变量,来保存程序启动的信息,通过其中结构体参数的改变来检测程序是正常运行还是在调试器中运行的。

一般情况中,程序启动是通过explorer.exe调用CreateProcess启动的,但是如果是由OD调试程序,则是由OD进程调用CreateProcess启动,不同的启动方式造成STARTUPINFO结构体参数的不同,要实现反调试. 那么需要一个API函数:

GetStartupInfo (STARTUPINFO) 使用此API可以在进程启动的时候获取启动信息结构体

程序通过GetStartupInfo这个API函数获取进程启动时候STARTUPINFO结构体的参数,通过检测参数来判断是否是由调试器启动,如果是调试器启动则终止进程。

STARTUPINFO结构体:

typedef struct _STARTUPINFOA {
DWORD cb;
LPSTR lpReserved;
LPSTR lpDesktop;
LPSTR lpTitle;
DWORD dwX;
DWORD dwY;
DWORD dwXSize;
DWORD dwYSize;
DWORD dwXCountChars;
DWORD dwYCountChars;
DWORD dwFillAttribute;
DWORD dwFlags;
WORD wShowWindow;
WORD cbReserved2;
LPBYTE lpReserved2;
HANDLE hStdInput;
HANDLE hStdOutput;
HANDLE hStdError;
} STARTUPINFOA, *LPSTARTUPINFOA;

利用原理可以写出下面函数:

1
2
3
4
5
6
7
8
9
10
BOOL CheckDebug() {  
STARTUPINFO si;
GetStartupInfo(&si);
if (si.dwFlags!=1 || si.dwX!=0 || si.dwY!=0 || si.dwXSize!=0 || si.dwYSize!=0 || si.dwXCountChars!=0 || si.dwYCountChars!=0 || si.dwFillAttribute!=0) {
return TRUE;
}
else {
return FALSE;
}
}

上面就是利用这个原理写的一个检测函数,为了验证这个函数,实现一个小demo测试一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include<stdio.h>
#include<windows.h>
#include<stdlib.h>

BOOL CheckDebug() {
STARTUPINFO si;
GetStartupInfo(&si);
if (si.dwFlags!=1 || si.dwX!=0 || si.dwY!=0 || si.dwXSize!=0 || si.dwYSize!=0 || si.dwXCountChars!=0 || si.dwYCountChars!=0 || si.dwFillAttribute!=0) {
return TRUE;
}
else {
return FALSE;
}
}

int main() {
BOOL check = CheckDebug();
if (check == TRUE) {
printf("It's a debugger");
exit(0);
}
else {
printf("no debugger");
system("pause");
}
return 0;
}

桌面启动:

BMxlQS.png

od运行:

BQSSgK.png

要反反调试也简单,找到关键语句,在od中修改指令跳过就行

花指令

花指令是程序中的无用代码,它对程序的运行没影响,少了它也能正常运行。加花指令后,对程序的静态反汇编代码就不会正常显示出来,加大了分析难度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

void shell(){
system("cmd");
}
void main(){
int a[5] = {0};
int b;
__asm{
jz label;
jnz label;
_emit 0xE8;
label:
}
scanf("%d", &b);
a[b] = (int)&shell;
return;
}

这里举例一个小demo,可以看到是通过内联汇编的方式在c语言程序中加入汇编代码,这里面的汇编代码不影响程序运行,同时使用_emit向程序代码段中加入0xE8这个字节,也是call的机器码,干扰了ida的判断:

BQoF3j.png

修改起来也容易,只要理解了这段花指令,将0xE8提出来,然后将后面的字节分析成指令

BQogVf.png

可以看到,分析后面的指令:

BQo7q0.png

最后将0xE8这个字节patch成nop:

BQTfw6.png

然后按p将main函数变成ida解析的函数,就可以按F5反汇编

BQOMes.png

总结

这次学习到的两个技巧都是比较简单的反调试手法,主要是为了加强对调试器和静态编译器的理解,反调试的手法是恶意代码首要特征和常用手法,之后在学习更新一些手法。