不知道為什麼,這周感覺過得特別漫長 …。

音樂

這禮拜大部分聽的都是舊專輯,也沒有挖掘到想讓我大書特書的新專輯,就不寫心得了。

Hack The Box

Source

這禮拜完成了 Footprinting 模組。基本上就是針對企業內部常見服務的枚舉與嗅探大全。涵蓋了 NFS, FTP, SMB, DNS, IMAP, SNMP 等等。學會了如何掃描這些服務、尋找設定錯誤的權限,或是預設憑證來當作突破口。這部分比較偏向體力活和記憶,就是把工具指令記熟。

Quine

我還記得大二下 OOP 教授說過他以前做過 Quine(也鼓勵我們自己做一個),當時讀 Ken Thompson 的經典論文 Reflections on Trusting Trust 時,也大概理解 Quine 的邏輯,但一直沒親手實作過,直到這禮拜 Tsoding 出這隻影片 Self-Reproducing Programs

其實他的思路非常簡單,就是把程式分成「框架」和「數據」兩部分,重點在於如何用一個字串來表示程式自身,並將其格式化輸出。

char *self = "...";
 
int main() {
    print("%s", self);
}

最後就可以變成這樣:

#include <stdio.h>
#include <string.h>
 
int main() {
    // 利用 `~` 當作佔位符表示自己(`self`)
    // 推薦用魔法來複製和編輯程式骨架
    // Emacs & Vim: Could your VSCode do this?
    char *self = "#include <stdio.h>\n#include <string.h>\n\nint main() {\n    char *self = \"~\";\n    int n = strlen(self);\n\n    for (int i = 0; i < n; i++) {\n        if (self[i] == 126) {\n            for (int j = 0; j < n; j++) {\n                switch (self[j]) {\n                    case '\\n':\n                        printf(\"\\\\n\");\n                        break;\n                    case '\"':\n                        printf(\"\\\\\\\"\");\n                        break;\n                    case '\\\\':\n                        printf(\"\\\\\\\\\");\n                        break;\n                    default:\n                        printf(\"%c\", self[j]);\n                }\n            }\n        } else {\n            printf(\"%c\", self[i]);\n        }\n    }\n}\n";
    int n = strlen(self);
 
    // 透過遍歷 `self` 輸出 `main.c` 的程式框架
    for (int i = 0; i < n; i++) {
        // 當遇到 `~` 時,代表我們要一五一十的把整個字串輸出出來
        if (self[i] == 126) {
            for (int j = 0; j < n; j++) {
                switch (self[j]) {
                    // 這是最簡單的情況,遇到 \n 不能換行,而是要輸出 \n,為了輸出 \n 就必須用到逃脫字元 \
                    case '\n':
                        printf("\\n");
                        break;
                    // 這很酷。在上面 `self` 時,我們如果要表示 ",就必須用逃脫字元 \。而在這裡我們為了輸出 \ 和 ",我們必須個別為他們添上逃脫字元,也就變成了 \\\"
                    case '"':
                        printf("\\\"");
                        break;
                    // 同理!
                    case '\\':
                        printf("\\\\");
                        break;
                    default:
                        printf("%c", self[j]);
                }
            }
        } else {
            printf("%c", self[i]);
        }
    }
}

超酷的!還有其他的一些東東:

  • Wiki - Bloo:一個收藏各種語言的各種版本 Quine 實做的網站。

  • Github - Quine-Relay 一個 Ruby 程式,生成 Rust,再生成 Scala… 經歷 128 種語言後再變回原本的 Ruby 程式碼。藝術品 …

Tsoding

When I say that the programming language doesn’t matter, I hope you know where I’m coming from.

編譯器

這禮拜也看了些編譯器的東西。

總之我現在大概知道了編譯器的架構,分成前端、中端、後端。我原本對編譯器的理解只有停在詞法分析、語法分析、語義檢查的前端部份,中端和後端是最近才知道的。不過就我觀察,大部分自幹的編譯器,好像也都只專住在前端而已。

不需要分號的 C 編譯器

我好像變成 Tsoding 的 “Fanboy” 了 …。其實原理意外簡單,tinycc 的 Parser 在分析完一段 Statement 後會預期並檢查是否有分號,我們只要把那個「強制檢查」改成「可選」甚至是「略過」就好。

#include <stdio.h>
 
int main(){
    printf("Hello World\n") 
    printf("Hello World\n")
    printf("Hello World\n")
    printf("Hello World\n")
    printf("Hello World\n")
 
    return 0
}
$ ./tcc no-semi.c
no-semi.c:5: error: ';' expected (got 'printf')

尋找 expected (got 是在哪裡報錯的。

ST_FUNC void skip(int c)
{
    if (tok != c) {
        char tmp[40];
        pstrcpy(tmp, sizeof tmp, get_tok_str(c, &tokc));
        tcc_error("'%s' expected (got '%s')", tmp, get_tok_str(tok, &tokc));
	}
    next();
}

所以我們需要找到哪裡呼叫了 skip(';')。透過 grep 可以發現它藏在 block 函數處理 Expression 的地方。

static void block(int flags)
{
...
                /* expression case */
            if (t != ';') {
                unget_tok(t);
    expr:
                if (flags & STMT_EXPR) {
                    vpop();
                    gexpr();
                } else {
                    gexpr();
                    vpop();
                }
                skip(';');
            }
...
}

這就是處理 Expression 的地方,把它邏輯改成「如果遇到 ; 再跳過」就可以了。

if (tok == ';') skip(';');

對於 return 也是如此。

...
    } else if (t == TOK_RETURN) {
        ...
        if (b)
            gfunc_return(&func_vt);
        if (tok == ';') skip(';');
        /* jump unless last stmt in top-level block */
        ...
    } else if (t == TOK_BREAK) {
...
$ ./tcc no-semi.c
$ ./a.out
Hello World
Hello World
Hello World
Hello World
Hello World

看!多奇妙啊!

寒假

寒假已經過了 5/9 個禮拜了天啊天啊天啊啊啊啊啊啊啊啊啊。

  • 預習編譯器、資料庫的課程,主要是把書粗略看一遍就差不多了
  • 剪輯演算法的影片,主要是動態規劃、貪婪演算法的部份(現在決定睡前剪)
  • 狂幹 HackTheBox Academy,當然希望可以直接把 CPTS Path 的所有 Modules 都看完
  • 制定專題《基於多維度漏洞挖掘之 IoT 設備安全審計與更新機制研究》的總體大綱