Hmily 发表于 2008-10-30 02:43

程序的自我效验

标 题: 【原创】程序的自我效验
作 者: ufozhyufo
时 间: 2008-10-28,19:48
链 接: http://bbs.pediy.com/showthread.php?t=75546

编程语言:C,汇编
编译环境:VC++6.0,MASM
建议工具:OD
时间:2008.10.28

程序的自我效验,就是程序自己检测自身的完整性,主要用于抵抗软件的动态调试(如断点),内存补丁,暴力破解等。

本文旨在介绍程序自我效验的基本方法,希望可以起到抛砖引玉的作用。(菜文一篇,高手飘过)

我们先来看一个例子:

#include<windows.h>
#include<stdio.h>

/***********************************************************
自我效验测试程序
***********************************************************/

DWORD Calculation(DWORD StartAddr,DWORD dwCodeLength);
void CheckSelfRelease(DWORD StartAddr,DWORD dwCodeLength);
voidTerminateCurrentProcess();

DWORDCalculation(DWORD StartAddr,DWORD dwCodeLength)
{
_asm
{
xor eax,eax
xor ebx,ebx
mov esi,StartAddr
mov ecx,dwCodeLength
Label001:
mov bl,byte ptr
add eax,ebx
inc esi
loop Label001
}

}

void TerminateCurrentProcess()
{
TerminateProcess(GetCurrentProcess(),0);
}


void CheckSelfRelease(DWORD StartAddr,DWORD dwCodeLength)
{
_asm
{
xor eax,eax
xor ebx,ebx
mov esi,StartAddr
mov ecx,dwCodeLength
Label001:
mov bl,byte ptr
add eax,ebx
inc esi
loop Label001

cmp eax,0x0B59//0x0B59是提前计算好的
je Label002
call TerminateCurrentProcess
Label002:
}
}


void main()
{
DWORD StartAddr=0;
DWORD EndAddr=0;
DWORD dwCodeLength=0;
DWORD dwChecksum=0;


_asm
{
Start:
xor eax,eax
xor ebx,ebx
inc eax
add ebx,eax
add eax,ebx

mov StartAddr,offset Start
mov EndAddr,offset End

End:
}

dwCodeLength=EndAddr-StartAddr;

CheckSelfRelease(StartAddr,dwCodeLength);
MessageBoxA(NULL,"0K!","INFO",MB_OK);

}
上面的程序用累加得到指定程序段的效验和,你完全可以使用其它的方法来计算效验和,
可以用SUB,ROR,ROL 。。。等等。我只是用尽可能简单的过程来描述。

如果内存中程序的指定部分没有被改动,就会输出一MessageBox,否则程序会结束自己。

到这读者可能要问0x0B59是怎样得到的,那就看下面的程序吧:

#include<windows.h>
#include<stdio.h>

DWORD __stdcall Calculation(DWORD StartAddr,DWORD dwCodeLength);//计算效验和
void__stdcall CheckSelf(DWORD StartAddr,DWORD dwCodeLength,DWORD dwChecksum);
void__stdcall TerminateCurrentProcess();

DWORD __stdcall Calculation(DWORD StartAddr,DWORD dwCodeLength)
{
_asm
{
xor eax,eax
xor ebx,ebx
mov esi,StartAddr
mov ecx,dwCodeLength
Label001:
mov bl,byte ptr
add eax,ebx
inc esi
loop Label001
}

}

void __stdcall TerminateCurrentProcess()
{
TerminateProcess(GetCurrentProcess(),0);
}


void __stdcall CheckSelf(DWORD StartAddr,DWORD dwCodeLength,DWORD dwChecksum)
{
_asm
{
xor eax,eax
xor ebx,ebx
mov esi,StartAddr
mov ecx,dwCodeLength
Label001:
mov bl,byte ptr
add eax,ebx
inc esi
loop Label001

cmp eax,dwChecksum
je Label002
call TerminateCurrentProcess
Label002:
}
}

void main()
{
DWORD StartAddr=0;
DWORD EndAddr=0;
DWORD dwCodeLength=0;
DWORD dwChecksum=0;


_asm
{
Start:
xor eax,eax
xor ebx,ebx
inc eax
add ebx,eax
add eax,ebx

mov StartAddr,offset Start
mov EndAddr,offset End

End:
}
dwCodeLength=EndAddr-StartAddr;

dwChecksum=Calculation(StartAddr,dwCodeLength);

CheckSelf(StartAddr,dwCodeLength,dwChecksum);
MessageBoxA(NULL,"OK!","INFO",MB_OK);
}

程序中的Calculation函数用于计算效验和,其他与第一个程序基本相同。0x0B59就是用这中方法得到的。
我们在OD中分析软件的时候,仅仅下了几个断点,按下 F9,程序就结束了,界面没有了,什么也没有了,十有八九是因为软件用了自我效验。

