2021年10月16日 星期六

藝術品

用說書服務大概衝刺了不少知識或資訊的累積,真的滿優的,雖說也會擔心不小心被洗腦了 XD 整體上還是很佩服樊登說書,就是一個 CP 值極佳的知識吸收工具,工具的好壞還是回歸使用者怎樣看待與善用。

大概前幾年開始用 podcast 聽一些國內外的訪談演講,便會覺得國外內容發達,拿來練英聽是不錯,但當作知識吸收就效率慢,剛好沒點太多技能在非母語上,很難 N倍速吸收。並且扣除掉演講類型的 podcast 外,許多都太過碎片化,直到碰到說書服務,且一口氣就是講 30-60 分鐘的說書,跟檯面上只說個 5-15 分鐘的 Youtuber 說書內容有很顯著的程度差距的,最重要的是可以融會貫通的提及幾本經典書共通點。暫時先持續享受這速食知識體驗,接著買了一堆電子書等著清 囧

回過頭來,發現年紀到了一個時間點後,看看周邊的大大們也對等的累積成就,沒有人是停留在之前的狀態的,就算有也長出不一樣的技術樹,如此就會想起跟幾位前輩哈拉一些八卦議題,俗稱的正妹的朋友還是正妹,強者的朋友還是強者。大概就能力越強、責任越大,人越有地位,主動攀談的朋友就肯定越多 XD 對應的八卦是強者們的領域,絕對存在許多雜訊或誘惑的。

回顧著周邊大大們,就像美術館裡的藝術品,只能遠遠欣賞著,沒什麼互動機會。當彼此努力向前者,也各處開花的,大家各自在自己的機緣處深耕,倒也不用說什麼離開舒適圈的事,強者是自成生態系,其舒適圈腹地是不斷擴大的,能力強大到做什麼事都像在舒適圈,世界的美好就是建構在這種最大亂度,不要求每個人都走同樣的路線。

最後 ,筆記一下近期的閒逛,無意間看到了一位要捐出版稅的大大,說真的我是被捐版稅吸引到的,恰好書名的領域跟工作有點相關,就仔細看了一下內容,依照內容原先以為也是三五、四十歲的人生經驗,結果在挖掘下去,才發現是二字尾的大大,這時就很納悶,究竟是怎樣的成長環境造就了這等人才?果真個性影響一生的選擇,其成長過程並不是俗稱的速成路線,在年輕時期便經歷過家庭/求學/人生各種議題的摸索跟闖蕩,造就目前的成就,瞬間想起在研究單位的好友,高中時期半工半讀、大學碰到恩師,接著在最高學府闖蕩時,碰到不認同的教授就選擇放棄不讀 XD 真的佩服。

此外也回憶起自己的一些個性,像是本身是略悲觀主義,但是屬於那種把最差的情境思考後,反而更大膽去嘗試任何事,且任何事的起步並不是追求一步到位的完美,而是追求不斷修正去趨近想要的目標。恰恰好看到有類似個性的人,感到共鳴。

認識一個人,就像翻開一本書,翻開的那一瞬間,就無法再把書闔上了。所以,書要慎選,不要亂翻,以免不小心就被感動了。

2021年9月20日 星期一

[書] 再讀一次: 你要如何衡量你的人生

今年春天被推坑聽了 樊登說書 ,這是個付費服務,趁活動就來個買一送一,大概每週都可以聽個 10本書,滿不錯的。前幾年我也用 podcast 聽一些英文頻道,像是 Talks at Google, Master of Scale with Reid Hoffman 等,後來發現,練著英聽不如加速自己的資訊/知識廣度的吸收,反而愛上樊登說書,像是陪小孩睡覺、運動時,可以用 2x 速度吸收,雖然對應有點減少了英聽的機會。

在三個多月內聽了幾百本書後,在回憶有哪些想深讀的書籍,其中第一本就是 克里斯汀生 的 你要入如何衡量你的人生 。事實上,我也趁 KOBO 電子書特價時,也把 克雷頓‧克里斯汀生 相關的書籍都買來收藏,還包括:

