吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 19870|回复: 22
收起左侧

[调试逆向] 对于栈回溯一点思考

  [复制链接]
690827027 发表于 2010-3-29 10:02
本帖最后由 690827027 于 2010-3-29 10:06 编辑

周末在家闲来无事,跟一同学聊天中,点击几下消息记录,结果QQ崩溃了,版本是2010
载入其中的DMP文件kv查看之,里面显示栈的内容很少,自己用dds 命令一看,WINDBG
在无符号分析的时候又出错了,决定自己写一个栈分析工具,

下面分析加代码只针对栈没有溢出和不存在使用JMP代替CALL调用情况

先说下个人思路,
intel CPU在函数调用的时候会将CALL的下一条指令自动压入堆栈
当然堆栈上还可能包含用户参数,和该上层函数的EBP

先来一个简单的例子

int add(int a,int b);

int main(int argc, char* argv[])
{

add(10,20);
return 0;
}
int add(int a,int b)
{
int c = a+ b;
return c;
}


然后直接断在
int c = a+ b;
这一行上

反汇编如下

00401070 55                   push        ebp
00401071 8B EC                mov         ebp,esp
00401073 83 EC 44             sub         esp,44h
00401076 53                   push        ebx
00401077 56                   push        esi
00401078 57                   push        edi
00401079 8D 7D BC             lea         edi,[ebp-44h]
0040107C B9 11 00 00 00       mov         ecx,11h
00401081 B8 CC CC CC CC       mov         eax,0CCCCCCCCh
00401086 F3 AB                rep stos    dword ptr [edi]
18:       int c = a+ b;
00401088 8B 45 08             mov         eax,dword ptr [ebp+8]
0040108B 03 45 0C             add         eax,dword ptr [ebp+0Ch]
0040108E 89 45 FC             mov         dword ptr [ebp-4],eax
19:       return c;
00401091 8B 45 FC             mov         eax,dword ptr [ebp-4]



然后查看下寄存器中的EBP

EAX = CCCCCCCC
EBX = 7FFDD000
ECX = 00000000
EDX = 00430D80
ESI = 00000000
EDI = 0013FF24
EIP = 00401088
ESP = 0013FED4
EBP = 0013FF24
EFL = 00000202 CS = 001B
DS = 0023 ES = 0023
SS = 0023 FS = 003B
GS = 0000 OV=0 UP=0 EI=1
PL=0 ZR=0 AC=0 PE=0 CY=0

而EBP所指内容为0013FF80,然后查看此处内存
会发现这个地方为一个调用栈,然后可以一直追溯到顶曾函数



0013FF24  80 FF 13 00  ....
0013FF28  41 10 40 00  A.@.
0013FF2C  0A 00 00 00  ....
0013FF30  14 00 00 00  ....


一个比较常见函数调用堆栈为

参数
CALL RET指令地址
上一个函数EBP地址
临时变量空间
EBX
ESI
EDI

下一个函数调用

参数
CALL RET指令地址
上一个函数EBP地址 ()
临时变量空间
EBX
ESI
EDI

其中EBP 和 ESP之间就是一个函数的私有领地

再看下这条语句的反汇编调用规则
12:       add(10,20);
00401038 6A 14                push        14h
0040103A 6A 0A                push        0Ah
0040103C E8 C4 FF FF FF       call        @ILT+0(add) (00401005)
00401041 83 C4 08             add         esp,8


0013FF24  80 FF 13 00  ....
0013FF28  41 10 40 00  A.@.
0013FF2C  0A 00 00 00  ....
0013FF30  14 00 00 00  ....

0013FF28  恰好为add         esp,8
指令地址即CALL的下一条指令地址
而且一定的特点,这个00401041
必须在合法空间内,而且可读,
还有它前面5个字节存在一个E8()




我们利用穷举法,从TEB里面的StackBase开始到达当期ESP而且
以INT型为变量为指针,再根据一些刷选条件就能获得接近完整的调用栈




typedef struct  _XXXFRAME
{
   DWORD dwesp; //函数CALL所在的内存栈地址
   DWORD dwretIP; //下一条指令地址返回地址
   DWORD dwcallip; //CALL 指令地址
   DWORD dwRetIPFuncAdr; //调用函数的起始IP地址
   DWORD dwMaybeEbp; //EBP法则中的EBP地址(可能存在)
}XXXFRAME,*PXXXFRAME;

  std::vector<XXXFRAME> StackFrameList;  //用来存放可疑栈
  CString StrText,StrBuffer;  CString StrTextFormat;
     RaisePrivilegeByName( SE_DEBUG_NAME);提升权限