上面的方法只对指定程序段进行了一次效验,很脆弱,可不可以实时监控程序的完整性呢?
答案是肯定的,我的想法是用CreateThread()来创建一子线程,实现实时监控:

#include<windows.h>
#include<stdio.h>

/***********************************************************
自我效验测试程序,用于监视内存中程序的完整性
***********************************************************/

DWORD Calculation(DWORD StartAddr,DWORD dwCodeLength);
voidCheckSelf(DWORD StartAddr,DWORD dwCodeLength,DWORD dwChecksum);
voidTerminateCurrentProcess();

DWORDCalculation(DWORD StartAddr,DWORD dwCodeLength)
{
_asm
{
xor eax,eax
xor ebx,ebx
mov esi,StartAddr
mov ecx,dwCodeLength
Label001:
mov bl,byte ptr
add eax,ebx
inc esi
loop Label001
}

}

void TerminateCurrentProcess()
{
TerminateProcess(GetCurrentProcess(),0);
}


void CheckSelf(DWORD StartAddr,DWORD dwCodeLength,DWORD dwChecksum)
{
_asm
{
xor eax,eax
xor ebx,ebx
mov esi,StartAddr
mov ecx,dwCodeLength
Label001:
mov bl,byte ptr
add eax,ebx
inc esi
loop Label001

cmp eax,dwChecksum
je Label002
call TerminateCurrentProcess
Label002:
}
}


void CheckSelfRelease(DWORD StartAddr,DWORD dwCodeLength)
{
_asm
{
xor eax,eax
xor ebx,ebx
mov esi,StartAddr
mov ecx,dwCodeLength
Label001:
mov bl,byte ptr
add eax,ebx
inc esi
loop Label001

cmp eax,0x0AF9
je Label002
call TerminateCurrentProcess

Label002:
}
}

void ThreadStartRoutine(DWORD * StartAddr)
{
for(;;)
CheckSelfRelease(*StartAddr,0x17);//无限循环,实时监视,直到程序终止。
}

void main()
{
DWORD StartAddr=0;
DWORD EndAddr=0;
DWORD dwCodeLength=0;
DWORD dwChecksum=0;



_asm
{
Start:
xor eax,eax
xor ebx,ebx
inc eax
add ebx,eax
add eax,ebx

mov StartAddr,offset Start
mov EndAddr,offset End

End:
}
dwCodeLength=EndAddr-StartAddr;

Calculation(StartAddr,dwCodeLength);

CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE)ThreadStartRoutine,
&StartAddr,0,NULL);//创建一个新的线程,循环监视内存中程序的完整性。
Sleep(88888);
printf("0k!");
}

/***************************************************************************
在C/C++ 代码中实现自我效验,可以使用以下代码段(仅供参考)

_asm
{
LabelStart:
mov dwStartAddr,offset LabelStart
}
。。。。。。
_asm
{
LabelEnd:
mov dwEndAddr,offset LabelEnd
}
***************************************************************************/

在VC中使用内联汇编总是感觉不太直接,所以用MASM 实现之,供喜欢汇编的读者参考。

;####################################

.386
.model flat,stdcall
option casemap:none

;####################################

include windows.inc
include kernel32.inc
include user32.inc

includelib kernel32.lib
includelib user32.lib
;####################################

;###################################################################

.data

;###################################################################

CodeLength=LabelEnd-LabelStart

.code

Calculation proc StartAddr:dword,dwCodeLength:dword
xor eax,eax
xor ebx,ebx
mov esi,StartAddr
mov ecx,dwCodeLength
Label001:
mov bl,byte ptr
add eax,ebx
inc esi
loop Label001

ret 8
Calculation endp

;###################################################################

CheckSelf proc StartAddr:dword,dwCodeLength:dword,dwChecksum:dword
xor eax,eax
xor ebx,ebx
mov esi,StartAddr
mov ecx,dwCodeLength
Label001:
mov bl,byte ptr
add eax,ebx
inc esi
loop Label001

cmp eax,dwChecksum
je Label002
call TerminateCurrentProcess
Label002:

ret 0ch
CheckSelf endp

;###################################################################

TerminateCurrentProcess proc

call GetCurrentProcess
push 0
push eax
call TerminateProcess

ret
TerminateCurrentProcess endp

;###################################################################

Start:

LabelStart:
xor eax,eax
inc eax
mov eax,0
sub eax,1
LabelEnd:

push CodeLength
mov eax,offset LabelStart
push eax
call Calculation

push eax
push CodeLength
mov eax,offset LabelStart
push eax
call CheckSelf

end Start


由于本人水平有限,文中疏漏之处在所难免,请读者不吝赐教。

swl 发表于 2008-10-30 16:57

没有学过编程,没看懂。来支持一下。

hanye0082 发表于 2009-7-2 17:52

还有待研究~!

aloneforu 发表于 2009-7-10 15:17

向50CB冲刺
页: [1]
查看完整版本: 程序的自我效验