創新者的用途理論、創新者的DNA、創新者的修煉、創新者的解答、繁榮的悖論、精讀克里斯汀生

不過,我大概還有一半還沒看過 XD 這次算是第二或第三次讀這本書,也買過實體書歲給幾位親友。每次讀都有種很深的省思,特別是用來調整生活步調、心態的時候,非常適合。部分篇章或思維比較強調宗教的,這部分我個人是給予尊重,但不一定接受,我自己解釋這些是信仰,有信仰的人,就像獵人漫畫中的庫拉皮卡,可以對生活注入更強烈的意志,只要表現出來的是好的,也都是好事的。

再次筆記一下再次閱讀的心情,要時常提醒自己,終究一生想要成為怎樣的人、是否有時常關注家庭跟親子發展、是否有播點時間去評估家庭成員的知識發展,有許多的投資是需要長時間的投入的,不能老想著立竿見影的標的。

2021年9月10日 星期五

Node.js 筆記- Loopback 2 與 MySQL 查詢緩慢的瓶頸排除 @ loopback-connector-mysql, connectionlimit

花了兩天幫同事追一個問題:為何有個服務重啟後,一直處於不穩甚至無法服務的狀態,但隨著時間是有機會變穩定的。

這算是一個 IoT 老服務了,當裝置上線時會跟服務保持連線(Websocket),其中有一些認證跟服務會使用到 DB server。當運行好一段時間,沒什麼大問題,反應速度也很良好。但碰到服務發布時,都會有一段期間無法正常使用。

此現象主要是 IoT 裝置們當初設計了自動連上雲台的架構,每台裝置啟動後,依序連上雲服務。但是,當服務更新時,若採到斷線機制時,將導致大量的裝置重新連線,而連線過程若有要認證就會佔用到 DB query 資源。此時若有些回應慢,將導致有些裝置認為服務不正常而自行斷線,並且又進入 retry 機制,導致災難性的自家人打自家人 DDoS 世界奇觀。

追了好一陣子,鎖定了一套使用 node.js - loopback 2 框架的服務,並且追到 DB query 瓶頸。主要透過對 db 查詢結果做 cache ,可以大幅度改善狀態,去減少 db query。並且當下 db 並未忙碌到回應緩慢。包括用其他對應機器追蹤查詢 db server 效率,以及在雲台出問題的機器上,直接用 mysql command line 查詢,即可交叉驗證應當是 node.js - loopback 的狀態問題。

原本也有意先把套件都升一升,可惜要跳到 LoopBack 4 並不是簡單的事 Orz 最後,老覺得這件事還是不太正常,就用 concurrent limit loopback mysql 關鍵字逛逛,大部分都在討論連線異常,很少提到連線數的問題,很幸運最後找到 connectionlimit 關鍵字,並且在查一下 loopback-connector-mysql 得知預設是只用 10 這個數字!

  
if (isNaN(s.connectionLimit)) {
    s.connectionLimit = 10;
  }

因此,就只需要在 loopback 框架下的 datasources.json ,多定義 connectionLimit 即可,例如來個 100 ,並且在系統上追蹤連線數:

$ netstat -np --inet | grep "YourDBServerIP:3306" | wc -l
100

這樣就搞定啦!當時想說 loopback 2 太舊太難升,是不是弄個類似 mysql command line 查詢機制來頂著用 XD 還因此配合 server 環境用 php5 寫好小工具了:

% echo '{"db_host":"DB_IP","db_user":"DB_USER","db_pass":"password","db_database":"DB_NAME","db_query":"SELECT count(*) FROM device;"}'  | jq ''     
{
  "db_host": "DB_IP",
  "db_user": "DB_USER",
  "db_pass": "password",
  "db_database": "DB_NAME",
  "db_query": "SELECT count(*) FROM device;"
}

% echo '{"db_host":"DB_IP","db_user":"DB_USER","db_pass":"password","db_database":"DB_NAME","db_query":"SELECT count(*) FROM device;"}'  | php db_helper.php /tmp/db_helper.log N M | jq ''
{
  "input": [
    [
      {
        "count(*)": "1234567"
      }
    ]
  ],
  "log": [
    0.34358501434326,
    0.11107587814331,
    0.47047901153564
  ],
  "status": true
}

