【文章标题】: 监视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

上传的附件 监视CreateObject.rar