能力值:
( LV4,RANK:50 )
|
-
-
2 楼
以下是我成功测试MessageBoxA的例子,其他函数未测试:
#include "stdafx.h"
#include <windows.h>
#include <stdarg.h>
/*****************************************
lib:库文件
FucName:函数名
...:参数,(注意,参数按汇编方式入栈,参数顺序倒着写)
*****************************************/
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表示结束
{
_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);
//最后一个参数-1是结束标志
return 0;
}
|
能力值:
( LV2,RANK:15 )
|
-
-
3 楼
有这个必要么
|
能力值:
( LV2,RANK:10 )
|
-
-
4 楼
谢谢你的回复
|
能力值:
( LV6,RANK:80 )
|
-
-
5 楼
挺难的,没法判断被调用函数的调用类型。MessageBoxA是__stdcall,printf是__cdecl。前者不需要invoke平衡堆栈,但后者需要。
这如何解决?
|
能力值:
( LV4,RANK:50 )
|
-
-
6 楼
经测试,
myInvoke("msvcrt.dll","printf","sss\n",-1);
system("pause");
成调用啊,不过Debug版本会提示错误,而Release正常,通常我们都是用Release版本吧?
|
能力值:
( LV6,RANK:80 )
|
-
-
7 楼
LS欢迎讨论。
我觉得你写这个以-1结尾有点牵强,如果参数里有-1怎么办?岂不是悲剧了。
至于release能成功的原因
PUSH EBP
MOV EBP,ESP
……
POP EBP
RETN
debug不成功是因为每次CALL调用都有退栈平衡检查
// 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
}
//找到调用invoke的返回地址,add esp,0xx 即可计算参数个数
if(*(PUSHORT)stack[0] == 0xC483)
{
argsize = *(PUCHAR)(stack[0]+2) - 8;//invoke参数大小 减去8(2*4) 就是要调用函数的参数的大小
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调用的堆栈平衡
}
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;
}
写了这么一段,发现以下几个问题:
0.网上查资料,不定参数函数,要么和LZ一样通过一个特定的参数如-1,来判断参数个数。
要么就是参数个数也传给函数。
1.不定参数函数,由调用函数平衡堆栈,比如这里由main平衡invoke
2.由于1,所以invoke调用完成以后会有ADD ESP,XXX,根据这个原理可以通过这里判断参数个数
3.当写好之后,测试正常。但当三个invoke同时出现的时候,release悲剧了。编译器直接优化了,三次调用完成以后才进行堆栈平衡。所以前两次均失败。
4.printf通过什么方式判断参数个数?
printf("%d\n%d\n%d\n",1);
1
4198786
1
猜测可能是根据%个数来确定参数个数。 于是,问题来了,到底有没有一个通用的方法,来确定不定参数函数的参数个数?
|
能力值:
( LV4,RANK:50 )
|
-
-
8 楼
那可以自定义结束标志的参数嘛。
|
能力值:
( LV2,RANK:10 )
|
-
-
9 楼
还没写过变参数的c函数呢,2楼方法不错
|
能力值:
( LV6,RANK:80 )
|
-
-
10 楼
不管如何设置,总能出现参数类似标识的情况。
实现函数已经完成了,现在讨论的是通用性。。
|
能力值:
( LV2,RANK:10 )
|
-
-
11 楼
额,好久没来了,我以为2楼以后就没人回复了
我是对二楼又补充了下,参数不用从倒过来写,也不用-1做标识
对于测试的三个函数可以,但是仍未处理调用约定问题
希望相互讨论,能继续完善它
#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;
}
|
能力值:
( LV2,RANK:10 )
|
-
-
12 楼
源码:
#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
运行结果:
(图片上传不了,不知怎么回事)
|
|
|