top of page
חיפוש

לפצח הגנה של Apple IIe בלי לדעת כלום על Apple IIe

Inbar Raz
לפני 2 דקות
זמן קריאה 15 דקות

אני לא גדלתי על אפל. היו לי חברים שהיה להם אפל, אבל לי היה משהו אחר (Dragon 64, שקיבלתי מסבא שלי בחיפה, ואח״כ Amstrad PC1512). אז כשמישהו ביקש עזרה בפיצוח הגנה של תוכנה בת 40 שנה ל-Apple IIe, הדבר ההגיוני היה להגיד ״לא מבין בזה כלום״.


כמובן שלא עשיתי את הדבר ההגיוני.


הסיפור הזה הפך בסוף להרצאה ב-KansasFest 2026, הכנס השנתי של קהילת ה-Apple II, ואח״כ גם ב-Hack.lu - אחד מכנסי ההאקרים האהובים עלי. אז מי שמעדיף לצפות בהרצאה באנגלית מאשר לקרוא - הנה היא:


ולמי שמעדיף לקרוא (ובעברית) - בואו נתחיל.


איך זה התחיל


אני אחד המנהלים של קבוצת פייסבוק ישראלית שעוסקת ברטרו-מחשוב. יום אחד קפצה לי הודעה שממתינה לאישור:


מצאתי את עצמי במצב קצת מביך. מצד אחד - פייסבוק לא ממש אוהבים פוסטים כאלו, ואישור שלו היה עלול להכניס אותי (ואת הקבוצה) לצרות. מצד שני - יש לי הרבה שנים של פיצוח תוכנות על PC, שם בעצם למדתי Reverse Engineering, ותמיד רציתי לנסות את זה על אפל.


אז לא אישרתי את הפוסט, אבל שלחתי לאודי הודעה פרטית:


הדבר הראשון ששאלתי אותו היה האם ניסה לאתר את הכותבים של התוכנה. מסתבר שכן, ושהם נתנו את ברכתם. התוכנה היא תוכנה בעברית משנת 1984, והיא מוגנת ע״י Dongle - תקע חומרה שמתחבר למחשב, ובלעדיו התוכנה לא עובדת.


נקודת הפתיחה: אפס


כל מה שקיבלתי מאודי היה שני קבצי Image של שני הדיסקטים. וזהו:


חשוב להבין מאיפה אני מתחיל: על ה-Apple II אני לא יודע כלום. כן, יש לי פה Apple IIc ו-Apple IIe שעובדים (או לפחות עבדו פעם), כי אני אספן. אבל כילד לא הייתי אפילו משתמש רגיל. לא הכרתי את הארכיטקטורה, לא את מערכת הקבצים, לא את תהליך העלייה, ולא את מערכת ההפעלה (אם אפשר לקרוא לזה ככה).


אז קיבלתי החלטה: אני הולך לעשות את זה בדיוק כמו שעשיתי את זה בשנות ה-90. כמה שפחות עזרה מכלים מודרניים, כמה שפחות רמזים, ונראה מה יוצא.


ניתוח תוכן הדיסקטים


השלב הראשון היה לחלץ את הקבצים מתוך ה-Image של הדיסקטים, כי, כאמור, גם את מערכת הקבצים של ה-Apple II לא הכרתי. את החילוץ עשיתי בעזרת +AppleSauce, שהוא מכשיר ארכוב דיסקטים כמו ה-Greaseweazle וה-Kryoflux שכבר דיברתי עליהם פה בבלוג, אבל עם התמחות בכונני Apple המוקדמים:


בדיקה מהירה הראתה שה-Images נוצרו באופן תקין, אז פשוט שפכתי את התוכן של מערכת הקבצים החוצה וקיבלתי רשימה של קבצים:


לא היה לי מושג על מה אני מסתכל, אבל בסדר, צריך להתחיל ממשהו. לדבר אחד כן שמתי לב רק בדיעבד: התאריכים. לא כל יום יש לי על הדיסק קבצים עם חותמת זמן של 1983 :-)


מערכת הפעלה? איזו מערכת הפעלה?


כשמדברים על מחשבים כמו ה-Commodore או ה-Apple, בד״כ לא מדברים על ״מערכת הפעלה״ - אם לא עושים כלום, המחשב עולה ל-BASIC. זה, בגדול, היה סך הידע שלי. חיפוש קצר הבהיר שבעצם היו כמה וכמה מערכות הפעלה ל-Apple II:


שמו של אחד הקבצים בדיסקט רמז באיזו מהן מדובר: ProDOS.


ניסיתי למצוא את מחרוזת הגרסה בקובץ הבינארי ו... כלום. לא מצאתי. לא הבנתי למה. אז הרשיתי לעצמי לרמות לשנייה, ושאלתי את Claude:



התשובה, מסתבר, היא ProDOS 1.0.1 מה-1 בינואר 1984, והמחרוזות, שלא מצאתי, מקודדות ב-High ASCII (הביט העליון דלוק). לא שמעתי על קידוד כזה ללא סיבה מיוחדת מעולם, אבל ברגע שיודעים מה לחפש - רואים את זה בבינארי בבירור:

