【C語言】基於網路傳輸函數的可行性分析~記憶體中暫存的數據可以作爲函數執行嗎?

2020-08-13 11:57:33

前言

在寫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;  //清正在發送標誌
 }
}