DWORD dwLowerESP = 0x0013e964,dwHigthesp = 0x00140000,dwUserTemp= 0; //硬编码栈高地址低地址
     LPDWORD lpdwStart = (LPDWORD)dwLowerESP , lpdwEnd = (LPDWORD)dwHigthesp,lpdwFisrt,lpdwTemp,lpdwAtNow;
  DWORD dwLen = 0,dwBufferlen = 4 * (lpdwEnd - lpdwStart),dwMem;
  char* lpszBuffer = StrText.GetBuffer(dwBufferlen);
  unsigned char* lpunMem = (unsigned char*)StrBuffer.GetBuffer(6);
  StrTextFormat.GetBuffer(255);StrTextFormat.GetBufferSetLength(255);
  HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,5092);//PID任务管理器获取
     MEMORY_BASIC_INFORMATION MemBasicInfo = {0};XXXFRAME oldTempFrame = {0};
  if(hProcess)
  {
         
   if(ReadProcessMemory(hProcess,lpdwStart,lpszBuffer,dwBufferlen,&dwLen))
   {
    lpdwFisrt = (LPDWORD)StrText.GetBuffer(0);
    lpdwEnd = lpdwFisrt + (dwBufferlen)/4;
    lpdwFisrt = lpdwEnd;
             lpdwEnd = (LPDWORD)StrText.GetBuffer(0);
             for(;lpdwFisrt > lpdwEnd;lpdwFisrt--)  //从STACKBASE开始,逐个刷选
    {
    XXXFRAME TempFrame = {0};
    dwMem = *lpdwFisrt;
    lpdwAtNow = (lpdwFisrt - (LPDWORD)StrText.GetBuffer(0)) + lpdwStart;
                if((dwMem) == 0 || (dwMem) >= 0x80000000)  //非法地址肯定不合适, 这个当然可以加大范围 比如低64KB  高 64KB
    {
           continue;
    }
    if(!VirtualQueryEx(hProcess,(LPDWORD)dwMem,&MemBasicInfo,sizeof(MEMORY_BASIC_INFORMATION)))  //查询内存页面属性
    {
     
     int nLastError = GetLastError();
     continue;
    }
    else
    {
        
                  if(MemBasicInfo.AllocationProtect == PAGE_NOACCESS ||MemBasicInfo.State == MEM_FREE)
        continue;
    }


下面是各种CALL的刷选条件 INTEL有好几十CALL调用,常见4,5种

    // --->      call XXXXX  //读取指令地址
    // dwretIP = [TempFrame.dwesp]  --->  xor eax ,eax
    // assume  dwMem is  ret address
    dwMem = dwMem - 6;  
     
                if(!ReadProcessMemory(hProcess,(LPDWORD)dwMem,lpunMem,6,&dwLen))
    {
                     continue;
    }
    //case  call    dword ptr [eax+58h]
    //  ff5058
    if(*(lpunMem + 3) == 0xFF)
    {
                     TempFrame.dwesp = (DWORD)lpdwAtNow;
      TempFrame.dwretIP = *lpdwFisrt;
      TempFrame.dwcallip = (*lpdwFisrt - 3);
    }
    //case CALL DWORD PTR DS:[40A270]
    if(*lpunMem == 0xFF && *(lpunMem + 1) == 0x15)
    {
     TempFrame.dwesp = (DWORD)lpdwAtNow;
     TempFrame.dwretIP = *lpdwFisrt;
     TempFrame.dwcallip = (*lpdwFisrt - 6);
     
                    // read addresss 40A270
     if(!ReadProcessMemory(hProcess,(LPDWORD)*(LPDWORD)(lpunMem + 2),lpunMem,4,&dwLen))
     {
      continue;
     }
                    
     TempFrame.dwRetIPFuncAdr = *(LPDWORD)(lpunMem);

    }

    if(*(lpunMem + 1) == 0xE8)
    {
     TempFrame.dwesp = (DWORD)lpdwAtNow;
     TempFrame.dwretIP = *lpdwFisrt;
     TempFrame.dwcallip = (*lpdwFisrt - 5);
     TempFrame.dwRetIPFuncAdr = *(LPDWORD)(lpunMem + 2) + 5 + TempFrame.dwcallip;
    }
    if(TempFrame.dwesp != 0)
    {
     if(oldTempFrame.dwretIP == TempFrame.dwretIP)
     {
      continue;
     }
                     
     dwUserTemp = (*(lpdwFisrt-1));
     if(dwUserTemp > dwLowerESP && dwUserTemp < dwHigthesp)
     {
                         TempFrame.dwMaybeEbp = dwUserTemp;
     }
     
     dwUserTemp = 0;
     for(std::vector<XXXFRAME>::reverse_iterator itsec = StackFrameList.rbegin();itsec != StackFrameList.rend();itsec++)
     {
      if(TempFrame.dwMaybeEbp + 4 == itsec->dwesp)
      {
       dwUserTemp = TempFrame.dwMaybeEbp ;
      
       StackFrameList.push_back(TempFrame);
       oldTempFrame = TempFrame;
       break;
      }
     }
     
     if(dwUserTemp)
      continue;
     
     TempFrame.dwMaybeEbp = dwUserTemp;
     if(oldTempFrame.dwesp == 0 )
     {
                      StackFrameList.push_back(TempFrame);
       oldTempFrame = TempFrame;
       continue;
     }

     if((oldTempFrame.dwesp - TempFrame.dwesp) > 20)
     {
      StackFrameList.push_back(TempFrame);
      oldTempFrame = TempFrame;
      continue;
     }
    }

    //case ff92c4000000    call    dword ptr [edx+0C4h]
    if(*lpunMem == 0xFF)
    {
     TempFrame.dwesp = (DWORD)lpdwAtNow;
     TempFrame.dwretIP = *lpdwFisrt;
     TempFrame.dwcallip = (*lpdwFisrt - 3);
     
    }
    if(TempFrame.dwesp != 0)
    {
     dwUserTemp = (*(lpdwFisrt-1));
     if(dwUserTemp > dwLowerESP && dwUserTemp < dwHigthesp)
     {
      TempFrame.dwMaybeEbp = dwUserTemp;
     }
     if(oldTempFrame.dwretIP == TempFrame.dwretIP)
     {
      continue;
     }
     dwUserTemp = 0;

利用函数临时变量空间刷选  距离大于20才存在一个CALL调用

     for(std::vector<XXXFRAME>::reverse_iterator itsec = StackFrameList.rbegin();itsec != StackFrameList.rend();itsec++)
     {
      if(TempFrame.dwMaybeEbp + 4 == itsec->dwesp)
      {
       dwUserTemp = TempFrame.dwMaybeEbp ;
       StackFrameList.push_back(TempFrame);
       oldTempFrame = TempFrame;
       break;
      }
     }
     if(dwUserTemp)
       continue;
     TempFrame.dwMaybeEbp = dwUserTemp;
     if((oldTempFrame.dwesp - TempFrame.dwesp) > 20)
     {
      StackFrameList.push_back(TempFrame);
      oldTempFrame = TempFrame;
      continue;
     }
    }
    }


//利用EBP规则再次刷选

    //parse the struct
             //uses ebp
    for(std::vector<XXXFRAME>::iterator ittemp = StackFrameList.begin() + 1;ittemp != StackFrameList.end();ittemp++)
    {
     std::vector<XXXFRAME>::iterator itfisrt = ittemp;
     if( (*itfisrt).dwesp )
     {
      dwUserTemp = itfisrt->dwMaybeEbp;
      itfisrt->dwMaybeEbp = 0;
      for(std::vector<XXXFRAME>::iterator itsec = StackFrameList.begin();itsec != itfisrt;itsec++)
      {
            if(dwUserTemp + 4 == itsec->dwesp)
      {
                           itfisrt->dwMaybeEbp = dwUserTemp;
         break;
      }
      }
     }
     
    }
            
      }
  }

