日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

有些人不走寻常路

發布時間:2024/4/11 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 有些人不走寻常路 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

大家周末好,又到了周末時間,給分享一些輕松有趣的內容,希望大家喜歡。

去年鵝廠內部極客圈舉辦了第二次極客大賽,題目如下:

"實現一個世界上最小的程序來輸出自身的MD5"

作為極客圈一員的我也參加了比賽,比賽競爭激烈,為了爭奪一個字節的優勢,大家都拿出自己的絕活,通過參加比賽,學到很多知識,重新刷新了我底層知識的理解,計算機原理的理解:

題目介紹和常規解法:最小MD5挑戰賽,你敢來戰嗎?

大神解法: 頂級極客技術挑戰賽,你敢來挑戰嗎?| 大神登峰造極

接下來,給大家分享一些不走尋常路的野路子方案,歡迎大家點評討論,腦洞大開。

野路子解法1-md5碰撞原理及實現

人間正道是滄桑

一. 原理

  • 已知A, 可以生成相同長度, 內容不同的X和Y, 使得md5(A+X)=md5(A+Y)

  • 已知A, B, X, Y, md5(A)=md5(B), md5(A+X)=md5(A+Y), 則md5(A+X)=md5(B+X)=md5(A+Y)=md5(B+Y)

  • 二. 構造步驟
    通過工具

    (http://www.win.tue.nl/hashclash/fastcoll_v1.0.0.5_source.zip),?

    可以構造出上面的X和Y:

    $ echo Hello > A $ echo World > B$ ./fastcoll -p A -o AX1 AY1 MD5 collision generator v1.5 by Marc Stevens (http://www.win.tue.nl/hashclash/) Using output filenames: 'AX1' and 'AY1' Using prefixfile: 'A' Using initial value: 7be4ac1acaf95d7b8f73709af7db5c10 Generating first block: .. Generating second block: S01...... Running time: 4.53125 s$ cat AX1 | md5 dbbd16368b2eb0f6596004ace403e012 $ cat AY1 | md5 dbbd16368b2eb0f6596004ace403e012$ cat AX1 B | md5sum 8b6ddb8f03cd3f7fe63f288f6688018a $ cat AY1 B | md5sum 8b6ddb8f03cd3f7fe63f288f6688018a

    這樣我們就有了指定1個bit的能力 (同時文件也會增大128字節).
    循環執行128次:

    fastcoll A -o AX1 AY1 cut_tail_128 AX1 > X1 cut_tail_128 AY1 > Y1fastcoll AX1 -o AX1X2 AX1Y2 cut_tail_128 AX1X2 > X2 cut_tail_128 AX1Y2 > Y2...

    每次生成的2個文件, 取末尾128字節, 得到X[i]和Y[i];
    然后就可以根據md5反推出Xi/Yi該選哪個了

    三. Show Me The Code
    test.c:

    #include <stdio.h> char const reserve[32] = "RR...",md5coll[128][128] = { "MM...", "MM...", ... },chkpos[128] = "PP...", chkval[128] = "VV..."; int main() {int i, j, p1, p2, x;for (i = 0; i != 16; ++i) {x = 0;for (j = 0; j != 8; ++j) {x <<= 1;p1 = i * 8 + j;p2 = ((unsigned char)chkpos[p1]) % 128;if (md5coll[p1][p2] == chkval[p1])x |= 1;}printf("%02x", x);}printf("\n");return 0; }

    先編譯test.c得到test, 再想辦法填充里面的md5coll, chkpos, chkval, 就可以達到反推md5的效果

    講解一下算法:

    整個程序分為5部分:

  • elf頭等

  • reserve數組, 用來64字節對齊

  • md5coll數組, 長度128*128, 用來放上面算出來的X[i]/Y[i]

  • chkpos/chkval數組, 用來查md5coll, 反推出自身md5輸出

  • main函數及其它

  • 階段一:

  • 編譯確定1 2 5

  • 階段二:

  • 跑128次fastcoll, 對比X[i]和Y[i]的差異, 可以構造出Y[i][chkpos[i]] == chkval[i], 從而算出chkpos和chkval

  • 階段三:

  • 將X[i]填充到md5coll數組, 至此可執行程序的md5就不會變了

  • 根據md5的二進制位, 填充對應的Xi或者Yi

  • 這樣就可以構造出能算出自身md5的程序了。


    野路子解法2-撿"漏"

    始從正道,因久攻不克,遂智取

    邪念初生

    最初是題目描述中的一個細節提醒了我:

    這里禁用socket,想必是為了防止對外通信,反過來說,如果能突破通信的限制,確實就可以不用自己辛苦計算結果了,通信的另一方直接把結果塞給它就好了。

    投石問路

    要想對外通信,不管用何種通信方式,都不免要通過一些系統調用的,所以先用一些不太常用的系統調用試探一波,看有沒有漏網的系統調用:

    int main() {int sv[2];int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);if (ret != 0){*(int *)0 = 0;}printf("xxx\n");return 0; }

    這里有一點要說明一下,就是每次提交結果,如果不通過,系統會有比較詳細的反饋(大概有使用了禁止的系統調用、crash,結果錯誤、返回值不為0這幾個),這個有助于我們試探信息。注意到上面代碼第6行,這里會故意crash,以此區分socketpair在沒有被禁止的前提下,這個調用究竟有沒有成功。

    上面程序的提交結果是Wrong Answer,表明這個調用成功了,證明確實有些系統調用沒有被禁止,此事可搞。

    首戰不利

    最初想到的是利用共享內存來傳遞信息:

  • 準備兩個可執行程序A和B

  • 先提交A,A的功能是把MD5(B)寫到共享內存

  • 接著再提交B,B從共享內存讀取A留下的內容并輸出即可達到目的。

  • 以下是A的代碼:

    int main() {int id = shmget(0x32147658, 1024, IPC_CREAT | 0666);if (id == -1) *(int *)0 = 0;char *data = (char *)shmat(id, 0, 0);if (!data) return 2;memcpy(data, "9afd0fbf6a61da4d64e92095476716ec", 32);//這串即是B的md5int ret = shmdt(data);if (ret == -1)return 3;return 0; }

    然后是B的:

    int main() {int id = shmget(0x32147658, 1024, IPC_EXCL);char *data = (char *)shmat(id, 0, 0);write(1, data, 32);_exit(0); }

    由于B做的事情及其簡單,所以體積很容易做得很小。
    然而,想法是很美好,提交A的時候卻得到 ”非法系統調用“。很簡單,共享內存相關的系統調用被禁了,浪費了上面十幾行代碼。

    再戰又不利

    接著又想到利用文件來傳遞信息:

  • 還是兩個可執行程序A和B

  • A把MD5(B)寫到一個文件

  • B讀出來輸出

  • 想得依然很美,但試遍系統中所有可能成功的地方(/tmp /run /var /dev等),都無法找到一個可以創建文件的目錄,或者一個可讀可寫的文件,此路依然不通。

    三戰還不利

    這時還沒有放棄文件的思路,而且想了個比較粗暴的招(也是實在沒辦法了):

  • 還是倒霉的A和同樣倒霉的B

  • A遍歷整個文件系統,找出可以創建文件的地方創建文件,或者找到一個可讀可寫的文件,寫入內容

  • B以同樣的方式遍歷文件系統,必能找到A之前留下的內容,讀之并輸出

  • 試了一下提交A居然成功了,但提交B的時候顯示Wrong Answer。此時懷疑是踩到了/dev下的某些設備文件,它們是可讀可寫的,但語義不是普通文件的語義,讀出來的和寫入的內容不一樣,典型的如/dev/uramdom,寫入的語義是給系統的隨機數生成器貢獻隨機事件,而讀取則是獲取隨機數。

    有錯就改,在A中添加校驗信息:

  • A先寫一個固定串TEST,再寫如MD5(B)

  • B在讀取的時候先看能不能讀到TEST,如果能的話,則繼續讀取后面的內容

  • 加上校驗之后,A可以成功找到通過校驗的文件,而B則始終無法讀到校驗信息TEST。。。

  • 這里終于開始懷疑判分系統除了禁用系統調用,還可能做了更深的隔離,并不會在同一個環境里測試不同的可執行文件,路路不通,一度打算放棄。

    山窮水盡

    放棄之前,照例是要再掙扎一番的,文件不行,再看看有沒有其它途徑,然后又試了另一種進程間通信方式fifo(命名管道),mknod(創建fifo的系統調用)系統調用確實沒有被禁,但fifo是存在于文件系統中的,無法創建文件,也就無法創建fifo,此法不靠譜,越想越絕望。

    峰回路轉

    此時大概只剩最后一種進程間通信方式了:消息隊列。死馬當活馬醫吧,反正這條路也快到頭了,不管結果怎么樣,能解脫是肯定的,直接上代碼,A的:

    int main(int argc, char **argv) {int msqid;int key=0xDEADDEAD;msqid = msgget(key, 0600 | IPC_CREAT);char buf[TEXT_SIZE];strcpy(buf, "eb861d89e1c8e775c39dab7878216093");int flag = msgsnd(msqid, buf, TEXT_SIZE, 0);if (flag < 0){perror("send messag eerror");return -1;}return 0; }

    B的:

    int main(int argc, char **argv) {int msqid;int key=0xDEADDEAD;msqid = msgget(key, 0600);char buf[TEXT_SIZE];int flag = msgrcv(msqid, buf, TEXT_SIZE, 0, 0);write(1, buf, TEXT_SIZE);_exit(0); }

    苦心人,天不負,就這樣,成功了,,,也就是說,系統禁了很多系統調用,唯獨忽略了消息隊列。

    上面的這個程序,稍加裁剪便達到了310字節,成功到頂一游。

    查漏補缺

    印象中Linux的進程間通信方式普遍有多個版本,除了上面的消息隊列,會不會還有漏網之魚呢,再次翻閱系統調用表,果然發現了另一個版本的消息隊列:

    • mq_open

    • mq_timedreceive

    • mq_timedsend

    這個應該也是可以利用的,不過沒有嘗試,連同上面的msgget一起,找組委會自首,請求原諒。。

    看完記得一鍵三連在看轉發,點贊

    是對文章最大的贊賞,感謝

    推薦閱讀

    頂級極客技術挑戰賽,你敢來挑戰嗎?| 大神登峰造極

    Linux Kernel TCP/IP Stack|Linux網絡硬核系列

    頂級C程序員之路

    總結

    以上是生活随笔為你收集整理的有些人不走寻常路的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。