首页
社区
课程
招聘
[矛与盾]调戏调试器:反断点技术
发表于: 2014-6-23 21:02 24678

[矛与盾]调戏调试器:反断点技术

bxc 活跃值
6
2014-6-23 21:02
24678

原帖及测试程序的地址:
【原创】反断点实例
本文我又发到我的CSDN博客里去了,还没审核通过

首先声明,理解本文需要对用户态的调试器原理有所了解,否则可能有些内容会不理解。
先来个测试程序,本文以分析和理解此程序为主。
测试程序下载
无论是逆向分析、还是脱壳破解,都离不开调试器。而windows下面用户态调试器最常用的,那就是OllyDbg了。
现在就用OD载入并调试该程序,如下图所示。

OD载入程序后,就停在了OEP(原始入口点)。
可能有经验的朋友观察入口代码,就会发现,该程序的入口是典型的VC++。但是不要着急,慢慢来。
现在F9运行程序,程序是控制台界面的。如下图所示:

提示下个断点,随便找个地方按F2下断点就行(不要下在INT 3处),我就下在OEP了。

刚下完断点,就被中断了,一般程序是不可能,因为入口点一般只调用一次。
现在F7单步一下试试,如下图:

单步一下,程序居然直接崩了,很奇怪吧。好了,就调试到这里。现在看TRE.exe的源码来讲解。

#include <SDKDDKVer.h>
#include <Windows.h>
#include <stdio.h>
#include <tchar.h>
#include <process.h>

#pragma comment(linker, "/ENTRY:MyEntry")

#define ONLY_ASM __declspec(naked)

#ifdef __cplusplus
extern "C"{
#endif

#ifdef UNICODE 
  int wmainCRTStartup(
#else
  int mainCRTStartup(
#endif
    void);

#ifdef __cplusplus
}
#endif

#ifdef UNICODE
#define _tmainCRTStartup wmainCRTStartup
#else
#define _tmainCRTStartup mainCRTStartup
#endif // UNICODE


typedef VOID (NTAPI * Func_RtlRaiseException)(__in PEXCEPTION_RECORD ExceptionRecord);
typedef NTSTATUS (NTAPI * Func_NtRaiseException)(__in PEXCEPTION_RECORD ExceptionRecord, __in PCONTEXT ContextRecord, __in BOOLEAN FirstChance);

int _tmain(int argc, _TCHAR* argv[]);
DWORD WINAPI ProtectFunc(void * lParam);

DWORD tid = 0;
DWORD dwtmp0;
DWORD dwtmp1;
Func_RtlRaiseException f_rre;
Func_NtRaiseException f_nre;
DWORD dwtarr[12];

char fName[17] =
{
  0x82, 0xb8, 0x9e, 0xad, 0xa5, 0xbf, 0xa9, 0x89, 0xb4,
  0xaf, 0xa9, 0xbc, 0xb8, 0xa5, 0xa3, 0xa2, 0X00
};

PVOID GetSectionAddrByName(HMODULE hMod, PCHAR pSecName, DWORD * pSecSize)
{
  if (!hMod || !pSecName) return NULL;

  PIMAGE_DOS_HEADER pDos;
  PIMAGE_NT_HEADERS32 pNtHeader;
  PIMAGE_SECTION_HEADER pSection;
  WORD sNum;
  WORD i;

  pDos = (PIMAGE_DOS_HEADER)hMod;
  if (pDos->e_magic != IMAGE_DOS_SIGNATURE) return NULL;
  pNtHeader = (PIMAGE_NT_HEADERS32)((UINT)hMod + pDos->e_lfanew);
  if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) return NULL;
  pSection = (PIMAGE_SECTION_HEADER)((UINT)pNtHeader + 0x18 + pNtHeader->FileHeader.SizeOfOptionalHeader);
  sNum = pNtHeader->FileHeader.NumberOfSections;

  for (i = 0; i < sNum; i++)
  {
    if (strcmp((PCHAR)pSection[i].Name, pSecName) == 0)
    {
      *pSecSize = pSection[i].Misc.VirtualSize;
      return (PVOID)((UINT)hMod + pSection[i].VirtualAddress);
    }
  }
  return NULL;
}