对于JMP或者栈溢出类型,个人谈点看法可以加上INLINE  HOOK 获得待用参数
再内存搜索JMP指令 当然也还可以结合SUB ESP ****一起刷选


本人编码水平有点菜,代码也写的有点仓促,仅适合32位机型,可能存在各种问题,影响阅读,略表歉意

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

ximo 发表于 2010-3-29 10:04
沙发,膜拜谭叫兽。
Hmily 发表于 2010-3-29 10:08
膜拜加学习,大牛应该加上吾爱破解发布,版权所有,偷窃必究....
wowocock 发表于 2010-3-29 10:09
然后直接断在
int c = a+ b;
这一行上

反汇编如下

00401070 55                   push        ebp
00401071 8B EC                mov         ebp,esp
00401073 83 EC 44             sub         esp,44h
00401076 53                   push        ebx
00401077 56                   push        esi
00401078 57                   push        edi
00401079 8D 7D BC             lea         edi,[ebp-44h]
0040107C B9 11 00 00 00       mov         ecx,11h
00401081 B8 CC CC CC CC       mov         eax,0CCCCCCCCh
00401086 F3 AB                rep stos    dword ptr [edi]
18:       int c = a+ b;
00401088 8B 45 08             mov         eax,dword ptr [ebp+8]
0040108B 03 45 0C             add         eax,dword ptr [ebp+0Ch]
0040108E 89 45 FC             mov         dword ptr [ebp-4],eax
19:       return c;
00401091 8B 45 FC             mov         eax,dword ptr [ebp-4]

别用RELEASE 版,INLINE 了。
hixiaosheng 发表于 2010-3-29 10:23
我来继续膜拜。。。
system98 发表于 2010-3-29 10:24
[s:208]
[s:208]
[s:208]
[s:208]
riusksk 发表于 2010-3-29 12:06
好像之前在第八个男人上面有两篇关于栈回溯的文章
秋风夜雪 发表于 2010-3-31 16:00
学习一下!!!
O_o 发表于 2010-4-7 09:11
都在膜拜了。
zsl01 发表于 2010-7-14 12:08
学习一下!!!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-22 10:58

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表