לא פלא שלא מצאתי את המחרוזת.


חיפוש אמולטור


כשפורצים הגנת תוכנה ו/או חומרה יש בגדול שתי גישות, כאשר לרוב משלבים את שתיהן.

  1. הנדסה חוזרת דינאמית - Dynamic reverse engineering - מריצים את הקוד ב-Debugger ועוקבים אחריו.

  2. הנדסה חוזרת סטטית - Static reverse engineering - קוראים את הקוד ב-Disassembler.


לצד הדינאמי בחרתי באמולטור. ה-Apple IIe שלי, שהיה תקין עד לא מזמן, הפסיק לעבוד ולא התחשק לי לבזבז זמן על החייאה שלו. אמולטור עם Debugger מובנה זה גם ככה נוח יותר.


האמולטור הראשון שמצאתי נראה היה מבטיח:


טענתי אליו את ה-Image של דיסקטים ו... כלום. שום דבר לא זז. לא היה לי מושג למה, וגם לא היה לי כח לברר. ואז מצאתי את Apple2TS - אמולטור ל-Apple II שכתוב ב-TypeScript, רץ בדפדפן (או כאפליקציה מקומית), ויש לו Debugger מובנה. כל מי שבקהילת ה-Apple II מכיר אותו. אני כמובן לא הכרתי.


והפעם - זה עבד. בוידאו אפשר לראות את האמולטור מבצע Boot מה-Image של הדיסקט:


זה גם המקום שבו התוכנה נתקעת, כי היא לא מוצאת את ה-Dongle. עוד נחזור לזה.


בשלב הזה הרגשתי שני דברים במקביל: שמחה, כי סוף סוף משהו עובד, וקצת טמבל. כי אם הייתי פשוט מריץ את התוכנה באמולטור מלכתחילה, הייתי רואה את מספר הגרסה של ProDOS על המסך, בלי לשאול אף אחד 🤦‍♂️


אבל ככה זה כשתוקפים משהו שאין לך עליו שום ידע מוקדם - תוקפים מכמה כיוונים במקביל, ולא תמיד בוחרים את המהיר ביותר.


הצד הסטטי: Ghidra


פה לא היתה הרבה התלבטות. אני עובד כבר הרבה זמן עם Ghidra, ה-Disassembler החינמי והפתוח שה-NSA שחררו לציבור (כן, באמת). השתמשתי בו גם בפרויקט הטלפון הציבורי.


עכשיו צריך רק למצוא את הקובץ הנכון. בדיסקט יש לא מעט קבצים, ואין לי מושג באיזה מהם ההגנה ממומשת. אודי, ששלח לי את הדיסקטים, כתב לי: ״אני חושב שזה ב-SEG.00. לא יודע אם זה עוזר, אבל הנה השיחה שלי עם Gemini.״


לא פתחתי את השיחה עם ג׳ימיני. החלטתי שאני לא משתמש ברמזים אלא אם אין ברירה. (נחזור גם לזה.)


לאיזו כתובת נטען הקובץ?


הניסיון הראשון היה לטעון את הבינארי ל-Ghidra ולנסות להבין מהקוד באיזו כתובת בזיכרון הוא אמור לשבת.


כשטוענים קובץ ל-Ghidra הוא נטען כברירת מחדל לכתובת 0. אבל בקוד יש פקודות עם כתובות מוחלטות (Immediate Addresses) - מספרים שמצביעים למקום ספציפי בזיכרון, בלי קשר למקום שבו הקוד רץ. אם מסתכלים עליהן, אפשר לנחש איפה הקוד באמת יושב:


הכתובות נראו כאילו הן בסביבת $1000. אז השתמשתי באפשרות של Ghidra להזיז (Relocate) קובץ שכבר נטען. זה טריק מאוד שימושי: בשלב הזה עוד לא עשיתי שום ניתוח של הקובץ, אבל אם זה קורה לכם בשלב מאוחר, זה חוסך לכם לעשות את כל הניתוח מחדש.


אחרי ההזזה ל-$1000 זה נראה היה... בסדר. אבל לא לגמרי:


חלק מהכתובות הסתדרו, וחלק הצביעו למקומות מאוד מוזרים. לא היה לי מושג מה קורה.


RTFM

בשלב הזה ניסיתי לקחת קיצור דרך, ושאלתי את Claude שאלה מאוד ממוקדת: לא ״תפצח לי את ההגנה״, אלא רק ״איזה קובץ נטען, ולאן״:


קיבלתי תשובה ארוכה. מאוד ארוכה. על פני כמה מסכים. ובכנות? זה היה יותר מדי. ב-1992 אולי הייתי קורא את כל זה, אבל היום - לא.


וכמו שהמספר היה אומר: כל התשובות היו שם. פשוט לא קראתי אותן.


אז חזרתי לאחת האמיתות הישנות, שלא השתנו מאז ועד היום: Read The Fucking Manual. פתחתי את המדריך של ProDOS וחיפשתי רמזים. ומצאתי:


תוכנות מערכת נטענות תמיד לכתובת $2000. מצויין!


אז שמתי Breakpoint בכתובת $2000 באמולטור:


