【技術筆記】Linux 程序保護機制

RELRO (RELocation Read Only)

RELRO 說明 gcc 編譯參數
No GOT writable, link_map writable gcc -Wl,-z,norelro code.c
Partial GOT writable, link_map readonly DEFAULT
Full GOT read only, no link_map and dl_resolver pointer gcc -Wl,-z,relro,-z,now code.c

CANARY

stack overflow - gcc generate canary or not

Canary gcc 編譯參數
Enable DEFAULT (when buffer large enough)
Disable gcc -fno-stack-protector code.c

NX (No-Execute) / DEP (Data Execution Prevention)

可以寫的地方不能執行

NX / DEP gcc 編譯參數 execstack
Enable DEFAULT execstack -s code
Disable gcc -z execstack code.c execstack -c code

ASLR (Address Space Layout Randomization)

Configuring ASLR with randomize_va_space

1
2
3
0 - 表示關閉進程地址空間隨機化。
1 - 表示 mmap, stack, vdso 隨機化。
2 - 表示比 1 多了 heap 隨機化。
1
2
sudo -s echo 0 > /proc/sys/kernel/randomize_va_space
sudo sysctl -w kernel.randomize_va_space=0

PIE (Position Independent Executables)

PIE gcc 編譯參數
Enable gcc -fpie -pie code.c
Disable DEFAULT

FRAME POINTER

有開的話是

1
2
leave
ret

沒開的話是

1
2
add rsp, 0x18
ret
Canary gcc 編譯參數
Enable DEFAULT
Disable gcc -fomit-frame-pointer code.c

checksec

checksec 是一個用來查看上述所說的保護機制的 bash script

1
2
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable  FILE
Full RELRO No canary found NX enabled PIE enabled No RPATH No RUNPATH 65 Symbols No 0 1 ./hello

pwntools 也有內建一個名字和功能都一樣的指令

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
Read more

【程式語言】Python GIL

GIL 解決了什麼問題

每個 python 的物件都有一個 reference count
可以透過 sys.getrefcount 這個函式查看

1
2
3
4
5
>>> import sys
>>> a = []
>>> b = a
>>> sys.getrefcount(a)
3

以上的例子,有 a, bsys.getrefcount 的參數三個 reference
在有很多 threads 的情況下我們必須防止這個 reference count 的 race condition
一個簡單的解決方法就是 GIL

為什麼選 GIL 作為解決方法

就因為簡單易用,讓開發者會想加入開發以及使用它 ( 正因為如此使得 python 這麼熱門 )

那 GIL 為什麼到現在都還沒被拿掉

因為很多提案雖然讓 multithread 效率增加但卻讓 singlethread 變慢了

python 3.2

在 python 3.2 稍微修改了 GIL 的運作機制 ( 小改進 )
原本在有 CPU-bound 和 IO-bound 的 threads 互搶時,IO-bound 要等很久才能拿回 GIL ( 詳情看這篇 )

有 GIL 怎辦

multiprocessing

用 multiprocessing 分許多 process,每個 process 會有獨立的 interpreter 和 memory space ( 不會有因為 GIL 卡住的問題 )
但是 multi-processing 比起 multi-threading 會有額外的 overhead

Alternative Python interpreters

可以用其他實作版本的 python interpreters
比如 Jython 和 IronPython 沒有 GIL


  1. https://realpython.com/python-gil/
  2. https://www.youtube.com/watch?v=Obt-vMVdM8s
  3. http://dabeaz.blogspot.com/2010/01/python-gil-visualized.html
  4. http://www.dabeaz.com/python/GIL.pdf
Read more

【程式語言】What's new in C++17

Structured Bindings

1
auto [a, b, c] = tuple('a', 1, 0.5);
1
2
3
4
5
map<int, int> mymap = {{1, 2}, {3, 4}};

for (const auto& [key, value] : mymap) {
// ...
}
1
2
3
for (auto [a, b] = tuple(0, 'a'); a < 5; a++) {
// ...
}

Template Argument Deduction

c++17
1
auto p = pair(2, 4.5);
c++14
1
auto p = pair<int, double>(2, 4.5);

Selection Initialization

1
2
3
if (auto a = getval(); a < 10) {
// ...
}
1
2
3
switch (auto ch = getnext(); ch) {
// ...
}

  1. https://www.fluentcpp.com/2018/06/19/3-simple-c17-features-that-will-make-your-code-simpler/
  2. https://hackernoon.com/a-tour-of-c-17-if-constexpr-3ea62f62ff65
  3. https://stackoverflow.com/questions/38060436/what-are-the-new-features-in-c17
Read more

【程式語言】Python 的奇淫技巧

列了一些我新發現的各種花招

Function Attributes (python 2.1)

PEP 232

1
2
3
4
5
6
7
8
9
def a():
a.count += 1

a.count = 0

for i in range(10):
a()

print(a.count) # 10

跟 C++ 中,在 function 裡面宣告 static 變數差不多

Keyword-Only Arguments (python 3.0)

PEP 3102

1
2
def func(a, b, *, c = None):
pass
  • func(1, 2, 3) 不行
  • func(1, 2, c = 3) 這樣才合法

Additional Unpacking Generalizations (python 3.5)

PEP 448

1
2
3
a = [1, 2, 3]
b = [*a, 4, 5]
print(b) # [1, 2, 3, 4, 5]

Merge Dicts

1
2
3
4
a = {1: 2}
b = {3: 4}
c = {**a, **b}
print(c) # {1: 2, 3: 4}

Metaclasses

Python Metaclasses

1
2
3
4
5
6
7
8
9
10
11
12
13
class A:
pass

B = type('B',
(A,),
{
'attr': 10,
'func': lambda obj: obj.attr
})

b = B()
print(b.attr) # 10
print(b.func()) # 10
正常寫法
1
2
3
4
5
6
7
8
9
10
11
class A:
pass

class B(A):
attr = 10
def func(self):
return self.attr

b = B()
print(b.attr) # 10
print(b.func()) # 10

看看就好xD

shallow vs deep copy

Shallow vs Deep Copying of Python Objects

assign

1
2
3
4
5
6
7
8
9
10
a = [[1, 2, 3], [4, 5, 6]]
b = a

b.append('hello')
b[0].append('world')

print(a)
# [[1, 2, 3, 'world'], [4, 5, 6], 'hello']
print(b)
# [[1, 2, 3, 'world'], [4, 5, 6], 'hello']

copy

1
2
3
4
5
6
7
8
9
10
11
12
import copy

a = [[1, 2, 3], [4, 5, 6]]
b = copy.copy(a)

b.append('hello')
b[0].append('world')

print(a)
# [[1, 2, 3, 'world'], [4, 5, 6]]
print(b)
# [[1, 2, 3, 'world'], [4, 5, 6], 'new object']

deepcopy

1
2
3
4
5
6
7
8
9
10
11
12
import copy

a = [[1, 2, 3], [4, 5, 6]]
b = copy.deepcopy(a)

b.append('hello')
b[0].append('world')

print(a)
# [[1, 2, 3], [4, 5, 6]]
print(b)
# [[1, 2, 3, 'world'], [4, 5, 6], 'new object']

  • copy.copy (shallow) 只複製該物件,不會複製子物件
  • copy.deepcopy (deep) 會遞迴複製所有子物件

shallow copy on list

1
2
a = [1, 2, 3]
b = list(a)
1
2
a = [1, 2, 3]
b = a[:]
1
2
a = [1, 2, 3]
b = a.copy()
1
2
3
4
import copy

a = [1, 2, 3]
b = copy.copy(a)

annotations

function annotations

1
2
3
4
5
def func(a: int, b: list) -> int:
pass

print(func.__annotations__)
# {'a': <class 'int'>, 'b': <class 'list'>, 'return': <class 'int'>}

class annotations

1
2
3
4
5
class A():
var: int

print(A.__annotations__)
# {'var': <class 'int'>}

variable annotations

1
2
3
4
5
a: int
b: int = 2

print(__annotations__)
# {'a': <class 'int'>, 'b': <class 'int'>}

intern string

1
2
s = 'hello'
s = sys.intern(s)

把字串存進快取池,相同的字串只會存一次
做字串比對會比較快且節省空間


  1. http://guilload.com/python-string-interning/
  2. http://www.laurentluce.com/posts/python-string-objects-implementation/
Read more

【演算法筆記】Bloom Filter

Bloom Filter

用來快速搜尋資料是否存在於資料庫

我們的資料庫是一個 $2^m$ 大小的陣列

1
db = [False] * (2 ** m)

加資料進資料庫

我們有 $k$ 個 hash function 的輸出都是一個 $m$ bits 的數 ( $< 2^m$ )

把資料 $x$ 加進資料庫就是等於,將 $x$ 的各個雜湊值的位置設成 True

1
2
for i in range(k):
db[hash[k](x)] = True

查詢資料是否在資料庫裡

查詢資料 $x$ 是否在資料庫裡

1
2
3
4
if all([db[hash[k](x)] for i in range(k)]):
# 資料 x 可能在資料庫裡
else:
# 資料 x 一定不在資料庫裡

  1. http://www.evanlin.com/BloomFilter/
  2. http://bryanpendleton.blogspot.com/2011/12/three-papers-on-bloom-filters.html