還設計了 db_helper.php 能不能支援最多跑 N 隻,每隻也來個 timeout M 秒,並透過 lock 機制來保護溝通等,想想這段旅行也是滿好玩的。先把尚未妥善測試的 php5 工具丟到 github 記一下。

2021年8月25日 星期三

[macOS] 使用 hashcat 驗證/回憶 iOS iPhone/iPad iTunes 備份檔 密碼

故事起源於幫長輩換手機,這時手機有用過 itunes 備份過一份並且加密,但...怎麼都想不起密碼。在網路上找尋解法時,大概可以看到幾款破解軟體(推論是免費試用,真正要破解時需加價)。此刻的不方便,反而要感謝起 Apple 資安服務,高規格保護資料!大家的破解原理都是一樣的:暴力解-猜密碼。

研究後,發現有個 hashcat 的 open source 統包了眾多加密系統的猜密碼任務,而網路上 2017年也以人講解使用過程,還滿仔細的:

Crack Encrypted iOS backups with Hashcat

筆記一下過程:
  1. 先找到 iOS 備份檔擺放的位置: https://support.apple.com/en-us/HT204215
    • macOS: ~/Library/Application Support/MobileSync/Backup/
  2. 備份都是目錄結構,找到目錄中的 Manifest.plist 檔案
  3. 依照裡頭的資訊,準備餵給 hashcat 的資料,可分成 iOS 10 以上跟 iOS 9 以前,組成 inputFromManifest.txt 資料
    • Less then iOS 10:
      • $itunes_backup$*<ver>*<WPKY>*<ITER>*<SALT>**
    • iOS 10 or later:
      • $itunes_backup$*<ver>*<WPKY>*<ITER>*<SALT>*<DPIC>*<DPSL>
如此,就可以靠 hashcat 來運算,其原理包括支援平行處理(GPU)去算出密碼,包括從字典檔、暴力猜等等的。此外,關於上述 2~3 步驟,其實也有人佛心提供工具:github.com/philsmd/itunes_backup2hashcat,透過 perl 小程式自動幫人從 Manifest.plist 組出資訊:

% perl itunes_backup2hashcat.pl yourBackupDir/Manifest.plist 
$itunes_backup$*10*#######*####*######*######*##################
% perl itunes_backup2hashcat.pl yourBackupDir/Manifest.plist > inputFromManifest.txt

後續就著重 hashcat 的指令筆記,若要更多指令資訊,逛個官方網站是最方便的:hashcat.net/wiki/doku.php?id=hashcat

以在 https://www.youtube.com/watch?v=MMySnPzsPYU 內提供的範例資訊:

% cat inputFromManifest.txt 
$itunes_backup$*10*c8c96e8d6175f1356da6dcf5791ad76be166232b4a01bd6974abb27c39034993591c15ba03a14e3b*10000*0ca3ccfc453b32f9ca9aceb754b87f4e0ab9cb95*10000000*bdee6869caa7999e9576f390a248a29f38d10d6f

這時都是 iOS 10 的密碼,因此在使用 hashcat 會採用 -m 148000 參數。若是 iOS 9 以前,要用 -m 14700 參數這些都有定義在官方網站的 wiki 中。

第一招:暴力解,此例猜4個數字:

% hashcat -d 1 -m 14800 ./inputFromManifest.txt -a 3 '?d?d?d?d'
hashcat (v6.2.3) starting

OpenCL API (OpenCL 1.2 (Jun 17 2021 15:24:17)) - Platform #1 [Apple]
====================================================================
* Device #1: Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz, 16320/16384 MB (4096 MB allocatable), 4MCU
* Device #2: Intel(R) Iris(TM) Plus Graphics 640, skipped

Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256

Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates

Optimizers applied:
* Zero-Byte
* Single-Hash
* Single-Salt
* Brute-Force
* Slow-Hash-SIMD-LOOP
* (null)