אם זו אכן כתובת הטעינה, אמורה להיות לי עצירה בתחילת כל קובץ שרץ. אז נתתי לאמולטור לרוץ ובדקתי מה קורה.


עצירה ראשונה - ה-Header של ה-Apple IIe:


המשכתי, עצירה שנייה - ProDOS:


המשכתי, ו... האמולטור לא חזר, אלא נתקע בלולאה אינסופית כלשהי:


זה המסך שראיתם קודם באמולטור. אם אנחנו נעצרים עליו - זה סימן שההגנה פעלה בהצלחה.


אז עכשיו אני יודע שהקובץ שנטען שני (ואחרון) לכתובת $2000 הוא זה שנתקע, ולכן הגיוני להניח שההגנה נמצאת בו. זו עדיין הנחה, אבל זו הנחה סבירה, וזה כיוון טוב. נותר רק לחזור על התהליך, לעצור בפעם השנייה ב-$2000, ולבדוק מהם הבתים (Bytes) שנמצאים שם:



את הבתים האלו ($4C, $95, $38, $84, $F4, $48) אני צריך לחפש בכל הקבצים בדיסקט ולמצוא את הקובץ שמתחיל בדיוק בהם:


ונחשו מה?... הקובץ הנכון הוא בכלל לא SEG.00. כלומר, הרמז שלא פתחתי היה שגוי. מוסר השכל: תחליטו על מתודולוגיה ותדבקו בה.


הקובץ שברילוקיישן


עכשיו, סוף סוף, טענתי את הקובץ הנכון. הדבר הראשון שהוא עושה (ז״א, מה שהפקודות שבו אומרות למעבד לעשות) זה לקפוץ לכתובת רחוקה, לקראת סוף הקובץ: $3895:


שם יש קוד שנראה כמו קוד של Relocation:


לא אלאה אתכם בכל הקריאה של הקוד ובניסיון לתת משמעות לכתובות בזיכרון, אבל הדרך הכי טובה לאמת השערה כזו היא פשוט לבדוק ב-Debugger מה נמצא במיקומי הזיכרון שמשמשים למשתנים:


במילה (Word) שבכתובת $9E (מסומנת באדום) יש את הערך $1000 - כתובת היעד, ובמילה שאחריה (מסומנת בכחול) יש $2000 - כתובת המקור. ההעתקה ממשיכה עד שכתובת היעד מגיעה ל-$2A00 (הבלוק המסומן בצהוב). כלומר בלוק בגודל $1A00, שהם 6656 בתים בבסיס 10.


ובמקרה (או שלא) - זה בדיוק הגודל של הקובץ:


אז הדבר הראשון שהתוכנה עושה זה להזיז את עצמה, את כולה, לכתובת $1000. זה מסביר למה הכתובות נראו ״כמעט נכון״ קודם - לפחות מתישהו הקוד אכן רץ מהכתובות האלו!


חזרה ל-Ghidra, עוד Relocation, הפעם מ-$2000 ל-$1000.


מתודולוגיה משנות ה-90: Zoom In


עכשיו מגיעים לחלק שבו אני חוזר לעשות את מה שעשיתי כילד.


התחלתי לעשות Reverse Engineering (או ב-״עברית״: לרברס) בגיל 14, ב-1989. בישראל של אז עוד לא היה אינטרנט, אז תוך שנה בערך פיתחתי לעצמי שיטות עבודה והייתי משוכנע שאני מלך העולם. (לא הייתי. בטוח שאחרים עשו את זה לפני.)


לאחת השיטות לרברוס דינאמי קראתי Zoom In. היא מאוד פשוטה:

  1. מריצים את התוכנה ב-Debugger.

  2. עושים Step Over, ועוד Step Over, ועוד Step Over...

  3. ברגע שאחת הקריאות לא חוזרת, או מחזירה תוצאה רעה (נגיד, הודעה בנוסח ״הדיסקט הזה לא מקורי״) - חוזרים לכתובת האחרונה שעליה דילגנו.

  4. הפעם עושים Step Into, וחוזרים על התהליך מההתחלה, רמה אחת פנימה.


זה בדיוק מה שעשיתי קודם עם ה-Breakpoint על כתובת $2000 :-)


כילד היה לי דף ארוך שעליו רשמתי כל כתובת לפני שדילגתי עליה (הדפים האלו עדיין שמורים אצלי איפשהו). היתרון של אמולטור עם Debugger מובנה הוא שבמקום לרשום על נייר, אפשר פשוט לשים Breakpoint נוסף לפני כל Step Over:


לקח קצת זמן, אבל בסוף הגעתי ל״נקודת האל-חזור״:


הפונקציה בכתובת $2336 קוראת ל-Subroutine (״שגרה״ בעברית) ב-$23AB, וזו לא חוזרת.


זה המיקוד הכי טוב שיש לי על ההגנה עד עכשיו.


מתודולוגיה משנות ה-90: Leave No Stone Unturned


עוד שיטה שפיתחתי בצעירותי, הפעם לצד הסטטי. פעם לא היו כלים כמו IDA (שיצא בסוף שנות ה-90) - עבדתי עם DEBUG, עם Turbo Debugger ועם Sourcer. כש-IDA הגיע, קיבלה השיטה הזו את השם Leave No Stone Unturned.