Read more

【指令怎麼用】SSH 的十大妙用

ssh config

~/.ssh/config
1
2
3
4
Host vps
HostName 123.45.67.89
User oalieno
Port 22000

每次都打一長串的參數很麻煩,可以先在 ~/.ssh/config 設定好
像上面這樣設定好後 ssh vps 就等同於 ssh -p 22000 oalieno@123.45.67.89

IdentityFile

~/.ssh/config
1
2
3
4
Host vps
HostName 123.45.67.89
User oalieno
IdentityFile ~/.ssh/id_rsa

IdentityFile 就是指定要用哪個 key,等同於 -i ~/.ssh/id_rsa
預設會抓 id_*.pub 中最新的 ( /usr/bin/ssh-copy-id 59 行 )

local port forwarding ( ssh tunnel )

~/.ssh/config
1
2
3
4
Host vps
HostName 123.45.67.89
User oalieno
LocalForward 5555 127.0.0.1:6666

設定好後可以直接打以下指令

1
2
3
ssh -f -N vps
# -f : run in background
# -N : not execute remote command, useful for forwarding ports

就會等同於以下指令

1
2
3
ssh -f -N -L 5555:127.0.0.1:6666 oalieno@123.45.67.89
# -f : run in background
# -N : not execute remote command, useful for forwarding ports

local:5555 -> remote:127.0.0.1:6666
執行這個指令,就會在 local 聽 5555 port 然後把流量都導到 remote 的 127.0.0.1 的 6666 port

remote port forwarding ( reverse ssh tunnel )

~/.ssh/config
1
2
3
4
Host vps
HostName 123.45.67.89
User oalieno
RemoteForward 6666 127.0.0.1:5555

設定好後可以直接打以下指令

1
2
3
ssh -f -N vps
# -f : run in background
# -N : not execute remote command, useful for forwarding ports

就會等同於以下指令

1
2
3
ssh -f -N -R 6666:127.0.0.1:5555 oalieno@123.45.67.89
# -f : run in background
# -N : not execute remote command, useful for forwarding ports

remote:6666 -> local:127.0.0.1:5555
執行這個指令之後,就會在 remote 聽 6666 port 然後把流量都導到 local 的 127.0.0.1 的 5555 port

dynamic port forwarding

~/.ssh/config
1
2
3
4
Host vps
HostName 123.45.67.89
User oalieno
DynamicForward 9999

設定好後可以直接打以下指令

1
2
3
ssh -f -N vps
# -f : run in background
# -N : not execute remote command, useful for forwarding ports

就會等同於打以下指令

1
2
3
ssh -f -N -D 9999 oalieno@123.45.67.89
# -f : run in background
# -N : not execute remote command, useful for forwarding ports

會開一個 SOCKS 代理伺服器聽在 local 的 9999 port,把流量都轉從 remote 的機器出去

情境一:存取內網資源

你在你租的 vps 伺服器上面跑了一個 mattermost 在測試
但你不想讓 mattermost 聽在 0.0.0.0 公開在網路上讓大家都可以連,有風險
所以你開在 127.0.0.1 只有 vps 本地可以連
但是你又想用你的筆電上的瀏覽器測試
這時候就可以用到 local port forwarding 把 local:8065 -> remote:127.0.0.1:8065
設定如下之後,執行 ssh -f -N vps-mattermost
就可以在本地瀏覽器上面輸入 http://localhost:8065 連到你架好的 mattermost 了

~/.ssh/config
1
2
3
4
Host vps-mattermost
HostName 123.45.67.89
User oalieno
LocalForward 8065 127.0.0.1:8065

情境二:透過跳板 ssh 到沒有 public ip 的機器

你在家裡組了一台桌機,但是這台桌機沒有 public ip,所以在外面的時候沒辦法 ssh 連到家裡的桌機
不過你剛好租了一台有 public ip 的 vps 伺服器
這時候就可以用到 remote port forwarding 把 remote:22000 -> local:127.0.0.1:22
設定如下之後,在桌機上執行 ssh -f -N vps-reverse-ssh
就可以從你的筆電先 ssh 到 vps
然後再從 vps 通過 remote port forwarding ssh 到桌機了
有 public ip 的 vps 伺服器在這裡扮演了跳板的角色

~/.ssh/config (桌機)
1
2
3
4
Host vps-reverse-ssh
HostName 123.45.67.89
User oalieno
RemoteForward 22000 127.0.0.1:22
~/.ssh/config (vps)
1
2
3
4
Host home
HostName 127.0.0.1
User oalieno
Port 22000

autossh

autossh 可以幫你自動重連

