Okay, here goes. First off, this is nontested, and just so you can get an idea of how to implement it. I think it works though lol. There are some comments around, but basically, you need to assemble asm in your function, you need to know convention, return type, etc. etc. It's not that hard to do with standard types, but floats and doubles use other registers and stacks, so you might wanna read up on that. functions, that returns structs, is usually very implementation defined and often optimized away so you probably wont have luck with that.
All in all, if you are not 100% about what you do, this is not the road you wanna go down if you need a stable system. Nevertheless, this is quite fun
//by shaggi: quick dirty nontested example of dllcall
//x86 ofc
//only works with very primitive types and stdcall atm
//should be used like:
#include <windows.h>
#include <iostream>
/*
int args [] = {1, 2};
int result;
result_t res = DllCall<rsint>(ret_t::rint, conv_t::cstdcall, (void*)0xDEADBEEF, ARRAYSIZE(args), args, & result);
*/
enum conv_t
{
ccdecl,
cstdcall,
cfastcall,
cthiscall
};
enum ret_t
{
rint,
rsint,
rfloat,
rdouble,
rptr
};
enum result_t
{
success,
bad_address,
bad_arguments,
bad_return,
nothing_happened
};
#define OPCODE_PUSH_SIZE 0x1
#define OPCODE_CALL_SIZE 0x1
#define CALL_ASM_SIZE 0x5
#define MOV_PTR_EAX_SIZE 0x5
#define RETN_SIZE 0x1
#define OPCODE_RETN 0xC3
#define POS_BUF 0x10
#define OPCODE_PUSH 0x68
#define OPCODE_CALL 0xE8
#define OPCODE_MOV_DWORD_PTR_FROM_EAX 0xA3
#define OPCODE_MOV_DWORD_PTR_FROM_EAX_SIZE 1
#define OPCODE_MOV_EAX 0xB8
#define MOV_EAX_SIZE 0x1
#define OPCODE_CALL_EAX 0xD0FF
#define CALL_EAX_SIZE 0x2
#define MOV_EAX_TOTAL_SIZE 0x5
//this might be wrong can't remember lol.
#define addressOf(abs, eip)
((abs - eip) - CALL_ASM_SIZE)
typedef void (_stdcall * empty_func)();
template<typename T> //retain SOME type security :p
result_t DllCall(ret_t return_type, conv_t convention, void * address, int argc, int args [], T * _return)
{
/* some checks */
if(!address)
return bad_address;
if(argc && !args)
return bad_arguments;
if(!_return)
return bad_return;
/* now we need to compile our own assembly, and floating point types differs a lot from standard dwords
so we split here */
switch (return_type)
{
case ret_t::rint:
case ret_t::rsint:
case ret_t::rptr:
/* these look quite similar for a start */
{
/* next up, conventions differ. */
switch(convention)
{
case conv_t::cstdcall:
/* in stdcall, callee cleans up so we dont have to worry about that.
arguments pushed right to left (?) */
{
/* so now we need a buffer. it needs to be:
(sizeof(native_int) + push_size) * argc +
mov eax, call_address (5)
call eax(2)
mov [void_buf], eax; // should be 5, cant remember
return (always just ret, we define the asm as taking no arguments and stdcall
*/
volatile DWORD buf = 0; // <- this will hold the return type
volatile DWORD * void_buf = &buf;
int iSize = (sizeof(int) + OPCODE_PUSH_SIZE) * argc + MOV_EAX_TOTAL_SIZE + CALL_EAX_SIZE + MOV_PTR_EAX_SIZE + RETN_SIZE + POS_BUF;
BYTE * asm_buf = (BYTE*)VirtualAlloc(NULL,iSize,MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
//now, asm_buf points to out little function
int temp = 0;
for(int i = argc - 1; i >= 0; i--) {
//Here we fill in push, and the param for each argc
*(BYTE*) & asm_buf[i * 5] = OPCODE_PUSH;
*(DWORD*) & asm_buf[i * 5 + OPCODE_PUSH_SIZE] = args[temp++];
}
int args_offset = argc ? argc * 5 : 0 ;
// assemble mov eax, address
*(BYTE*) & asm_buf[args_offset] = OPCODE_MOV_EAX;
*(DWORD*) & asm_buf[args_offset + MOV_EAX_SIZE] = (DWORD)address;
//assemble call eax
*(WORD*) & asm_buf[args_offset + MOV_EAX_TOTAL_SIZE] = (WORD)OPCODE_CALL_EAX;
int current_offset = args_offset + MOV_EAX_TOTAL_SIZE + CALL_EAX_SIZE;
// assemble mov [void_buf], eax
*(BYTE*) & asm_buf[current_offset] = OPCODE_MOV_DWORD_PTR_FROM_EAX;
*(DWORD*) & asm_buf[current_offset + OPCODE_MOV_DWORD_PTR_FROM_EAX_SIZE] = reinterpret_cast<DWORD>(void_buf);
*(BYTE*) & asm_buf[current_offset + MOV_PTR_EAX_SIZE] = OPCODE_RETN;
/* now we have hopefully assembled a small function that looks like this:
for each arg in args
push arg
mov eax, address
call eax
mov [void_buf], eax
retn 0
lets call it! lol
*/
empty_func asm_func = (empty_func)asm_buf;
(*asm_func)(); //omg
// now buf should hold the return value...
*_return = (T) buf;
//all done! now clean up
VirtualFree(asm_buf,iSize,MEM_DECOMMIT);
//and return
return result_t::success;
}
}
break;
}
case ret_t::rfloat:
{
break;
}
case ret_t::rdouble:
{
break;
}
}
return result_t::nothing_happened;
}
int __stdcall func_one(int a, int b, int c) {
std::cout << "a: " << a << ", b: " << b << ", c: " << c << std::endl;
return a + b + c;
}
int main() {
int args[] = {1,2,3};
int result;
result_t res = DllCall<signed int>(ret_t::rsint, conv_t::cstdcall, reinterpret_cast<void*>(func_one), ARRAYSIZE(args), args, & result);
std::cout << "result of DllCall: " << res << ", returned " << (int)result << std::endl;
system("pause");
return 0;
}
E: if you're having a hard time reading this, i suggest you look at my processcall udf, which does the same thing, only in autoit