ถอดรหัสสคริปต์ bash ที่ถูกทำให้อ่านยากบนเสื้อยืด Uniqlo
(tris.sherliker.net)- เสื้อยืดลาย Akamai ที่วางขายในร้าน Uniqlo มีการพิมพ์ โค้ด bash ที่ถูกทำให้อ่านยาก ซึ่งนำผลลัพธ์จาก
base64 --decodeไปรันด้วยevalแต่จริง ๆ แล้วเป็นอีสเตอร์เอ็กสำหรับแคมเปญPeace for All - เมื่อถอด Here string ที่เข้ารหัสด้วย Base64 จะได้สคริปต์ที่แสดงข้อความ
♥PEACE♥FOR♥ALL♥ซ้ำ ๆ ในเทอร์มินัลเป็น แอนิเมชันคลื่นไซน์ - Base64 ไม่มีการแก้ไขข้อผิดพลาด ทำให้การถอดข้อความจากภาพเป็นเรื่องยุ่งยาก ผู้เขียนจึงตรวจสอบ padding ของสตริง รวมถึงเครื่องหมายคำพูดและวงเล็บปีกกาให้ตรงกัน แล้วเปรียบเทียบผลจาก Android OCR, Tesseract และ Claude ก่อนจัดระเบียบ
- สคริปต์ที่ถอดรหัสแล้วใช้
tput,bcและ ANSI escape แบบ 256 สีร่วมกัน เพื่อคำนวณตำแหน่งตัวอักษรและสีตามขนาดเทอร์มินัล และคืนค่าเคอร์เซอร์เมื่อจบด้วยCTRL+C - Akamai อธิบายว่าโค้ดจริงด้านหลังเสื้อเชื่อมโยงกับ Linux และมีการแก้ไขฟอนต์จากที่คาดไว้ตอนแรกว่าเป็น Consolas เป็น Roboto Mono
โค้ดที่รันได้บนด้านหลังเสื้อยืด
- ด้านหน้าเสื้อยืด Uniqlo x Akamai
Peace for Allมีรูปหัวใจอยู่ในวงเล็บปีกกา ส่วนด้านหลังพิมพ์ บล็อกตัวอักษรและตัวเลข ขนาดใหญ่ - เมื่อดูใกล้ ๆ บล็อกดังกล่าวเป็นโค้ด bash ที่เริ่มด้วย shebang และมีโครงสร้างคือถอด Here string ที่เข้ารหัสด้วย Base64 ด้วย
base64 --decodeแล้วส่งต่อให้eval - มองเผิน ๆ อาจดูเหมือน “วิธีแพร่กระจายไวรัส” แต่จุดประสงค์จริงคือการแสดง ข้อความอีสเตอร์เอ็ก
- เสื้อยืดนี้เป็นผลิตภัณฑ์ที่ Akamai ออกแบบเพื่อสนับสนุน แคมเปญ Peace for All ของ Uniqlo
กระบวนการ OCR และการถอดข้อความ
- Base64 ไม่มีฟังก์ชันแก้ไขข้อผิดพลาด ดังนั้นหากต้องการย้ายสตริงที่พิมพ์อยู่ให้เป็นรูปแบบที่รันได้ จำเป็นต้อง ถอดข้อความให้สมบูรณ์แบบ
- สตริงจบด้วย padding ตามที่คาดไว้ และเครื่องหมายคำพูดกับวงเล็บปีกกาก็ตรงกัน จึงมีความเป็นไปได้สูงว่าเป็นโค้ดที่ครบถ้วน
- การถอดข้อความใช้สามวิธีร่วมกัน
- OCR ในตัวของ circle-to-search บน Android
- Tesseract พร้อมตัวเลือกและการปรับแต่งบางอย่าง
- OCR และการสร้างตารางตำแหน่งผ่าน Claude
- นำผลทั้งสามมาเปรียบเทียบด้วย diff เพื่อหาจุดที่ไม่ตรงกัน แล้วจัดการส่วนที่เหลือด้วยมือ
- ดีไซน์เสื้อยืดแบบอื่นในไลน์ Uniqlo x Akamai เดียวกันดูเหมือนถูกตัดออกจากบล็อกโค้ดที่กว้างกว่า โดย imports ถูกตัด และ
returnจบแค่retuทำให้อยู่ในสภาพที่คอมไพล์ไม่ได้
พฤติกรรมของสคริปต์ bash ที่ถอดรหัสแล้ว
- เมื่อถอดรหัส Base64 จะพบ อีสเตอร์เอ็ก bash ที่มีคอมเมนต์ประกอบอย่างดี
- ตอนต้นของสคริปต์มีข้อความแสดงความยินดีเป็นภาษาอังกฤษและญี่ปุ่น
Congratulations! You found the easter egg! ❤️おめでとうございます!隠されたサプライズを見つけました!❤️
- สตริงเป้าหมายของแอนิเมชันจะวนซ้ำในรูปแบบ
♥PEACE♥FOR♥ALL♥PEACE♥FOR♥ALL... - ขนาดเทอร์มินัลถูกดึงด้วย
tput cols,tput linesและคำนวณความยาวสตริงด้วย${#text}ของ bash - เคอร์เซอร์ถูกซ่อนด้วย
tput civisและเมื่อได้รับSIGINTจะคืนเคอร์เซอร์ด้วยtput cnorm; exit
วิธีสร้างแอนิเมชัน
- ลูปทำงานต่อเนื่องในรูปแบบ ลูปไม่สิ้นสุด
for (( t=0; ; t+=1 )) - ในแต่ละรอบ จะดึงตัวอักษรที่จะพิมพ์ปัจจุบันจาก
textทีละตัว - การคำนวณตำแหน่งใช้
bc -lเพื่อหาค่าไซน์angle = t * 0.2sine_value = s(angle)x = (cols / 2) + (cols / 4) * sine_value
- ค่า
xที่คำนวณได้จะถูกปรับให้อยู่ในขอบเขตเทอร์มินัล ไม่ต่ำกว่า 0 และไม่เกินcols - 1 - สีถูกคำนวณเป็น กราเดียนต์ ระหว่างค่า 12 ซึ่งเป็น cyan และค่า 208 ซึ่งเป็น orange ในพาเล็ต 256 สี
- การแสดงผลใช้ ANSI escape และ
tput cupเพื่อพิมพ์ตัวอักษรที่ลงสีแล้วในตำแหน่งที่กำหนด จากนั้นเลื่อนลงบรรทัดถัดไปด้วยการขึ้นบรรทัดใหม่ - ผลลัพธ์คือข้อความแคมเปญ
Peace for Allปรากฏในเทอร์มินัลเป็น ลูปคลื่นไซน์ ต่อเนื่อง
ฟอนต์และคำอธิบายดีไซน์จาก Akamai
- ตอนแรกมีการคาดว่าฟอนต์คือ Consolas แต่หลังจากผู้ใช้ Hacker News ชื่อ ralphinus ตรวจสอบ จึงแก้ไขเป็น Roboto Mono
- หลักฐานที่ทำให้คาดผิด ได้แก่เลข
0ที่มีเส้นเฉียงตื้น ๆ, เลข1ที่ไม่มี serif และเส้นโค้งมนของBDygกับเลข2 - ตอนเปิดตัวเสื้อยืด Akamai อธิบายข้อความของดีไซน์ผ่าน ข่าวประชาสัมพันธ์
- สีน้ำตาลอมเหลืองสดสื่อถึงเคสพลาสติก “beige box” ของคอมพิวเตอร์ยุคอินเทอร์เน็ตแรกเริ่ม ส่วนหัวใจด้านหน้าแสดงถึงการใช้อินเทอร์เน็ตเพื่อจุดประสงค์ที่ดีทั่วโลก
- โค้ดจริงด้านหลังเป็นการอ้างอิงถึง Linux โดย Akamai เชื่อมโยงสิ่งนี้ว่าเป็น “ภาษาโอเพนซอร์สของอินเทอร์เน็ต” และเป็นภาษาร่วมที่มุ่งสู่โลกที่ปลอดภัยและเชื่อมโยงกันมากขึ้น
- อีกตัวอย่างหนึ่งของผู้ที่สนใจเสื้อยืดตัวเดียวกันคือบทความของ Wen Chuan Lee ซึ่งเชื่อมต่อไปยังบทความที่เกี่ยวข้องอีกชิ้น
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
“เสื้ออีกตัวที่ Uniqlo x Akamai ขายในไลน์เดียวกัน มีโค้ดที่เห็นชัดว่าเป็น ดีไซน์ที่ยังไม่เสร็จ”
นึกภาพว่าต้องเอาเสื้อยืดไปคืนเพราะมันทำงานผิดพลาดสิ ตลกดี
“ทำไมถึงคืนสินค้าคะ/ครับ ไซซ์ไม่พอดีหรือไม่ถูกใจเหรอคะ/ครับ?”
“ไม่ครับ/ค่ะ บรรทัดที่ 37 มี syntax error เลยรันไม่ได้ แล้วผม/ฉันก็กังวลว่าคนบนถนนจะคิดว่าผม/ฉันกำลังโปรโมตการเขียนสคริปต์ Bash ที่ไม่ปลอดภัยอยู่”
ถ้าชอบอะไรแบบนี้ คุณอาจชอบงานของ Martin Kleppe ด้วย ตัวอย่างเช่น Quine Clock
https://aem1k.com/qlock/
ผม/ฉันเคย reverse engineer เป็นเวอร์ชันที่ไม่ obfuscate ไว้เมื่อหลายปีก่อนด้วย
https://gist.github.com/olooney/a89db3932b089925b71b68d7e9f2...
เขายังทำ ASCII visualization ดี ๆ ไว้อีกเยอะ
https://aem1k.com/
มันน่าประทับใจมากก็จริง แต่ก็ยัง...
ฟอนต์ไม่ใช่ Consolas แต่เป็น Roboto Mono
ที่แปลกกว่านั้นคือ ตัวฟอนต์เองเป็นฟอนต์ monospace ที่ถูกต้อง แต่การจัดตัวพิมพ์บนเสื้อกลับไม่ใช่ มีการใส่ kerning โดยเฉพาะคู่ ‘Iy’ ที่เห็นชัด และตัวอักษรแคบ ๆ อย่าง ‘i’ ดูเหมือนจะใช้พื้นที่แนวนอนน้อยกว่า เดาว่าน่าจะจัดด้วยเครื่องมืออย่าง “optical kerning” ของ InDesign
เหตุผลที่ใช้ฟอนต์ monospace คือมันเข้ากับกริดได้ดี แต่ภายในกริดนั้นก็ยังมีพื้นที่ให้ขยับตัวอักษรไปซ้ายหรือขวาเล็กน้อยได้ และอาจทำให้ฟอนต์ monospace อ่านง่ายขึ้นได้ แน่นอนว่าทุกครั้งที่ตัวอักษรถูกดันไปทางซ้าย พื้นที่ด้านขวาก็จะใหญ่ขึ้น ซึ่งก็มีโอกาสสูงพอ ๆ กันที่จะออกมาแย่มาก
เสื้อยืดตัวนี้ดีจริง ๆ มีวิดีโอดี ๆ ที่ดีไซเนอร์ตัวจริงอธิบายขั้นตอนการทำ รวมถึงขั้นตอนที่จงใจ ทำให้ OCR ยาก ด้วย: https://youtu.be/jocGLiecpjU?t=526
https://archive.org/details/1983-01-compute-magazine/page/96...
บน Samsung S24U ผม/ฉันกดปุ่ม “circle to search” บนหน้าจอหลักค้างไว้เพื่อเปิดอินเทอร์เฟซเครื่องมือ AI แล้วกดค้างที่ข้อความเพื่อคัดลอกทั้งหมดในครั้งเดียว ไม่รู้ชื่อทางการ แต่ใช้เวลาประมาณ 2 วินาที
การ OCR สิ่งนี้เป็นฝันร้าย และเป็น benchmark ที่ดีสำหรับพวกที่อ้างตัวว่าเป็น โมเดล OCR/vision ชั้นดี
แต่ถ้าเอาภาพให้เครื่องมือแบบ agentic ที่ใช้ vision model ดี ๆ อย่าง Claude หรือ GPT รุ่นใหม่ แล้วสั่งให้แบ่งเป็นบรรทัด จากนั้น OCR ทีละบรรทัด ก็น่าจะทำได้ค่อนข้างง่าย
ผม/ฉันยังสงสัยด้วยว่าสคริปต์นี้เองก่อนถูก obfuscate เขียนด้วย LLM หรือเปล่า ดูเหมือนมีคอมเมนต์เยอะพอสมควร แต่ในกรณีนี้ก็ยังโอเค :)
มีข้อความแสดงความยินดีทั้งภาษาอังกฤษและญี่ปุ่น และมีอีโมจิหัวใจหนึ่งอัน มีความตั้งใจทำกราเดียนต์จากสีฟ้าอมเขียวไปส้ม แต่ช่วงสีเริ่มจากบล็อก ANSI แล้วไปจบกลางบล็อก 256 สี ทั้งที่สีเทอร์มินัล 256 สีไม่ได้จัดเรียงแบบกราเดียนต์ตั้งแต่แรก อีกอย่างที่ท้ายลูปไม่มี sleep ซึ่งถ้าเป็น LLM น่าจะใส่เพิ่มแบบระวังไว้ก่อน
ตัวอักษรแยกจากกันได้ง่าย และไม่มีพื้นหลังเชิงโครงสร้างที่ทำให้ตัวอ่านสับสน ต่างจากกรณีอย่างวันหมดอายุบนฝาอะลูมิเนียมของโยเกิร์ต ไม่มี texture คล้ายข้อความที่จะทำให้ผลลัพธ์เละ และฟอนต์ก็สะอาด เป็นมาตรฐาน
ตอนนี้จำชื่อไม่ได้แล้ว ช่วงนั้นมีเรื่องให้เสียสมาธิเยอะเกินไป
แปลกที่ถ้าพยายามคัดลอกทั้งสตริงรวมถึงส่วน hashbang ประสิทธิภาพจะแย่ลงมาก สงสัยว่าข้างในทำอะไรถึงได้ผลดีขนาดนี้
เรียกการเข้ารหัส base64 ว่า obfuscation เนี่ยนะ
นึกว่าจะได้เห็นเทคนิค deobfuscation ของ JS เท่ ๆ แต่พูดอย่างสุภาพคือผิดหวังจนบรรยายไม่ถูก และทำวันทั้งวันพังเลย
ว้าว ผม/ฉันเห็นเสื้อตัวนั้นในร้านแล้วเคยบอกแฟนว่า “สคริปต์นั่นไม่มีทางทำงานได้จริงหรอก คงเป็นแค่ของตกแต่ง” น่าจะขุดต่ออีกหน่อย
ใช้ได้เลย สิ่งเดียวที่เสียดายเกี่ยวกับสคริปต์นี้คือ น่าจะใส่
sleep 0.1
ไว้ในลูปด้วย แบบนั้นเวลามันแสดงผลในเทอร์มินัลจะได้อ่านทันจริง ๆ เทอร์มินัลยุคนี้เลื่อนเร็วเกินไปจนมองไม่ทันว่าข้อความไหลผ่านไปอย่างไร
ถ้าหน่วงให้เหลือประมาณ รีเฟรช 10Hz ก็น่าจะดูดีมาก
ขำตรงที่ผู้เขียนลองใช้เครื่องมือ OCR หลายตัวแล้วผลลัพธ์ออกมาไม่คงเส้นคงวา จากนั้นก็ใช้เวลานานแก้ผลลัพธ์ที่ดีที่สุดด้วยมือ แทนที่จะพิมพ์เองไปเลย
Google OCR ที่ใช้ผ่าน Google Lens บนมือถือก็ดึงข้อความออกมาได้ค่อนข้างดี แต่ไม่ได้แม่น 100% ถึงอย่างนั้น เวลาที่ใช้แก้น่าจะน้อยกว่าการคัดลอกด้วยมือมาก ไม่รู้ว่าผู้เขียนใช้อะไร แต่ถ้าเขาแชร์ว่าการลอง OCR เป็นอย่างไรก็คงดี และเดาว่าน่าจะลองใช้เครื่องมือ OCR แบบง่าย ๆ
ถ้าเป็นผมก็คงเลือก OCR แน่นอน ต้องเตรียมพร้อมสำหรับ เสื้อยืด Bash ตัวถัดไปที่จะเจอ
อดีตเพื่อนร่วมงานเคยมีเสื้อที่มี โปรแกรม Go อยู่บนเสื้อ ซึ่งผมคิดมาตลอดว่ามันเท่มาก
[0] https://github.com/GL-Kageyama/UNIQLO_Akamai_T-shirt_Code