在kerui学软件逆向分析大半年了,我来发一份技术分析贴,为啥吾爱发个图这么难呢!!!
注:修复源码在帖子后面,附件当中也有一份
第一部分: 问题描述和简要分析
ODV1.10给32位Unicode版本的程序下消息断点时,弹出如下框
先提三个问题 1、产生这个问题的原因是什么? 2、如何利用工具和实用的策略去分析它? 3、如何修复这个问题? 下面将围绕着这三个问题,展开分析和验证
如何做呢?基本策略如下 1、根据已有的事实和现象,提出假设和猜想 2、通过现有的跟踪技术,找到问题源,验证提出的假设和猜想 3、写程序修复这个问题 先来简单分析一下原因: 为了找到问题原因,我们先列一些事实(使用OD打开不同版本的程序,有Unicode版本的程序也和Ansi版本的程序) 1、Unicode版本程序的下消息断点会出错,而Ansi版本的程序下消息断点不会有问题。 2、OD软件是本身也是程序员写的,难免程序也会有BUG 通过以上两点信息,说明消息断点功能是完整的,至于为什么Unicode版本程序下消息断点出错,还不得而知,很有可能是程序BUG。另外注意到Unicode版本程序的下消息断点会出错,并弹出错误对话框,那么接下来能否通过这个错误对话框,追本求源,找到触发问题的事件源,就是揭开迷题的关键了。 如何根据触发错误点,找到一些关键的信息? 这里我们采用的策略是:
在弹框这个点下断点,利用栈回溯,找到弹出ffff05C9这个信息的来源函数
第二部分验证猜想的,找到问题源
1、当错误点触发时,使用spy++,查看弹框类型,可以看到注册窗口类型是#32770(Dialog),猜测是MessageBox弹出的框。 接下来将以MessageBox为分析的入口点,查找获得ffff05C9这个信息的源函数
利用OD函数返回和的栈回溯,始终跟踪存放ffff05C9值,我们很容易跟到上面的位置如下条指令,但还是未找到哪个函数给这个地址赋的值 00498D33 |. 8B43 24 MOV EAX,DWORD PTR DS:[EBX+24] ;// 在这个关键代码处下软件写入断点([EBX+24]当中存放的值是ffff05C9)
说明还未到关键的位置,继续跟我们会发现,EBX是在Getsortedbyselection当中赋的值,并且是通过[004DE928 +11c]这个堆空间的值通过计算处到的, 细心的读者会发现,其实004DE928 是一个全局地址,如下代码所示: 004989A5 |. 50 PUSH EAX ; /Arg2 => 00000000
004989A6 |. 68 28E94D00 PUSH OllyDbg.004DE928 ; |Arg1 = 004DE928 ASCII "Table of windows"
004989AB |. E8 2CCEFBFF CALL OLLYDBG._Getsortedbyselection ; \_Getsortedbyselection
分析Getsortedbyselection函数代码后,我们发现004DE928 +11c这个地址是个堆空间的地址,
我在这里并未想到其他的办法继续追踪,学过PE的朋友知道,全局地址是在exe加载时从文件当中读取的,并赋值到内存地址当中。
如果我们在OD刚刚加载应用程序未调试之前,到004DE928 地址下断点,就会有新的发现。
现在的问题变成了,跟踪004DE928 +11c这个堆地址什么时候初始化的问题了,我在这里的策略是:抢先在初始化堆之前下断点
断点已经命中,如下代码所示::[EBX+11C] = EAX
00454E11 |. 51 PUSH ECX ; |Size
00454E12 |. 6A 00 PUSH 0 ; |Address = NULL
00454E14 |. E8 DFA30500 CALL <JMP.&KERNEL32.VirtualAlloc> ; \VirtualAlloc
00454E19 |. 8983 1C010000 MOV DWORD PTR DS:[EBX+11C],EAX
00454E1F |. 85F6 TEST ESI,ESI ; OLLYDBG.00497D80
接下来通过在[EBX+11C] VirtualAlloc分配的区域的下访问断点
仔细查看初始化的堆空间,已经存在重要数据了,说明最关键的初始化call已经获得了ffff05C9,接下来继续通过栈回溯跟踪
通过层层返回,通过系统函数,到达用户层代码:最终发现是GetClassLongA 这个API获得的ffff05C9 查阅MSDN可以知道GetClassLong返回是注册窗口类的回调函数地址。分析不难发现ffff05C9这个回调函数地址是错误的。
为了增加说服力,我们拿Ansi版本的应用程序来做试验:
OD窗口当中列出的ClassProc地址是全是7xxx打头的如下图所示:
数据窗口查看其中一个75b1af93地址,确实是可执行代码,如下图所示:
于是我们得到这个样一个实事:当使用GetClassLongA获得Unicode版本的窗口回调函数地址时,返回值是ffff0xxx,其值是错误的
当使用GetClassLongA获得Ansi版本的窗口回调函数地址时,返回值是7xxx,其值是正常的。另外我们知道微软为API提供了Ansi版本的和Unicode版本的函数接口。
由上分析可得知,OD当中使用GetClassLongA获得去Unicode版本窗口至少不正常的,应该使用GetClassLongW去获得Unicode版本窗口。
为了证实上面的猜想接下来在OD当中手动修改00497B23这条代码,为将CALL GetClassLongA编辑为 CALL GetClassLongW。如下图所示:
为啥发个图这么难呢!!!
然后重新打开或者刷新OD的window窗口
如下所示:notepad的claproc地址不再是ffffxxxx打头的了。
OK,到此为止我们已经证实我们的猜想,并且基本上确认问题由于OD未检查应用程序窗口是Ansi版本还是Unicode版本,直接调用的GetClassLongA,导致不兼容Unicode版本的窗口应用程序
第三部分:解决方案
解决问题步骤:
1、程序当中需要判断窗口程序是Unicode版本还是Ansi版本的
2、然后根据返回值,调用GetWindowLongA或者GetWindowLongW
由上面步骤可知,该解决方案,需要添加代码较多,我们首先想到的是让程序在执行原GetWindowLongA函数之前,先跳(JMP)到我们修复程序当中,然后再确定是Ansi版本还是Unicode版本,再调用GetWindowLongW或者GetWindowLongA。
基于上面的思想,修复代码基本上确定了。
接下来是考虑注入方案了。
考虑通用性,首先可以排除使用另外一个注入程序来操作;其次加壳、修改PE之类注入也可以排除太麻烦了,这里我们选择OD的插件功能,完成注入。
要写OD插件,就要遵守 OllyDbg 所提供给插件开发者的可用接口规范
简单起见,这里只完成必须的两个接口函数就OK了。
1、ODBG_Plugindata(必需)仅返回一个字符串指针,该字符串就用于标识该插件的唯一名称。
2、ODBG_Plugininit(必需)该函数检查插件版本是否与当前OllyDbg对应,如果对应就执行函数中的其它操作,并执行其它回调函数,表示插件安装成功。
接下来创建简单版本动态链接库工程MessageInterFix:
MessageInterFix要完成如下两项工作:
1)、在DLL被加载时,执行对原程序的调整工作,让程序在执行原GetWindowLongA函数之前,先跳(JMP)到我们修复程序当中,然后再确定是Ansi版本还是Unicode版本,再调用GetWindowLongW或者GetWindowLongA。
2)、在DLL被卸载时,执行对原程序的恢复工作,保证DLL被卸载之后,主程序仍能正常运行。
本程序选择在IAT表挂勾,当然其他地方也可以。
说了那么多,该上代码了:
完整修复代码如下:
#include "stdafx.h"
#include "Plugin.h"
#pragma comment(lib,"OLLYDBG")
DWORD g_nOldpfn = 0;
extc int _export cdecl ODBG_Plugindata(char shortname[32])
{
strcpy(shortname,"MessageInterFix");
return PLUGIN_VERSION;
}
extc int _export cdecl ODBG_Plugininit(int ollydbgversion,HWND hw,
ulong *features)
{
if(ollydbgversion > PLUGIN_VERSION)
{
return -1;
}
return 0;
}
//************************************************************
// 函数名称: FixClassLongProc
// 函数说明: 回调函数
// 作 者: unsummon
// 时 间: 2016/1/19
// 返 回 值: DWORD
//************************************************************
DWORD __stdcall FixClassLongProc(HWND hWnd,int nIndex)
{
int nRetAddr;
if (IsWindowUnicode(hWnd) == TRUE)
{
nRetAddr = GetClassLongW(hWnd,nIndex);
}
else
{
nRetAddr = GetClassLongA(hWnd,nIndex);
}
if (nRetAddr < 0)
{
nRetAddr = GetClassLongW(hWnd,nIndex);
}
return nRetAddr;
}
//************************************************************
// 函数名称: SetHookClassLongA
// 函数说明: 挂勾函数
// 作 者: unsummon
// 时 间: 2016/1/19
// 返 回 值: BOOL
//************************************************************
BOOL SetHookClassLongA()
{
DWORD nNewPfn = 0;
DWORD nOriginalOffset = 0;
DWORD OldProtect = 0;
DWORD nTargetPfnAddr = *(DWORD *)(0x004af420 + 2);//nTargetPfnAddr 位置存放的是函数指针
//保存原来函数指针
memcpy(&g_nOldpfn,(DWORD *)nTargetPfnAddr, 4);
nNewPfn = (DWORD)FixClassLongProc;
//挂勾新的函数指针
if(VirtualProtect((void*)nTargetPfnAddr,6,PAGE_EXECUTE_READWRITE,&OldProtect))
{
*(DWORD *)(nTargetPfnAddr) = nNewPfn;
return VirtualProtect((void *)nTargetPfnAddr,6,OldProtect,&OldProtect);
}
return FALSE;
}
//************************************************************
// 函数名称: UnSetHookClassLongA
// 函数说明:
// 作 者: unsummon
// 时 间: 2016/1/19
// 返 回 值: BOOL
//************************************************************
BOOL UnSetHookClassLongA()
{
//恢复原来函数指针
DWORD OldProtect = 0;
DWORD nTargetPfnAddr = *(DWORD *)(0x004af420 + 2);
if(VirtualProtect((void*)nTargetPfnAddr,6,PAGE_EXECUTE_READWRITE,&OldProtect))
{
*(DWORD *)(nTargetPfnAddr) = g_nOldpfn;
return VirtualProtect((void *)nTargetPfnAddr,6,OldProtect,&OldProtect);
}
return TRUE;
}
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch(ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
SetHookClassLongA();
}
break;
case DLL_PROCESS_DETACH:
{
UnSetHookClassLongA();
}
break;
}
return TRUE;
}
file:///D:/Users/Administrator/Documents/My%20Knowledge/temp/af53ed06-ce67-4bf6-becd-1bf1c2ae75a3.pngfile:///D:/Users/Administrator/Documents/My%20Knowledge/temp/af53ed06-ce67-4bf6-becd-1bf1c2ae75a3.pngfile:///D:/Users/Administrator/Documents/My%20Knowledge/temp/af53ed06-ce67-4bf6-becd-1bf1c2ae75a3.pngfile:///D:/Users/Administrator/Documents/My%20Knowledge/temp/af53ed06-ce67-4bf6-becd-1bf1c2ae75a3.png
file:///D:/Users/Administrator/Documents/My%20Knowledge/temp/af53ed06-ce67-4bf6-becd-1bf1c2ae75a3.png
|