好友
阅读权限40
听众
最后登录1970-1-1
|
本帖最后由 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位机型,可能存在各种问题,影响阅读,略表歉意 |
|
发帖前要善用【论坛搜索】功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。 |
|
|
|
|