2021年 西邮Linux兴趣小组 纳新免试题揭秘
文章目錄
- 引言
- 第一關
- 第二關
- 第三關
- 第四關
- 第五關
- 總結
引言
小組2020年的免試題的四位出題人是:小組18級成員李兆龍,20級成員趙子瑋,劉樹杭,任子澗。
同時19級成員周闊,戚凱萌,胡哲寧,李毅恒參與題目審查與展示工作。
目前題目鏈接還沒有下線,想要挑戰的同學可以繼續嘗試挑戰。
好了,現在讓我們來開啟一場頭腦風暴吧!
題解并不是我一個完成的,而是把幾份題解拼在一起。
第一關
題目地址:q.xiyoulinux.org
首先,首頁沒有任何可以點擊的觸發事件,直接看網頁源碼,
可以看到這里有一個奇怪的文件,文件名00111110_00110001_00110000_00110000_00110000_00110000
一共 8 組數字,每組 8 位數 用下劃線隔開,每組數字
分別轉換成 ASCII 字符得到>10000的結果
打開文件,好家伙全是 01 串,文件下載下來編輯器打開,找到第10001行,我們注意到一個奇怪的現象,
之前所有行都是80列,從10001行開始出現了81列,10009行又出現了80列,10010行
是81列,直到10710行以后又恢復成整齊的80列
豎著來看,突出的第81列每 8 行空一行,一共出現 79 組,根據先前的經驗,我們將突出的這 81 列提取出來,得到一個 01 串
01101100 01100101 01110011 01110011 00100000 00110000 00110000 00110001 00110001 00110001 00110001 00110001 00110000 01011111 00110000 00110000 00110001 00110001 00110000 00110000 00110000 00110001 01011111 00110000 00110000 00110001 00110001 00110000 00110000 00110000 00110000 01011111 00110000 00110000 00110001 00110001 00110000 00110000 00110000 00110000 01011111 00110000 00110000 00110001 00110001 00110000 00110000 00110000 00110000 01011111 00110000 00110000 00110001 00110001 00110000 00110000 00110000 00110000 01111100 01100111 01110010 01100101 01110000 00100000 00100010 00110000 00110001 00110000 00110001 00110000 00110001 00110000 00100010 01111100 01110111 01100011 00100000 00101101 01101100翻譯過來就是
less 00111110_00110001_00110000_00110000_00110000_00110000|grep "0101010"|wc -l
熟悉 linux 的同學這下就看懂了,則是一條命令,我們打開終端在這個二進制文件的所在目錄執行,得到了
18451這個結果。
我們翻到 18451 行,觀察后發現 18454 行與 18451 行完全相同,
嘗試解析中間的 18451 行到 18454 行的內容,得到了出乎意料的答案
Awesome!
第一關通關
第二關
題目地址:http://q.xiyoulinux.org/a4wpAj
網頁上只有三個東西:一首詩、一張奇奇怪怪的圖片以及一段音樂。無視那個版權聲明
這段音樂貌似沒有什么特別之處,只是一首優美的純音樂…我們先從圖片入手。
這首李白的詩提到了彩虹,彩虹的成因大家都知道:陽光因為折射發生色散。而計算機中表示顏色的方法是什么呢?這個相信讀者也知道:將每種顏色“拆分”成紅綠藍三種顏色,用三個 0-255 的整數分別表示三種顏色的“量”。這就是眾所周知的rgb顏色表示法。如果再仔細觀察這張圖片,會發現這張圖片是半透明的。如果讀者把這張圖片下載下來,會發現這張圖片是png格式的。png格式的圖片是支持透明度的,除了用3個 0-255 的整數來表示紅綠藍的“量”之外,還有一個 0-255 的值alpha表示透明度。這就是rgba顏色表示法。
在這張圖片中,每一個小方格對應一種顏色,每種顏色
對應了4個 0-255 的值,也就是4個字節。所以這張圖片中是存儲著某些數據的。我們可以用Python中的圖片處理庫Pillow來解碼。
解碼結果如下:
.file "showoffsets.c".text.section .rodata.str1.1,"aMS",@progbits,1 .LC0:.string "%zu\n".section .text.startup,"ax",@progbits.p2align 4.globl main.type main, @function main: .LFB11:.cfi_startprocpushq %r12.cfi_def_cfa_offset 16.cfi_offset 12, -16pushq %rbp.cfi_def_cfa_offset 24.cfi_offset 6, -24leaq .LC0(%rip), %rbppushq %rbx.cfi_def_cfa_offset 32.cfi_offset 3, -32leaq array(%rip), %rbxleaq 168(%rbx), %r12.p2align 4,,10.p2align 3 .L2:movq (%rbx), %rsimovq %rbp, %rdixorl %eax, %eaxaddq $8, %rbxcall printf@PLTcmpq %r12, %rbxjne .L2popq %rbx.cfi_def_cfa_offset 24xorl %eax, %eaxpopq %rbp.cfi_def_cfa_offset 16popq %r12.cfi_def_cfa_offset 8ret.cfi_endproc .LFE11:.size main, .-main.globl arraySize.section .rodata.align 8.type arraySize, @object.size arraySize, 8 arraySize:.quad 21.globl array.data.align 32.type array, @object.size array, 168 array:.quad 1555019.quad 1561326.quad 1561326.quad 1561291.quad 1556547.quad 1561304.quad 1560062.quad 1560062.quad 1561320.quad 1557817.quad 1560302.quad 1561329.quad 1561332.quad 1561295.quad 1560062.quad 1559069.quad 1560300.quad 1561390.quad 1560305.quad 1561349.quad 1560317.ident "GCC: (GNU) 10.2.0".section .note.GNU-stack,"",@progbits嗯?這是個啥?如果讀者熟悉gcc編譯器,會發現這是一段由gcc編譯出的匯編語言程序。
我們用gcc匯編并鏈接成可執行文件,然后執行:
gcc showoffsets.s然后執行這個可執行程序:會輸出以下文本:
1555019 1561326 1561326 1561291 1556547 1561304 1560062 1560062 1561320 1557817 1560302 1561329 1561332 1561295 1560062 1559069 1560300 1561390 1560305 1561349 1560317嗯?這又是個啥???
我們仔細觀察這個匯編文件,發現匯編文件是由一個叫showoffsets.c的文件編譯出來的,所以這些數字可能是某個文件的偏移量。在這個網頁上,還有一段音樂沒有用到,那這些偏移量極有可能就是這個音樂文件的偏移量了。我們在用萬能的Python將這些偏移量對應的字節連接起來。
from typing import Listoffsets: List[int] = [0x17ba4b,0x17d2ee,0x17d2ee,0x17d2cb,0x17c043,0x17d2d8,0x17cdfe,0x17cdfe,0x17d2e8,0x17c539,0x17ceee,0x17d2f1,0x17d2f4,0x17d2cf,0x17cdfe,0x17ca1d,0x17ceec,0x17d32e,0x17cef1,0x17d305,0x17cefd]# 音樂文件的路徑 music_path: str = "music_path"def decode(music_path: str) -> str:str_bytes: bytes = b""with open(music_path, "rb") as music_file:music_bytes: bytes = music_file.read()music_file.close()for i in offsets:str_bytes += chr(music_bytes[i]).encode("utf-8")return str_bytes.decode("utf-8")if __name__ == "__main__":print(decode(music_path))Python輸出結果是:https://dwz.mk/7fAjEr
哈?這不就是第3題的url嗎?
第三關
題目地址:https://dwz.mk/7fAjEr
這道題目的提示其實已經非常明確了,重要的點有兩個,一點是樹,一點是顏色。
所以答案已經呼之欲出了,那就是紅黑樹!奇了怪了,為什么這里會出現四種顏色呢,如果對紅黑樹的旋轉操作了解的話就會清楚其實在紅黑樹的刪除操作中有一種很特殊的旋轉方式。首先我們來看看刪除操作的一般過程。
刪除操作首先要確定待刪除節點有幾個孩子,如果有兩個孩子,不能直接刪除該節點。而是要先找到該節點的前驅(該節點左子樹中最大的節點)或者后繼(該節點右子樹中最小的節點),然后將前驅或者后繼的值復制到要刪除的節點中,最后再將前驅或后繼刪除。由于前驅和后繼至多只有一個孩子節點,這樣我們就把原來要刪除的節點有兩個孩子的問題轉化為只有一個孩子節點的問題。
此時如果要刪除的節點為紅色不違背紅黑樹約束,但是如果是黑色節點則違背約束,此時需要進行旋轉操作,大多數的刪除操作對子樹節點顏色有明確定義,但是其中有一種旋轉操作不需要,即如下這種情況:
所以這里把題目中的圖案映射到圖中左邊的樹,進行一個旋轉操作就可以得到另外一顆樹,把它按題目規則序列化則可得到正確答案。
即:122332312
第四關
題目地址:http://q.xiyoulinux.org/e9ON0C
本題目的考察點在于 RSA 算法。
打開網頁就能清晰的看到網頁上有一串莫名其妙的字符串,仔細觀察后會發現他們只含有 0-9 和 a-f,相信所有的人都能想到這就是一個 16 進制數。
除此之外,網頁最上部的三張照片也指引了另一個線索。通過圖片搜索或者直接查看網頁下方的版權聲明就可十分容易得知這三張照片對應的人物分別是: Ronald Linn Rivest、Adi Shamir、Leonard Adleman。發現了三個人的名字后,查查他們是誰,從他們三人的簡介中不難發現他們 3 人是 RSA 算法的發明者。
在此,感謝 Ronald Linn Rivest、Adi Shamir、Leonard Adleman 發明 RSA 算法一事為計算機科學作出的杰出貢獻,他們打開了通往非對稱加密算法的大門。
結合上面的十六進制數與 RSA 算法這個線索,我想不難猜到那串 16 進制數便是使用 RSA 算法加密后得到的密文了。
通過查找相關資料,就會發現 RSA 算法的解密是十分困難的,RSA 算法的可靠性來源于對大數進行因數分解的數學問題。
無論如何,不可能在只有密文的情況下完成解密。
回想圖片下方的那句話「他們打開了一扇通往新世界的大門……」,這句話自然不僅僅是對他們三人的功績的贊嘆,更是暗含了本題的提示:「這三張圖片是通往下一關的大門的鑰匙」。我相信結合整個題目的語境,不難理出這層含義。
下載這三張圖片,用 16 進制編輯工具打開,在圖片的最后面便可發現出題人給出的信息
e=65537
p=25862393087395144904578136066432622071613031108961508523776645974920112279571321274880403508998728336416174207954650261632773395336341858263117921946924709870308337780316993582832462776992833790440844633839729622746959330775633201140396009253194156382940738022047306941239966667693815888617107292778086039906056984287341581589863951680315082177719811893617114865593727125600782409006574479692430841244064660640437304837974726422158658744419697666717354373939752082268152242347379175058796398284230036914365748933140856442051681786956193712489422851825444698625063619154124978192545710964521177077153134997996710676819
q=29404999009444129808207101477302908884432848806969786898545939981324275388514204221673553167648241936370278958848658876075010146581347728219935243641031572735178324398168430951159612634336354969866091993303600114502652385983156726049132047290354556664894573942338038767376264479356691953013331654008659792346607521273164498138004555036020341291404697545594383622685494235403111339833079531850829660280651225344340068461823095092950097635435100884314446767160779554365653863499309534183976510778377077896265226321210788500229578828827219348128512401723649114755662577546818759852162552127571807817650455670247102677521
好了,現在獲得了 e、p、q 就可以開始解密了。
下面將講述如何手動對 RSA 算法進行解密。當然互聯網上有大量的 RSA 算法的解密工具,通過工具完成解密也是完全沒問題的。
解密密文需計算出 d。根據 RSA 算法有:
ed≡1(modφ(n))ed\equiv1 \pmod{\varphi(n)}ed≡1(modφ(n))
n=pqn=pqn=pq
并且
gcd?(p,q)=1\gcd(p,q)=1gcd(p,q)=1
又根據歐拉定理得到
φ(n)=(p?1)(q?1)\varphi(n)=(p-1)(q-1)φ(n)=(p?1)(q?1)
若用 CCC 表示消息的密文,用 MMM 表示消息的明文,求得 ddd 之后,通過
M≡Cd(modn)M\equiv C^d\pmod{n}M≡Cd(modn) 就能得到明文了。
好了,思路通順了。現在求解 ddd。
有人可能要疑惑,為什么要出這樣的一道題。這道題的選擇事實上也經過了一些深思,我希望免試題不只是「腦筋急轉彎式」的游戲,更能從中看出作答者的部分能力。這也是我作為出題人對答題者的尊重。
答題者若使用工具完成本題,那說明答題者具有良好的信息收集能力、有細心觀察的能力、有用簡單的方式解決問題的能力。
答題者若是通過編程的方式運用一定的算法思想解決本題,那答題者或許會用到同余的性質、模意義下的乘法逆元等數論知識,也需要掌握擴展歐幾里德、快速冪等算法知識。還可能需要手動處理大數運算的問題。
為了便于計算,題解中使用 Python 進行演示。
def exgcd(a, b, arr):if b == 0:arr[0] = 1arr[1] = 0return agcd = exgcd(b, a % b, arr)arr[0], arr[1] = arr[1], arr[0]-(a//b)*arr[1]return gcddef modularMultiplicativeInverse(a, n):tmp = [0, 0]exgcd(a, n, tmp)return (tmp[0] % n+n) % ndef quickPower(a, b, n):result = 1while b > 0:if b & 1:result *= aresult %= na *= aa %= nb >>= 1return result % np = 25862393087395144904578136066432622071613031108961508523776645974920112279571321274880403508998728336416174207954650261632773395336341858263117921946924709870308337780316993582832462776992833790440844633839729622746959330775633201140396009253194156382940738022047306941239966667693815888617107292778086039906056984287341581589863951680315082177719811893617114865593727125600782409006574479692430841244064660640437304837974726422158658744419697666717354373939752082268152242347379175058796398284230036914365748933140856442051681786956193712489422851825444698625063619154124978192545710964521177077153134997996710676819 q = 29404999009444129808207101477302908884432848806969786898545939981324275388514204221673553167648241936370278958848658876075010146581347728219935243641031572735178324398168430951159612634336354969866091993303600114502652385983156726049132047290354556664894573942338038767376264479356691953013331654008659792346607521273164498138004555036020341291404697545594383622685494235403111339833079531850829660280651225344340068461823095092950097635435100884314446767160779554365653863499309534183976510778377077896265226321210788500229578828827219348128512401723649114755662577546818759852162552127571807817650455670247102677521 e = 65537 d = modularMultiplicativeInverse(e, (p-1)*(q-1)) ciphertext = 0x45e87286867bd11052f174312f8bb844cec756ce260ec5a34071a1fc4c06b168252a16a75a30ecdaf64f6a0dad7dbb04b11064e192d1f86086a3198b99e75acaba27c5d68acf75dc4df35edfbb0d30b70591350019602a827d97d3f3882ae9c5b191c95ef46db432c9c13a072cba0eab91209ac645a26249d2562996e8ce228b8e7bd81f8d8d44585c02d8f47854bc0618076e4969b439a7363d55a8eaf824dbade37e16ea22566a7e7c142c82b45b44bcc8d250fcc14eee586e9e267312dba3e9822e4314087fd51981a84e03074c696d5158fe6104fd35d25fd09ffb0fead4916e5aa578d92bb212bbeca0fe5e98de35e00a1787ab5e60842cfad26d68709e81dd37b6eecf9eb6f3f9b228f1acd7cc2e92c778e0243540934669de91020b9cd0321fa276f9a730e6090df05312e00457a68c9411f7ce351b2ada114a54a8cad1524d83698c1bd337d73380aadac7d96aa9e9edd00ece644cc6cdbdca962b67e234a2a6731b8b72d7248fe9686270141a364a78346c8b7615a1d6c1d7d4a84b9ed89e6d2f631a5192ea8184cdd36af1b99e94d7869d2d88ab1b7e72f2e518ceab750c5f5143f3c48214119b87f26faa8a334e622462b26d96bff14c776dbf310139fb57d417bf55bea50b3b030fb75ecd71a26c518d71808cb17ebfe34a6655227fe0eab74cb9b56a085ddc5a78c8b06d52c236618b1a3dfe7816b132ecde00 message = quickPower(ciphertext, d, p*q) buf = [] while message > 0:buf.append(message & 0xFF)message >>= 8 buf.reverse() for i in buf:print(chr(i), end="") print()可以看到輸出的結果為
RSAis1Public-KeyCrypt0systemThat1sWidelyusedForSecureDa7aTransmission.
在網頁中唯一的輸入框提交即可進入第 5 題。
細心的話,不難發現是最終結果為這句話的變式:
RSA is a public-key cryptosystem that is widely used for secure data transmission.[1]
在此,請允許我以及XiyouLinuxGroup全體成員再次表達對「在計算機科學領域作出杰出貢獻的科學家」的敬意。
第五關
題目地址:http://q.xiyoulinux.org/ieskd
第 5 題原計劃為對并查集的考察,但由于一些原因最終替換為了本題。
本題并沒有考察什么算法的思想。打開本題網站,最先看到的就是一個 XiYou Linux Group 小組成立 15 周年的紀念圖。
當然,紀念圖不可能毫無緣由的出現在題目中,正如你所猜測的那樣:紀念圖實際上是本題目的提示。
可以看到,圖片下方有一句「請掃碼:」,里面還含有一個「冒號」!!!如果能聯想到后文的字符串大概是一個二維碼就解決了本題的一大半。
實際上,只要使用圖片中出現的 15 也就是 0xF 與每個字符進行 XOR 操作并將其輸出,就能得到本題的答案,也就是本題需要去掃描的二維碼了。
#include <stdio.h> #include <stdlib.h> unsigned char buf[50000]; int main() {FILE *input = fopen("/home/xiyoulinux/5.txt", "r");if (!input) {perror("找不到源文件");exit(EXIT_FAILURE);}ssize_t size = fread(buf, sizeof(buf[0]), sizeof(buf), input);if (size <= 0) {perror("讀取失敗");exit(EXIT_FAILURE);}ssize_t i;for (i = 0; i < size; i++)buf[i] ^= 0x0F;buf[i] = '\n';if (fclose(input)) exit(EXIT_FAILURE);FILE *output = fopen("displayQRcode.txt", "w");if (!output) {perror("創建 displayQRcode.txt 失敗");exit(EXIT_FAILURE);}if (fwrite(buf, sizeof(char), i, output) != i) {perror("寫入 displayQRcode.txt 失敗");exit(EXIT_FAILURE);}if (fclose(output))perror("關閉文件失敗"); }作為結果的二維碼非常的大,答題者可能需要經過一定的縮放才能正常的掃描。
掃描二維碼后得到一個字符串:
`c)Ic#/lFKyk?#R5
這個字符串由什么用?看看網站中宣傳圖中的微信公眾號二維碼,可以想到去將字符串發送給微信公眾號。
發送后將收到公眾號的宣布你通關的消息。
第 4 題、第 5 題 題解采用 知識共享署名-相同方式共享 4.0 國際許可協議進行許可。
總結
免試題的目的不在于為了刁難參與者而設立,而是在于為了激發每一個對技術有不懈追求的極客!
時代的變遷飛速,今時也早已不同于往日,數據庫,操作系統內核,分布式,Web應用,服務端編程等等簡單的名詞背后是幾十年的技術累積與數不盡的實現細節,面對著眼前的數座大山,我們難道要像一個懦弱者一樣東推西阻,畏縮不前?
我想技術這條路從某種角度來講確實是枯燥的,乏味的,這也意味著不是所有人都可以數十年如一日的在這片屬于自由者的理想國深耕。想要取得些許果實,唯有熱情,努力與持之以恒。
很幸運XiyouLinuxGroup聚集著這樣一群人。
博學之,審問之,慎思之,明辨之,篤行之。如是而已。
參考鏈接:
2018 Linux興趣小組免試題解析
2017 Linux興趣小組免試題解析
2016 Linux興趣小組免試題解析
2015 Linux興趣小組免試題解析
2014 Linux興趣小組免試題解析
2013 Linux興趣小組免試題解析
總結
以上是生活随笔為你收集整理的2021年 西邮Linux兴趣小组 纳新免试题揭秘的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 这家酒店的机器人用事实证明:“懒人”改变
- 下一篇: AI公司资源搜集