C中的低级函数调用?

C中的低级函数调用?,c,function,assembly,arguments,low-level,C,Function,Assembly,Arguments,Low Level,假设我们有一个位于已知地址的函数func。我们不知道这个函数需要多少参数或什么样的数据类型 我们得到了一个数组,其中包含与函数所期望的适当字节数相对应的数据。例如,假设我们有functionfunc(uint8a,uint16b,uint8c),数组将是0x0A,0x0B,0x0C,0x0D,其中0x0A是a的值,0x0B0C是b的值,0x0D是c的值 给定此数组和函数的地址,如何在C或内联汇编中调用此函数 编辑:我还应该提到,这段代码将在ARM处理器上运行。这是我的尝试。它可以编译,但我没有测

假设我们有一个位于已知地址的函数
func
。我们不知道这个函数需要多少参数或什么样的数据类型

我们得到了一个数组,其中包含与函数所期望的适当字节数相对应的数据。例如,假设我们有function
func(uint8a,uint16b,uint8c)
,数组将是
0x0A,0x0B,0x0C,0x0D
,其中
0x0A
a
的值,
0x0B0C
b
的值,
0x0D
c
的值

给定此数组和函数的地址,如何在C或内联汇编中调用此函数


编辑:我还应该提到,这段代码将在ARM处理器上运行。

这是我的尝试。它可以编译,但我没有测试它

0x12345678是已知的函数地址

假设此函数不返回任何内容

“data”var是传递给它的数据

“data”var也可以由任意结构{}替换

函数必须知道如何从其单指针arg指向的地址中提取数据

void (*fun)(void*) = (void (*)(void*))(0x12345678);

int main(int argc, char *argv[])
{
    unsigned char   data[] = {1, 2, 3, 4, 5}; 

    fun(data);
}

==========

如果不知道函数调用约定,就无法做到这一点。您不能只是将数据转储到堆栈中,然后期望函数处理它。如果它是一个
\uu cdecl
函数,则必须在执行后清除堆栈,否则会损坏它。如果它是一个uu fastcall函数,它需要
ecx/rcx
edx/rdx
寄存器中的前两个参数。(这也依赖于平台!)如果是_uthis调用,则必须通过
ecx
寄存器(也依赖于平台)提供指向对象实例的指针

编辑:
根据,参数可以通过堆栈和寄存器传递(第18、30页)。因此,以上所有内容仍然适用。

鉴于您最近的评论,我认为您可以通过在每个函数调用中传递一个额外的参数(即函数的“类型”)来完成您想要完成的任务。例如,假设您对将遇到的所有函数类型进行了枚举:

enum funcType {
    V_U8_U16_U8,  // Means: void func(uint8_t, uint16_t, uint8_t)
    V_U16_U16,    // Means: void func(uint16_t, uint16_t)
    U32_U32,      // Means: uint32_t func(uint32_t)
    ...
}
然后在主机端,可以传输funcType和其他信息。在目标端,您需要执行以下操作:

type = GetFunctypeFromStream();
switch(type) {
    case V_U8_U16_U8:
    {
        void (*func)(uint8_t, uint16_t, uint8_t) = GetFuncPointerFromStream();
        uint8_t  arg1 = GetU8ArgFromStream();
        uint16_t arg2 = GetU16ArgFromStream();
        uint8_t  arg3 = GetU8ArgFromStream();

        func(arg1, arg2, arg3);
        break;
    }
    case V_U16_U16:
    {
        void (*func)(uint16_t, uint16_t) = GetFuncPointerFromStream();
        uint16_t arg1 = GetU16ArgFromStream();
        uint16_t arg2 = GetU16ArgFromStream();

        func(arg1, arg2);
        break;
    }
    case U32_U32:
    {
        uint32_t (*func)(uint32_t) = GetFuncPointerFromStream();
        uint32_t arg1 = GetU32ArgFromStream();

        ret = func(arg1);
        break;
    }
    ...
}
当然,你可能不会手写上面所有的内容。您可能会通过编写一些脚本为您生成每个案例来自动生成代码。此外,有符号类型和无符号类型可以合并为一种类型,因为就调用函数而言,这并不重要


只要函数类型的数量远小于函数的数量,此解决方案就会有所帮助。如果您所有的函数都是不同类型的,那么这没有帮助,因为您基本上只是对所有函数做了一个switch语句(就像您所说的您希望避免的那样)。

我发现这个问题很棘手,并给了它一些时间和想法。让我重复这个问题:您希望能够在(任何)地址调用函数,并希望向其传递任意类型和长度的参数列表。让我们定义一个名为“call_it”的函数,提供地址和指向参数数组的指针;此函数必须执行堆栈管理和调用。“call_it”可以是纯C函数吗?答案是“否”,因为您无法按照我们必须的方式操作C中的堆栈。特别是,您不能在调用后动态生成推送指令和堆栈清理指令

不过,您可以短暂退出到assembler,以执行推送和清理。讨论中的许多其他答案/参与者给出了相关方面和示例,因此我只提供了一个伪代码解决方案/示例:

typedef struct PRM {int type; void *arg;};
#define T_INT   1

int call_it(int (*address)(), struct PRM *prms, int n_prms)
{
    int nbytes=0;
    prms += n_prms-1;
    for (;n_prms>0; n_prms--, prms--) {
        switch (prms->type) {
            case T_INT:
                __asm mov ax,prms.arg
                __asm push ax
                nbytes += sizeof(int);
                break;
        }
    }
    address();
    __asm add sp, nbytes;
}

(对不起,我只会说X86)

一点也不会。问题是,我们不知道如何将它放在堆栈和寄存器中,或者该函数的未知调用约定想要什么。您可以声明不知道该函数需要多少个参数,然后声明它需要3个参数,一个字节、一个短字节和一个字节。您只需为函数声明一个typedef,然后将函数的地址强制转换为指向该类型函数的指针,并使用指定的参数调用它。这是一个解释数组的示例函数。我们只有数组和函数的地址。我们不知道这个函数需要多少个参数或什么样的数据类型。按相反的顺序将每个参数推到堆栈上,从函数返回时调用函数,将每个参数从堆栈中弹出。@user3629249但如果不知道每个参数的大小,就不能这样做(如果有四个字节,是四个
char
s还是一个
int
?)。您不能指定函数采用void指针,因为您不知道函数采用的参数类型或数量。编译器只需将
数据的地址
传递给函数,并假设
void*
是它将采用的参数。问题的关键是如何构建invo具有任意参数集的函数的kation序列,一个您无法控制的参数集。我没有提到这将在ARM体系结构上运行,因此x86寄存器定义不适用。但是,基本理论应该是合理的。您能推荐一篇关于参数和堆栈如何相关的文章吗?正确。正如我在pr中提到的在前面的评论中,这个理论应该仍然是正确的。我正在对我的程序进行反汇编,并正在设计一种方法,在这种方法中,我手动增加适当的寄存器和堆栈。编译器可以很好地预测它们将参数分配给哪些寄存器,所以我几乎可以肯定这是可以做到的。感谢您的非常透彻的解释不幸的是,这种方法不是m