הרעיון: עוברים על כל הקובץ, ולכל הפחות מסמנים איזורים כקוד או כדאטה. אזורים שלא נותחו מפריעים לכלי לעשות את הניתוח האוטומטי שלו כמו שצריך - מפספסים Jump Tables, מפספסים שגרות. ומצד שני, ברגע שמסמנים משהו כקוד, הניתוח האוטומטי ממשיך משם לכל מקום שהוא קופץ אליו, וזה מאיץ את העבודה. יש חריגים, ולא תמיד צריך לעבור על הכל. אבל כשמדובר בקובץ של פחות מ-7KB? בהחלט.


ועוד כמה הרגלים:

  • לתת שמות לכל דבר. כשהמקום הזה מוזכר במקום אחר בקוד, פתאום רואים שם במקום כתובת.

  • העוגנים הכי טובים בשלב הזה הם מחרוזות. נותנים למיקום שלהן שם משמעותי, אפילו את המחרוזת עצמה.

  • להוסיף Repeatable Comment - הערה שמופיעה בכל מקום בקוד שמפנה לאותה כתובת. אם שמים בה את המחרוזת עצמה, אז בכל פעם שקוד מפנה אליה יודעים מיד שזו מחרוזת, ואיזו. זה עוזר מאוד להבין את ההקשר.


בעיית הקידוד (שוב)


רק שפה - המחרוזות בעברית מקודדות:


את זה די ציפיתי לראות. מחשבי Apple IIe שנמכרו בארץ הגיעו עם מתג בחזית שמחליף את ה-Character ROM לגרסה עם עברית, בד״כ על חשבון האותיות הקטנות באנגלית. אז ידעתי שלא אראה אותיות עבריות בבינארי, ושאצטרך לפענח את הקידוד כדי להבין מה כתוב.


גם בפרויקט הטלפון הציבורי, ב-2019, היו מחרוזות עבריות מקודדות. אבל אז לא היה לי Claude Code. עכשיו יש, ולמשימה המטופשת הזו של פענוח קידוד הרשיתי לעצמי קיצור דרך. כתבתי לו: ״הטקסט הבא הוא במקור בעברית. מצא את שיטת הקידוד.״


והדבר הראשון ש-Claude עשה היה... לסרב. משהו בנוסח ״מצטער, הבקשה נוגדת את מדיניות השימוש״:


אתם צוחקים עלי? זה לא האקינג, זה לא קראקינג, זה קידוד. אולי הוא חשב שאני מנסה לשבור הצפנה. לא משנה.


עברתי ל-ChatGPT, עם אותה בקשה ועם קצת יותר הקשר: שמדובר בבינארי של Apple II מתחילת שנות ה-80, ושזו כנראה החלפה פשוטה:


בהתחלה זה נראה מבטיח - ״כן, סביר מאוד שזה צופן החלפה חד-אלפביתי פשוט״:


ואז הוא התחיל ניתוח ארוך, ניסה, ניסה שוב, ולמרות שהניתוח נראה הגיוני - המסקנות היו שגויות. כשביקשתי את הטבלה הסופית, היא הייתה פשוט לא נכונה:


גם הדוגמאות שהוא נתן היו לא נכונות:



היה לי את הזמן לדבג למה הוא טועה, אבל לא היה לי שום חשק. ובכל זאת, הבטחתי לעצמי לעשות את זה כמו בשנות ה-90. רוצה שמשהו ייעשה כמו שצריך? תעשה אותו בעצמך.


