Chinaunix首页 | 论坛 | 博客
  • 博客访问: 147409
  • 博文数量: 17
  • 博客积分: 2146
  • 博客等级: 大尉
  • 技术积分: 305
  • 用 户 组: 普通用户
  • 注册时间: 2008-01-07 23:26
文章分类

全部博文(17)

文章存档

2011年(1)

2009年(5)

2008年(11)

分类: WINDOWS

2009-09-12 08:25:45

继续上回的讨论,在开始之前,首先感谢论坛里热心的兄弟们给出的建议和帮助,在他们的帮助下,我对这个问题有了进一步的理解。
尤其感谢syncpk99兄弟提供了国外的完整解决方案()的和给与详细解释的OwnWaterloo 兄弟,感谢!
你们是未来的希望。鉴于国外的这个解决方案已经大大的超过了我的预期,因此这里不再讨论我的想法,按照我的理解,对这个方案
谈点体会。
上次讨论我们实行了初步的动态调用的可行性,但是,若想达到灵活的调用,我们必须解决参数压栈的问题,问题在于如何将未知个数
的参数们压进栈中呢?以下参考的做法,见代码:
int DyCall(void* pfAddr,void* pData,int nsize)
{      
        //pfAddr为被调用函数的地址,pData为参数的"串"缓冲区,nsize为参数缓冲区的大小,以上几个参数将从其他方法构造
 int nRet;
 __asm
 {
   //注意调用方式,这是stdcall  
   push esi                     ; 保存esi和edi,因为我们要用这两个来拷贝串,拷贝串的目的是模拟压栈
   push edi
   mov  esi, dword ptr [pData]  ; esi 指向参数串
   mov  ecx, nsize       ; ecx 保存串长度
 
   sub  esp, ecx                ; 以stdcall的方式在栈上开辟size大小的空间
   mov  edi, esp                ; edi 指向栈顶
   rep movsb                    ; 拷贝参数到edi指向的地址,即栈上,拷贝的循环次数由ecx保存的大小决定
   call pfAddr       ; 开始调用
   pop  edi                     ; 恢复 edi, esi
   pop  esi
   mov nRet,eax                 ;取得回值   
 }
 return nRet;
}
以上是windows下stdcall方式的实现的调用过程,其他的调用方式对参数压栈的处理是类似的。基本思想是改push为copy,很巧妙不是,问题
又来了,如何构造参数缓冲区呢?以下我做了几个例子。先来两个加载库和卸载库的函数,节省体力
#include
#include
int DyLoad(HMODULE* phMod,FARPROC* pfAddr,char* libName,char* procName)
{
 //加载函数库
 *phMod = LoadLibraryA(libName);
 //取得函数地址
 if(*phMod != NULL)
  *pfAddr = GetProcAddress(*phMod,procName); 
 return 0;
}
int DyUnload(HMODULE hMod)
{
 //释放函数库
 if(hMod != NULL)
  FreeLibrary(hMod);
 return 0;
}
下面是 Beep的构造参数缓冲区及调用过程,Beep的原型是BOOL Beep(DWORD dwFreq,DWORD dwDuration),有两个DWORD的参数,就是两个int
void __Beep(DWORD dwFreq,DWORD dwDuration)
{
 HMODULE hMod ; 
 FARPROC pfAddr ;
 DyLoad(&hMod,&pfAddr,"kernel32.dll","Beep"); //加载库,并取得Beep的地址
 int nRet ;//返回值
 //构造缓冲区
 BYTE buf[255] = {0} ; 
 ZeroMemory(buf,sizeof(buf));                //Beep的原型是BOOL Beep(DWORD dwFreq,DWORD dwDuration),
 memcpy(buf,&dwFreq,sizeof(dwFreq));         //按照stdcall的顺序,从左向右顺序拷贝到缓冲区
 memcpy(buf + sizeof(dwFreq),&dwDuration,sizeof(dwDuration)); 
 //构造结束
 
 int len = sizeof(dwFreq) + sizeof(dwDuration)  ; //计算缓冲区真实长度
 
 nRet = DyCall(pfAddr,buf,len);   // 开始调用
 printf("%d",nRet);
 DyUnload(hMod);
}
再来个不含参数的GetCurrentProcessId(),原型是DWORD GetCurrentProcessId()
void __GetCurrentProcessId()
{
 HMODULE hMod ; 
 FARPROC pfAddr ;
 DyLoad(&hMod,&pfAddr,"kernel32.dll","GetCurrentProcessId");//加载库,并取得GetCurrentProcessId的地址
 int nRet ;//返回值
 //构造缓冲区
 BYTE buf[255] = {0} ;
 ZeroMemory(buf,sizeof(buf));  
 int len = 0; //无参数,置0即可
 nRet = DyCall(pfAddr,buf,len);  //开始调用
 printf("%d",nRet);
 DyUnload(hMod);
}
一个char*参数的SetConsoleTitleA,原型是SetConsoleTitle(char* pszTitle)
void __SetConsoleTitle(char* title)
{
 HMODULE hMod ; 
 FARPROC pfAddr ;
 DyLoad(&hMod,&pfAddr,"kernel32.dll","SetConsoleTitleA");
 int nRet ;
 BYTE buf[255] = {0} ;
 int len = 0;
 ZeroMemory(buf,sizeof(buf));
 memcpy(buf,&title,sizeof(title));
 len += sizeof(title);//strlen(title);
 nRet = DyCall(pfAddr,buf,len);
 printf("%d",nRet);
 DyUnload(hMod);
}
再来个多种参数的MessageBoxW,原型是int MessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType);
void __MessageBox()
{
 HMODULE hMod  ;
 FARPROC pfAddr ;
 DyLoad(&hMod,&pfAddr,"user32.dll","MessageBoxW");
 int nRet ;//返回值
 //初始化调用所需的参数
 DWORD* dHandle = NULL;               //没有所属窗口
 TCHAR* pszText = L"您好";
 TCHAR* pszCaption =L" 谢谢 ";
 int ntype = MB_OK|MB_APPLMODAL|MB_ICONINFORMATION;
 int len = 0;
 //构造缓冲区,并计算大小
 BYTE buf[255] = {0} ;
 ZeroMemory(buf,sizeof(buf));
 memcpy(buf,&dHandle,sizeof(dHandle));  //拷贝所属窗口参数
 len += sizeof(dHandle);
 memcpy(buf + len,&pszText,sizeof(pszText)); //拷贝显示的消息文本
 len += sizeof(pszText) ;                     
 memcpy(buf + len,&pszCaption,sizeof(pszCaption)); //拷贝显示的消息标题
 len += sizeof(pszCaption) ;
 memcpy(buf + len,&ntype,sizeof(ntype));          // 拷贝消息框风格参数
 len += sizeof(ntype);
 nRet = DyCall(pfAddr,buf,len);                   //调用
 printf("%d",nRet);
 DyUnload(hMod);
}
int main()

        __SetConsoleTitle("Demo");
 __GetCurrentProcessId();
 __MessageBox();
 __Beep(750,300);
 return 0;
}
入口函数:
好,既然这个办法可行了,剩下的问题是如何在脚本里根据解析到的函数原型构造参数缓冲区,毕竟,写包装函数不是我们的最终目的,有兴趣的
可以参照的做法,根据参数类型去构造参数缓冲区,这样,我们把任务一步一步的推向了调用者
阅读(2424) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~