Professional Documents
Culture Documents
Andromedous-Systemcall.Hijacking
Andromedous-Systemcall.Hijacking
,0X00מבוא
הכותרת "מערכות הפעלה" היא כותרת עצומה ,ולא ניתן בסדנה אחת להכיר כל מה שקשור בפיתוח
בתחום זה .לעומת זאת ,מטרת הסדנה היא ליצור היכרות בסיסית עם כמה שיותר מרכיבים שקשורים
לפיתוח רכיבים הקשורים למערכת ההפעלה.
משיקולים שונים של נוחות או פרקטיות ,בסדנה נפתח בסביבת לינוקס.
אחד היתרונות הגדולים בפיתוח ללינוקס הוא שכל הקוד של מערכת ההפעלה מפורסם ואפשר להיעזר
בו .כאן תמצאו את כל הקוד של לינוקס.
מטרת הסדנה היא לתת בסיס לפיתוח רכיבים של מערכת ההפעלה )במקרה שלנו.(kernel module ,
בנוסף ,בסדנה זו יש דגש על למידה עצמאית ,מציאת מקורות מידע ,ונגיעה במושגים שונים הקשורים
למערכות הפעלה כמו proc files, system callsועוד.
בחלק הראשון של הסדנה נפתח רכיב של מערכת ההפעלה שעוקב אחרי פעולות קבצים שנעשות
ברחבי המחשב ,ונותן ממשק נוח לקבל מידע זה.
חשוב לציין שרמת הפיתוח ו"אורך הקוד" הנדרש בסדנה הוא לא גבוה ,ורוב האתגר הוא מחקרי /
לימודי .לכן ,נסו להיכנס לעומק של כל נושא .חקרו מה עושה כל פונקציה או קטע קוד -אין קופסאות
שחורות.
,0X01הכנות
ככלל ,רצוי לפתח רכיבי מערכות הפעלה במכונה וירטואלית ) .(VMהסיבה לכך היא שקל מאוד לעשות
טעות קטנה ולגרום ל)kernel oopsזכרו ,בקרנל אין מי שיתפוס את האקספשנים שלנו וכל חריגה תהרוג
בדם קר את התהליך ואיתו כל מערכת ההפעלה( ,או לטעון למערכת ההפעלה רכיב שגורם לה להפסיק
לעלות .זה משהו שאתם פחות רוצים שיקרה ולכן נשתמש במכונה וירטואלית חדשה לכל הניסויים
המשוגעים שלנו.
ניתן להשתמש בכל תוכנת וירטואליזציה ) .(vmware, virtualboxניתן להתקין על המכונה ווירטואלית
)כמעט( כל הפצת לינוקס פופולרית .אני בחרתי ב ,Ubuntu 16.04אך התהליך הוא זהה.
ברגע שיש לכם מכונה וירטואלית עם לינוקס מותקן עלייה ,הצטיידו בעורך הטקסט האהוב עליכם ,ואתם
מוכנים לצאת לדרך.
כדי להדגיש ,אזכיר שוב לא לעבוד על מערכת הפעלה אמתית אלא רק על מכונה וירטואלית .בנוסף,
ברגע שהתקנתם לינוקס על המכונה הווירטואלית ,שמרו עותק ) (snapshotשל המכונה כדי שלא
תצטרכו להתקין לינוקס מחדש אם )כאשר( משהו ישתבש.
מערכת ההפעלה מחולקת בגדול ל user spaceו ) kernel spaceמומלץ לקרוא עוד בנושא -היעזרו
בחומרים של מערכות הפעלה משנים קודמות ,אותם קיבלתם בתחילת השנה( .המשתמש רץ ב user
,spaceוכאשר הוא צריך לבצע פעולות שדורשות את התערבות מערכת ההפעלה )התנהלות עם זיכרון,
תקשורת ,קבצים וכדומה ,בעצם כל דבר (...הוא מבקש ממערכת ההפעלה להריץ בשבילו פונקציות
)שרצות ב .(kernel spaceבקשות אלו נקראות .system calls
בשונה מתוכנה רגילה ,אנו נכתוב קוד שרץ ב .kernel spaceיש סיבות רבות לכתיבת קוד קרנל ,כמו
ריצה בהרשאות גבוהות ,שינוי דרך הפעולה של מערכת ההפעלה ,גישה ישירה לזיכרון ,חיסכון של
מעבר תכוף בין kernel spaceל ,user spaceכתיבת תכנות המסייעות למערכת ההפעלה כמו
driversועוד.
כמו הקוד של מערכת ההפעלה ,גם הקוד שלנו יהיה כתוב בשפת .C
כעת ,אני מציג לפניכם את הגרסה של ה Linux Kernelלתוכנה הקלסית!Hello, world ,
על תהליך הקמיפול והבניה של Kernel Moduleניתן ללמוד כאן .כדי לדעת את הבסיס כל מה שאתם
צריכים הוא לקרוא עד 4.1כולל ) .Kernel Includesניתן לחפש מקורות מידע אחרים או יותר יפים ,אבל
אל תסתבכו יותר מדי ,כל המידע שצריך נמצא בקישור הזה.
הערה :לא להתחיל לעבוד ישר .לקרוא את כל מה שרלוונטי ולחזור למסמך זה להערות נוספות.
הערות נוספות:
.1לא להשתמש ב modules_installכ .targetאנו רוצים רק לקמפל ולא להתקין שום דבר,
השתמשו ב ) modulesחלק (2.3
.2אנו נעבוד בשיטת חלוקת הקבצים לשני קבצים נפרדים Makefile ,ו ) Kbuildחלק ,3.2דוגמה
.(2
.3הפרמטר C-של הפעולה makeמקבל תיקייה של Kernelבנוי מראש כדי להשתמש במשאבים
שלו לבנות את המודול שלנו ,אנו ניתן את הקרנל שאנו מריצים כרגע ,שנמצא ב:path
/lib/modules/$(shell uname -r)/build
כאשר
)$(shell uname -r
מפעיל את הפקודה uname -rאשר מחזירה את השם המלא של הקרנל ,ומכניס אותה
למחרוזת.
.4בקובץ Kbuildנשתמש בפעולה obj-mכדי לבנות קובץ koשניתן לטעון כמודול לקרנל.
,0X05הרצת המודול
שמחים ועליזים ,עם מודול ביד ,הגיע הזמן להכניס את המודול שלנו לרשימת המודולים ולהריץ אותו
כחלק ממערכת ההפעלה! לשם כך נשתמש בפקודה insmodכדי לטעון את המודול )הפקודה manב
terminalהיא חבריכם הטוב ביותר( .שימו לב שהפקודה insmodצריכה הרשאות rootכדי לפעול
-השתמשו בפקודה .sudo
במידה ואין שגיאות ,ההכנסה עברה בהצלחה .כמו קודם ,במידה ויש שגיאות ,הסתכלו בשלבים
הקודמים כדי לאתר בעיות.
אבל רגע ,איפה ההדפסה שלנו?
כדי להבין זאת ,נחזור לפונקציה .printkהמטרה של פונקציה זו היא לא פלט למשתמש ,אלא דרך
למודולים בקרנל לעשות logלפעולות שלהם .כדי לצפות ב logזה ,ניתן להשתמש בפקודה dmesg
הערה :מטרת הפקודה tailהיא להדפיס את סוף הקלט שלה )במקרה זה בחרתי להדפיס את 5השורות
האחרונות( .יש הרבה מאוד מודולים שמדפיסים באמצעות printkולכן נרצה רק את הסוף.
ניתן לראות שמספר מודולים אחרים כתבו ,logוהרשומה האחרונה היא ההדפסה של המודול שלנו:
!Hello, Kernel
בנוסף ,ניתן לראות שלינוקס כועס עלינו על kernel taintברשומה למעלה ,נגיע לזה בהמשך.
למעשה ,המודול שלנו כרגע לא עושה יותר מדי ,ונרצה לשלב זה להסיר אותו מהקרנל ) אין טעם
להשאיר סתם רכיבים שלא עושים כלום .כדי להסיר את המודול שלנו ,נשתמש בפעולה התאומה של
insmodהנקראת .rmmodנפעיל שוב את הפעולה :dmesg
נזכר שבשלבים הקודמים ,מערכת ההפעלה העירה לנו על משהו שנקרא .Kernel taintהבעיה היא
שלינוקס מתגאה ברישיון התוכנה החופשי שלה ) ,(open sourceומכיוון שהוספנו רכיב חדש למערכת
ההפעלה ,גם הוא צריך להיות בעל רישיון חופשי .מכיוון שלא ציינו שום דבר לגבי רישיון התוכנה בקוד
שלנו ,הקרנל לא מחשיב אותו כ .open source
אז לפני שמפתחי לינוקס יבואו לדפוק אצלכם בדלת ,מצאו דרך לתייג את הקוד שלכם להיות בעל רישיון
חופשי מסוג GNU Public License
כדי לוודא שהצלחתם ,הכניסו שוב את המודול בעזרת insmodושימו לב שהפעם הקרנל שמח ומאושר,
ולא כותב שאתם מבצעים פשע נורא מסוג .Kernel taint
כעת ,לאחר שלמדנו כיצד לכתוב Kernel moduleבסיסי ,נחזור למטרה שלנו:
נרצה ליצור מערכת שמנטרת את פעולות המשתמש .זאת נעשה באמצעות שיטה הנקראת System
) .call hookingתזכורת על System callsורשימה של קריאות מערכת ניתן למצוא כאן(.
ניתן לראות שבהרבה פעולות בסיסיות של המשתמש )כתיבה וקריאה לקבצים ,יצירת ,socketועוד( יש
שימוש ב .system callsבמידה ונוכל להדפיס הודעה בכל פעם שיש שימוש בקריאת מערכת מסוימת )
,openלדוגמה( ,נוכל בעצם לעקוב אחר פעולות שונות שהמשתמש עושה! )אם נחזור לאותה דוגמה,
נוכל להדפיס הודעה בכל פעם שמתבצעת פתיחת קובץ(
למזלנו ,מערכת ההפעלה שומרת טבלה של פוינטרים לפונקציות קריאות המערכת .שינוי פוינטרים אלה
לפונקציות משלנו יאפשר לנו לבצע פעולות מסוימות בכל פעם שמשתמש מנסה לבצע קריאת מערכת
)פתיחת קובץ לדוגמה(.
חשוב לזכור שאם נשנה את הפוינטרים לקריאות המערכת ,הפונקציות המקוריות לא יקראו .אם נחזור
לדוגמה של פתיחת הקובץ ,שינוי הפוינטרים בטבלת ה system callsייתן לנו את האפשרות להריץ קוד
משלנו ,אך הקוד המקורי של פתיחת הקובץ לא ייקרא ,והקובץ לא ייפתח.
כדי לפצות על בעיה זו ,נשמור את הפוינטר המקורי של קריאת המערכת אותה אנו רוצים לשנות,
ובפונקציה שאנו יוצרים נקרא לפונקציה המקורית בנוסף לקוד שלנו כדי לבצע את הפעולה המקורית
)ובעצם לא לשבור את הפונקציונליות של מערכת ההפעלה(.
למזלנו ,ברוב ההפצות הפופולריות )ובוודאות ב (Ubuntuישנו קובץ הנקרא System mapאשר מחזיק
את הכתובות למספר רב של משתנים ,טבלאות ,פונקציות )ועוד( במערכת ההפעלה ,וביניהם גם טבלת
קריאות המערכת שלנו!
כדי ללמוד איך לעבוד עם ה ) syscall tableולוודא שהכל עד עכשיו עובד( ,נכתוב מודול )או יותר נכון,
נרחיב את המודול שלנו( שמשתמש בקריאת המערכת timeכדי לקבל ,unix timeולהדפיס אותו
באמצעות .printk
חשוב להדגיש שלקרוא ל system callבתוך הקרנל זה דבר לא נכון ,זה משהו שלא אמור לקרות,
והסיבה היחידה שאנחנו עושים את זה היא לוודא שאנחנו יודעים איך לעבוד עם הטבלה .אל תשתמשו
אף פעם בקריאות מערכת בתוך קוד שרץ בקרנל -אלו פונקציות שאמורות לתת שירות ל .user space
קודם כל ,נגדיר את טבלת קריאות המערכת באמצעות הכתובת שמצאנו בSystem.map
)הכתובת היא מה שהיה רשום ב System.mapשלי ,לא בהכרח הכתובת שתהיה אצלכם(.
בטבלה זו נשתמש כמערך של פוינטרים לפונקציות קריאות המערכת .כדי לגשת לפונקציה ספציפית
נשתמש ב macrosאשר מוגדרים ב ,asm/unistd.hונראים כך:
__NR_syscallname
לדוגמה ,הכתובת לקריאת המערכת writeתימצא ב
]table_address[__NR_write
הערה :הקומפיילר לא ייתן לנו לקרוא לפונקציות ישירות מהטבלה -הקוד הבא לא יתקמפל:
;)err = table_address[__NR_write](fd,buffer,char_count
כדי להשתמש בטבלה ,יש לשמור את כתובת הפונקציה בפוינטר לפונקציה ,ולאחר מכן לקרוא לפונקציה
באמצעות הפוינטר ששמרנו) .על פוינטרים לפונקציות ב Cניתן לקרוא כאן(
כל הכבוד! כעת אתם יודעים כיצד לעבוד עם טבלת קריאות המערכת ,אפשר להתקדם .
כדי לבטל את מצב ,write protectנעשה פעולת BITWISE ANDבין המצב הנוכחי של האוגר לבין
מספר בינארי שכולו 1חוץ מהביט של ) write protectהביט החמישי שערכו 16לפי ויקיפדיה( .מכיוון
שאורך ה cr0משתנה בין מעבדים ,יש סיכוי שהקומפיילר יוסיף אפסים למספר שנכתוב ,וכך נקבל מספר
שלא תכננו לכתוב .לכן ,נכתוב את המספר ההפוך )מלא באפסים( ונשתמש בפקודה BITWISE NOT
)~( כדי להפוך את האפסים לאחדים ,ולקבל את המספר שאנחנו רוצים.
כדי להפעיל חזרה את מצב ,write protectנעשה פעולת BITWISE ORבין המצב הנוכחי של האוגר
לבין מספר בינארי שכולו 0חוץ מהביט של write protect
וודאו שאתם מבינים למה הפעולות משנות את הביט הספציפי של Write Protectאבל שומרות על שאר
המצב המקורי של האוגר -נכבה את write protectלפני שאנחנו משנים את טבלת קריאות המערכת,
ונדליק אותו בחזרה מיד אחרי השינוי כדי לשחזר את המצב של המעבד.
.1בחרו system callשמסמל פעולה של משתמש אחריה תרצו לעקוב ) מועמדים פופולריים הם
, open, write, socketאך תוכלו לבחור מה שתרצו(
.3שנו את הפוינטר בטבלה לפונקציה חדשה שתכתבו ,שמדפיסה ל dmesgאת האירוע שקרה
ומידע עליו) .לא לשכוח לבטל (write protect
.4וודאו שבפונקציה החדשה אתם קוראים לפונקציה המקורית באמצעות הפוינטר ששמרתם ב.2
.6חשבו על עוד קריאות מערכת והוסיפו אותם לקוד שלכם .נסו לעקוב אחרי כמה שיותר פעולות
של המשתמש וללמוד על פעולות של system callsשאתם לא מכירים.
מקורות נוספים: