1 คะแนน โดย GN⁺ 2023-10-16 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • สเปรดชีตกรรมสิทธิ์อายุ 30 ปี Lotus 1-2-3 ถูกรันแบบเนทีฟบน Linux ซึ่งยังไม่มีอยู่ในช่วงที่ซอฟต์แวร์เปิดตัว ผ่านข้อมูลที่กู้คืนมาโดยบังเอิญและการแฮ็กไบนารี
  • จากแบ็กอัป BBS ในยุค 1990 พบ LPL add-in SDK ที่คิดว่าสูญหายไปแล้ว รวมถึง Lotus 1-2-3 for UNIX ทำให้สามารถ build ปลั๊กอินตัวอย่างและวิเคราะห์ไบนารี UNIX ได้
  • ดิสทริบิวชัน UNIX มี Intel 80386 COFF object ที่ถูกบีบอัดชื่อ 123.o อยู่ภายใน และสัญลักษณ์ราว 20,000 รายการพร้อมข้อมูลดีบักช่วยเปิดเผยโครงสร้างภายใน
  • การพอร์ตไม่ได้จบแค่การแปลง COFF เป็น ELF แต่ต้องเลี่ยง ความแตกต่างของ system call และ ABI ของ struct ระหว่าง Linux กับ UNIX ด้วย coffsyrup และฟังก์ชัน wrapper
  • ปัจจุบันพอร์ตนี้รันได้โดยไม่ crash และ ใช้งานได้ 100% แต่ยังมีปัญหาบางส่วนและการพอร์ต terminal driver ที่เหลืออยู่ ทำให้เวอร์ชันที่รันผ่าน DOS emulation ยังดูดีกว่า

จุดเริ่มต้นของการย้ายแอปกรรมสิทธิ์อายุ 30 ปีมายัง Linux

  • Lotus 1-2-3 ยังคงเป็น abandonware อายุ 30 ปี แต่การค้นพบและการแฮ็กหลายอย่างทำให้เกิด พอร์ตแบบ Linux native ได้
  • เป้าหมายการพอร์ตคือ Lotus 1-2-3 R3 แบบคลาสสิก ส่วน Lotus 1-2-3 เองยังมีการออก release จนถึงปี 2002
  • โปรเจกต์นี้เริ่มจากงานดูแล driver lotusdrv เพื่อให้ Lotus 1-2-3 ทำงานได้ดีบนระบบสมัยใหม่
    • driver API ถูกทำความเข้าใจด้วย reverse engineering และ driver เดิมทำงานได้ดีบนระบบสมัยใหม่

LPL SDK ที่สูญหายและแบ็กอัป BBS

  • Lotus 1-2-3 ถูกออกแบบให้ขยายความสามารถได้ด้วยปลั๊กอิน หรือ add-in แต่ add-in ต้องเขียนด้วยภาษาพิเศษชื่อ LPL
  • คอมไพเลอร์ LPL และ SDK สูญหายไปแล้ว และ Lotus ไม่ได้แจก SDK ฟรี แต่ขายในราคา 395 ดอลลาร์
  • SDK ในรูปแบบ warez ถูกกู้คืนผ่านอดีต sysop ที่เก็บแบ็กอัปเทปของระบบ BBS ในยุค 1990 ไว้
    • ADK ที่กู้คืนมาได้ดาวน์โหลดได้ที่ 123DADK.ZIP
    • โปรแกรม LPL ตัวอย่างเผยแพร่เป็น modern.pl
  • ในแบ็กอัป BBS ชุดเดียวกันยังมีสำเนา warez ของ Lotus 1-2-3 for UNIX ซึ่งเชื่อกันโดยทั่วไปว่าสูญหายไปแล้วด้วย

การวิเคราะห์ดิสก์อิมเมจ Lotus 1-2-3 for UNIX

  • เวอร์ชัน UNIX เหลืออยู่เป็นชุด ไฟล์ TD0 ตั้งแต่ 123UNIX1.TD0 ถึง 123UNIX5.TD0
  • พบว่า TD0 เป็นฟอร์แมตดิสก์อิมเมจบีบอัดแบบเก่าจากยุค 1980 และใช้ samdisk แปลงเป็น raw disk image
  • ในอิมเมจที่แปลงแล้ว ไฟล์แรกเป็น tar archive ส่วนที่เหลือเป็น ASCII cpio archive แบบ pre-SVR4 หรือ odc
  • ดิสก์อิมเมจทั้งหมดถูกอัปโหลดไว้บน Internet Archive
  • เมื่อแตกไฟล์ออกมา ใต้ lotus/123.v10 พบ executable, help, keymap, ข้อมูล driver สำหรับ printer และ video, ไฟล์ตัวอย่าง, manual และอื่น ๆ
    • โครงสร้างทั้งหมดมีระดับ 35 ไดเรกทอรีและ 338 ไฟล์

123.o ที่กลายเป็นเบาะแสสำคัญ

  • พบไฟล์ขนาดใหญ่ที่ถูกแบ่งเป็น 123.o.z_1 และ 123.o.z_2 ในไดเรกทอรี sysV386/lib
  • เมื่อนำสองส่วนมาต่อกันและแตกไฟล์ พบว่า 123.o เป็น Intel 80386 COFF object file
    • object file ที่ไม่ได้ถูก stripped
    • มี 5 section
    • symbol offset 0x1efbdc
    • มีสัญลักษณ์ 19,755 รายการ

      • optional header size 28
      • ไฟล์นี้มี private symbol และข้อมูลดีบัก จึงช่วยอย่างมากในการทำความเข้าใจโครงสร้างภายในของ Lotus 1-2-3
      • คำตอบที่ได้รับจาก alt.folklore.computers ชี้ไปในทางว่าไฟล์นี้ไม่ได้ถูกใส่มาโดยผิดพลาด
      • เนื่องจาก dlopen() ยังไม่ได้ถูกใช้อย่างแพร่หลายใน UNIX ช่วงต้นยุค 1990 จึงมีความเป็นไปได้สูงว่าเป็น partially linked object สำหรับลิงก์ซ้ำกับส่วนขยาย

โครงสร้างภายในที่ข้อมูลดีบักเปิดเผย

  • เวอร์ชัน UNIX ใกล้เคียงกับ การพอร์ตซอร์สโดยตรง จากเวอร์ชัน DOS จึงทำงานด้วยวิธีเดียวกันเป็นส่วนใหญ่
  • ข้อมูลดีบักที่รวมมาช่วยไขข้อสงสัยหลายอย่างเกี่ยวกับภายในของ Lotus 1-2-3
  • เพื่อปรับปรุงการแสดงผลกราฟ ผู้พัฒนาต้องการ hook เข้ากับ rasterizer ใน driver แต่การทำความเข้าใจพฤติกรรมจากเอกสารเพียงอย่างเดียวทำได้ยาก
  • ข้อมูลดีบักทำให้ทราบว่า rasterizer สร้าง โปรแกรม bytecode ขนาดเล็กแบบไดนามิก และ graphics engine จะตีความโปรแกรมนั้น
  • หลังจากรู้ opcode แล้ว ก็สามารถ disassemble และแก้ไขมันใน raster.c เพื่อใช้ปรับปรุง output ได้

การแปลงจาก COFF เป็น ELF

  • objcopy สามารถแปลง COFF object file เป็นฟอร์แมต ELF ที่ใช้บน Linux ได้
  • ใช้คำสั่ง objcopy -I coff-i386 -O elf32-i386 123.o 123elf.o เพื่อแปลง 123.o เป็น 123elf.o
  • ผลลัพธ์ที่แปลงแล้วระบุเป็น ELF 32-bit LSB relocatable, Intel 803386, version 1 (SYSV), not stripped
  • ตาม objdump เวลาคอมไพล์ของ object file ต้นฉบับคือ 8 กันยายน 1990 เวลา 06:23:50 ซึ่งเป็นช่วงก่อน Linux เวอร์ชันแรกจะออกมา

การจัดการความแตกต่างของ ABI ระหว่าง Linux กับ UNIX

  • ปัญหาแรกคือ Linux กับ UNIX ไม่ได้ใช้ อินเทอร์เฟซ system call ที่เข้ากันได้
  • เวอร์ชัน UNIX ใช้อินเทอร์เฟซ lcall7 เช่น open() เรียก callgate ในรูปแบบ call 0x7:0x0
  • บน Linux ไม่มีการรองรับ callgate นี้ ดังนั้นหากรันตรง ๆ จะเกิด crash
    • Linux เคยมีการรองรับความเข้ากันได้ของ lcall7 และ lcall27 อยู่ช่วงหนึ่ง แต่ปัจจุบันไม่มีแล้ว
  • เป้าหมายคือการลบการเรียกเหล่านี้ออก และ route ทุกการเรียกให้ผ่าน glibc

Relocation และ coffsyrup

  • พยายามลบ symbol ด้วย objcopy --strip-symbol open แบบง่าย ๆ แต่ objcopy ปฏิเสธ เพราะ open ถูกใช้ใน relocation
  • relocatable object file มีการอ้างอิง symbol และข้อมูล relocation เพื่อให้ทำงานได้ไม่ว่าจะถูกโหลดที่ address ใด
  • หากต้องการลบ symbol ต้องแก้ไข ข้อมูล relocation ที่อ้างถึง symbol นั้นไปพร้อมกัน
  • เพื่อการนี้จึงเขียนเครื่องมือขนาดเล็ก coffsyrup ขึ้นมา
    • สามารถลบ COFF symbol ที่ objcopy ปฏิเสธได้
    • ในตัวอย่างจะค้นหาและจัดการ symbol open กับ relocation ที่เกี่ยวข้อง

ฟังก์ชัน UNIX ที่ไม่เข้ากันในระดับไบนารี

  • ฟังก์ชัน UNIX มาตรฐานจำนวนมาก เข้ากันได้ในระดับซอร์ส แต่ ไม่เข้ากันในระดับไบนารี
  • ตัวอย่างสำคัญคือ struct stat
    • โค้ด C เดียวกันสามารถคอมไพล์ได้บน UNIX หลายตระกูล
    • แต่ object file ที่คอมไพล์แล้วอาจพังบนระบบอื่น เพราะขนาดของ struct stat หรือ offset ของ st_size ต่างกัน
  • ฟังก์ชันหลักที่ต้องจัดการ ได้แก่ stat(), times(), uname(), fcntl(), ioctl() เป็นต้น
  • แนวทางคือเปลี่ยนชื่อ symbol ด้วย objcopy ทำให้เป็น undefined ด้วย coffsyrup แล้วเขียน ฟังก์ชัน wrapper ที่แปลง struct ของ Linux เป็น struct ของ UNIX
  • termios เป็นจุดที่ยากเป็นพิเศษ เพราะพฤติกรรมรายละเอียดต่างกันอย่างละเอียดระหว่างระบบ UNIX และไม่เข้ากัน ทำให้ดีบักยาก

การตรวจสอบไลเซนส์และผลการรัน

  • หลังจากแฮ็กหลายอย่าง โปรแกรมรันได้โดยไม่ crash แต่จะไม่ทำงานหากไม่มี ไลเซนส์
  • เมื่อตั้ง breakpoint ที่ exit() เพื่อตรวจสอบ พบว่า symbol ภายในที่ตรวจสอบไลเซนส์ที่ถูกต้องคือ lic_init()
  • เมื่อตรวจโค้ดด้วย IDA พบว่าการตรวจไลเซนส์ทำโดยค้นหาไฟล์ LICENSE.000 และตรวจว่าวันหมดอายุ ชื่อผู้ใช้ และชื่อระบบภายในไฟล์ตรงกับค่าที่ระบบรายงานหรือไม่
  • ท้ายที่สุด Lotus 1-2-3 ก็ถูกพอร์ตมายังระบบปฏิบัติการใหม่อย่าง Linux
  • สถานะปัจจุบันมีดังนี้
    • รันได้โดยไม่ crash
    • ใช้งานได้ 100%

      • ยังต้องเก็บรายละเอียดปัญหาบางอย่างเพิ่มเติม
      • ยังเหลืองานพอร์ต terminal driver
      • ตอนนี้เวอร์ชัน DOS ที่รันผ่าน emulation ยังดูดีกว่า แต่สามารถแก้ไขได้
      • โค้ดพอร์ตเผยแพร่บน github