~/.ssh/config
1
2
3
4
5
6
7
Host vps
HostName 123.45.67.89
User oalieno
IdentityFile ~/.ssh/id_rsa
LocalForward 5555 127.0.0.1:6666
ServerAliveInterval 30
ServerAliveCountMax 3

設定好後可以直接打以下指令

1
2
3
4
autossh -M 0 -f -N vps
# -M 0 : autossh echo port, recommend disable it by setting it to 0
# -f : run in background
# -N : not execute remote command, useful for forwarding ports

就會等同於打以下指令

1
2
3
4
autossh -M 0 -f -N -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -L 5555:localhost:6666 oalieno@123.45.67.89
# -M 0 : autossh echo port, recommend disable it by setting it to 0
# -f : run in background
# -N : not execute remote command, useful for forwarding ports

escape sequence

有時候連線斷掉後,畫面就會卡在那裡
這時候就可以直接在鍵盤上打 ~. 這個 escape sequences 就可以直接跳出來啦,感覺像是在逃脫 vim 呢xD
下面還有更多的神秘金手指可以打

1
2
3
4
5
6
7
8
9
10
11
12
Supported escape sequences:
~. - terminate connection (and any multiplexed sessions)
~B - send a BREAK to the remote system
~C - open a command line
~R - request rekey
~V/v - decrease/increase verbosity (LogLevel)
~^Z - suspend ssh
~# - list forwarded connections
~& - background ssh (when waiting for connections to terminate)
~? - this message
~~ - send the escape character by typing it twice
(Note that escapes are only recognized immediately after newline.)

  1. https://linux.die.net/man/5/ssh_config
  2. https://nerderati.com/2011/03/17/simplify-your-life-with-an-ssh-config-file/
  3. https://www.everythingcli.org/ssh-tunnelling-for-fun-and-profit-local-vs-remote/
  4. https://askubuntu.com/questions/29942/how-can-i-break-out-of-ssh-when-it-locks
  5. https://johnliu55.tw/ssh-tunnel.html
Read more

【密碼學筆記】Fermat's Factorization Method

有一個奇合數 $n$ ,是兩個奇質數的乘積 $n = pq$
令 $a = \frac{p + q}{2}, b = \frac{p - q}{2}$ 那麼 $n = (a + b)(a - b) = a^2 - b^2$
但是今天我們不知道 $p, q$ ,而我們想要分解 $n$
那我們就猜 $a = \lceil \sqrt{n} \rceil$ ,測試 $a^2 - n$ 是不是完全平方數
猜到的話可以從 $a, b$ 反推回 $p, q$
沒猜到就把 $a$ 加一繼續猜

使用條件

當 $|p-q|$ 很小,可以有效的分解合數 $n$

程式碼 ( python )

1
2
3
4
5
6
7
8
9
10
11
import math
import gmpy2

def fermat(n):
a = math.ceil(math.sqrt(n))
b2 = a * a - n
while not gmpy2.iroot(b2, 2)[1]:
a = a + 1
b2 = a * a - n
b = math.sqrt(b2)
return [a + b, a - b]
Read more

【密碼學筆記】Pollard's p - 1 Algorithm

有一個合數 $n$,有一個質因數 $p$
Pollard’s p - 1 algorithm 可以在 $p-1$ 的最大質因數很小的情況下,有效的分解合數 $n$
可以用在分解 RSA 產生的公鑰 $n$,以此破解 RSA,常見於一些 CTF 的題目中

根據費馬小定理
當 $a$ 不是 $p$ 的倍數
$a^{p-1} \equiv 1 \pmod{p} \to a^{k(p-1)} \equiv 1 \pmod{p} \to a^{k(p-1)} - 1 \equiv 0 \pmod{p}$ for some $k$
所以 $gcd(a^{k(p-1)} - 1, n)$ 一定會是 $p$ 的倍數

Pollard’s p - 1 algorithm 就是嘗試去構造出 $k(p-1)$ ,並且令 $a = 2$ ( 只要 $p \ne 2$ 上面的推論就是對的 )
也就是測試 $gcd(2^{1} - 1, n), gcd(2^{1 \times 2} - 1, n), gcd(2^{1 \times 2 \times 3} - 1, n), …$
當 $1 \times 2 \times 3 \cdots$ 是 $p-1$ 的倍數,我們就成功分解 $n$

使用條件

當 $p-1$ 最大的質因數很小,可以有效的分解合數 $n$

程式碼 ( python )

1
2
3
4
5
6
7
8
9
import math

def pollard(n):
a, b = 2, 2
while True:
a, b = pow(a, b, n), b + 1
d = math.gcd(a - 1, n)
if 1 < d < n:
return d
Read more