ONLY_ASM INT_PTR WINAPI GetFuncAddr(INT_PTR hMod, const PCHAR pName)
{
  _asm
  {
    push ebp
      mov ebp, esp
      sub esp, 0x10                 //为局部变量开辟空间
      push ebx
      push esi
      push edi
      mov ebx, [ebp + 0x08]
      mov eax, [ebx + 0x3c]         //dosheader->e_lfanew
      mov eax, [ebx + eax + 0x78]    //导出表地址
      test eax, eax                //判断导出表地址是否为空
      je ReturnNull
      add eax, ebx                  //加模块基址
      //取出输出表中一些有用的值   
      mov  ebx, [eax + 0x18]
      mov[ebp - 0x04], ebx
      mov  ebx, [eax + 0x1C]
      add  ebx, [ebp + 0x08]
      mov[ebp - 0x08], ebx
      mov  ebx, [eax + 0x20]
      add  ebx, [ebp + 0x08]
      mov[ebp - 0x0C], ebx
      mov  ebx, [eax + 0x24]
      add  ebx, [ebp + 0x08]
      mov[ebp - 0x10], ebx

      mov esi, [ebp + 0x0C]
      test esi, 0xFFFF0000
      jne Get_API_AddressByName
      mov eax, esi
      dec eax
      jmp Get_API_AddressByIndex

      //函数名取地址
    Get_API_AddressByName :
    xor eax, eax
      mov edi, [ebp - 0x0C]
      mov ecx, [ebp - 0x04]

    LoopNumberOfName :
                     mov esi, [ebp + 0x0C]
                     push eax
                     mov ebx, [edi]
                     add ebx, [ebp + 0x08]
                   Match_API :
                             mov al, byte ptr[ebx]
                             cmp al, [esi]
                             jnz Not_Match
                             or al, 0x00
                             jz Get_API_Index_Found
                             inc ebx
                             inc esi
                             jmp Match_API
                           Not_Match :
    pop eax
      inc eax
      add edi, 0x04
      loop LoopNumberOfName
      jmp ReturnNull

    Get_API_Index_Found :
    pop eax

    Get_API_AddressByIndex :
    mov ebx, [ebp - 0x10]
      movzx eax, word ptr[ebx + eax * 0x02]
      imul eax, 0x04
      add  eax, [ebp - 0x08]
      mov  eax, [eax]
      add  eax, [ebp + 0x08]
      jmp ReturnVal

    ReturnNull :
    xor eax, eax

    ReturnVal :
    pop edi
      pop esi
      pop ebx
      add esp, 0x10
      pop ebp
      retn 0x08
  }
}

void MyInit1()
{
  INT_PTR NtBase = 0;
  PCHAR pc = fName;

  __asm mov eax, fs:[0x30]
  __asm mov eax, [eax + 0x0C]
  __asm mov eax, [eax + 0x1C]
  __asm mov eax, [eax + 0x08]
  __asm mov NtBase, eax

  f_nre = (Func_NtRaiseException)GetFuncAddr(NtBase, fName);

  while (*pc)
  {
    *pc ^= 0xCC;
    pc++;
  }

  CreateThread(NULL, 4194304, ProtectFunc, MyInit1, 0, &tid);

  if (!tid || !f_nre)
  {
    printf("初始化失败!\n");
    ExitProcess(0);
  }
}

void MyInit()
{
  HMODULE hMod = GetModuleHandle(NULL);
  PCHAR pc = fName;
  PIMAGE_DOS_HEADER pDos;
  PIMAGE_NT_HEADERS32 pNtHeader;

  pDos = (PIMAGE_DOS_HEADER)hMod;

  while (*pc)
  {
    *pc ^= 0xCC;
    pc++;
  }

  MyInit1();
  if (pDos->e_magic != IMAGE_DOS_SIGNATURE) return;
  pNtHeader = (PIMAGE_NT_HEADERS32)((UINT)hMod + pDos->e_lfanew);
  if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) return;
  return;
}

