【C语言】基于网络传输函数的可行性分析~内存中暂存的数据可以作为函数执行吗?

前言

在写stm32程序的时候,忽然想到能不能通过网络把函数发到单片机,这涉及到怎么序列化一个函数的问题,然后试了一下,结果发现可以把C语言函数复制到malloc出来的空间里,然后强制类型转换后还能运行!赶紧试试把一个函数的序列编码成一个数组,然后把数组指针强制转换成函数指针,发现函数可以正常执行!
但是在接下来的测试中,却发现这个方案有非常多的问题,还跟系统运行环境有很大关系。
感谢百度“C语言吧”的“GTA小鸡”、“宇文nice”和“aaaaaaa421”三位大佬提供支持,原贴链接:贴吧链接,笔者是这个贴的楼主。

win10测试

完整代码见附录1。
测试分为两步:

  1. 首先获取标准库“math.h”中定义的sin函数的长度,打印此函数的二进制编码,然后向系统申请sin函数长度的空间,接着把sin函数的二进制编码复刻到申请出来的内存中,然后通过指针的类型强制转换,执行复刻到malloc出来的空间中的函数。
  2. 把max函数的二进制编码写到一个数组中,对该数组指针进行强制类型变换后执行,观察是否运行成功。

获取函数长度的方法

在电脑端,函数退出的机器码是
0xC9 对应汇编leave;
0xC3 对应汇编retn。
只要从函数指针开始找,找到0xC9C3,就能获得函数的长度。以下是获取函数长度的函数。

/**
 * 获取函数的大小
 * @param fun_p 函数的指针
 * @return 函数的大小(字节)
 */
u64 getFunSize(const u8 *fun_p) {
    //寻找函数返回的特征值0xC9C3
    u64 fun_size = 1;
    u8 temp = *fun_p;
    while (1) {
        if (temp == 0xC9 && *(fun_p + fun_size) == 0xC3)
            break;
        else {
            temp = *(fun_p + fun_size);
            fun_size++;
        }
    }
    fun_size++;
    return fun_size;
}

函数复刻

函数复刻通过指针读取已经定义好的函数,把函数的二进制编码复制到用户的内存中,以下是实现代码。

/**
 * 内存复制
 * @param dec 目的基地址
 * @param src 源基地址
 * @param size 复制的长度(字节)
 */
void mycopy(u8 *dec, const u8 *src, u64 size) {
    for (u64 i = 0; i < size; i++)
        *(dec + i) = *(src + i);
}

运行复刻出来的函数

	u64 fun_size = getFunSize((u8 *)sin);
    //复刻sin函数
    u8 *bar = malloc(fun_size * sizeof(u8));
    mycopy(bar, (u8 *)sin, fun_size);
    //运行复刻出来的函数
    double (*_sin)(double);
    _sin = (void *) bar;
    printf("复刻出来的sin函数运行结果\n");
    printf("%f\n", _sin(PI / 2));
    free(bar);

运行编码的函数

被编码成数组的max函数的C语言原型如下:

int max(int a, int b){
	return a > b ? a : b;
}

把上面的函数序列用数组表示,以下代码把数组作为函数执行。

	/**
	 * 编码成数组的max函数
	 */
	u8 fun_table_max[] = {0x55, 0x89, 0xE5, 0x83, 0xEC, 0x8, 0x8B, 0x45, 0x8, 0x89, 0x45, 0xF8, 0x8B, 0x45, 0xC, 0x89,
                       0x45, 0xFC, 0x8B, 0x45, 0xF8, 0x39, 0x45, 0xFC, 0x7D, 0x6, 0x8B, 0x45, 0xF8, 0x89, 0x45, 0xFC,
                       0x8B, 0x45, 0xFC, 0xC9, 0xC3,};
	//运行数组编码的max函数
    int (*_max)(int, int);
    _max = (void *) fun_table_max;
    printf("数组编码的max函数运行结果\n");
    printf("%d\n", _max(5, 6));

测试结果

实测在win10系统,编译器是MinGW Version:3.15的环境下运行成功,运行结果见附录2,运行成功的截图如下图所示。
提供一下成功运行的编辑结果bin文件:

链接:https://pan.baidu.com/share/init?surl=-qqAykpyrcL3p8cGyYSp3g
提取码:l2pd

MinGW Version:3.15的环境下运行成功
但在VC编译器中,运行失败,见下图:
VC编译器中编译失败

汇编分析

为了研究为什么能成功,拿到运行成功的编译出来的exe可执行文件,用OllyDbg进行调试,结果如下:

复刻出来的sin函数入口

复刻出来的sin函数入口

复刻出来的sin函数执行后

复刻sin函数运行结果

数组编码的max函数入口

数组编码的max函数入口

数组编码的max函数内部

数组编码的max函数内部

main函数返回

main函数返回

汇编分析结论

正常的函数调用在OllyDbg中会显示 call <具体函数>,而运行复刻出来的或者用数组编码的函数,则会显示call eax。在大佬的引导下,笔者知道了window系统有DEP这种机制,用于保护电脑免受病毒的攻击,但是笔者所使用的MinGW编译器(V3.15)默认不启动DEP,为复刻函数以及数组函数的运行提供了便利,详见MinGW编译器的GitHub的Issues《gcc: DEP and ASLR not enabled by default #6674》,部分摘录如下:

(salowenh commented on 12 Jul)
The libgsf is flagged by tools such as BinSkim due to apparently not enabled safe exception handlers, DEP and ASLR security options.
Any thoughts on how to create a more secure compilation?
(Biswa96 commented on 12 Jul)
mingw-w64 gcc environment has many reputation of being flagged by anti virus programs. Search “mingw-w64 virus alert” and you will get many related issues. The alerts are mainly false positive which may not be related to disabled DEP and ASLR.
(salowenh commented on 12 Jul)
support was added in gcc 10.1.0, it’s indeed disabled by default (i’m not sure why)

在stm32上测试

既然在windows平台上的部分环境测试通过,那么本方案在单片机上的表现如何?
为了回答这个问题,笔者拿出来stm32f1开发版,把类似的代码烧录到单片机,进行测试,代码详见附录3
需要注意的是,平台换了,机器码也会相应改变,在获取函数大小的部分,函数结束特征码变成了0xFCE7。

真机烧录测试

代码烧录完成,打开串口调试助手,复位后,接收到的字符如下图所示:
在这里插入图片描述
单片机的LED并没有闪烁,说明程序卡在了复刻函数执行的第88行。

软件仿真测试

为了一探究竟,打开了Keil5自带的软件仿真面板,看看程序在执行复刻函数时,发生了什么。
首先给第88行“printf("%d\r\n",_max(4,6));”打上断点,按下F5,程序执行到这里,然后,点击F11单步调试,
Keil5软件仿真
在这个地方发生了错误!
错误点
然后被拽进了“HardFault_Handler”,单片机被一个死循环锁死。
然后被拽进了“HardFault_Handler”

在51单片机上测试

具体代码详见附录4
为了最后心中仅存的希望,拿出了尘封已久的8051开发版做测试,还是同一个注意事项,8051单片机的函数返回机器码是0x22,但是为了防止函数中的操作数也有0x22导致误判,所以在8051上人为地设置函数长度为50。

真机烧录测试

在真机上,通过串口发回来的数据,判断函数是否正常执行,结果如下:
8051真机烧录测试
函数运行不正常,但还是接着往下执行进入死循环了。

软件仿真测试

使用keil5自带的仿真软件,看看单片机到底是怎么执行的。点击上方“d牌放大镜”图标进入仿真模式,在运行复刻函数部分打上断点,按下F5来到断点处,开始F11单步执行到复刻函数调用处,F11跟进去看看

内部通过一个无条件跳转,跳到了一片空间
无条件跳转
继续F11跟着跳过去看看,结果来到了串口的初始函数处!
跳到了串口初始化处
看来这个方案在51上也实现不了。

结论

在做完这一套实验仿真下来,笔者有种唏嘘的感觉。在高级的运行平台windows10上因为MinGW自己的原因实现了我的想法,但在低端的执行平台如stm32、8051上却失败了。但是也了解了什么是堆栈可执行,DEP等概念,知道了MinGW默认不开启DEP服务。同时让多年没用过的8051出来吹吹风,重温一下当年学习C语言的原因。
点个赞呗?

附录

1、win10测试的代码

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define u8 unsigned char
#define u64 unsigned long
#define PI 3.14159265f
/**
* 编码成数组的max函数
*/
u8 fun_table_max[] = {0x55, 0x89, 0xE5, 0x83, 0xEC, 0x8, 0x8B, 0x45, 0x8, 0x89, 0x45, 0xF8, 0x8B, 0x45, 0xC, 0x89,
                      0x45, 0xFC, 0x8B, 0x45, 0xF8, 0x39, 0x45, 0xFC, 0x7D, 0x6, 0x8B, 0x45, 0xF8, 0x89, 0x45, 0xFC,
                      0x8B, 0x45, 0xFC, 0xC9, 0xC3,};

