首页
社区
课程
招聘
[原创]程序的自我效验
发表于: 2008-10-28 19:48 26690

[原创]程序的自我效验

2008-10-28 19:48
26690

编程语言: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);
void  TerminateCurrentProcess();

DWORD  Calculation(DWORD StartAddr,DWORD dwCodeLength)
{
        _asm
        {
                        xor eax,eax
                        xor ebx,ebx
                        mov esi,StartAddr
                        mov ecx,dwCodeLength
Label001:
                        mov bl,byte ptr [esi]
                        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 [esi]
                        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 [esi]
                        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 [esi]
                        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);
void  CheckSelf(DWORD StartAddr,DWORD dwCodeLength,DWORD dwChecksum);
void  TerminateCurrentProcess();

DWORD  Calculation(DWORD StartAddr,DWORD dwCodeLength)
{
        _asm
        {
                        xor eax,eax
                        xor ebx,ebx
                        mov esi,StartAddr
                        mov ecx,dwCodeLength
Label001:
                        mov bl,byte ptr [esi]
                        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 [esi]
                        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 [esi]
                        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 [esi]
                        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 [esi]
                        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

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


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 7
支持
分享
最新回复 (34)
雪    币: 398
活跃值: (343)
能力值: (RANK:650 )
在线值:
发帖
回帖
粉丝
2
esi, ebx不能随便破坏
不然可能后果很严重
2008-10-28 19:51
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
相当强大,学习了
2008-10-28 20:04
0
雪    币: 222
活跃值: (10)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
4
拜读了,谢谢
2008-10-28 20:54
0
雪    币: 239
活跃值: (11)
能力值: ( LV9,RANK:140 )
在线值:
发帖
回帖
粉丝
5
感谢shoooo 大哥的指点,我们可以在函数的开头及结尾分别插入pusha,popa的,这样就可以保证函数调用前后寄存器的值保持不变.多谢了.
2008-10-29 11:33
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
学习了,我们也只能看看
2008-10-29 11:59
0
雪    币: 268
活跃值: (95)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
7
学习了,在做看雪第一阶段第二题的时候,里面有个自校验和反调试,一直就在想用高级语言怎么实现,看来还是得内联汇编了。谢谢ufozhyufo分享。
2008-10-29 14:21
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
新人学习   顶一下   
2008-10-30 14:10
0
雪    币: 598
活跃值: (282)
能力值: ( LV13,RANK:330 )
在线值:
发帖
回帖
粉丝
9
不管怎样,这一句得排除在外,不然得不到正确的检验值:
      cmp eax,0x0B59  //0x0B59是提前计算好的
2008-10-30 14:56
0
雪    币: 163
活跃值: (41)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
10
顶一下。。。。。。
2008-10-30 17:43
0
雪    币: 303
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
顶下,学习了。
2008-10-31 01:42
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
顶下·
2008-11-1 15:16
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
gry
13
膜拜ing
完全不懂
2008-11-3 09:26
0
雪    币: 221
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
只有看的份了..看来要更努力才行~~~
2008-11-3 14:08
0
雪    币: 340
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
学习了....
2008-11-4 21:03
0
雪    币: 6
活跃值: (1125)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
遇到过一个程序OD加载完之后,单步一下就直接退出程序了,好像比楼主的方法更厉害,对于楼主的校验方法只要找到校验的验证函数,改调转就可以了.
2008-11-5 11:48
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
相当强大,学习了
2008-11-5 16:37
0
雪    币: 195
活跃值: (57)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
学习.........................
2008-11-6 20:01
0
雪    币: 203
活跃值: (214)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
看样子你的代码还是比较简单的那种,有时候程序本身都有可能修改自己的代码.
2008-11-7 13:21
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
学习了,,,
2008-11-8 11:26
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
gry
21
新人学习   顶一下
2008-11-8 16:20
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
我的问题是  无法读代码     哎    再好  也只能看看了
2008-12-2 21:41
0
雪    币: 1301
活跃值: (25)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
23
写的通俗明白。谢谢分享。
2008-12-5 11:23
0
雪    币: 204
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
腾讯的 趣味CrackMe就用的这个原理
====================
004039DD   .  2BD7          sub     edx, edi                         ;  cm.00403271
004039DF   .  8BCA          mov     ecx, edx
004039E1   .  81C1 40010000 add     ecx, 140
004039E7   .  33C0          xor     eax, eax
004039E9   >  0FB61F        movzx   ebx, byte ptr [edi]
004039EC   .  03C3          add     eax, ebx
004039EE   .  D1C0          rol     eax, 1
004039F0   .  47            inc     edi
004039F1   .^ E2 F6         loopd   short cm.004039E9
004039F3   .  3D 3E5BDF02   cmp     eax, 2DF5B3E
004039F8   .^ 0F85 AAFEFFFF jnz     cm.004038A8
004039FE   .^ E9 73F8FFFF   jmp     cm.00403276
2008-12-8 17:58
0
雪    币: 202
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
在修改代码后的某个位置添加:
jmp hehe
db xxx
hehe:

在xxx处赋一个算出来值,使你的校验满足,是不是就可以破解呢?
2008-12-26 11:39
0
游客
登录 | 注册 方可回帖
返回
//