用C语言解码函数指针

用C语言解码函数指针,c,function-pointers,C,Function Pointers,是否可以将函数指针内容存储在C中。我知道可以将各种指针存储在变量中。但如果我能“展开”一个整数指针(指向整数)或字符串指针(指向无符号字符),我就不能对函数指针进行解码了 更清楚地说,我的意思是将机器代码指令存储在一个变量中。您缺少一个重要的事实:函数不是C中的(一级)对象 C中有两种基本类型的指针:数据指针和函数指针。可以使用*取消对这两个变量的引用 相似之处到此为止。数据对象具有存储的值,因此取消引用数据指针可访问此值: int a = 5; int *b = &a; int c =

是否可以将函数指针内容存储在C中。我知道可以将各种指针存储在变量中。但如果我能“展开”一个整数指针(指向整数)或字符串指针(指向无符号字符),我就不能对函数指针进行解码了


更清楚地说,我的意思是将机器代码指令存储在一个变量中。

您缺少一个重要的事实:函数不是C中的(一级)对象

C中有两种基本类型的指针:数据指针和函数指针。可以使用
*
取消对这两个变量的引用

相似之处到此为止。数据对象具有存储的值,因此取消引用数据指针可访问此值:

int a = 5;
int *b = &a;
int c = *b; // 5
函数就是这样,一个函数。您可以调用函数,因此可以调用取消引用函数指针的结果。它没有存储值:

为了便于处理,C允许在将函数分配给函数指针时省略
&
运算符,并且在通过函数指针调用函数时也可以省略显式解引用

简而言之:使用指针不会改变数据对象和函数的语义


在此上下文中还要注意,函数和数据指针不兼容。无法将函数指针指定给
void*
。甚至可以有一个平台,其中函数指针的大小与数据指针的大小不同



实际上,在函数指针与数据指针具有相同格式的平台上,您可以通过将指针强制转换为
const char*
“说服”编译器访问位于那里的实际二进制代码。但请注意,这是未定义的行为。

C中的指针是内存中某个对象的地址。
int*
int
的地址,指向函数的指针是函数代码存储在内存中的地址

虽然可以从内存中函数的地址读取一些字节,但它们只是字节,而不是其他任何字节。您需要知道如何解释这些字节,以便“将机器代码指令存储在变量中”。这里真正的问题是知道在哪里停止,一个函数的代码在哪里结束,另一个函数的代码在哪里开始

这些东西不是由语言定义的,它们取决于许多因素:处理器体系结构、操作系统、编译器、用于编译代码的编译器标志(用于优化f.e.)


这里真正的问题是:假设您可以“将机器代码指令存储在变量中”,您希望如何使用它?它只是一个字节序列,对大多数人来说毫无意义,不能用于执行函数。如果您不是在编写编译器、链接器、仿真器、操作系统或类似的东西,那么对于函数的机器码指令,您就没有什么有用的方法。(如果您正在编写上述内容之一,那么您知道答案,并且您不会就此或其他地方提出此类问题。)

此处的代码应该是一个框架,用于将代码注入程序。但是,如果您在诸如Linux或Windows之类的SO中执行它,则在执行第一条指令之前会出现异常
fn_ptr

#include <stdio.h>
#include <malloc.h>

typedef int FN(void);

int main(void)
{
        FN * fn_ptr;
        char * x;

        fn_ptr = malloc(10240);
        x = (char *)fn_ptr;

        // ... Insert code into x that points the same memory of fn_ptr;
        x[0]='\xeb'; x[1]='\xfe'; // jmp $ that is like while(1)
        fn_ptr();

        return 0;
}
#包括
#包括
类型定义内部FN(无效);
内部主(空)
{
FN*FN_ptr;
char*x;
fn_ptr=malloc(10240);
x=(char*)fn_ptr;
//…将指向fn_ptr相同内存的代码插入x;
x[0]='\xeb';x[1]='\xfe';//jmp$类似于while(1)
fn_ptr();
返回0;
}
如果使用gdb执行此代码,将获得以下结果:

(gdb)l
2#包括
3.
4类型定义内部FN(无效);
5.
6内部干管(空)
7   {
8 FN*FN_ptr;
9个字符*x;
10
11 fn_ptr=malloc(10240);
12 x=(字符*)fn_ptr;
13
14/…在x中插入代码,该代码指向fn_ptr的相同内存;
15 x[0]='\xeb';x[1]='\xfe';//jmp$类似于while(1)
16 fn_ptr();
17
18返回0;
19  }
(gdb)b 11
断点1位于0x400535:文件p.c,第11行。
(gdb)r
启动程序:/home/sergio/a.out
断点1,主()位于p.c:11
11 fn_ptr=malloc(10240);
(gdb)p fn_ptr
$1=(FN*)0x7fffffffde30
(gdb)n
12 x=(字符*)fn_ptr;
(gdb)n
15 x[0]='\xeb';x[1]='\xfe';//jmp$类似于while(1)
(gdb)Px[0]
$3 = 0 '\000'
(gdb)n
16 fn_ptr();
(gdb)Px[0]
$5 = -21 '\353'
(gdb)Px[1]
$6 = -2 '\376'
(gdb)s
程序接收信号SIGSEGV,分段故障。
0x00000000000602010英寸??()
(gdb)在哪里
#0 0x00000000000602010英寸??()
#1 0x0000000000400563位于p.c:16的主管道()中
(gdb)
您如何看到GDB信号a
SIGSEGV,分段故障
,位于fn_ptr指向的地址,尽管内存中的指令是有效指令