/**
* 获取函数的大小
* @param fun_p 函数的指针
* @return 函数的大小(字节)
*/
u64 getFunSize(const u8 *fun_p) {
//寻找函数返回的特征值0xC9C3
    u64 fun_size = 1;
    u8 temp = *fun_p;
    while (1) {
        if (temp == 0xC9 && *(fun_p + fun_size) == 0xC3)
            break;
        else {
            temp = *(fun_p + fun_size);
            fun_size++;
        }
    }
    fun_size++;
    return fun_size;
}

/**
* 内存复制
* @param dec 目的基地址
* @param src 源基地址
* @param size 复制的长度(字节)
*/
void mycopy(u8 *dec, const u8 *src, u64 size) {
    for (u64 i = 0; i < size; i++)
        *(dec + i) = *(src + i);
}

/**
* 展示一个函数的二进制编码
* @param fun_p 函数指针
* @param fun_size 函数长度(字节数)
*/
void funDisplay(const u8 *fun_p, u64 fun_size) {
    for (u64 i = 0; i < fun_size; i++)
        printf("0x%X,", *(fun_p + i));
    printf("\n");
}

int main() {
    u64 fun_size = getFunSize((u8 *) sin);
    printf("函数长度:%lu字节\n", fun_size);

    printf("sin函数的二进制编码\n");
    funDisplay((u8 *) sin, fun_size);

    //复刻sin函数
    u8 *bar = malloc(fun_size * sizeof(u8));
    mycopy(bar, (u8 *) sin, fun_size);
    //运行复刻出来的函数
    double (*_sin)(double);
    _sin = (void *) bar;
    printf("复刻出来的sin函数运行结果\n");
    printf("%f\n", _sin(PI / 2));
    free(bar);
    //运行数组编码的max函数
    int (*_max)(int, int);
    _max = (void *) fun_table_max;
    printf("数组编码的max函数运行结果\n");
    printf("%d\n", _max(5, 6));

    return 0;
}

2、win10端测试结果

函数长度:1493字节
sin函数的二进制编码
0xFF,0x25,0x4,0x51,0x40,0x0,0x90,0x90,0xFF,0x25,0xFC,0x50,0x40,0x0,0x90,0x90,0xFF,0x25,0xD4,0x50,0x40,0x0,0x90,0x90,0xFF
,0x25,0xE8,0x50,0x40,0x0,0x90,0x90,0xFF,0x25,0xF8,0x50,0x40,0x0,0x90,0x90,0x55,0x89,0xE5,0x5D,0xE9,0x5F,0xF8,0xFF,0xFF,0
x90,0x90,0x90,0x90,0x90,0x90,0x90,0xFF,0xFF,0xFF,0xFF,0x78,0x1A,0x40,0x0,0x0,0x0,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0
,0x0,0x55,0x89,0xE5,0x83,0xEC,0x8,0x8B,0x45,0x8,0x89,0x45,0xF8,0x8B,0x45,0xC,0x89,0x45,0xFC,0x8B,0x45,0xF8,0x39,0x45,0xF
C,0x7D,0x6,0x8B,0x45,0xF8,0x89,0x45,0xFC,0x8B,0x45,0xFC,0xC9,0xC3,
复刻出来的sin函数运行结果
1.000000
数组编码的max函数运行结果
6

Process finished with exit code 0

3、stm32测试端代码

不上传依赖了,因为单片机型号变了,依赖跟着改变,只上传核心部分的代码。
main.c

#include "led.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "lcd.h"
#include "usmart.h"

const u8 max_fun_table[] = {0x46,0x8a,0x42,0x1,0xdd,0x10,0x46,0x70,0x47,0x8,0x46,0xfc,};

int max(int a,int b){
	return a > b ? a : b;
}

int min(int a,int b){
	return a < b ? a : b;
}

int abs(int a){
	return a > 0 ? a : -a;
}

/**
 * 获取函数的大小
 * @param fun_p 函数指针
 * @return 函数的大小(字节)
 */
u32 getFunSize(const u8 *fun_p) {
    //寻找特征码0xFCE7
    u32 fun_size = 1;
    u8 temp = *fun_p;
    while (1) {
        if (temp == 0xFC && *(fun_p + fun_size) == 0xE7)
            break;
        else {
            temp = *(fun_p + fun_size);
            fun_size++;
        }
    }
    fun_size++;
    return fun_size*2;
}

