C++怎樣實現(xiàn)Win32 ASM中的invoke調(diào)用 bydarkst 2012-11-20, 21:34:35 不知道有沒有人能幫忙實現(xiàn)下面這樣一個invoke函數(shù)或者有更好的方法 DWORD invoke(char* lib, char* func_name, ...) { //這邊大概是這么寫 Farproc func = GetProcAddress(LoadLibrary(lib), func_name); //下面就是把后面的不定參數(shù)傳遞給上面這個函數(shù) return func(...); } //我覺得可能是要先取出參數(shù)個數(shù)va_list va_start va_end //然后從后往前依次__asm push parma,最后call func //但是感覺還是比較復雜,而且不夠通用(不一定是stdcall) //這樣做的目的是不用通過導入表實現(xiàn)動態(tài)調(diào)用函數(shù),關鍵是這要是一個通用函數(shù) 調(diào)用如下: invoke("kernel32.dll", "Sleep", 1000); int nRet = invoke("user32.dll", "MessageBoxA", NULL, "hello", "info", MB_OK); invoke("msvcrt.dll", "printf", "1+2=%d\n", 3); lononan 2012-11-20, 22:59:13 以下是我成功測試MessageBoxA的例子,其他函數(shù)未測試: 73733 #include "stdafx.h" #include <windows.h> #include <stdarg.h> /***************************************** lib:庫文件 FucName:函數(shù)名 ...:參數(shù),(注意,參數(shù)按匯編方式入棧,參數(shù)順序倒著寫) *****************************************/ int myInvoke( char* lib,char* FucName ,...) { va_list arg_ptr; int nResult; int funAdd=(int)GetProcAddress(LoadLibrary(lib),FucName); va_start(arg_ptr, FucName); int p; while((p=va_arg( arg_ptr, int ))!=-1)//如果p==-1表示結(jié)束 { _asm push p; } _asm { call [funAdd]; mov nResult,eax } va_end(arg_ptr); return nResult; } int main(int argc, char* argv[]) { myInvoke("user32.dll","MessageBoxA",MB_YESNO,"標題","消息",0,-1); //最后一個參數(shù)-1是結(jié)束標志 return 0; } LOVEZ 2012-11-20, 23:07:54 :confused: 有這個必要么 bydarkst 2012-11-21, 11:40:20 以下是我成功測試MessageBoxA的例子,其他函數(shù)未測試: 73733 #include "stdafx.h" #include <windows.h> #include <stdarg.h> /***************************************** lib:庫文... 謝謝你的回復:cool: HOWMP 2012-11-21, 13:27:55 :o:挺難的,沒法判斷被調(diào)用函數(shù)的調(diào)用類型。MessageBoxA是__stdcall,printf是__cdecl。前者不需要invoke平衡堆棧,但后者需要。 這如何解決? lononan 2012-11-21, 14:52:12 :o:挺難的,沒法判斷被調(diào)用函數(shù)的調(diào)用類型。MessageBoxA是__stdcall,printf是__cdecl。前者不需要invoke平衡堆棧,但后者需要。 這如何解決? 經(jīng)測試, myInvoke("msvcrt.dll","printf","sss\n",-1); system("pause"); 成調(diào)用啊,不過Debug版本會提示錯誤,而Release正常,通常我們都是用Release版本吧? HOWMP 2012-11-21, 16:01:31 :3:LS歡迎討論。 我覺得你寫這個以-1結(jié)尾有點牽強,如果參數(shù)里有-1怎么辦?豈不是悲劇了。 至于release能成功的原因 PUSH EBP MOV EBP,ESP …… POP EBP RETN debug不成功是因為每次CALL調(diào)用都有退棧平衡檢查 // invoke.cpp : 定義控制臺應用程序的入口點。 // #include "stdafx.h" #include <windows.h> /* 0012FF64 |0040103D 返回到 invoke.0040103D 來自 invoke.00401000 0012FF68 |00402110 ASCII "user32.dll" 0012FF6C |00402104 ASCII "MessageBoxA" 0012FF70 |00000000 0012FF74 |004020FC ASCII "hello" 0012FF78 |004020F4 ASCII "info" 0012FF7C |00000000 */ #define MEMCPY(pDest,pSource,S) \ __asm mov edi,pDest \ __asm mov esi,pSource \ __asm mov ecx,S \ __asm xor eax,eax \ __asm cld \ __asm lodsb \ __asm stosb \ __asm dec ecx \ __asm test ecx,ecx \ __asm _emit 0x75 \ __asm _emit 0xF9 #define NAKED __declspec(naked) int NAKED invoke(char* lib,char* funname ,...) { PULONG stack,arg; ULONG argsize,bak_esp,fun; __asm { mov stack,esp } //找到調(diào)用invoke的返回地址,add esp,0xx 即可計算參數(shù)個數(shù) if(*(PUSHORT)stack[0] == 0xC483) { argsize = *(PUCHAR)(stack[0]+2) - 8;//invoke參數(shù)大小 減去8(2*4) 就是要調(diào)用函數(shù)的參數(shù)的大小 arg = &stack[3]; } else { __asm mov eax,-1 goto RET; } fun = (ULONG)GetProcAddress(LoadLibraryA((PCHAR)stack[1]),(PCHAR)stack[2]); __asm { mov bak_esp,esp sub esp,argsize MEMCPY(esp,arg,argsize) call fun mov esp,bak_esp //保證C調(diào)用的堆棧平衡 } RET: __asm { ret } } int _tmain(int argc, _TCHAR* argv[]) { invoke("kernel32.dll", "Sleep", 1000); invoke("user32.dll", "MessageBoxA", NULL, "hello", "info", MB_OK); invoke("msvcrt.dll", "printf", "1+2=%d\n", 3); getchar(); return 0; } 寫了這么一段,發(fā)現(xiàn)以下幾個問題: 0.網(wǎng)上查資料,不定參數(shù)函數(shù),要么和LZ一樣通過一個特定的參數(shù)如-1,來判斷參數(shù)個數(shù)。 要么就是參數(shù)個數(shù)也傳給函數(shù)。 1.不定參數(shù)函數(shù),由調(diào)用函數(shù)平衡堆棧,比如這里由main平衡invoke 2.由于1,所以invoke調(diào)用完成以后會有ADD ESP,XXX,根據(jù)這個原理可以通過這里判斷參數(shù)個數(shù) 3.當寫好之后,測試正常。但當三個invoke同時出現(xiàn)的時候,release悲劇了。編譯器直接優(yōu)化了,三次調(diào)用完成以后才進行堆棧平衡。所以前兩次均失敗。 4.printf通過什么方式判斷參數(shù)個數(shù)? printf("%d\n%d\n%d\n",1); 1 4198786 1 猜測可能是根據(jù)%個數(shù)來確定參數(shù)個數(shù)。 于是,問題來了,到底有沒有一個通用的方法,來確定不定參數(shù)函數(shù)的參數(shù)個數(shù)? lononan 2012-11-21, 16:43:54 那可以自定義結(jié)束標志的參數(shù)嘛。 NWmLWB 2012-11-21, 20:42:03 還沒寫過變參數(shù)的c函數(shù)呢,2樓方法不錯 HOWMP 2012-11-22, 14:09:38 那可以自定義結(jié)束標志的參數(shù)嘛。 :o:不管如何設置,總能出現(xiàn)參數(shù)類似標識的情況。 實現(xiàn)函數(shù)已經(jīng)完成了,現(xiàn)在討論的是通用性。。 bydarkst 2012-12-06, 17:30:01 額,好久沒來了,我以為2樓以后就沒人回復了 我是對二樓又補充了下,參數(shù)不用從倒過來寫,也不用-1做標識 對于測試的三個函數(shù)可以,但是仍未處理調(diào)用約定問題 希望相互討論,能繼續(xù)完善它 #include <windows.h> #include <stdio.h> #include <iostream> #include <vector> using namespace std; int StdInvoke(char* lib,char* FucName ,...) { va_list ap; int nResult, nCount, nFunAddr; vector<int> args; nCount = ((((*(int*)(*(int*)((int)&lib-4)))&0xff0000) >>16)/4) - 2; HMODULE hModule = lib?LoadLibrary(lib):GetModuleHandle("kernel32.dll"); nFunAddr = (int)GetProcAddress(hModule, FucName); va_start(ap, FucName); for (int i=0; i< nCount; i++) args.push_back(va_arg(ap, int)); while (!args.empty()) { i = args.back(); args.pop_back(); __asm push i } _asm { call [nFunAddr]; mov nResult,eax } va_end(ap); if(lib) FreeLibrary(hModule); return nResult; } int main(int argc, char* argv[]) { StdInvoke("msvcrt.dll", "printf", "1+2=%d %s\n", 1+2, "haha"); StdInvoke(NULL,"Sleep",3000); if(IDYES == StdInvoke("user32.dll","MessageBoxA", 0, "content", "title", MB_YESNO)) printf("you pressed yes\n"); else printf("you pressed no\n"); return 0; } pengkui 2012-12-09, 15:12:18 源碼: #include <windows.h> #include <stdio.h> unsigned long __declspec(naked) _calleresp() { __asm mov eax, esp __asm add eax, 4 __asm ret } int __declspec(naked) _invoke(const char* libstr, const char* funcstr, ...) { __asm { mov [esp-4],edi mov edi, [esp] mov [eax], edi sub eax, 4 mov [eax], esp sub eax, 4 mov edi, [esp-4] mov [eax], edi mov edi, eax add edi, 4 sub eax, 4 mov [eax], ecx add esp, 0x04 mov eax, [esp] call DWORD ptr[LoadLibraryA] mov ecx, [esp] push ecx push eax call DWORD ptr[GetProcAddress] add esp, 0x04 call eax mov esp, edi add esp, 4 mov ecx,[esp] mov esp, [esp-4] mov [esp], ecx mov esp, edi mov edi, [esp-4] mov ecx, [esp-8] mov esp, [esp] ret } } #define invoke(libstr, funcstr, ...) _invoke(libstr, funcstr, __VA_ARGS__, NULL, NULL, NULL, NULL, _calleresp()) #define INVOKE_TEST(libstr, funcstr, ...) printf("TEST:\t%s(%s)\tRET:\t%d\n\n", libstr, funcstr, invoke(libstr, funcstr, __VA_ARGS__)) int main(int argc, char* argv[]) { INVOKE_TEST("msvcrt.dll", "printf", "看雪論壇-%s\n", "pengkui"); INVOKE_TEST("invokeTestDll.dll", "cdeclFunc"); INVOKE_TEST("invokeTestDll.dll", "stdcallFunc"); return 0; } 測試dll,.c: #include <stdio.h> int __declspec(dllexport) __stdcall stdcallFunc() { return printf("%s\n", __FUNCTION__); } int __declspec(dllexport) __cdecl cdeclFunc() { return printf("%s\n", __FUNCTION__); } 測試dll, .def LIBRARY "invokeTestDll" EXPORTS stdcallFunc = stdcallFunc cdeclFunc = cdeclFunc 運行結(jié)果: E:\pictures\捕獲.png (圖片上傳不了,不知怎么回事) |
|