瀏覽看雪論壇,發現有人講了一個有趣的C語言程式。
原帖地址:http://bbs.pediy.com/showthread.php?p=389887
我做了一點點的修改,最後列印出 i O y(中間那個是個心的形狀,運行程式就知道了)
#include <stdio.h>int main(){ const short int c1 = 49920; const int c2 = 1073742008; int (*pf)() = (int (*)())&c2; printf("%c %c %c\n", *((char*)pf()+1)-0x11,*(char*)pf()-0x4a, *((char*)pf()+1)-0x1); return 0; }
這兩句代碼:
const short int c1 = 49920;const int c2 = 1073742008;
定義了兩個局部變數,數值轉換成16進位為:
const short int c1 = 0xc300;const int c2 = 0x400000b8;
其中變數c1的地址為:0x0012FF44 ,佔兩個位元組,c2的地址為:0x0012FF40,佔四個位元組。
這兩個變數佔據了連續的空間。變數賦值後,從0x12fff40開始的記憶體單中繼存放區的位元組碼為:B8 00 00 40 00 C3 。對應的彙編碼是:
mov eax,400000h ret
用OD試試,輸入二進位代碼或者彙編代碼就可以看到
接下來的這句:
int (*pf)() = (int (*)())&c2;
定義了一個函數指標,參數為NULL,傳回值為int類型。 這個函數指標,指向上面的彙編碼(注意記憶體位址的順序)。這樣,後面執行pf(),就執行了這段彙編碼。
接下來,
printf("%c %c %c\n", *((char*)pf()+1)-0x11,*(char*)pf()-0x4a, *((char*)pf()+1)-0x1);
先看*(char*)pf()-0x11這個運算式, 執行了了pf指向的彙編代碼,從彙編代碼看,這個函數調用後的傳回值是0x400000,pf()前面的char *是把函數的傳回值轉換成一個char*型指標,這個指標指向0x400000,前面再加個*號,表示取0x400000地址的內容,由於是char *型指標,因此從這個地址取一個位元組。
*(char*)pf()-0x11 表示的是從0x400000取出的位元組內容再減去0x11。
熟悉PE檔案結構的朋友一定知道,對於exe檔案0x400000是記憶體載入的基地址。也就是說,0x400000 位元組的內容對應的是0x4D,0x400001 位元組的內容對應的是0x5A.
這是我們常說的pe檔案起始的兩個位元組,"MZ"。這樣,運算式*(char*)pf()-*就可以隨意修改了。
送給女朋友做個禮物吧。
沒事的時候可以瀏覽些論壇,經常能發現一些很多有趣的東西,很多時候這些不經意的發現,往往增加了生活的樂趣。這個文章比較早了,一開始看到這個程式也沒想明白,看了一下解釋才豁然開朗,當然這種代碼在實際中是不可能使用的,不過我倒是很喜歡搜集這些稀奇古怪的小代碼,平添了生活的樂趣。