主要说一下调试器中经常用的断电 INT3?断点,INT3断点是断点的一种,在诸如Ollydbg中的快捷键是F2,是一种很常用的断点类型。INT3指令的机器码为CC,所以通常也称之为CC指令。当被调试进程执行INT3指令导致一个异常时,调试器就会捕捉这个异常从而停在断点处,然后将断点处的指令恢复成原来的指令。也就是说,你在某一地址下端,调试器会将其起始修改成CC,当执行到该指令时,会引发异常,交给调试器处理,然后你就可以为所欲为了。

反该断点,方法自然也很简单了,他不是修改了么?我们可以用传说中的CRC32算法比对代码断,防止代码被修改。先说下简单的方法,扫描关键函数地址首部前100个字节。关键部分用汇编实现,为了防止对ReadProcessMemory()等API做HOOK处理。当然你也可以用该API遍历首部。

BOOL DetectFuncInt3Breakpoints()
{
    BOOL bFoundDebug;
    bFoundDebug=FALSE;
    DWORD dwAddr;
    dwAddr = (DWORD)GetProcAddress(LoadLibrary("user32.dll"),"YourFunction"); //将FARPROC类型转换成DWORD
    __asm
    {
            cld               ;检测代码开始
            mov     edi,dwAddr
            mov     ecx,100  ;100bytes
            mov     al,0CCH ;字母前面必须有0
            repne   scasb
            jnz     DebugNotFound
            mov bFoundDebug,1
DebugNotFound:             
    }
    return bFoundDebug;
}

有一行代码我一开始也没搞太懂,SCASB~~顺便科普下吧

在汇编语言中SCASB是一条字符串操作指令,源自“SCAN String Byte”的缩写。该指令的具体操作
是:
计算 AL – byte of [ES:EDI] , 设置相应的标志寄存器的值;
修改寄存器EDI的值:如果标志DF为0,则 inc EDI;如果DF为1,则 dec EDI。
SCASB指令常与循环指令REPZ/REPNZ合用。例如,REPNZ SCASB 语句表示当 寄存器ECX>0 且 标志寄存器ZF=0,则再执行一次SCASB指令。
比较寄存器AL的值不相等则重复查找的字
repnz指令说明:重复执行其后面的指令,CX或ECX存放最多比较次数,DI或EDI存放查找表首地址,AL或AX或EAX存放想查找的内容。当(CX或ECX)= 0 或 ZF=1 退出重复,否则,(CX或ECX)自减一,执行其后的串指令。CX或ECX为0结束是因为已经查表完毕,没有匹配到;ZF=1说明 “比较的结果为0”,也就是查找到一样的内容,说明匹配到想要查找的内容。
SCASB(字节)
SCASW(字)
SCASD(双字)(386及其后继机型可用)
执行的操作:
(AL) – ((destination-index)),(destination-index) ← (destination-index) +或- 1
(AX) – ((destination-index)),(destination-index) ← (destination-index) +或- 2
(EAX) – ((destination-index)),(destination-index) ← (destination-index) +或- 4
指令把AL,AX或EAX的内容与由目的变址寄存器指向的附加段中的一个字节、字或双字进行比较,并不保存结果,只根据结果设置标志位。

该方法只能对于首部100个字节检测有效,如何再其后下断,令人却是无可奈何,但是你可以全text段检测,但是那样效率十分低下

int FindTextSegment()
{
    PIMAGE_DOS_HEADER pDosHeader;
    PIMAGE_NT_HEADERS32 pNtHeaders;
    PIMAGE_SECTION_HEADER pSectionHeader;
    DWORD dwBaseImage = (DWORD)GetModuleHandle(NULL); //获取当前进程的基址
    
    
    pDosHeader = (PIMAGE_DOS_HEADER)dwBaseImage;
    pNtHeaders = (PIMAGE_NT_HEADERS32)((DWORD)pDosHeader + pDosHeader->e_lfanew);
    pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pNtHeaders + sizeof(pNtHeaders->Signature) + sizeof(IMAGE_FILE_HEADER) + 
    (WORD)pNtHeaders->FileHeader.SizeOfOptionalHeader);
        
    DWORD dwAddr = pSectionHeader->VirtualAddress + dwBaseImage; //获取代码段在内存中的地址
    DWORD dwCodeSize = pSectionHeader->SizeOfRawData;    //获取代码段对齐后的大小
    int bFound = 0;
    
    __asm
    {
            cld               ;检测代码开始
            mov     edi,dwAddr
            mov     ecx,10h ;检测0x10个字节测试,原则应该是dwCodeSize大小
            mov     al,0CCH ;字母前面必须有0
            repne   scasb
            jnz     NotFound
            mov bFound,1
NotFound:             
    }
    return bFound;
}

 

但是可以计算其内存text段CRC32与硬盘中文件比对,至于读文件和读内存(上文提到过)就不用说了~现在附上CRC32校验代码。

首先是头文件~

//crc32.h
#ifndef _CRC32_H
#define _CRC32_H

uint crc32( uchar *buf, int len);

#endif

核心内容:

#include <stdio.h>
#include "crc32.h"

static uint   CRC32[256];
static char   init = 0;

//初始化表
static void init_table()
{
    int   i,j;
    uint   crc;
    for(i = 0;i < 256;i++)
    {
         crc = i;
        for(j = 0;j < 8;j++)
        {
            if(crc & 1)
            {
                 crc = (crc >> 1) ^ 0xEDB88320;
            }
            else
            {
                 crc = crc >> 1;
            }
        }
         CRC32[i] = crc;
    }
}

//crc32实现函数
uint crc32( uchar *buf, int len)
{
    uint ret = 0xFFFFFFFF;
    int   i;
    if( !init )
    {
         init_table();
         init = 1;
    }
    for(i = 0; i < len;i++)
    {
         ret = CRC32[((ret & 0xFF) ^ buf[i])] ^ (ret >> 8);
    }
     ret = ~ret;
    return ret;
}

先这样吧,明天还得上班—-哎

页面下部广告

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

鲁ICP备17018668号-1