请注意,LM代码:EB FE仅对英特尔(或兼容)处理器有效。此LM代码对应于汇编代码:jmp$。

假设我们讨论的是冯·诺依曼体系结构

基本上,我们只有一个包含指令和数据的内存。然而,现代操作系统能够控制内存访问权限(读/写/执行)

按标准,将函数指针强制转换为数据指针是未定义的行为。虽然我们说的是Linux、gcc和现代x86-64 CPU,但您可以进行这样的转换,您将得到一个指向内存只读可执行段的指针

例如,看看这个简单的程序:

#包括
int func(){
返回1;
}
int main(){
无符号字符*代码=(void*)func;
printf(“%02x\n%02x%02x%02x\n%02x%02x%02x%02x%02x\n%02x\n%02x\n”,
*代码,
*(代码+1),*(代码+2),*(代码+3),
*(代码+4),*(代码+5),*(代码+6),*(代码+7),*(代码+8),
*(代码+9),
*(代码+10));
}
汇编时使用:

gcc -O0 -o tst tst.c
我的机器上的输出是:
gcc -O0 -o tst tst.c
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <malloc.h>
#include <sys/mman.h>
#include <stdint.h>
#include <inttypes.h>

typedef int FNi(int,int);
typedef double FNd(double,double);

const char prg[][250] = {
    // int multiply(int x,int y)
    {
    0x55,                       // push     %rbp
    0x48,0x89,0xe5,             // mov      %rsp,%rbp
    0x89,0x7d,0xfc,             // mov      %edi,-0x4(%rbp)
    0x89,0x75,0xf8,             // mov      %esi,-0x8(%rbp)
    0x8B,0x45,0xfc,             // mov      -0x4(%rbp),%eax
    0x0f,0xaf,0x45,0xf8,        // imul     -0x8(%rbp),%eax
    0x5d,                       // pop      %rbp
    0xc3                        // retq
    },

    // double multiply(double x,double y)
    {
    0x55,                       // push     %rbp
    0x48,0x89,0xe5,             // mov    %rsp,%rbp
    0xf2,0x0f,0x11,0x45,0xf8,   // movsd  %xmm0,-0x8(%rbp)
    0xf2,0x0f,0x11,0x4d,0xf0,   // movsd  %xmm1,-0x10(%rbp)
    0xf2,0x0f,0x10,0x45,0xf8,   // movsd  -0x8(%rbp),%xmm0
    0xf2,0x0f,0x59,0x45,0xf0,   // mulsd  -0x10(%rbp),%xmm0
    0xf2,0x0f,0x11,0x45,0xe8,   // movsd  %xmm0,-0x18(%rbp)
    0x48,0x8b,0x45,0xe8,        // mov    -0x18(%rbp),%rax
    0x48,0x89,0x45,0xe8,        // mov    %rax,-0x18(%rbp)
    0xf2,0x0f,0x10,0x45,0xe8,   // movsd  -0x18(%rbp),%xmm0
    0x5d,                       // pop    %rbp
    0xc3                        // retq
    }
};

int main(void)
{
#define FMT "0x%016"PRIX64

    int ret=0;

    FNi * fnI_ptr=NULL;
    FNd * fnD_ptr=NULL;

    void * x=NULL;

    //uint64_t p = PAGE(K), l =  p*4; //Max memory to use!
    uint64_t p = 0, l =  0, line=0; //Max memory to use!

    do {
        p = getpagesize();line = __LINE__;
        if (!p) {
            ret=line;
            break;
        }

        l=p*2;
        printf("Mem page size  = "FMT"\n",p);
        printf("Mem alloc size = "FMT"\n\n",l);

        x = mmap(NULL, l, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);line = __LINE__;
        if (x==MAP_FAILED) {
            x=NULL;
            ret=line;
            break;
        }

        //Prepares function-pointers. They point the same memory! :)
        fnI_ptr=(FNi *)x;
        fnD_ptr=(FNd *)x;

        printf("from x="FMT" to "FMT"\n\n",(int64_t)x,(int64_t)x + l);

        // Calling the functions coded into the array prg

        puts("Copying prg[0]");

        // It injects the function prg[0]
        memcpy(x,prg[0],sizeof(prg[0]));

        // It executes the injected code
        printf("executing int-mul = %d\n",fnI_ptr(10,20));

        puts("--------------------------");
        puts("Copying prg[1]");

        // It injects the function prg[1]
        memcpy(x,prg[1],sizeof(prg[1]));

        //Prepares function pointers.

        // It executes the injected code
        printf("executing dbl-mul = %f\n\n",fnD_ptr(12.3,3.21));


    } while(0); // Fake loop to be breaked when an error occurs!

    if (x!=NULL)
        munmap(x,l);

    if (ret) {
        printf("[line"
               "=%d] Error %d - %s\n",ret,errno,strerror(errno));
    }
    return errno;
}
int multiply(int a, int b)
{
    return a*b;
}

double dMultiply(double a, double b)
{
    return a*b;
}