/**
* 展示一个函数的二进制编码
* @param fun_p 函数指针
* @param fun_size 函数大小(字节数)
*/
void funDisplay(const u8 *fun_p, u32 fun_size) {
    for (int i = 0; i < fun_size; i++)
        printf("0x%X,", *(fun_p + i));
    printf("\r\n");
}
 	
 int main(void)
 { 
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
	delay_init();	    	 //延时函数初始化	  
	uart_init(9600);	 	//串口初始化为9600
	LED_Init();				//初始化与LED连接的硬件接口

	u8 *foo = (void *)max;
	u32 fun_size = getFunSize(foo);
	printf("max函数二进制编码\r\n");
	funDisplay(foo,fun_size);
	
	foo = (void *)min;
	fun_size = getFunSize(foo);
	printf("min函数二进制编码\r\n");
	funDisplay(foo,fun_size);
	
	foo = (void *)abs;
	fun_size = getFunSize(foo);
	printf("abs函数二进制编码\r\n");
	funDisplay(foo,fun_size);
	
	//复刻函数测试
	u8 temp_fun_table[200];
	foo = (void *)max;
	fun_size = getFunSize(foo);
	//函数复制
	for(int i = 0;i < fun_size;i++){
		temp_fun_table[i] = *(foo + i);
	}
	//函数运行
	int(*_max)(int,int);
	_max = (void *)temp_fun_table;
	printf("复刻sin运行结果\r\n");
	printf("%d\r\n",_max(4,6));
	
	while(1){								    
		LED0=!LED0;	
		delay_ms(200);								  
	}										    
}	

4、8051单片机端测试代码

#include <reg51.h> //包含头文件,在“reg51.h”上右键单击,并打开,可以看到它里面的定义
                   //当然也可以改成 reg52.h  STC.H 功能一样的,只是定义的IO口有一点区别,51单片机可以通用。

#define jingzhen     11059200UL			 /*使用22.1184M晶体*/	 
#define botelv   9600UL		     /*波特率定义为9600*/
#define u8 unsigned char
u8 zifuchuan[]="执行结果\r\n";			//待显示字符。
volatile u8 sending;
sbit s2=P3^4;

u8 max(u8 a,u8 b){
	return a > b ? a : b;
}

void delay(u8 i)
{
	u8 j,k;
	for(j=i;j>0;j--)
		for(k=90;k>0;k--);
}
void init(void)				//串口初始化
{
 EA=0; //暂时关闭中断
 TMOD&=0x0F;  //定时器1模式控制在高4位
 TMOD|=0x20;    //定时器1工作在模式2,自动重装模式
 SCON=0x50;     //串口工作在模式1
 TH1=256-jingzhen/(botelv*12*16);  //计算定时器重装值
 TL1=256-jingzhen/(botelv*12*16);
 PCON|=0x80;    //串口波特率加倍
 ES=1;         //串行中断允许
 TR1=1;        //启动定时器1
 REN=1;        //允许接收 
 EA=1;         //允许中断
}

void send(u8 d)		  //发送一个字节的数据,形参d即为待发送数据。
{
 
 SBUF=d; //将数据写入到串口缓冲
 sending=1;	 //设置发送标志
 while(sending); //等待发送完毕
}

void sendc(u8 * pd)
{
 while((*pd)!='\0') //发送字符串,直到遇到0才结束
 {
  send(*pd); //发送一个字符
  pd++;  //移动到下一个字符
 }
}

sbit led=P1^0;  //定义一个LED 为P1.0 IO口
u8 foo[50];
u8 *bar;

void main()     //C语言主函数
{  
	int i;
	u8 res = 0;
	u8(*_max)(u8,u8);
	init();
	led=0;   //单片机IO P1.0脚输出一个低电平,点亮发光管。 高电平为5V 低电平为0。
	bar = (void *)max;
	//函数复刻
	for(i = 0;i < 49;i++){
		foo[i] = *(bar + i);
		send(bar[i]);
	}
	foo[49] = 0;
	//运行复刻的函数
	_max = (void *)foo;
	res = _max(9,12);
	sendc(zifuchuan);
	while(1){
		delay(1000);
		led = !led;
		send(res);
	}
}

void uart(void) interrupt 4		 //串口发送中断
{
 if(RI)    //收到数据
 {
  RI=0;   //清中断请求
 }
 else      //发送完一字节数据
 {
  TI=0;
  sending=0;  //清正在发送标志
 }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值