【文章标题】: 监视VB调用CreateObject
【文章作者】: Jiangjing
【下载地址】: 自己搜索下载
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
背景:
工作中开发VB程序,不同工程中调用Dll,基本上都是动态,使用CreateObject方法,本来厂商有个调试器来监视VB程序的
CreateObject,但是在某些电脑上运行崩溃.极度郁闷.有时候Active dll被破坏了没法大致确定位置.不用在测试机器上加
一大堆源码跑起来跟踪.所以想DIY一个自己的伪监视器...
开工:
随便写个代码 '---VB Code
Private Sub Command1_Click()
On Error GoTo ErrHandler
Dim t As Object
Set t = CreateObject("pro_dll.clsdll") '--随便写个名称
Exit Sub
ErrHandler:
MsgBox Err.Number & vbCrLf & Err.Description
End Sub
编译为EXE,执行,问题来了,怎么来监视呢?拿出经常用的工具OD,来调试跟踪一下这个EXE
发现CreateObject调用的是MSVBVM60.DLL的rtcCreateObject2
先看下MSVBVM60.DLL导出的函数接口
==================================================
函数名称 : rtcCreateObject
偏移地址 : 0x735192b7
相对偏移地址 : 0x000d92b7
顺序数 : 625 (0x271)
文件名 : msvbvm60.dll
类型 : 导出函数
==================================================
==================================================
函数名称 : rtcCreateObject2
偏移地址 : 0x735193c0
相对偏移地址 : 0x000d93c0
顺序数 : 716 (0x2cc)
文件名 : msvbvm60.dll
类型 : 导出函数
==================================================
以下是EXE汇编
00402084 . 53 push ebx
00402085 . 68 04194000 push 00401904 ; pro_dll.clsdll
0040208A . 8D45 B0 lea eax, dword ptr [ebp-50]
0040208D . 50 push eax
0040208E . FF15 78104000 call dword ptr [<&MSVBVM60.#716>] ; MSVBVM60.rtcCreateObject2
于是我们跟踪进去,调试一下,CreateObject成功的时候与失败的时候应该有区分的.....[暂时忽略on error resume next]
dll空间
735193C0 > 55 push ebp ; rtcCreateObject2 //入口
735193C1 8BEC mov ebp, esp
735193C3 83EC 78 sub esp, 78
735193C6 8B45 0C mov eax, dword ptr [ebp+C]
往下走...省略一大堆代码
73519636 5F pop edi
73519637 5E pop esi
73519638 5B pop ebx
73519639 C9 leave
7351963A C2 0C00 retn 0C
7351963D 8B45 8C mov eax, dword ptr [ebp-74]
73519640 50 push eax
73519641 8B08 mov ecx, dword ptr [eax]
73519643 FF51 08 call dword ptr [ecx+8]
73519646 8B45 98 mov eax, dword ptr [ebp-68]
73519649 66:C745 E8 0900 mov word ptr [ebp-18], 9
7351964F ^ EB D6 jmp short 73519627
73519651 8D45 D8 lea eax, dword ptr [ebp-28]
73519654 50 push eax
73519655 FF75 0C push dword ptr [ebp+C] //[ebp+C]里面存放的就是CreateObject的参数,需要这东西,才知道调用了什么组件
73519658 FF15 30ED5473 call dword ptr [7354ED30] ; ole32.CLSIDFromProgIDEx
7351965E 3BC6 cmp eax, esi //如果创建Active dll 成功,eax=0,否则eax不等于0
73519660 0F8C A7000000 jl 7351970D //我们修要修改这个跳转,来调用一下自己的代码
73519666 56 push esi
73519667 56 push esi
73519668 FF15 041A4473 call dword ptr [<&OLEAUT32.#201>] ; OLEAUT32.SetErrorInfo
注意分析
7351965E 3BC6 cmp eax, esi //关键点在这
--找一段空白的空间[7353C3D6],先跳转到7353C3D6,处理完之后,在返回73519663,这样就不影响原有功能
73519654 . 50 push eax
73519655 . FF75 0C push dword ptr [ebp+C]
73519658 . FF15 30ED5473 call dword ptr [7354ED30]
7351965E . E9 732D0200 jmp 7353C3D6 //修改汇编
73519663 90 nop
73519664 90 nop
73519665 90 nop
73519666 > 56 push esi
73519667 . 56 push esi
--7353C3D6
7353C3D3 00 db 00
7353C3D4 00 db 00
7353C3D5 00 db 00
7353C3D6 > 83F8 00 cmp eax, 0 //根据eax值来判断CreateObject成功失败!!!
7353C3D9 . 75 04 jnz short 7353C3DF
7353C3DB . 6A 01 push 1 //参数1
7353C3DD . EB 02 jmp short 7353C3E1
7353C3DF > 6A 00 push 0 //参数1
7353C3E1 > FF75 0C push dword ptr [ebp+C] //参数2 备注:[ebp+c]是CrateObject的第一个参数
7353C3E4 . E8 8CFFFFFF call 7353C375 //我的[函数A]
7353C3E9 . 83C4 08 add esp, 8 //注意,,,这里传了2个参数进去.需要处理一下
7353C3EC . 90 nop
7353C3ED . 90 nop
7353C3EE . 3BC6 cmp eax, esi //调用添加的[函数A]后还原原有逻辑
7353C3F0 .^ 0F8C 17D3FDF>jl 7351970D //xxx
7353C3F6 .^ E9 6BD2FDFF jmp 73519663 //跳回,继续顺序执行
--7353C375
7353C374 90 nop
7353C375 90 nop //
7353C376 90 nop
7353C377 90 nop
7353C378 56 push esi
7353C379 68 30CC5373 push 7353CC30 //注意1
7353C37E FF15 F0114473 call dword ptr [<&KERNEL32.LoadLibrar>; kernel32.LoadLibraryA //找到这个API
7353C384 8BF0 mov esi, eax
7353C386 85F6 test esi, esi
7353C388 74 23 je short 7353C3AD
7353C38A 68 20CC5373 push 7353CC20 //注意2
7353C38F 56 push esi
7353C390 FF15 F4114473 call dword ptr [<&KERNEL32.GetProcAdd>; kernel32.GetProcAddress //找到这个API,
7353C396 85C0 test eax, eax
7353C398 74 0C je short 7353C3A6
7353C39A 8B4C24 0C mov ecx, dword ptr [esp+C]
7353C39E 8B5424 08 mov edx, dword ptr [esp+8]
7353C3A2 51 push ecx
7353C3A3 52 push edx
7353C3A4 FFD0 call eax
7353C3A6 56 push esi
7353C3A7 FF15 60104473 call dword ptr [<&KERNEL32.FreeLibrar>; kernel32.FreeLibrary
7353C3AD 5E pop esi
7353C3AE C3 retn
//这里需要注意4个地方
1:这段代码对于不是很熟悉汇编的不好理解,
可以用VC写段调用dll的代码,2个参数值,然后用OD调试一下,把汇编代码粘贴进来
2:kernel32.LoadLibraryA,kernel32.GetProcAddress,kernel32.FreeLibrary 这3个API函数的地址怎么确定,
可以在OD里面bpx hmemcpy 查找原有程序调用该API地址,复制过来即可
3:[注意1],有个push,这个是LoadLibraryA的参数,一猜就知道,那就是这段汇编要调用我们自己定义的一个xxx.dll文件名
4:[注意2],也有个push,是kernel32.GetProcAddress的参数,就是xxx.dll导出的函数接口YYY
剩下的就是我们编写xxx.dll中的YYY函数,
函数原型应该能猜解出来
void GetString(char *p,int iFlag) 参数iflag是先入栈的,然后依次向左...
接下来dll应该改什么呢?把每次调用的CrateObject的第一个参数[ebp+c]插入数据库(最近一直搞数据库,脑袋都僵硬了),
实时刷新.这样好像不太好,应该FindWindow,然后SendMessage,把里面的2个值发送到我们自己的监视程序界面上去,才显
的直观,如果有更好的意见或建议,欢迎拍板砖
以下是xxx.dll里面YYY函数
发送格式[参数1]:进程ID号|pro_dll.clsdll,[参数2:] 0或1
void WINAPI ReadParam(char *p,int iFlag)
{
HWND hWnd=::FindWindow(NULL,"GetMessage"); //监视程序EXE名GetMessage
if (!hWnd)
{
//MessageBox(NULL,"GetMessage没有运行!","警告",MB_OK|MB_ICONSTOP);
return;
}
char tmp[200];
memset(tmp,0,200);
int i=0,j=0;
char t1=0,t2=0;
//先获取PID号
sprintf(tmp,"%u|",GetCurrentProcessId()); //为了避免任何一个调用该dll的文件发送消息,监视程序通过这个来判断
j=strlen(tmp);
//*p的内容为Unicode ,手动抓取参数值
while (1==1 && j<200)
{
t1=*p;
t2=*p++;
if (t1=='\0' && t2=='\0') //如果有2个连续的的\0,结束,VB的编码默认是Unicode
{
goto EndHandler;
}
if (t1!='\0') //只发现一个\0跳过去
{
tmp[j]=t1;
j++;
}
p++;
}
EndHandler:
COPYDATASTRUCT cda;
cda.dwData=(unsigned long)iFlag;
cda.cbData=strlen(tmp);
cda.lpData=(void*)tmp;
::SendMessage(hWnd,WM_COPYDATA,NULL,(LPARAM)&cda); //传给监视程序!
return ;
}
GetMessage程序,添加一个消息COPYDATA来接受数据的时候,根据接受的进程号来区分是哪个进程,不然混淆了...
BOOL CGetMessageDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct)
{
// TODO: Add your message handler code here and/or call default
CString tmp;
CString m_GetPID;
UpdateData();
tmp.Format("%s",(char *)pCopyDataStruct->lpData);
tmp=tmp.Left(pCopyDataStruct->cbData);
m_GetPID.Format("%d",theApp.dwPID);
if(m_GetPID!=tmp.Left(tmp.Find('|',1))) //保持从dll格式一致,分离|左右2边的数据
{
//进程PID校验,避免数据混乱
goto ContinueHandler;
}
m_GetPID.Format("%d",iIndex);
m_List.InsertItem(0,m_GetPID);
tmp=tmp.Right(tmp.GetLength()-tmp.Find('|',1)-1);
m_Str=tmp;
m_List.SetItemText(0,1,tmp);
if (pCopyDataStruct->dwData==0)
m_List.SetItemText(0,2,"X失败X");
else
m_List.SetItemText(0,2,"--");
iIndex++;
ContinueHandler:
tmp.ReleaseBuffer();
m_GetPID.ReleaseBuffer();
UpdateData(false);
return CDialog::OnCopyData(pWnd, pCopyDataStruct);
}
//代码写的比较乱,欢迎各位扔砖头.
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2010年10月16日 5:41:14
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
上传的附件: