本帖最后由 JoyChou 于 2013-5-9 13:10 编辑
今天看到zxcfvasd发了一篇关于int检测的CM文章
然后给大家分享下怎么实现的。
这里用VS写了一个内嵌的汇编代码
C代码:
[C++] 纯文本查看 复制代码 #include <windows.h>
BOOL DetectFuncBreakpoints();
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd )
{
if (DetectFuncBreakpoints())
{
MessageBox(NULL, "检测到int3断点", "结果", MB_OK);
return 0;
}
else
{
MessageBox(NULL, "没有检测到int3断点", "结果", MB_OK);
}
return 0;
}
BOOL DetectFuncBreakpoints()
{
BOOL bFoundOD;
bFoundOD=FALSE;
DWORD dwAddr;
dwAddr = (DWORD)GetProcAddress(LoadLibrary("user32.dll"),"MessageBoxA"); //将FARPROC类型转换成DWORD
__asm
{
cld ;检测代码开始
mov edi,dwAddr
mov ecx,100 ;100bytes
mov al,0CCH ;字母前面必须有0
repne scasb
jnz ODNotFound
mov bFoundOD,1
ODNotFound:
}
return bFoundOD;
}
再发一个win32汇编写的吧
[C++] 纯文本查看 复制代码 ;检测时,BP MessageBoxA
.386
.model flat,stdcall
option casemap:none
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
.const
szKernelDll db 'user32.dll',0
szAPIMessbox db 'MessageBoxA',0
szCaption db '结果',0
szFound db '发现API断点',0
szNotFound db '未发现断点',0
.code
start:
invoke GetModuleHandle,addr szKernelDll
invoke GetProcAddress,eax,addr szAPIMessbox ;API地址
cld ;检测代码开始
mov edi,eax ;API开始位置
mov ecx,100H ;检测100字节
mov al, 55H
xor al, 99H ;55和99异或等于0xCC
repne scasb
cmp ecx, 0h
jnz debugger_found
invoke MessageBox,NULL,addr szNotFound,addr szCaption,MB_OK
jmp exit
debugger_found: invoke MessageBox,NULL,addr szFound,addr szCaption,MB_OK
exit: invoke ExitProcess,NULL
end start
首先我们得知道一个问题,当用OD调试的时候,我们F2或者下bp断点的时候,即是下int3断点 OD载入分析: 比如我们在00401000 F2下断,此时应该00401000地址应该是CC 18204000 因为OD做了对int3的隐藏,我们看到的还是是68,而不是我们下断时的CC,当然这也是为了我们方便。
说这么多是为了理解00401021 |. F2:AE repne scas byte ptr es:[edi] 这句话 因为当[edi]等于我们下断的时,此时数据窗口并不等于CC。 这也是我调试的觉得有点困惑的地方,分享一下。
[C] 纯文本查看 复制代码 00401000 >/$ 68 18204000 push APISoftB.00402018 ; /pModule = "user32.dll"
00401005 |. E8 5A000000 call <jmp.&KERNEL32.GetModuleHandleA> ; \GetModuleHandleA
0040100A |. 68 23204000 push APISoftB.00402023 ; /ProcNameOrOrdinal = "MessageBoxA"
0040100F |. 50 push eax ; |hModule
00401010 |. E8 55000000 call <jmp.&KERNEL32.GetProcAddress> ; \GetProcAddress
00401015 |. FC cld
00401016 |. 8BF8 mov edi,eax
00401018 |. B9 00010000 mov ecx,0x100
0040101D |. B0 55 mov al,0x55
0040101F |. 34 99 xor al,0x99
00401021 |. F2:AE repne scas byte ptr es:[edi]
000401023 |. 83F9 00 cmp ecx,0x0 ; ecx不等于0表示检测到int3断点
00401026 |. 75 15 jnz short APISoftB.0040103D
00401028 |. 6A 00 push 0x0 ; /Style = MB_OK|MB_APPLMODAL
0040102A |. 68 2F204000 push APISoftB.0040202F ; |Title = "结果"
0040102F |. 68 40204000 push APISoftB.00402040 ; |Text = "未发现断点"
00401034 |. 6A 00 push 0x0 ; |hOwner = NULL
00401036 |. E8 1D000000 call <jmp.&USER32.MessageBoxA> ; \MessageBoxA
0040103B |. EB 13 jmp short APISoftB.00401050
0040103D |> 6A 00 push 0x0 ; /Style = MB_OK|MB_APPLMODAL
0040103F |. 68 2F204000 push APISoftB.0040202F ; |Title = "结果"
00401044 |. 68 34204000 push APISoftB.00402034 ; |Text = "发现API断点"
00401049 |. 6A 00 push 0x0 ; |hOwner = NULL
0040104B |. E8 08000000 call <jmp.&USER32.MessageBoxA> ; \MessageBoxA
00401050 |> 6A 00 push 0x0 ; /ExitCode = 0
00401052 \. E8 07000000 call <jmp.&KERNEL32.ExitProcess> ; \ExitProcess
对代码段的int3检测
[C++] 纯文本查看 复制代码 #include <windows.h>
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
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; //获取代码段对齐后的大小
BOOL bFound = FALSE;
__asm
{
cld ;检测代码开始
mov edi,dwAddr
mov ecx,10h ;检测0x10个字节测试,原则应该是dwCodeSize大小
mov al,0CCH ;字母前面必须有0
repne scasb
jnz NotFound
mov bFound,1
NotFound:
}
if (bFound)
{
MessageBoxA(NULL, "检测到断点", "结果", 0);
}
else
{
MessageBoxA(NULL, "没有检测到断点", "结果", 0);
}
return 0;
}
最后说下原理:
先得到函数的地址,设置一个检测的字节长度(我这里设的0x100),然后就一直搜索0xCC,如果发现了就表示检测到了。反之亦然。
不过只能检测bp断点和F2(int3断点),硬件等断点是检测不到的,硬件断点可以用CONTEXT结构体里面的iDr0,iDr1,iDr2,iDr3成员检测,这里就不多说了。
跳过检测方法:
1、修改检测时的跳转
2、下 hr等强点的断点
网上很多关于int3检测的资料,所以大家想学习的善于利用好搜索功能能学到很多东西的。
|