1 ความคิดเห็น

 
GN⁺ 2023-10-16
ความคิดเห็นบน Hacker News
  • แทบไม่น่าเชื่อว่าเขากู้สำเนา warez ของ SDK จาก เทปแบ็กอัป ของระบบ BBS เก่า ๆ แล้วถึงขั้นบิลด์ปลั๊กอินตัวอย่างได้
    ทำให้นึกถึง Why History Needs Software Piracy

    • เคยอยากรวบรวมคู่มือ อัปเดตเฟิร์มแวร์ ไดรเวอร์ และยูทิลิตีของซินธิไซเซอร์กับอุปกรณ์ Eurorack ที่ยังขายอยู่ตอนนี้ เพื่อสร้าง อาร์ไคฟ์สำหรับการเก็บรักษาระยะยาว
      อุปกรณ์พวกนี้เป็นฮาร์ดแวร์ที่จะยังคงอยู่ได้อีกหลายสิบปี ดังนั้นถ้ามีคลังเก็บแบบรวมศูนย์และคงทนที่ทั้งคอมมูนิตี้ใช้ได้ แทนที่จะเป็นแค่การเก็บไว้ในคลาวด์ส่วนตัวแยกตามผู้ผลิต ก็จะมีคุณค่าต่อสาธารณะมาก
      เคยคิดถึงวิธีแจกจ่ายอาร์ไคฟ์ทุกปี หรือขาย archival Blu-ray ในรูปแบบสื่อกายภาพที่น่าจะอยู่ได้ราว 100 ปีด้วย
      แบบนั้นโอกาสที่ใครสักคนจะมีไฟล์ .bin หายากไฟล์สุดท้ายที่จำเป็นต่อการกู้เฟิร์มแวร์ที่เสียของโมดูล DSP Eurorack บน ARM ก็น่าจะเข้าใกล้ 1
  • พ่อของผมเก็บ ไฟล์ Lotus 1-2-3 สำคัญไว้เต็มไปหมดในโน้ตบุ๊กสำรองที่ลง Windows รุ่นเก่า และบางครั้งยังต้องเปิดดู ทำให้สภาพนั้นค่อนข้างน่ากังวล
    เมื่อไม่นานมานี้ผมรู้ว่า LibreOffice สามารถแปลงไฟล์ Lotus เป็น ODF/ODS ได้ แม้กระทั่งจากบรรทัดคำสั่ง เลยใช้สคริปต์ bash ง่าย ๆ แปลงทั้งหมด
    ผมอัปโหลดไฟล์ที่แปลงแล้วขึ้น Nextcloud ที่มี Collabora แบบยกชุด แล้วให้บัญชีกับวิธีใช้งานสั้น ๆ แก่พ่อ ตอนนี้เขาดูไฟล์ออนไลน์ได้ และไฟล์ก็ถูกรวมอยู่ในรูทีนแบ็กอัปปกติของเซิร์ฟเวอร์ด้วย
    ขอบคุณนักโบราณคดีข้อมูลแบบ OP และ ผู้ร่วมพัฒนา LibreOffice อย่างมาก

    • เก่าขนาดนั้นแล้วยังเป็นไฟล์สำคัญอยู่ น่าสงสัยทีเดียวว่าเป็นไฟล์ประเภทไหน
  • นึกถึงตอนอายุ 7 ขวบที่ขอให้พ่อซื้อ Nintendo ให้ แต่พ่อกลับซื้อ 286 PC พร้อม Lotus 1-2-3 และเกมผจญภัยข้อความ Indiana Jones มาให้แทน

    • เคยเห็น LISP Machine ที่ Computer Museum ใน Boston แล้วดูเท่มาก เลยบอกว่าอยากได้เครื่องนั้น แต่สุดท้ายได้ Commodore 64 แทน
      ดังนั้นภาษาแรกของผมจึงไม่ใช่ LISP แต่เป็น BASIC และผมตั้งใจจะโทษโศกนาฏกรรมนี้สำหรับความล้มเหลวทั้งหมดในชีวิต โดยไม่รับฟังข้อโต้แย้งใด ๆ
    • เคยตื๊อให้ซื้อ NES แต่พ่อซื้อ C64 ให้พร้อมบอกว่า “อันนี้ดีกว่า เดี๋ยวโตไปก็รู้เอง” และสุดท้ายมันก็เปลี่ยนชีวิตผมจริง ๆ
    • อยากได้ BBC Micro แต่ได้ ZX Spectrum แทน ซึ่งผลลัพธ์แล้วดีกว่ามากในการทำให้ผมสนใจการเขียนโปรแกรม
      ถึงตอนนั้น Spectrum จะอยู่ในช่วงขาลงแล้วก็ตาม
    • ได้ Apple IIgs กับ Leisure Suit Larry มา แต่ทั้งผมและพี่ชายที่โตกว่าก็ตอบ คำถามยืนยันอายุ ตอนเริ่มเกมได้ไม่แม่นพออย่างสม่ำเสมอ
  • กำลังทำงานคล้าย ๆ กันอยู่ แต่เพิ่มขั้นตอนแปลงบางส่วนของโปรแกรมเป็น ออบเจ็กต์ไฟล์ ก่อน
    ดังนั้นจึงไม่ถูกผูกกับออบเจ็กต์ไฟล์ที่ยังหลงเหลืออยู่เท่านั้น แต่ต้องทำ reverse engineering พอสมควรเพื่อให้มันทำงานได้
    ผมทำ proof of concept ด้วย aln ซึ่งเป็นลิงเกอร์ของ Atari Jaguar โดยแยกไฟล์ปฏิบัติการ Linux x86 a.out ออกเป็นออบเจ็กต์ไฟล์ ELF แล้วลิงก์กลับเป็นไฟล์ปฏิบัติการ ELF x86 โดยเลี่ยงความต่างของ ABI
    https://forums.atariage.com/topic/354341-porting-the-original-atari-jaguar-sdk-to-elf/

  • น่าสนใจในหลายแง่
    กระบวนการย้ายจาก ไบนารี UNIX ไปเป็นไบนารี Linux โดยไม่มีซอร์สโค้ด ไม่มีคอมไพเลอร์และภาษาต้นฉบับ ดูเจ๋งมาก และนี่แหละคือคอนเทนต์แบบ HN
    ยังทำให้นึกได้ด้วยว่าในปี 2023 Usenet ยังถูกใช้เพื่ออย่างอื่นนอกจากการแจกไฟล์ rar
    และทำให้นึกถึงวัฒนธรรม BBS ก่อนอินเทอร์เน็ตจะแพร่หลาย ที่ช่วยกระจายความหลงใหลและซอฟต์แวร์แชร์แวร์ดี ๆ ไปสู่คนทั่วไป

    • ดูเหมือนว่า จิตวิญญาณแบบ BBS กำลังกลับมา
      ยิ่งเห็นแนวโน้มที่ Twitter และโซเชียลมีเดียอื่น ๆ กำลังพังทลายก็ยิ่งรู้สึกแบบนั้น ส่วน Gen Z ที่อายุมากหน่อยก็หลงใหลความย้อนยุคยุค 80–90 ถึงขั้นดาวน์โหลดเสียงเจรจาของโมเด็มไปตั้งเป็นริงโทน
      หลานวัย 23 ของผมก็เพิ่งทำแบบนั้น และผลงานอย่าง American Horror Story หรือ Stranger Things ก็ยิ่งหล่อเลี้ยงความถวิลหาอดีตนี้
      เท่ากับว่าคำกล่าวเก่า ๆ ที่ว่าถ้ารอนานพอ ทุกอย่างจะกลับมาฮิตอีกครั้งนั้นถูกต้อง
  • เมื่อเดือนพฤษภาคม 2022 ปีที่แล้วก็มีการถกเถียงดี ๆ เช่นกัน
    https://news.ycombinator.com/item?id=31455968

  • https://scenelist.org/ ที่ลิงก์ไว้ในบทความยอดเยี่ยมนี้ เป็น ระเบิดความคิดถึง ที่ผมต้องการพอดีเมื่อเช้านี้
    เหมาะมากสำหรับการเดินทางย้อนความทรงจำ และขอแนะนำอย่างยิ่ง

  • ลิงก์ที่เกี่ยวข้อง
    Lotus 1-2-3 for Linux - https://news.ycombinator.com/item?id=35807639 - พฤษภาคม 2023, 73 ความคิดเห็น
    Lotus 1-2-3 - https://news.ycombinator.com/item?id=35872758 - พฤษภาคม 2023, 117 ความคิดเห็น
    Lotus 1-2-3 For Linux - https://news.ycombinator.com/item?id=31455968 - พฤษภาคม 2022, 83 ความคิดเห็น
    Lotus 1-2-3 arbitrary resolution - https://news.ycombinator.com/item?id=26316637 - มีนาคม 2021, 123 ความคิดเห็น

  • ผมลืม 1-2-3 ไปสนิท ทั้งที่เคยลองใช้อยู่พักหนึ่งเมื่อปีที่แล้ว
    ไบนารี 123 ยังรันได้ดีบน Linux และการที่ซอฟต์แวร์ซึ่งหายไปนานแล้วทำงานได้เฉย ๆ นั้นถือเป็นความสำเร็จที่น่าทึ่งทีเดียว

  • ผมอยากได้อะไรแบบนี้มาตลอด เพราะแทบไม่ได้ใช้ฟอนต์หรือรูปภาพในสเปรดชีตอยู่แล้ว สเปรดชีตบนเทอร์มินัล จึงเหมาะพอดี
    ดีใจที่ในที่สุดก็ออกมาแล้ว และหวังว่าจะพอร์ตไปยัง Mac กับ Windows ด้วย

    • ในเว็บไซต์เดียวกันยังมีรีวิว โปรแกรมสเปรดชีตบนเทอร์มินัลสำหรับ Linux หลายตัวด้วย
      https://lock.cmpxchg8b.com/spreadsheet.html
    • Emacs มีโหมดสเปรดชีตชื่อ SimpleEmacsSpreadsheet มานานแล้ว
      ผมไม่เคยลองใช้ แต่ก็น่าตรวจสอบดู
      อีกอย่าง Org-Mode ของ Emacs ก็เป็นเครื่องมือที่ขุดค้นได้ไม่รู้จบ และยังมีวิธีใช้เซลล์ตารางของ org-mode เหมือนสเปรดชีตร่วมกับฟีเจอร์ Calc ของ Emacs ด้วย
      https://www.youtube.com/watch?v=15aDIvb5LJs
      ดูทรงพลังทีเดียว และตามสไตล์ Emacs คือรันได้ทั้งในเทอร์มินัลและสภาพแวดล้อมแบบหน้าต่าง
    • มี sc อยู่แล้ว: https://www.unix.com/man-page/linux/1/sc/