Watchdog: Temperature abort trigger set to 100c

Host memory required for this attack: 1 MB

[s]tatus [p]ause [b]ypass [c]heckpoint [f]inish [q]uit => 

這時隨時都可以按 s 去看暴力解的進度:

Session..........: hashcat
Status...........: Running
Hash.Name........: iTunes backup >= 10.0
Hash.Target......: $itunes_backup$*10*c8c96e8d6175f1356da6dcf5791ad76b...d10d6f
Time.Started.....: Wed Aug 25 20:37:03 2021 (6 mins, 50 secs)
Time.Estimated...: Wed Aug 25 23:30:00 2021 (2 hours, 46 mins)
Kernel.Feature...: Pure Kernel
Guess.Mask.......: ?d?d?d?d [4]
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:        1 H/s (13.34ms) @ Accel:128 Loops:256 Thr:1 Vec:4
Recovered........: 0/1 (0.00%) Digests
Progress.........: 0/10000 (0.00%)
Rejected.........: 0/0 (0.00%)
Restore.Point....: 0/1000 (0.00%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:8056320-8056576
Candidate.Engine.: Device Generator
Candidates.#1....: 1234 -> 1124
Hardware.Mon.SMC.: Fan0: 100%
Hardware.Mon.#1..: Temp: 66c

[s]tatus [p]ause [b]ypass [c]heckpoint [f]inish [q]uit => 

而幸運解出密碼時,會顯示以下資訊,主要是 Status 會顯示 Cracked 而密碼則是紀錄在冒號後面 (:1234)

$itunes_backup$*10*c8c96e8d6175f1356da6dcf5791ad76be166232b4a01bd6974abb27c39034993591c15ba03a14e3b*10000*0ca3ccfc453b32f9ca9aceb754b87f4e0ab9cb95*10000000*bdee6869caa7999e9576f390a248a29f38d10d6f:1234
                                                          
Session..........: hashcat
Status...........: Cracked
Hash.Name........: iTunes backup >= 10.0
Hash.Target......: $itunes_backup$*10*c8c96e8d6175f1356da6dcf5791ad76b...d10d6f
Time.Started.....: Wed Aug 25 20:37:03 2021 (8 mins, 26 secs)
Time.Estimated...: Wed Aug 25 20:45:29 2021 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Mask.......: ?d?d?d?d [4]
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:        1 H/s (11.09ms) @ Accel:128 Loops:256 Thr:1 Vec:4
Recovered........: 1/1 (100.00%) Digests
Progress.........: 512/10000 (5.12%)
Rejected.........: 0/512 (0.00%)
Restore.Point....: 0/1000 (0.00%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:9984-9999
Candidate.Engine.: Device Generator
Candidates.#1....: 1234 -> 1124
Hardware.Mon.SMC.: Fan0: 100%
Hardware.Mon.#1..: Temp: 65c

Started: Wed Aug 25 20:36:57 2021
Stopped: Wed Aug 25 20:45:30 2021

第二招:將可能的密碼寫在 myPasswordlist.txt 中去驗證,指令:

% cat myPasswords.txt 
5678
09876
1234

% hashcat -d 1 -m 14800 ./inputFromManifest.txt ./myPasswords.txt 
hashcat (v6.2.3) starting

OpenCL API (OpenCL 1.2 (Jun 17 2021 15:24:17)) - Platform #1 [Apple]
====================================================================
* Device #1: Intel(R) Core(TM) i5-7360U CPU @ 2.30GHz, 16320/16384 MB (4096 MB allocatable), 4MCU
* Device #2: Intel(R) Iris(TM) Plus Graphics 640, skipped

Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256

Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1

Optimizers applied:
* Zero-Byte
* Single-Hash
* Single-Salt
* Slow-Hash-SIMD-LOOP
* (null)

Watchdog: Temperature abort trigger set to 100c

Host memory required for this attack: 1 MB

Dictionary cache built:
* Filename..: ./myPasswords.txt
* Passwords.: 3
* Bytes.....: 16
* Keyspace..: 3
* Runtime...: 0 secs

The wordlist or mask that you are using is too small.     
This means that hashcat cannot use the full parallel power of your device(s).
Unless you supply more work, your cracking speed will drop.
For tips on supplying more work, see: https://hashcat.net/faq/morework

Approaching final keyspace - workload adjusted.           

$itunes_backup$*10*c8c96e8d6175f1356da6dcf5791ad76be166232b4a01bd6974abb27c39034993591c15ba03a14e3b*10000*0ca3ccfc453b32f9ca9aceb754b87f4e0ab9cb95*10000000*bdee6869caa7999e9576f390a248a29f38d10d6f:1234
                                                          
Session..........: hashcat
Status...........: Cracked
Hash.Name........: iTunes backup >= 10.0
Hash.Target......: $itunes_backup$*10*c8c96e8d6175f1356da6dcf5791ad76b...d10d6f
Time.Started.....: Wed Aug 25 20:52:58 2021 (10 secs)
Time.Estimated...: Wed Aug 25 20:53:08 2021 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (./myPasswords.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:        0 H/s (0.18ms) @ Accel:128 Loops:256 Thr:1 Vec:4
Recovered........: 1/1 (100.00%) Digests
Progress.........: 3/3 (100.00%)
Rejected.........: 0/3 (0.00%)
Restore.Point....: 0/3 (0.00%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:9984-9999
Candidate.Engine.: Device Generator
Candidates.#1....: 5678 -> 1234
Hardware.Mon.SMC.: Fan0: 20%
Hardware.Mon.#1..: Temp: 58c

...

大概就這兩招筆記一下,未來忘記密碼時,就可以靠 hashcat 幫忙批次測試記憶中的密碼了

2021年7月20日 星期二

Node.js 筆記- 使用 Puppeteer 進行 JS Injection 與 Custom function 定義實作 @ Puppeteer v10, Node.js v16.5.0

之前使用 Puppeteer 方便自己撰寫一些監控 http requests ,像是即時監控 remote resource 變化。接著,要來把玩 JS Injection。

如果使用 Puppeteer 的用法是包括持續瀏覽網頁的話,那適合在網頁 onload 的情況下植入:

// https://pptr.dev/#?product=Puppeteer&version=v10.0.0&show=api-event-domcontentloaded
// https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event
//
// The load event is fired when the whole page has loaded, including all dependent resources such as stylesheets and images. This is in contrast to DOMContentLoaded, which is fired as soon as the page DOM has been loaded, without waiting for resources to finish loading.
//
page.on('load', async () => {
console.log('on.load');
});

如果是瀏覽網頁後,立馬植入 JS Code 運行並且任務就結束了,那就是適合在 network 連線閒置後處理:

await page.goto(target_url, {
waitUntil: 'networkidle0',
});
console.log('networkidle0');

而 JS Injection 的方式:

await page.evaluate((js_code) => {return Promise.resolve(window.eval(js_code));}, js_resource_content);

其中 js_resource_content 就是預計植入的 JS Code 字串。例如有多份程式碼要植入,可以跑回圈:

for(let i=0 ; i<js_resource.length ; ++i) {
console.log('inject: '+js_resource[i]+', size: '+js_resource_content[i].length);
await page.evaluate((js_code) => {return Promise.resolve(window.eval(js_code));}, js_resource_content[i]);
}

其中 js_resource[] 紀錄的是 js code 網址,而 js_resource_content[] 是紀錄該 js code 網址的內容。

最後,提一下自訂函數的寫法:

// https://github.com/puppeteer/puppeteer/blob/main/examples/custom-event.js
// Define a window._puppeteer_helper function on the page.
await page.exposeFunction('_puppeteer_helper', (data) => {
console.log(`_puppeteer_helper fired`);
});

如此,又可以靠 JS Injection 去呼叫 window._puppeteer_helper ,或是靠 phage.evaluate 執行了:

let result = await page.evaluate((data_to_page) => {
let data = data_to_page;
return Promise.resolve(window.eval(`
window._puppeteer_helper();
`));
}, 'nothing');