最近CoolShell博主做了一個很有意思的在線puzzle,這些謎題很有趣同時也有一定的難度。由於水平有限,我並沒有通關,我覺得這些題還是很值得一做的,從中可以學到很多東西。
例如其中的第二題:

題目中給出了一個鍵盤和一行看不懂的字符串。我們發現這個鍵盤的鍵盤布局和現在通用的鍵盤(QWERTY鍵盤)不一樣,它叫做Dvorak鍵盤。這里就不多作解釋了,詳細的可以去Google。鍵盤圖片明顯在提示我們:要通過兩種鍵盤的布局映射,將給出的字符串轉換成QWERTY鍵盤下的輸出。當然,你可以自己一對一寫出來,不過在線轉換工具更方便。
macb() ? lpcbyu(&gbcq/_\021%ocq\012\0_=w(gbcq)/_dak._=}_ugb_[0q60)s+轉換之后得到:
main() { printf(&unix["\021%six\012\0"],(unix)["have"]+"fun"-0x60);}這是1987年
國際C語言混亂代碼大賽(The International Obfuscated C Code Contest, IOCCC)一等獎的獲獎代碼,由貝爾實驗室的David Korn提交。當然平時我們不會寫出這么復雜難懂的代碼,但是分析這樣的代碼卻可以擴展我們的知識。
int main()
{
/* unix被編譯器內定為一個宏
* 相當於#define unix 1 */
printf("unix=%d\n", unix); /* =1 */
/* 打印字符串"un",因為"fun"是個字符數組
* "fun"+1相當於字符指針右移,指向"un" */
printf("%s\n","fun"+1);
/* "have"是個字符數組,"have"[1]即字符a
* 輸出97,即第二個字符'a'的ASCII值。*/
printf("%d\n", "have"[1]);
printf("%d\n", 'a');
/* 在C語言中,x[1] = 1[x] */
printf("%d\n", (1)["have"]);
/* 97 - 96 = 0x61 - 0x60 = 1 */
printf("%d\n", (1)["have"] - 0x60);
/* 所以 "fun"+((1)["have"]-0x60) 相當於"fun"+1,輸出"un" */
printf("%s\n", "fun" + ((1)["have"] - 0x60));
/* 將其中的1用unix代替 */
printf("%s\n", (unix)["have"]+"fun"-0x60);
/* 以上為后半部分 = "un" */
/* 下面兩個都輸出"bcde", 因為指針都是從'b'開始 */
printf("%s\n", "abcde" + 1);
printf("%s\n", &"abcde"[1]);
/* &"abcde"[1] == &(1)["abcde"] 輸出一樣 */
printf("%s\n", &(1)["abcde"]);
/* 1用unix代替 */
printf("%s\n", &unix["abcde"]);
/* 下面輸出"%six" 並換行 */
printf("%s", &"?%six\n"[1]);
/* 注意:
\012 = 0x0a = \n,
第一個字符 \021 被跳過
\0 是空字符 */
/* 同樣輸出"%six" 並換行 */
printf("%s", &"\021%six\012\0"[1]);
/* 相當於這樣 */
printf("%s", &unix["\021%six\012\0"]);
/* 把字符串"%six\n"當作格式,輸出"ABix" */
printf(&unix["\021%six\012\0"], "AB");
/* 相當於這樣 */
printf("%six\n", "AB");
/* 所以下面的可以輸出"unix" */
printf("%six\n", (unix)["have"]+"fun"-0x60);
/* 至此,問題解決!!!輸出"unix" */
printf(&unix["\021%six\012\0"],(unix)["have"]+"fun"-0x60);
return 0;
}
這段代碼主要用到了x[a]和指針運算的一些知識,相信上面的步驟和注釋已經很清楚了,最終結果就是輸出unix。
