2010-10-10 87 views
19

我的手上有太多時間,開始想知道我是否可以編寫一個自我修改的程序。爲此,我在C中編寫了「Hello World」,然後使用十六進制編輯器在編譯的可執行文件中查找「Hello World」字符串的位置。是否有可能修改這個程序打開自己並覆蓋「Hello World」字符串?C程序可以修改其可執行文件嗎?

char* str = "Hello World\n"; 

int main(int argc, char* argv) { 

    printf(str); 

    FILE * file = fopen(argv, "r+"); 

    fseek(file, 0x1000, SEEK_SET); 
    fputs("Goodbyewrld\n", file);  
    fclose(file);  

    return 0; 
} 

這是不行的,我假設有什麼東西阻止它打開自己,因爲我可以拆分到這兩個獨立的程序(一個「Hello World」和東西修改),它工作正常。

編輯:我的理解是,當程序運行時,它完全加載到內存中。因此,硬盤上的可執行文件是所有意圖和目的的副本。爲什麼它會修改自己是一個問題?

是否有解決方法?

感謝

+0

作弊引擎ftw !! – 2010-10-10 02:58:44

+1

哈哈,沒有。只是閒散的好奇心。 – Joel 2010-10-10 03:06:00

+0

作弊引擎通過在程序中查找值並讓你改​​變它們來工作。它可以給你額外的生命在視頻遊戲等,但我不認爲你真的要欺騙引擎作爲答案。 – 2010-10-10 03:30:56

回答

29

在Windows上,當程序運行時,整個*.exe文件是使用memory-mapped-file functions in Windows映射到內存中。這意味着該文件不一定全部被加載,而是文件的頁面在被訪問時按需加載。

當以這種方式映射文件時,另一個應用程序(包括它本身)無法寫入同一個文件,以便在運行時更改它。 (另外,在Windows上,正在運行的可執行文件也不能重命名,但它可以在基於inode的文件系統的Linux和其他Unix系統上運行)。

可以更改映射到內存中的位,但是如果您這樣做,操作系統會使用「寫入時複製」語義來執行此操作,這意味着基礎文件在磁盤上不會更改,而是複製內存中的頁面是通過修改而完成的。在允許這樣做之前,您通常必須在有問題的存儲器上撥動保護位(例如VirtualProtect)。

曾經,它在低級別彙編程序中非常常見,這些彙編程序在非常有限的內存環境中使用自修改代碼。但是,沒有人再這樣做了,因爲我們沒有在相同的受限環境中運行,而且現代處理器有很長的管道,如果你開始從它們下面改變代碼,它會變得非常不安。

+0

請注意,在Unix/Linux上,儘管您可以重命名甚至刪除磁盤上正在運行的可執行文件,但它在內存中保持不變,直到進程死亡。 – 2010-10-10 14:14:20

+0

當有必要時,還可以創建修改版本的文件副本,啓動新的進程以執行文件替換並自行終止。 – 2011-09-04 19:47:12

0

如果您在Windows操作系統,相信它鎖定的文件,以防止它,而其正在運行被修改。這就是爲什麼你經常需要退出程序才能安裝更新。在linux系統上也是如此。

+0

您可以修改程序運行時的代碼。 (在Windows上,這將是'WriteProcessMemory()')。這是你的調試器的工作原理。這就是說,這是一個非常糟糕的主意。 – asveikau 2010-10-10 03:01:42

+0

我不認爲我曾經聲稱這是一個很好的:P。但爲什麼Windows應該鎖定它?我的理解是,當程序運行時,它完全加載到內存中。因此,硬盤上的可執行文件是所有意圖和目的的副本。爲什麼會修改這個問題? – Joel 2010-10-10 03:03:37

+0

@asveikau這與磁盤上的文件不在內存中有關,但是你對在內存中可以做什麼是正確的。 – 2010-10-10 03:07:24

4

這是非常依賴操作系統。某些操作系統會鎖定該文件,因此您可以嘗試通過在某處創建一個新副本來作弊,但是您只是運行該程序的另一個compy。

其他操作系統對文件進行安全檢查,例如, iPhone,所以編寫它將會是一個很大的工作,而且它只是一個只讀文件。

與其他系統,你甚至可能不知道文件在哪裏。

1

在許多平臺上都有不可移植的方法。例如,在Windows中,您可以使用WriteProcessMemory()執行此操作。不過,在2010年,這通常是一個非常糟糕的主意。這不是DOS代碼組裝時的日子,而是爲了節省空間。很難做到正確,而且基本上要求穩定性和安全性問題。除非你像調試器那樣做了一些非常低級的事情,我會說不用擔心這一點,否則你所引入的問題不值得你獲得任何好處。

