首页
社区
课程
招聘
[旧帖] C++怎样实现Win32 ASM中的invoke调用 0.00雪花
发表于: 2012-11-20 21:34 8419

[旧帖] C++怎样实现Win32 ASM中的invoke调用 0.00雪花

2012-11-20 21:34
8419
不知道有没有人能帮忙实现下面这样一个invoke函数或者有更好的方法

DWORD invoke(char* lib, char* func_name, ...)
{
    //这边大概是这么写
    Farproc func = GetProcAddress(LoadLibrary(lib), func_name);
    //下面就是把后面的不定参数传递给上面这个函数
    return func(...);
}

//我觉得可能是要先取出参数个数va_list va_start va_end
//然后从后往前依次__asm push parma,最后call func
//但是感觉还是比较复杂,而且不够通用(不一定是stdcall)

//这样做的目的是不用通过导入表实现动态调用函数,关键是这要是一个通用函数

调用如下:
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);

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 0
支持
分享
最新回复 (11)
雪    币: 7583
活跃值: (5422)
能力值: ( 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;
}
上传的附件:
2012-11-20 22:59
0
雪    币: 76
活跃值: (206)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
3
有这个必要么
2012-11-20 23:07
0
雪    币: 113
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
谢谢你的回复
2012-11-21 11:40
0
雪    币: 2865
活跃值: (2668)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
5
挺难的,没法判断被调用函数的调用类型。MessageBoxA是__stdcall,printf是__cdecl。前者不需要invoke平衡堆栈,但后者需要。

这如何解决?
2012-11-21 13:27
0
雪    币: 7583
活跃值: (5422)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
6
经测试,
myInvoke("msvcrt.dll","printf","sss\n",-1);
system("pause");
成调用啊,不过Debug版本会提示错误,而Release正常,通常我们都是用Release版本吧?
2012-11-21 14:52
0
雪    币: 2865
活跃值: (2668)
能力值: ( 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

猜测可能是根据%个数来确定参数个数。

于是,问题来了,到底有没有一个通用的方法,来确定不定参数函数的参数个数?
2012-11-21 16:01
0
雪    币: 7583
活跃值: (5422)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
8
那可以自定义结束标志的参数嘛。
2012-11-21 16:43
0
雪    币: 84
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
还没写过变参数的c函数呢,2楼方法不错
2012-11-21 20:42
0
雪    币: 2865
活跃值: (2668)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
10
不管如何设置,总能出现参数类似标识的情况。
实现函数已经完成了,现在讨论的是通用性。。
2012-11-22 14:09
0
雪    币: 113
活跃值: (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;
}
2012-12-6 17:30
0
雪    币: 4
活跃值: (25)
能力值: ( 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


运行结果:

(图片上传不了,不知怎么回事)
2012-12-9 15:12
0
游客
登录 | 注册 方可回帖
返回
//