אז ״שברתי את הצופן״. אני יודע שזה נשמע מפוצץ, אבל מדובר ברמה של חידות ההחלפה במוסף ״שבעה ימים״. וכמו שחשדתי, ההחלפה היא רציפה: ה-א׳ ממופה לתו ` (הגרש ההפוך), ומשם כל אות עוקבת פשוט ממשיכה ברצף של ה-ASCII.


ואז Automator נכנס לתמונה


עכשיו, כשיש לי את הקידוד, אני צריך לתרגם את כל המחרוזות ב-Ghidra. ידנית? לא תודה.


אחד הדברים שאני הכי אוהב ב-Mac זה Automator, שמאפשר ליצור סקריפטים שרצים מתוך תפריט ההקשר (Context-sensitive menu). כבר יש לי סקריפט ישן מאוד שימושי: כל ישראלי מכיר את הרגע הזה שבו מקלידים משפט שלם ואז מגלים שהמקלדת הייתה בשפה הלא נכונה. אז כתבתי פעם סקריפט שמתרגם את הג׳יבריש בחזרה לשפה הנכונה:


כל מה שהייתי צריך לעשות זה להחליף את טבלת ההמרה:


והתוצאה:


כן, האותיות בסדר הפוך, כי עברית נכתבת מימין לשמאל. אבל זה עובד, ואפשר לחזור ל-Ghidra ולתת שמות והערות לכל המחרוזות.


תוך כדי העבודה, התברר שמדובר ב-Pascal Strings: בית אחד של אורך, ואחריו הבתים של המחרוזת. ל-Ghidra יש טיפוס מובנה לזה, אז השתמשתי בו. החריגה היחידה הייתה המחרוזת האחרונה, שאורכה $2B, ומשהו שם לא הסתדר - היא דרסה איזו טבלה. החלטתי להתעלם, כי לפחות ידעתי מה כתוב בה ויכולתי לתת לה שם והערה.


ואז מצאתי את הטבלה עצמה - המיפוי של המקלדת העברית:


בבינארי יש פשוט את האותיות באנגלית לפי סדר האותיות העבריות שהן ממופות אליהן. הראשונה היא T - וב-T, כמו שכל מי שמקליד עברית יודע, יושבת ה-א׳:


נחמד לקבל אישור שאני בכיוון הנכון :-)


פרמטרים בתוך הקוד. שוב.


ואז הגיע משהו שממש לא אהבתי, ושכבר פגשתי פעם אחת - באותו פרויקט טלפון ציבורי, לפני שבע שנים. אז, המעבד היה מעבד לא מוכר (לי) של RCA, ושם לראשונה נתקלתי בקוד (כנראה מקומפל) שבו הפרמטרים לפונקציה נכתבים בתוך הקוד, מיד אחרי פקודת הקריאה לשגרה. אז גם פה זה היה ככה:


זה מאוד מעצבן. השגרה צריכה כמובן לטפל במחסנית ולתקן את כתובת החזרה, אבל הבעיה האמיתית היא שרוב ה-Debuggers וה-Disassemblers לא יודעים להתמודד עם זה. אם לא יודעים כמה בתים של מידע יש אחרי הקריאה, ה-Disassembler יפרש מידע כקוד, או ידלג על קוד כי חשב שזה מידע, ויוצא בלגן.


יכולתי לכתוב סקריפטים או לשנות את הגדרת המעבד ב-Ghidra, אבל לא התחשק לי. במקום זה קראתי את הקוד של כל פונקציה כזו, כדי להבין לפחות כמה בתים של פרמטרים היא לוקחת. למשל, אם ידוע שפונקציית ההדפסה מקבלת 4 בתים, אפשר לדלג עליהם ולהכריח את ה-Disassembler להתייחס ל-$2487 כקוד.

ואז מצאתי עוד אחת: פונקציה שמזיזה בלוקים של זיכרון (ובעצם שתיים כאלו, ש-memmove ו-memcpy שלהן לא באמת שונות). זו לוקחת יותר פרמטרים: 2 בתים לכתובת מקור, 2 לכתובת יעד, וגודל הבלוק.


בשלב הזה הבנתי שאני לא יכול להמשיך לעשות את זה ידנית, ויצרתי ב-Ghidra טיפוסי מידע (Data Types) משלי. ככה אפשר להחיל אותם על הבתים שאחרי כל קריאה, וגם להגדיר שמספר מסויים הוא Offset - כך שכשפונקציית ההדפסה מקבלת כתובת של מחרוזת, אני רואה מיד איזו מחרוזת:


  • הטיפוס args_memcpy - מילה ליעד, מילה למקור, מילה לגודל.

  • הטיפוס args_print - מיקום על המסך (X ו-Y) והמחרוזת עצמה.

  • הטיפוס args_mli - לקריאות ל-MLI (שאני חושב שזה Machine Language Interface) של ProDOS. ובסוף לא היה בו צורך בכלל, כי הוא לא שיחק שום תפקיד בפרויקט. אבל ככה זה כשאוספים מידע.


מה זו הכתובת הזו?


וכאן מגיעים לחלק שעליו באמת לא ידעתי כלום.


קצת אסמבלי של 6502 ידעתי, כי נתקלתי בו בעבר. זה היה מספיק בשביל הפרויקט (ועדיין - ה-Datasheet של המעבד היה פתוח כל הזמן על המסך השני). אבל בכל ארכיטקטורה יש כתובות זיכרון עם תפקיד מיוחד: חומרה, I/O, משתני מערכת. ובלי להכיר אותן, אי אפשר באמת לקרוא את הקוד.


מצאתי אתר באינטרנט עם טבלה גדולה של כל הכתובות האלו ב-Apple II:


כאן שוב הרשיתי לעצמי קיצור דרך, כי זו עבודה שחורה ותו לא: ביקשתי מ-Claude לקרוא את הדף ולייצר סקריפט שרץ מתוך Ghidra ויוצר את כל התוויות (Labels), כולל Repeatable Comment לכל אחת.



למשל, הכתובת KBD קיבלה את ההערה ״המקש האחרון שנלחץ, ועוד 128 אם ה-Strobe לא נוקה״, יחד עם סוג הגישה (קריאה). מידע שערכו לא יסולא בפז עבור מישהו שלא מכיר את המכונה.


ואז גיליתי את הכפתור


כל הפרויקט לקח בערך שבוע וחצי של עבודה לסירוגין. אחרי כמה ימים עם הכלים, גיליתי פיצ׳ר ב-Apple2TS: יש Save Memory. הוא שומר את כל הזיכרון לקובץ:


סליחה, מה?


כל הזמן הזה ניסיתי להבין מה הוזז לאן, מה יש בכתובות הגבוהות, ואיזה קוד מעתיק לשם את הבתים האלו - כשבעצם יכולתי פשוט לשמור את הזיכרון ולטעון אותו כולו ל-Ghidra. זה לא x86 עם Segments ו-Offsets; זה בלוק רציף אחד של 64KB. תמונה אחת של כל הזיכרון ברגע הריצה - וזהו.


אז זה מה שעשיתי. נתתי שמות לאזורים (הקוד שנטען מהדיסקט, RAM, אזור ה-I/O והסלוטים), בלי לדעת בכלל אם יש משמעות לאזורים כמו $D000, $E000 או $F000.



ושוב הרגשתי קצת טמבל. אבל אנשים בגילי קוראים מדריכים רק כשאין ברירה. בשביל זה, בסופו של דבר, המציאו את RTFM.


מוצאים את ההגנה


תזכורת: יש לנו את הכתובת $2336, שכשעושים עליה Step Over - היא לא חוזרת:


אם נכנסים פנימה אל $23AB:


יש קריאה לפונקציה בזיכרון הגבוה, ומיד אחריה הדפסה של הודעת הפתיחה. הודעה שמעולם לא הגעתי אליה, כי ההגנה פעלה.


נכנסים עוד פנימה:


עכשיו, כשיש לי את כל תוכן הזיכרון ושמות למקומות החשובים, אני רואה פונקציה שניגשת ל-Annunciator. לא היה לי מושג מה זה. חיפשתי, ומסתבר שזה חלק מהמנגנון של יציאת ה-Game I/O, ש-Apple II משתמש בה לג׳ויסטיק ול-Paddles. וזה מעולה, כי הדבר היחיד שכן ידעתי זה שה-Dongle מתחבר ליציאה הזו. ומה הפונקציה עושה? בדיוק מה שהייתם מצפים: מפעילה את היציאה, קוראת ערך, ויש שם לולאה עם BPL שסופרת:


הערת אגב קטנה, שגם אותה לא ידעתי קודם: ה-Apple II קורא את מיקום ה-Paddle בעזרת קבל (Capacitor). טוענים אותו ע״י הפעלת ה-Anunciator ומודדים כמה זמן לוקח לו לפרוק - וזה תלוי במיקום של הידית. כאן אין Paddle, אז ככל הנראה ה-Dongle הוא זה ששולט בזמן הפריקה, והקוד מצפה לערכים מאוד ספציפיים.


בשלב הזה שמחתי מאוד. אני עדיין לא יודע איך להתמודד עם זה, אבל אני יודע בוודאות שמצאתי את המקום.


הקוד המוצפן


התחלתי לקרוא את כל הפונקציות הרלוונטיות, ובניתי לעצמי גרף קריאות:


מסתבר שהקוד שבודק את ה-Dongle מוצפן. הקוד בזיכרון הגבוה מפענח אותו, קורא לו, ומצפין אותו בחזרה (FUN_d8ec):


אז אם מחפשים את קוד ההגנה בקובץ או בזיכרון - לא מוצאים אותו.


ועכשיו לפיצוח עצמו. הפונקציה שקראתי לה dongle_verify קוראת את הקלט מה-Dongle פעמיים:


  • אם הקריאה השנייה גדולה מ-$20 (32) - רע.

  • אם הקריאה הראשונה (שנשמרת ב-X) קטנה מאותו $20 - רע.


רק אם שני התנאים לא מתקיימים, כלומר גם ה-BCC וגם ה-BCS לא קופצים, מגיעים ל-RTS - חזרה מהשגרה. זה הטוב.


אז אפשר לגרום לשתי הבדיקות להצליח.


או...


אפשר פשוט לא לבדוק בכלל.


אם דורסים את הבית הראשון של הפונקציה בפקודת RTS, אז בכל פעם שמישהו קורא ל-dongle_verify - הוא פשוט מקבל הצלחה.


ניסיתי. שיניתי בית אחד ל-RTS:


והרצתי:


זה מסך שמעולם לא ראיתי קודם!


פרצתי את זה :-)


מאמולטור לדיסקט


זה נחמד שזה עובד אצלי בזיכרון של האמולטור, אבל יש לי חבר שמחכה לפתרון, וזה צריך לעבוד אצלו. אז צריך לתקן (Patch) את התוכן שעל הדיסקט.


והחדשות הטובות: צריך לשנות בית אחד, והוא אפילו לא באזור המוצפן. (וגם אם היה - ההצפנה היא הוספה פשוטה של מספר, אז זה לא היה סיפור גדול לחשב את הערך הנכון ידנית.)


עוד דבר שסופר לי בהתחלה: ההגנה לא נבדקת רק בטעינה, אלא גם בזמנים אקראיים בהמשך. כנראה שהכותבים חשבו: ״מה אם מישהו יחבר את הפלאג, יטען את התוכנה, ואז יעביר את הפלאג לחבר?״. אבל מכיוון שדרסתי את הפונקציה שבודקת את הפלאג - לא משנה מתי קוראים לה, היא תמיד מצליחה. הייתי שמח להגיד שזה היה מתוכנן. זה לא היה. זה פשוט יצא ככה.


אז מחפשים את רצף הבתים בקובץ ה-Image, ומחליפים בית אחד:


ומנסים לטעון את הדיסקט המתוקן ב-Apple2TS:


וזהו! המקום שבו התוכנה תמיד נתקעה - עבר. העברית על המסך משובשת, כי לא היה לי Character ROM עברי לאמולטור, אבל זה עובד.


אחרי הפיצוח


שלחתי את הדיסקט המתוקן לחבר שממנו הכל הגיע. הוא היה באקסטזה, כמעט בדמעות. בקבוצה המשותפת, הוא כתב: ״אתם לא מבינים כמה אני מתרגש. ענבר, אתה קוסם.״

ובתור Potterhead גאה, ״You're a wizard, Inbar״ היה בהחלט רגע משמעותי בשבילי.


ואז רציתי לתת בחזרה. הייתי כל כך אסיר תודה על Apple2TS, שנכנסתי לאתר ותרמתי 100 דולר. סביר שלא אשתמש בו שוב, אבל זה הרגיש לי הדבר הנכון לעשות.


כתבתי לג׳ונתן, אלוף ה-Apple שבקבוצת האספנים, שמצאתי כלי מדהים, והוא עונה לי בנונשלנטיות: ״כן, אני מכיר את הכותב.״


מה?!


אז ביקשתי שיגיד לו שהוא יופיע בהרצאה שלי, כשאעשה אחת (כבר ידעתי שאם זה יצליח, תהיה הרצאה). וגם שיגיד לו שאני רוצה כמה פיצ׳רים. ג׳ונתן ענה ״תגיד לו בעצמך״ ופתח קבוצת צ׳אט לשלושתנו...


מיד ביקשתי שינויים, ככה שבסוף גם עזרתי לשפר את הכלי. ו-Chris, הכותב, הוא זה ששכנע אותי להגיש את ההרצאה ל-KansasFest. תודה, Chris!


אז מה הלאה?


יכולתי לעצור פה, אבל החלטתי להעלות את ההימור: לבנות Dongle משלי, שירצה את הבדיקה וכך נוכל להריץ את התוכנה הלא פרוצה.


היו לי תמונות של משהו שנראה כמו התקע המקורי, ותיאורטית, עם מספיק זמן ומאמץ, יכולתי לרברס את המעגל (רואים שם טרנזיסטורים, למשל 2222A רגיל לגמרי):


אבל אני רוצה לעשות את זה אחרת: לממש משהו עם Arduino או Pi, שיתנהג בצורה שתספק את התוכנה. ועכשיו, כשאני כבר יודע מה הקוד מצפה לקבל, וגם מצאתי (כמובן, רק בשלב הזה) תיעוד מפורט שמסביר בדיוק איך עובדת יציאת ה-Game I/O ומה הערכים אמורים להיות - זה לא אמור להיות מסובך מדי.


הבעיה היתה שבשביל לבדוק Dongle צריך Apple II אמיתי, וה-Apple IIe שלי, כאמור, לא עובד. שאלתי אחד מחבר, אבל משום מה הגרסה הלא פרוצה רצה עליו בלי שום בעיה. ואי אפשר לפרוץ משהו שכבר עובד...


אז החלטתי לעבור ל-Apple IIc, שגם יש לי (בכל זאת, אני אספן) והוא אפילו גם עובד.


בחנתי את התיעוד וגיליתי שיש כמה דברים ששונים בין Apple IIe ו-Apple IIc:


עדכנתי את העבודה ב-Ghidra בשינויים הנחוצים:


והתחלתי לחשוב על איך אני ניגש לבעיה.


הרעיון הראשון שלי היה להשתמש בציוד מדידה, כגון אוסצילוסקופ ו-Logic Analyzer כדי למדוד מתחים וסיגנלים על הרגליים של החיבור ולראות מה קורה. מן הסתם, זה הפיתרון הכי פחות פשוט ובנוסף גם קלטתי שבעוד שלושה ימים אני מציג בכנס בבואנוס איירס והייתי שמח אם הייתי מצליח לשחזר את הפלאג עד אז, ולכן בפעם הראשונה, בלב שלם, החלטתי להיעזר בקלוד ולקצר דרך.


נתתי לקלוד לקרוא את כל עבודת ה-Reverse Engineering שלי דרך Ghidra MCP for Claude וביקשתי ממנו לומר לי איך הפלאג צריך להיראות:


קלוד חשב וחשב, ובסוף הוציא את שרטוט המעגל הזה:


זה נראה היה לי קצת לא הגיוני, כי לכאורה צריך להתחבר לשני פורטים ולא רק לאחד. שאלתי את קלוד אם הוא בטוח, והוא ענה בשיא הביטחון העצמי: ״כמובן, והנה ההסבר המלא״:


זה היה מאד מרשים, אבל עדיין לא משכנע.


ממש במקרה, אחד הכותבים של התוכנה, שכאמור נמצא איתנו בקבוצת הווטסאפ, שלח לי הסבר על איך הפלאג עובד:


שלחתי את זה לקלוד וביקשתי ממנו להתייחס גם למידע הזה.


״אה! כמובן שטעיתי!״ ענה קלוד, כמו שהוא אוהב 🤦‍♂️🤦‍♂️🤦‍♂️. ואז הוא הוציא ניתוח חדש, שהיה הרבה יותר הגיוני:


עם זה כבר יכולתי לעבוד.


מכיוון שאני מתקן ומשפץ מחשבים ישנם, כל הרכיבים היו באמתחתי וניגשתי למלאכת ההרכבה:


זה שרטוט המעגל יחד עם כל הרכיבים.


פינת העבודה שלי נראתה כך:


התחלתי להלחים. באמצע הדרך גיליתי שעשיתי טעות, אז פירקתי את הכל, חזרתי לנקודת ההתחלה והלחמתי מחדש. התוצאה הסופית לא היתה אסתטית במיוחד, אבל ממילא זה אב-טיפוס ולא אכפת לי:


רגע האמת הגיע! חיברתי את הפלאג אל ה-Apple IIc, הרצתי את התוכנה, ו...:


גורנישט.


נו, טוב, היה שווה לנסות, אבל אני מניח שכשאחזור מבואנוס איירס אוכל לחזור לזה ולדבג את הפלאג כדי להבין למה הוא לא עבד.


השעה היתה כבר 23:30 והייתי עייף. נכנסתי למקלחת לפני השינה ובעודי במקלחת הרצתי בראש את המצגת ומה עשיתי.


ואז:


פתאום נזכרתי בקטע הקוד שקורא את הערך מהפלאג:


קטע הקוד ניגש לפורט מס׳ 2. אני חיברתי את הפלאג לפורט מס׳ 1.


יצאתי מהמקלחת, התלבשתי מחדש וחזרתי אל המחשב. חיברתי את הפלאג לפורט 2:


ו...:


זה עבד!!!


זיווד הפלאג


עכשיו שהפלאג עובד, אפשר להתחיל לדבר על נראות. השתמשתי בלוח פיתוח גדול אבל ניצלתי רק חלק קטן ממנו. השתמשתי ב-Dremel Tool כדי לחתוך את החתיכה שהשתמשתי בה:


השלב הבא היה לתכנן מארז ולהדפיס אותו בתלת-מימד. המדפסת החדשה שלי - Bambu Lab P2S - אכלה את זה בלי מלח:


וכך זה נראה מורכב:



הצגה בכנס ekoparty


יומיים אח״כ הייתי כבר בדרך אל בואנוס איירס, כדי להציג את הפרוייקט בכנס ekoparty. מכיוון שהצלחתי לייצר את הפלאג, החלטתי שאני לוקח את המחשב איתי ומבצע Live Demo על הבמה.


אודי, שהתחיל את כל העניין הזה, נתן לי במתנה מתאם HDMI ל-Apple IIc וניר, שגם כבר הוזכר פה, נתן לי ספק כח מודרני וקל שאוכל לקחת איתי.


בקונקשן בפרנקפורט הרמתי את ה-setup יחד עם מסך נייד שכבר הופיע בבלוג שלי:


ובאמת על הבמה הכל עבד!


אחרית דבר


זה היה פרוייקט ממש כיפי שהצליח לגעת בהרבה אנשים, יותר ממה שהערכתי. כמה לקחים ממנו:

  • פריצה (Cracking - בעצם פיצוח) זה כיף. רצוי, כשזה חוקי. פה מדובר בתוכנה בת 40 שנה, והכותבים נתנו את רשותם.

  • המתודולוגיה שעבדה בשבילי בשנות ה-90 עובדת גם היום.

  • כלי AI הוא כלי נהדר לעבודה השחורה. לחשיבה עצמה - לפחות בפרויקט הזה - זה עדיין היה תפקידי.

  • ולומדים תוך כדי עשייה.


ועוד דבר חשוב: כמה אספנים, ואני ביניהם, מנסים לשמר את ההיסטוריה של סצנת המחשוב הישראלית. כל תוכנה בעברית שאנחנו מוצאים היא אירוע גדול בשבילנו. לבעל הדיסקטים יש עוד כמה תוכנות ישנות, ואמרתי לו: כל מה שיש לך, תזרוק עלי.


אז אם גם לכם יש משהו כזה בבוידם - תגידו לי.


תודה שקראתם!

 
 
 

תגובות


על הבלוג

שמי ענבר רז. עם כניסת הקורונה לחיינו בשנת 2020 התחלתי תחביב חדש: איסוף ושחזור מחשבי רטרו. השאיפה היא להחזיר כמה שיותר מהם למצב עובד :-)

 

מכיוון שלא חסרים משאבים בתחום בשפה האנגלית, בחרתי דווקא לכתוב בעברית לטובת הקוראים המקומיים.

רשתות חברתיות

הרשמה

אם אתם רוצים לקבל עדכון בכל פעם שאני מפרסם פוסט חדש, אז אתם מוזמנים להירשם.

תודה על ההרשמה!

© 2025 by Inbar Raz. Powered and secured by Wix

bottom of page