ONLY_ASM int CallCrt()
{
  __asm call _tmainCRTStartup
  /* 下面的指令就是混淆视线的 */
  __asm retn
  __asm nop
  __asm push ebp
  __asm mov ebp, esp
  __asm int 3
  __asm int 1
  __asm mov esp, ebp
  __asm pop ebp
  __asm retn
}

ONLY_ASM int MyEntry()
{
  __asm
  {
    call MyInit
      jmp CallCrt
      /* 下面的指令就是混淆视线的 */
      push ebp
      mov ebp, esp
      call IsDebuggerPresent
      push 1
      mov dwtmp0, eax
      call _tmain
      push dword ptr [ebp + 0x08]
      call ProtectFunc
      cmp dwtmp1, 0
      pop ecx
      pop ecx
      jnz JMP0
      push 0x1
      call _tmain
      pop ecx
    JMP0 :
    push 0xC0000409
      call ProtectFunc
      pop ecx
      pop ebp
      retn

      push    ebp
      mov     ebp, esp
      push    0x17
      call    IsDebuggerPresent
      test    eax, eax
      je      JMP0
      push    0x2
      pop     ecx
      mov     dwtarr[0], eax
      mov     dwtarr[1], ecx
      mov     dwtarr[2], edx
      mov     dwtarr[3], ebx
      mov     dwtarr[4], esi
      mov     dwtarr[5], edi
      mov     word ptr dwtarr[6], ss
      mov     word ptr dwtarr[7], cs
      mov     word ptr dwtarr[8], ds
      mov     word ptr dwtarr[9], es
      mov     word ptr dwtarr[10], fs
      mov     word ptr dwtarr[11], gs
      pushfd
      push    0x4
      call _tmain
      pop     eax
      imul    eax, eax, 0x0
      mov     dword ptr ss : [ebp + eax - 0x8], ecx
      push    0x4
      pop     eax
      mov     esp, ebp
      pop     ebp
      retn
  }
}

PBYTE WINAPI CompBytes(PBYTE pm0, PBYTE pm1, UINT mMax, PBYTE pOByte)
{
  UINT i;

  for (i = 0; i < mMax;i++)
  {
    if (pm0[i] != pm1[i])
    {
      if (pm0[i] == 0xCC)
      {
        *pOByte = pm1[i];
        return &pm0[i];
      }
    }
  }
  return NULL;
}

