เหตุการณ์ที่ทำไฟคริสต์มาสพัง
(whizzy.org)- ระหว่างกระบวนการ reverse engineering เพื่อเชื่อมต่อ ไฟ LED Bluetooth LE ราคาถูก เข้ากับ Home Assistant ผู้เขียนทดลองค่าของเอฟเฟกต์ที่ซ่อนอยู่ในสายไฟ LED แบบ addressable ความยาว 10M จนทำให้คอนโทรลเลอร์หยุดทำงาน
- ใช้ Bluetooth HCI snoop ของ Android และ Wireshark/tshark เพื่อจับแพ็กเก็ต BLE ที่แอปเขียนไปยังไฟ แล้วติดตามไบต์ควบคุมในคำขอเขียน
btatt.opcode.method==0x12 - แพ็กเก็ตของแอป iDeal LED ดูไม่เหมือนค่า on/off แบบง่าย ๆ และหลังจาก decompile APK พร้อมอ้างอิงบทวิเคราะห์เดิม ก็ถอดรหัสได้ด้วย คีย์ AES แบบคงที่ใน
libAES.so - แพ็กเก็ต on/off ที่ถอดรหัสแล้วสรุปได้ว่าเป็น header คงที่ และต่างกันที่ไบต์ที่ 5 เป็น
1/0จากนั้นจึงรันฟังก์ชันสี ความสว่าง และเอฟเฟกต์ซ้ำ ๆ เพื่อบันทึกรูปแบบไบต์ - เมื่อส่งค่า 8 บิตที่เกินช่วง 5 บิต
0x1Fที่แอปใช้กับ RGB ก็ทำให้สีสว่างขึ้นได้ แต่ทันทีที่ส่งหมายเลขเอฟเฟกต์ 12 ไฟก็ดับและ Bluetooth advertising ก็หายไป
จุดเริ่มต้นของการเชื่อมต่อไฟ BLE ราคาถูกเข้ากับระบบอัตโนมัติในบ้าน
- จากสมมติฐานว่าอุปกรณ์ที่สื่อสารผ่าน Bluetooth LE และมีแอปเฉพาะสามารถผสานเข้ากับระบบอัตโนมัติในบ้านได้ จึงทำ reverse engineering แถบ LED ราคาถูกหลายรุ่น
- ก่อนหน้านี้ ผู้เขียนเคยเชื่อมต่อ แถบ LED แบบ non-addressable ความยาว 5M ที่ควบคุมผ่าน Bluetooth LE ราคา £2.38 เข้ากับ Home Assistant ได้ภายในไม่กี่ชั่วโมง และเผยแพร่โค้ดที่เกี่ยวข้องไว้ที่ bj_led
- งาน reverse engineering BLE ของคอนโทรลเลอร์ LEDnetWF ก็มีอยู่ที่ lednetwf_ble
- เป้าหมายครั้งนี้คือ สายไฟ LED แบบ addressable ความยาว 10M ที่วางอยู่บนโต๊ะ และควบคุมด้วยแอป “iDeal LED”
- แอปมีฟีเจอร์จำนวนมากและทำงานได้ค่อนข้างดี
- LED น่าจะเป็น WS2812 หรือผลิตภัณฑ์ที่คล้ายกัน
- สินค้าเป็นไฟที่ซื้อจาก AliExpress
จับไบต์ BLE ที่แอปส่ง
- หากต้องการควบคุมอุปกรณ์ด้วยซอฟต์แวร์ของตนเอง ก่อนอื่นต้องตรวจสอบ ลำดับไบต์ ที่แอปส่งไปยังอุปกรณ์ผ่าน Bluetooth
- โปรโตคอลไฟทั่วไปอาจประกอบด้วย header, ไบต์คำสั่ง เช่น on/off หรือเปลี่ยนสี และ footer ที่อาจเป็น checksum
- บน Android จับข้อมูลตามลำดับต่อไปนี้
- เปิดโหมดนักพัฒนา
- ติดตั้งแอปไฟ
- เปิดใช้
Bluetooth HCI snoopในการตั้งค่านักพัฒนา - สั่งงานในแอป เช่น เปิดและปิดไฟ
- คัดลอก log ไปยังคอมพิวเตอร์ด้วย
adb pull sdcard/btsnoop_hci.log .
- เมื่อเปิด log ใน Wireshark จะเห็นไบต์ที่ส่งไปยังไฟ
- ตัวอย่าง filter คือ
bluetooth.dst == ff:ff:ff:ff:ff:ff && btatt.opcode.method==0x12 - ต้องเปลี่ยน MAC address เป็น MAC ของไฟจริง
btatt.opcode.method==0x12หมายความว่ามีการเขียนจากอุปกรณ์ Android ไปยังไฟ
- ตัวอย่าง filter คือ
- หากใช้ tshark จะดึงค่าแพ็กเก็ตได้โดยตรงจาก terminal
tshark -r <filename> -T fields -e btatt.valueจะแสดง payload ที่เขียนไปยังคอนโทรลเลอร์ LED
โปรโตคอล iDeal LED ที่ replay แบบง่าย ๆ ยังไม่พอ
- ไฟบางรุ่นแสดงรูปแบบที่อ่านแทบตรงไปตรงมาสำหรับการ on/off
- ตัวอย่างคือรูปแบบที่
69 96 02 01 01และ69 96 02 01 00ปรากฏซ้ำ - ไบต์สุดท้ายเปลี่ยนระหว่าง
1และ0เพื่อแทนการเปิด/ปิด
- ตัวอย่างคือรูปแบบที่
- ไฟ iDeal LED ครั้งนี้มีลำดับไบต์ที่ยาวกว่ามากและซ้ำ ๆ กัน แม้จะแยกแพ็กเก็ตสองแบบที่ตรงกับ on/off ได้ แต่ค่าดูเหมือน noise
- หากจุดประสงค์คือแค่เปิดและปิด การ replay โดยส่งลำดับไบต์ที่จับมาได้กลับไปตามเดิมอาจเพียงพอ
- สามารถใช้
gatttoolเชื่อมต่อกับอุปกรณ์ BLE แล้วส่งไบต์ได้ - handle ที่จะส่งต้องตรวจสอบจาก Wireshark
- สามารถใช้
- แต่หากต้องการควบคุมมากขึ้น จำเป็นต้องเข้าใจโครงสร้างแพ็กเก็ต จึงเข้าสู่ขั้นตอนวิเคราะห์ตัวแอป Android เอง
Decompile APK และค้นหาคีย์ AES
- ดาวน์โหลด APK แล้วเปิดด้วย jadx เพื่อตรวจสอบโค้ดของแอป
- พบการอ้างอิงถึง AES ใน source จึงเป็นไปได้ว่าโปรโตคอลถูกเข้ารหัส
- ตั้งสมมติฐานต่อไปนี้กับข้อมูลที่เข้ารหัส
- ciphertext ของการกระทำเดียวกันไม่เปลี่ยนทุกครั้ง จึงอาจมีคีย์ที่สอดคล้องกัน
- เนื่องจาก MCU พลังงานต่ำต้องถอดรหัสอย่างรวดเร็ว คีย์สั้นจึงได้เปรียบ
- คีย์อาจไม่เฉพาะต่ออุปกรณ์แต่ละตัว และอาจเป็นคีย์คงที่
- ในแอปมีไลบรารี AES ที่คอมไพล์แล้วชื่อ
libAES.soซึ่งวิเคราะห์ด้วยjadxอย่างเดียวไม่ได้ - พบ บทวิเคราะห์ ที่มีผู้อื่นใช้
ida freedecompile ไลบรารี AES และค้นพบคีย์ที่ฝังไว้ จึงนำคีย์นั้นมาทดลอง - เมื่อถอดรหัสด้วยโหมด AES ECB ของ
Crypto.Cipherแพ็กเก็ต on/off ก็เปลี่ยนเป็นรูปแบบที่มีความหมาย- ค่าที่ถอดรหัสแล้วปรากฏเป็น
05 54 55 52 4E 01 ...และ05 54 55 52 4E 00 ... - หลัง header คงที่ ไบต์ที่ 5 เปลี่ยนเป็น
1หรือ0เพื่อแทน on/off - ส่วนที่เหลือถูกเติมด้วย 0
- ค่าที่ถอดรหัสแล้วปรากฏเป็น
- ตั้งแต่ขั้นตอนนี้เป็นต้นไป จึงสามารถถอดรหัสแพ็กเก็ตที่แอปส่ง และทำซ้ำการควบคุมเดียวกันในโค้ดของตนเองได้
บันทึกรูปแบบไบต์แยกตามฟังก์ชัน
- ขยายขอบเขตโปรโตคอลด้วยการรันทุกฟังก์ชันของแอปทีละรายการและบันทึกไบต์ที่ส่ง
- ทำซ้ำแต่ละการกระทำหลายครั้ง และแทรกรูปแบบปิด/เปิดไฟเพื่อแบ่ง section
- เปลี่ยนสีหลายครั้งตามลำดับ red, green, blue
- เปลี่ยนความสว่างเป็น 100%, 50%, 10%, 50%, 100%
- ใส่ off/on ระหว่างแต่ละกลุ่ม เพื่อให้หาขอบเขตใน capture log ได้ง่าย
- ด้วยวิธีนี้จึงตรวจสอบได้ว่าไบต์ใดเปลี่ยนไปตามการกระทำ และจับคู่การกระทำที่บันทึกไว้กับแพ็กเก็ตที่จับมาได้
หมายเลขเอฟเฟกต์ 12 ที่ทำให้คอนโทรลเลอร์กลายเป็น brick
- ระหว่างตรวจสอบการเปลี่ยนสี พบว่าแอปไม่ส่งค่าที่มากกว่า
0x1Fสำหรับค่า red, green, blue0x1Fคือช่วง 5 บิต- เมื่อส่งค่า 8 บิตโดยตรง สีที่สว่างกว่าก็ทำงานได้
- เพื่อดูว่ามีเอฟเฟกต์เพิ่มเติมนอกเหนือจาก 10 เอฟเฟกต์ที่แอปใช้หรือไม่ จึงรัน loop
range(20)เพื่อส่งหมายเลขเอฟเฟกต์ตามลำดับ- ตั้งแต่ 1 ถึง 10 ทำงานตามปกติ
- ที่ 11 พบการทำงานที่ดูเหมือนโหมดซ่อนอยู่
- เมื่อข้ามไปที่ 12 ไฟก็ดับ
- หลังจากนั้นไฟไม่กลับมาติดอีก
- ไม่ทำ Bluetooth advertising อีกต่อไป
- เชื่อมต่อไม่ได้
- แม้กดปุ่มค้างไว้ตอนเปิดไฟก็ไม่กู้คืน
- ถอดปลั๊กทิ้งไว้ทั้งคืนก็ไม่กลับมา
- ผู้เขียนสันนิษฐานว่า firmware อาจเสียหายจาก buffer overflow แต่ยังยืนยันสาเหตุไม่ได้
- ตัว LED เองเป็น LED แบบ addressable มาตรฐาน จึงสามารถเชื่อมต่อกับไมโครคอนโทรลเลอร์ตัวอื่นเพื่อนำสายไฟกลับมาใช้ใหม่ได้
ผลลัพธ์ที่เหลืออยู่และข้อควรระวัง
- แม้จะล้มเหลว แต่ผู้เขียนก็จัดทำเอกสารโปรโตคอลส่วนใหญ่ไว้ และสร้างโปรเจกต์ GitHub ที่มี คอมโพเนนต์แบบกำหนดเองสำหรับ Home Assistant รวมอยู่ด้วย
- คอมโพเนนต์ทำงานได้ แต่การทดลองในลักษณะเดียวกันอาจทำให้คอนโทรลเลอร์ไฟเสียได้ จึงควรดำเนินการโดยรับความเสี่ยงเอง
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
ดูเหมือนไม่ใช่ “เฮดเดอร์คงที่” ของ FTA เท่าไร แต่เป็นรูปแบบที่มี ไบต์ความยาว (“คำสั่งยาว 5 ไบต์”), คำสั่ง
TURN, อาร์กิวเมนต์ 0/1 แล้วเติม 0 ที่เหลือให้ครบ 16 ไบต์มากกว่า54 55 52 4Eมองแวบเดียวก็เห็นว่าเป็นตัวอักษรพิมพ์ใหญ่TURNถ้ารู้ว่าตัวเลขเริ่มที่
0x30, ตัวพิมพ์ใหญ่เริ่มที่0x41, ตัวพิมพ์เล็กเริ่มที่0x61ก็จะหาลายแพตเทิร์นอักขระกับตัวเลขใน ดัมป์เลขฐานสิบหก ได้ง่ายความรู้แบบนี้มีประโยชน์กับสตริงสั้น ๆ แต่ถ้าดูแพ็กเก็ตหรือดัมป์หน่วยความจำ รูปแบบ hexdump มาตรฐานจะดีที่สุด
0x54 55 52 4EคือT U R Nจริง ๆuintธรรมดาอาจเป็น ปัญหาแหล่งจ่ายไฟ ก็ได้ ตระกูล WS281x เปิดให้สว่างมากได้ และถ้าต่อหลายดวงรวมกันก็กินกระแสหนักมาก
สตริป 100 ดวงกับแหล่งจ่ายไฟ 12V 3A นั้นพอเปิดสีขาวความสว่างสูงสุดทั้งเส้นได้แบบเฉียด ๆ และแสบตามาก
ดังนั้นผมคงดูฟิวส์ขาดก่อน เฟิร์มแวร์ที่ใช้ความสว่างแค่ถึง 31 จากค่าสูงสุด 255 ก็เป็นเบาะแสเหมือนกัน
0x1fตอนแรกผมก็คิดแบบนั้นเหมือนกัน ถ้าโชคดี แหล่งจ่ายไฟอาจมีฟิวส์และขาดแค่ฟิวส์ก็ได้ต่อให้กู้คืนไม่ได้ สายไฟ LED WS281x มาตรฐานก็ยังต่อกับ Raspberry Pi ได้ และใช้คอนโทรลเลอร์ LED แบบระบุตำแหน่งได้โอเพนซอร์สของผมได้ด้วย :) https://github.com/mbevand/ledthemfight
มีเอฟเฟกต์ในตัว และทำมาแบบโมดูลาร์มากสำหรับงาน DIY แค่ Python สองบรรทัดก็สร้างโมดูลเอฟเฟกต์ LED แบบคัสตอมง่าย ๆ ได้ เดโมอยู่ที่นี่: https://youtu.be/qpd2rILsnM4
12V, แดง, เขียว, น้ำเงินน่าจะเป็นสตริป RGB อนาล็อกที่ทั้งเส้นติดเป็นสีเดียวกัน ไม่ใช่แบบระบุตำแหน่งแต่ละดวงได้
https://www.aliexpress.com/item/1005005485885067.html
ใครที่อยากลองยุ่งกับไฟ LED ควรดู WLED ให้ได้ มีเอฟเฟกต์ในตัวเยอะ มีเว็บ GUI ใช้ ESP32 หรือ ESP8266 ราคาถูกมากเป็นคอนโทรลเลอร์ และตอบสนองต่อเสียงได้ด้วย ตอนนี้ผมรัน WLED กับไฟคริสต์มาสในบ้านอยู่ ดูดีมาก
เป็นวิธีจำกัดการใช้พลังงานที่เรียบร้อยดี
บทความดี นึกถึงตอนที่พยายามรีเวิร์สเอนจิเนียร์ไฟที่มีอยู่ แล้วพบว่ามันถูก เข้ารหัส ไว้
ตัวหนึ่งคือ amaran 60d และที่เหลือเป็นไฟ LED แบตเตอรี่ SmallRig RM75 อยู่สองสามตัว ผมอยากเปิดปิดทั้งหมดด้วยสคริปต์เดียวโดยไม่ต้องไปกดแยกสองแอป
ใช้เวลาไปพอสมควรกับการรีเวิร์สเอนจิเนียร์แอปและโปรโตคอล และดูเหมือนไฟทั้งสองแบบใช้ขั้นตอนเจรจาแบบเดียวกัน แต่ implement ด้วยไลบรารีคนละตัว
พยายามเลียนแบบ การแลกเปลี่ยนกุญแจ Diffie-Hellman ที่ทำตอนเชื่อมต่อ แต่สุดท้ายก็ยอมแพ้ เท่าที่จำได้ หลังจากนั้นยังมีอีกหนึ่งหรือสองขั้นตอน และหลังส่งกุญแจแรกไป มันก็ส่งตัวเลขที่ดูสุ่มอีกตัวหนึ่ง (กุญแจอีกตัว?) มา แต่ผมหาไม่ออกว่ามันต้องการอะไร
อ่านบทความนี้แล้วทำให้นึกว่าควรลองใส่กุญแจที่ฮาร์ดโค้ดไว้ดูว่าทำงานไหม
ถ้ามี การเชื่อมต่อโอเพนซอร์ส สำหรับ Elgato Stream Deck หรือ Home Assistant ก็น่าจะเป็นกรณีสำเร็จครั้งใหญ่ สำหรับคนทำ YouTube ที่ถ่ายวิดีโอ talking head ที่โต๊ะ น่าจะดีกว่าการคุมไฟผ่านแอปมือถือเยอะ
พยายามทำระบบอัตโนมัติกับไฟคริสต์มาสที่เชื่อมต่อผ่าน BLE แล้วกลายเป็น ขยะอิเล็กทรอนิกส์ ตอนนี้คุณก็ทำได้เหมือนกัน
iPhone ผมไม่แน่ใจ แต่บน Android ถ้าปล่อยไว้เบื้องหลัง ก็จับกิจกรรม BLE ได้ใน GUI สะอาด ๆ ของมือถือ ใช้อยู่ทุกวัน
ทำให้นึกถึง MCU คีย์บอร์ด ที่กลายเป็นอิฐเมื่อได้รับคำสั่งไฟบางอย่าง
สุดท้าย OpenRGB ต้องปิดไม่ให้โมดูลนั้นรันอัตโนมัติกับฮาร์ดแวร์ดังกล่าว บางครั้งซอฟต์แวร์ของผู้ผลิตเองก็กระตุ้นบั๊กเดียวกันด้วย
น่าเสียดายที่โดยปกติแล้วการเข้าสู่โหมด in-system programming ต้องส่งรายงาน USB HID แต่ตัวอุปกรณ์ที่ได้รับผลกระทบไม่แม้แต่จะ enumerate อีกต่อไปแล้ว
ส่วนจริง ๆ แล้วเป็นเฟิร์มแวร์เสียหาย หรือฮาร์ดแวร์พังเพราะพฤติกรรมที่ไม่ได้กำหนดแบบอื่นนั้นเป็นอีกเรื่องหนึ่ง
“ส่ง plaintext ทางไร้สายมันไม่ปลอดภัยไม่ใช่เหรอ”
“ไม่ต้องห่วง เราใส่ การเข้ารหัส AES แล้ว”
เพราะวิธีจับคู่มันเป็นแบบนั้น แอปจะ broadcast แพ็กเก็ตไปที่
255.255.255.255แบบสุ่มสี่สุ่มห้า และอุปกรณ์เป้าหมาย (หลอดไฟ ปลั๊ก ฯลฯ) จะรออยู่ในโหมด promiscuousเนื้อหาแพ็กเก็ตถูกป้องกันด้วย WPA2 และอื่น ๆ แต่ความยาวแพ็กเก็ตไม่ได้ถูกป้องกัน จึงส่งแพ็กเก็ตจิ๋วจำนวนมหาศาลที่ตั้งค่าความยาวของแต่ละแพ็กเก็ตให้เป็นค่าไบต์ ASCII ของอักขระถัดไปในแฮนด์เชกการตั้งค่า น่าจะส่งซ้ำหลายรอบด้วย
นั่นเลยทำให้การจับคู่ใช้เวลา 2 นาที แล้วมักหยุดกะทันหันก่อนตัวนับจะถึง 0 เสมอ
ตอนที่ผมรีเวิร์สเอนจิเนียร์ BLE controller ของเตาผิงเมื่อปีที่แล้ว สิ่งนี้แหละที่ผมกังวลมาก
โดยเฉพาะคำสั่ง “Set password” ที่รับอินพุตเป็นไบต์ดิบ แต่แอป OEM ส่งเฉพาะข้อมูลตัวเลขที่จำกัดความยาวเท่านั้น
โชคดีที่ดูเหมือนว่าพอไฟดับชั่วครู่ มันก็ลืมทุกอย่างที่เคยเกิดขึ้นก่อนหน้านั้นหมด
ดังนั้น BLE controller ส่วนใหญ่ของแบรนด์เดียวกันน่าจะยังใช้รหัสผ่านเริ่มต้น
0000อยู่เหมือนเดิม ถ้าไฟดับสักครั้ง สุดท้ายก็กลับไปอยู่สภาพนั้น ถ้าว่างจริง ๆ ก็น่าจะเป็นหัวข้อ BLE wardriving ที่ใช้ได้ทีเดียวผมเองก็เอาผลรีเวิร์สเอนจิเนียร์ไปทำเป็นการเชื่อมต่อกับ Home Assistant แล้ว (https://github.com/kaechele/napoleon-efire) และเขียนเอกสารของระบบกับโปรโตคอลไว้ด้วย (https://bonaparte.readthedocs.io/en/latest/index.html)
กำลังหา ไฟประดับที่ยืดหยุ่นและทนทาน สำหรับติดบนแร็ครถยนต์
อยากได้แบบใช้แบตเตอรี่ เหมาะกับกลางแจ้ง/ทุกสภาพอากาศ และถ้ากล่องแบตเตอรี่ใช้เคเบิลไทร์ยึดกับพื้นผิวได้ง่ายก็จะดี
ถ้าเป็นไปได้อยากได้ฟอร์มแฟกเตอร์แบบ “mini” อย่าง
T5และเป็น RGB ที่โปรแกรมได้ เพื่อจะใช้ตามฤดูกาล เช่น แดง/เขียวตอนคริสต์มาส ม่วง/ส้มตอนฮาโลวีนอยากรู้ว่ามีใครรู้จักสินค้าที่ตรงเงื่อนไขนี้ไหม เงื่อนไขสามข้อแรกหาได้ตาม Walmart หรือ Home Depot แต่หาตัวที่ตรงเงื่อนไขสุดท้ายได้ยาก
ค้นหาแล้วเจอแต่สัญญาณรบกวนเต็มไปหมด
[1] https://cdn.christmaslightsetc.com/images/CategoryDetail/788...
เช่น แถบ LED 12V ที่ กันน้ำ IP67 (แบบอยู่ในท่อซิลิโคน) หาได้ง่าย [0] และมีจำนวน LED มากกว่าสายไฟแบบไฟคริสต์มาสมาก เอฟเฟกต์ก็น่าจะแรงกว่า
แต่คุณต้องซื้อมายโครคอนโทรลเลอร์อย่าง Arduino, ESP32, ESP8266 มาโปรแกรมเอง และต้องหาวิธีดึงไฟจากแบตเตอรี่รถยนต์เอง [1]
อาจใส่อุปกรณ์อิเล็กทรอนิกส์ไว้ในรถ แล้วปล่อยแค่สายออกไปนอกฝากระโปรงท้าย โดยหวังพึ่งซีลฝากระโปรงท้ายเดิมให้กันน้ำได้
[0] https://www.aliexpress.com/item/1005004289391906.html
[1] https://www.aliexpress.com/item/1005005977505151.html
จะบัดกรีสายต่อเพิ่มหรือเชื่อมเป็นเชนต่อกันก็ง่าย
https://www.aliexpress.com/item/4000105913323.html
จากที่อ่านมา สินค้าแบบนี้หรือไฟ 5V คล้าย ๆ กันดูเหมือนจะใช้กันแทบเป็นมาตรฐานในโชว์ไฟคริสต์มาสกลางแจ้ง
#projectsกับ#showcaseได้เลย มีโปรเจกต์ติดตั้ง ไฟ RGB LED บนรถเยอะมากแต่ระวังนะ ด้านนี้เผลอ ๆ จะติดง่าย
ลิงก์เชิญ: https://discord.gg/eVhhh2Wh
ขอเห็นภาพการ แกะ/แยกชิ้นส่วน ไฟคริสต์มาสที่เสียได้ไหม?
ถ้ารู้ว่าข้างในมีชิปอะไร อาจได้เบาะแสว่ามีหน่วยความจำแฟลชหรือไม่ และดัมพ์ออกมาได้ง่ายแค่ไหน