1

自修改代碼用於在存儲器修改,而不是在文件(如運行時間解包作爲UPX做)。另外,程序的文件表示是比較困難的,因爲相對虛擬地址,可能的遷移和修改所需要的大多數更新(例如通過改變Hello world!longer Hello World頭,你需要擴展在文件中的數據段的操作)。

我建議你先學會在記憶中學習。對於文件更新,最簡單和更通用的方法是運行該程序的副本,以便修改原始文件。

編輯:不要忘記的主要原因是使用了自修改代碼:

1)混淆,使得被實際執行的代碼不是代碼,就用簡單的看對文件進行靜態分析。

2)性能,類似於JIT。

它們都沒有從修改可執行文件中受益。

0

在Windows CE的新版本,其中在用戶空間中運行的應用程序(ATLEAST低於5.x的更新),(相比於早期版本的所有應用程序在管理員模式下運行),應用程序甚至無法讀取它自己的可執行文件。

4

目前所有的答案或多或少圍繞的事實是,今天你不能輕易做自我修改機器代碼了。我同意,對於今天的PC來說,這基本上是真實的。

但是,如果你真的想在行動中看到自己的自修改代碼,你有一些可用的可能性:

  • 試用微控制器,越簡單的人沒有先進的流水線。我找到的最便宜和最快的選擇是一個MSP430 USB-Stick

  • 如果一個模擬對你來說可以,你可以運行一箇舊的非流水線平臺的模擬器。

  • 如果你想要自我修改的代碼只是爲了它的樂趣,你可以在Corewars有更多的樂趣與自毀代碼(更確切地說是敵人破壞)。

  • 如果你願意從C移到說一種Lisp方言,代碼編寫代碼是很自然的存在。我建議Scheme這是故意保持小。

6

如果您使用的是Windows,你可以做到以下幾點:

的Step-by-Step示例:

  1. 呼叫VirtualProtect()你要修改的代碼頁,與PAGE_WRITECOPY保護。
  2. 修改代碼頁。
  3. 呼叫VirtualProtect()上修改後的代碼的頁面,使PAGE_EXECUTE保護。
  4. 致電FlushInstructionCache()

欲瞭解更多信息,請參見How to Modify Executable Code in Memory(歸檔:2010年8月)

2

如果我們談論的是在x86環境中不應該是不可能的事情了。它應該謹慎使用,因爲x86指令是可變長度的。長指令可能會覆蓋下面的指令,而較短的指令會從應該被noped的覆蓋指令中留下殘餘數據。

當86首次成爲受保護英特爾的參考手冊建議採用以下方法進行調試訪問XO(僅執行)方面:

  1. 創建一個新的空選擇器(遠指針的「高」的一部分)
  2. 設置其屬性的XO區域
  3. 新的選擇的訪問屬性必須設置RO數據,如果你只是想看看在它的
  4. ,如果你要修改的訪問屬性必須是數據設置爲RW DATA

所以這個問題的答案在最後一步。如果您希望能夠插入調試程序所執行的斷點指令,則RW是必需的。比80286更現代的處理器具有內部調試寄存器,以實現非侵入式監控功能,這可能導致發佈斷點。

Windows提供了以Win16開頭的構建塊。他們可能仍然存在。我認爲微軟稱這類指針操作爲「thunking」。


我曾經在PL/M-86上爲DOS寫過一個非常快的16位數據庫引擎。當Windows 3.1到達(運行在80386s)時,我將它移植到Win16環境。我想利用32位內存,但沒有可用的PL/M-32(或Win32)。

解決我程序中使用以下面的方式置信轉換

  1. 定義32位遠指針的問題(sel_16:offs_32),使用結構
  2. 分配32位數據區域(< =>> 64KB大小)使用全局內存並通過16位遠指針(sel_16:offs_16)接收它們格式
  3. 通過複製選擇器填充結構中的數據,然後使用16位乘法與32位結果計算偏移量。
  4. 加載的指針/結構分成ES:EBX使用指令大小覆蓋前綴
  5. 訪問使用指令的大小和操作數大小前綴

的組合中的數據。一旦機制錯誤釋放它的工作沒有一個順利。我的程序使用的最大內存區域是2304 * 2304雙精度,出現在40MB左右。即使在今天,我也會稱之爲「大塊」的記憶。在1995年,它是典型的SDRAM棒(128 MB PC100)的30%。

相關問題