DWORD WINAPI ProtectFunc(void * lParam)
{
  PBYTE poMem;
  PBYTE oAddr;
  DWORD mSize;
  PBYTE nAddr;
  NTSTATUS ns;
  EXCEPTION_RECORD ER;
  CONTEXT ct;
  BYTE OByte;

  oAddr = (PBYTE)GetSectionAddrByName(GetModuleHandle(NULL), ".text", &mSize);
  poMem = (PBYTE)HeapAlloc(GetProcessHeap(), 0, mSize);
  
  if (!poMem)
  {
    printf("HeapAlloc失败!退出...\n");
    ExitProcess(0);
  }

  memcpy_s(poMem, mSize, oAddr, mSize);

  while (true)
  {
    nAddr = CompBytes(oAddr, poMem, mSize, &OByte);
    if (nAddr)
    {
      while (*nAddr != OByte)
      {
        /* 抛出假断点异常 */
        ER.ExceptionCode = EXCEPTION_BREAKPOINT;
        ER.ExceptionFlags = 0;
        ER.ExceptionRecord = NULL;
        ER.ExceptionAddress = nAddr;
        ER.NumberParameters = 1;
        ER.ExceptionInformation[0] = 0;

        ct.ContextFlags = CONTEXT_FULL;
        ct.Eip = DWORD(nAddr);
        ct.Dr0 = 0;
        ct.Dr1 = 0;
        ct.Dr2 = 0;
        ct.Dr3 = 0;
        ct.Dr6 = 0;
        ct.Dr7 = 0;
        ct.SegGs = 0x2B;
        ct.SegFs = 0x53;
        ct.SegEs = 0x2B;
        ct.SegDs = 0x2B;
        ct.SegCs = 0x23;
        ct.EFlags = 0x00000246;
        ct.SegSs = 0x2B;

        ns = f_nre(&ER, &ct, FALSE);
        Sleep(60);
      }
    }
    Sleep(80);
  }

  HeapFree(GetProcessHeap(), 0, poMem);
  return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
  printf("下个断点试试!:)\n");
  getchar();
  printf("按任意键退出!\n");
  getchar();
  return 0;
}
void MyInit()
{
  HMODULE hMod = GetModuleHandle(NULL);
  PCHAR pc = fName;
  PIMAGE_DOS_HEADER pDos;
  PIMAGE_NT_HEADERS32 pNtHeader;

  pDos = (PIMAGE_DOS_HEADER)hMod;

  while (*pc)
  {
    *pc ^= 0xCC;
    pc++;
  }

  MyInit1();
  if (pDos->e_magic != IMAGE_DOS_SIGNATURE) return;
  pNtHeader = (PIMAGE_NT_HEADERS32)((UINT)hMod + pDos->e_lfanew);
  if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) return;
  return;
}

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 3
支持
分享
最新回复 (48)
雪    币: 963
活跃值: (828)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
分析得很好
2014-6-23 21:03
0
雪    币: 14308
活跃值: (5326)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
MARK。。。
2014-6-23 21:19
0
雪    币: 39
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
4
好文章。。学习了。
2014-6-23 21:26
0
雪    币: 23
活跃值: (35)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
mark!
2014-6-23 21:28
0
雪    币: 227
活跃值: (450)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
6
内存校验.下硬断  看读的地方 写个HOOK 然后就河蟹了
2014-6-23 21:52
0
雪    币: 202
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
分析的不错啊
2014-6-23 22:16
0
雪    币: 7296
活跃值: (5371)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
8
感谢分享,不错
2014-6-23 22:41
0
雪    币: 204
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
分析的很不错,受教了
2014-6-23 22:50
0
雪    币: 228
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
Good job!
单步运行程序崩溃 大概是因为你设置的伪CONTEXT 不是真正的合法的CONTEXT ,比如ESP = 0x00000 这样程序继续执行当然挂了 因为继续执行是要把你的伪CONTEXT直接设置回去的
2014-6-23 23:47
0
雪    币: 10329
活跃值: (3182)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
调戏调试器:反断点技术 mark
2014-6-24 00:03
0
雪    币: 257
活跃值: (67)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
12
道高一尺,魔高一丈
2014-6-24 01:03
0
雪    币: 95
活跃值: (119)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
好完整的分析呀,,,,,,
2014-6-24 09:00
0
雪    币: 0
活跃值: (60)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
赞一个。分析的很好
2014-6-24 09:25
0
雪    币: 114
活跃值: (180)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
Mark~
2014-6-24 09:26
0
雪    币: 188
活跃值: (671)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
ART CPU

59DD284C  |. 91029003              "LLVM can't disassembly !"
好多地方有这个,必须手动识别.thumb
2014-6-24 11:19
0
雪    币: 106
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
Good!
2014-6-24 11:43
0
雪    币: 2673
活跃值: (3560)
能力值: ( LV13,RANK:1760 )
在线值:
发帖
回帖
粉丝
18
顶一个...
2014-6-24 11:44
0
雪    币: 27
活跃值: (127)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
19
向楼主学习
2014-6-24 11:59
0
雪    币: 124
活跃值: (619)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
20
干掉线程,如果不通讯的话。
2014-6-24 12:03
0
雪    币: 14
活跃值: (88)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
good!!
2014-6-24 12:33
0
雪    币: 377
活跃值: (1835)
能力值: ( LV9,RANK:410 )
在线值:
发帖
回帖
粉丝
22
分析的不错,支持一个!
2014-6-24 12:46
0
雪    币: 341
活跃值: (85)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
23
mark
2014-6-24 12:54
0
雪    币: 217
活跃值: (908)
能力值: (RANK:290 )
在线值:
发帖
回帖
粉丝
24
good  job   mark
2014-6-24 14:09
0
雪    币: 11
活跃值: (60)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
赞,学到很多
2014-6-24 15:35
0
游客
登录 | 注册 方可回帖
返回