如何在內存中執行二進制代碼之linux平臺

之前寫了一篇關於win平臺下,從內存執行二進制碼的文章,所以此文主要修改自那篇。

大家可能會很好奇,我們的任意程序,不就是在內存中執行的二進制機器碼嗎?

不,今天我要說的是,我們如何把實現指定功能的一段二進制機器碼,放到我們的程序中,然後在需要的時候,直接調用它。

當然,這段代碼也有其他用途,故而有了shell code的暱稱,參考百度百科:

https://baike.baidu.com/item/shellcode/4051847?fr=aladdin

思考:
我們需要解決以下問題

  • 二進制代碼從哪裏來?
  • c/c++中如何調用它?

這些問題,接下來,都會得到解決。
不過,我們先來看看效果。

一、執行二進制代碼效果

main.c:

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>

typedef int(*AddFunc)(int, int);

int main() 
{
    // int add(int a, int b)函數64位系統下二進制碼
	unsigned char add_binaryCode[] = {
		0x55,
		0x48, 0x89, 0xe5,
		0x89, 0x7d, 0xfc,
		0x89, 0x75, 0xf8,
		0x8b, 0x55, 0xfc,
		0x8b, 0x45, 0xf8,
		0x01, 0xd0,
		0x5d,
		0xc3
	};

	// 申請EXECUTE屬性內存
	void* execBuf = mmap(NULL, sizeof(add_binaryCode), PROT_WRITE | PROT_EXEC,MAP_ANON | MAP_PRIVATE, -1, 0);

	// 拷貝二進制碼
	memcpy(execBuf, add_binaryCode, sizeof(add_binaryCode));

	// 執行二進制碼
	AddFunc func = (AddFunc)execBuf;
	int ret = func(1, 2);
	printf("result:%d\n", ret);

	// 釋放內存
	munmap(execBuf, sizeof(add_binaryCode));
	return 0;
}

Ubuntu環境64位下編譯執行

gcc main.c -o main.out
./main.out

add_binaryCode中裝的是add函數二進制碼,C函數形式如下:

int add(int a, int b)
{
	return a + b;
}

我們不能在數組中執行二進制碼,這樣會報異常,故需要申請帶可執行屬性的內存,然後拷貝到其中,強轉類型後,調用此函數。

運行結果:

在這裏插入圖片描述

運行結果正確,且沒有報異常。

小結:

我們可以把邏輯代碼封裝爲函數,並轉換爲二進制碼,然後在c/c++中進行調用執行。

到此我們解決了,在c/c++中如何執行二進制碼的問題。

接下來,我們來解決二進制碼如何生成的問題。

二、二進制代碼的生成

個人認爲有2種方式生成二進制碼

1.純手寫十六進制機器碼

2.採用c/c++等高級語言編寫程序,編譯後,對其反彙編進而獲得十六進制機器碼

第一種,好比回到了紙帶打孔編程的石器時代,需要了解x86指令集及其對應機器碼,能力有限,直接放棄。

接下來,採用第二種方式,大概講下,怎麼通過自己寫的函數,去提取生成的二進制碼。

1. 編寫add測試函數

add.c:

int add(int a, int b)
{
	return a + b;
}

2. 編譯,生成*.o文件

gcc -c add.c -o add.o

3. 提取add函數二進制碼

objdump -j .text -d add.o

每行":"後面的十六進制數字就是機器碼。如下:

在這裏插入圖片描述

故,我們把這些機器碼拷貝出來,放到數組中

unsigned char add_binaryCode[] = {
	0x55,
	0x48, 0x89, 0xe5,
	0x89, 0x7d, 0xfc,
	0x89, 0x75, 0xf8,
	0x8b, 0x55, 0xfc,
	0x8b, 0x45, 0xf8,
	0x01, 0xd0,
	0x5d,
	0xc3
};

這些機器碼就是我們add函數編譯後生成的二進制碼,即最終在內存中,cpu執行的機器碼。我們讓這個數組按照第一節中的方式,就可以在c/c++代碼中進行調用執行了。

三、從文件中讀取二進制碼並執行

似乎本節的意義並不大,無非就是讀取出來,放到buffer中,再執行罷了,主要的執行原理、二進制碼生成都已經講完了,這個就留給大家自行擴展吧。

四、總結

機器碼的獲取,比較關鍵的就是objdump這條命令。

另外執行時,比較關鍵的是,需要把機器碼放到mmap指向的內存中去,不能在普通內存中執行。

代碼地址:

https://gitee.com/bailiyang/cdemo/tree/master/C++/08ExecBinaryFromBuffer/ExecBinaryFromBuffer_linux

參考鏈接:

《執行機器碼》


===================================================

===================================================

業餘時間不定期更新一些想法、思考文章,歡迎關注,共同探討,沉澱技術!

            

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章