הקטעים הבאים כוללים סוגים נפוצים של קריסות מחשב, ניתוח של
דגימת קריסה לדוגמה ודיון על מצבות. כל סוג קריסה כולל
פלט debuggerd
לדוגמה עם הדגשה של הוכחות מרכזיות כדי לעזור
ניתן להבדיל בין סוג הקריסה הספציפי.
ביטול
הפלות מעניינות כי הן מכוונות. יש הרבה דרכים שונות
לביטול (כולל התקשרות
abort(3)
,
כשל
assert(3)
,
באמצעות אחד מסוגי הרישום הקטלניים שספציפיים ל-Android), אבל כולן כפופות
מתקשרת אל abort
. שיחה למספר abort
מסמנת את השיחה
שרשור עם SIGABRT, כך שמסגרת עם הכיתוב 'בטל' בעוד libc.so
SIGABRT הם הדברים שצריך לבדוק בפלט debuggerd
כדי
להכיר את המקרה הזה.
יכול להיות שיש 'הודעת ביטול' מפורשת השורה הזו. כדאי גם לעיין
פלט של logcat
, כדי לראות מה תועד השרשור הזה לפני כן
שהוא הורג את עצמו, כי בניגוד לassert(3)
או לרמה גבוהה של מקרי מוות
מתקני רישום ביומן, abort(3)
לא מקבלים הודעות.
הגרסאות הנוכחיות של Android כוללות את
tgkill(2)
אז את המקבצים שלהם הכי קל לקרוא,
לבטל(3) בראש הרשימה:
pid: 4637, tid: 4637, name: crasher >>> crasher <<< signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr -------- Abort message: 'some_file.c:123: some_function: assertion "false" failed' r0 00000000 r1 0000121d r2 00000006 r3 00000008 r4 0000121d r5 0000121d r6 ffb44a1c r7 0000010c r8 00000000 r9 00000000 r10 00000000 r11 00000000 ip ffb44c20 sp ffb44a08 lr eace2b0b pc eace2b16 backtrace: #00 pc 0001cb16 /system/lib/libc.so (abort+57) #01 pc 0001cd8f /system/lib/libc.so (__assert2+22) #02 pc 00001531 /system/bin/crasher (do_action+764) #03 pc 00002301 /system/bin/crasher (main+68) #04 pc 0008a809 /system/lib/libc.so (__libc_init+48) #05 pc 00001097 /system/bin/crasher (_start_main+38)
גרסאות ישנות יותר של Android עברו נתיב מפותל בין הגרסה המקורית
מבטלים את הקריאה (מסגרת 4 כאן) ואת השליחה בפועל של האות (מסגרת 0 כאן).
זה היה נכון במיוחד ב-ARM 32-bit,
__libc_android_abort
(מסגרת 3 כאן) לפלטפורמות האחרות
רצף של raise
/pthread_kill
/tgkill
:
pid: 1656, tid: 1656, name: crasher >>> crasher <<< signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr -------- Abort message: 'some_file.c:123: some_function: assertion "false" failed' r0 00000000 r1 00000678 r2 00000006 r3 f70b6dc8 r4 f70b6dd0 r5 f70b6d80 r6 00000002 r7 0000010c r8 ffffffed r9 00000000 sl 00000000 fp ff96ae1c ip 00000006 sp ff96ad18 lr f700ced5 pc f700dc98 cpsr 400b0010 backtrace: #00 pc 00042c98 /system/lib/libc.so (tgkill+12) #01 pc 00041ed1 /system/lib/libc.so (pthread_kill+32) #02 pc 0001bb87 /system/lib/libc.so (raise+10) #03 pc 00018cad /system/lib/libc.so (__libc_android_abort+34) #04 pc 000168e8 /system/lib/libc.so (abort+4) #05 pc 0001a78f /system/lib/libc.so (__libc_fatal+16) #06 pc 00018d35 /system/lib/libc.so (__assert2+20) #07 pc 00000f21 /system/xbin/crasher #08 pc 00016795 /system/lib/libc.so (__libc_init+44) #09 pc 00000abc /system/xbin/crasher
אפשר לשחזר מופע קריסה מהסוג הזה באמצעות crasher
abort
.
ביטול הפניה של מצביע null
זו הקריסה המקורית הקלאסית, ולמרות שזה רק מקרה מיוחד את סוג הקריסה הבא, כדאי לציין את זה בנפרד כי בדרך כלל פחות מחשבה.
בדוגמה הבאה, למרות שהפונקציית הקריסה נמצאת
libc.so
, מכיוון שהפונקציות של המחרוזת פועלות רק על
את המצביעים שלהם, אפשר להסיק
strlen(3)
הופעלה קריאה עם מצביע null; והקריסה הזאת אמורה לעבור ישירות
המחבר של קוד השיחה. במקרה הזה, מסגרת מס' 01 היא המתקשר הלא נכון.
pid: 25326, tid: 25326, name: crasher >>> crasher <<< signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0 r0 00000000 r1 00000000 r2 00004c00 r3 00000000 r4 ab088071 r5 fff92b34 r6 00000002 r7 fff92b40 r8 00000000 r9 00000000 sl 00000000 fp fff92b2c ip ab08cfc4 sp fff92a08 lr ab087a93 pc efb78988 cpsr 600d0030 backtrace: #00 pc 00019988 /system/lib/libc.so (strlen+71) #01 pc 00001a8f /system/xbin/crasher (strlen_null+22) #02 pc 000017cd /system/xbin/crasher (do_action+948) #03 pc 000020d5 /system/xbin/crasher (main+100) #04 pc 000177a1 /system/lib/libc.so (__libc_init+48) #05 pc 000010e4 /system/xbin/crasher (_start+96)
אפשר לשחזר מופע קריסה מהסוג הזה באמצעות crasher
strlen-NULL
.
ביטול הפניה של מצביע null לכתובת נמוכה
במקרים רבים כתובת השגיאה לא תהיה 0, אלא מספר נמוך אחר. שניים או
ברוב המקרים, כתובות שמורכבות מ-3 ספרות הן תופעה נפוצה מאוד,
היא כמעט בהחלט לא ביטול הפניה של מצביע null -
נדרש היסט של 1MiB. זה קורה בדרך כלל כשיש לכם קוד
מבטל את ההפניה של מצביע null כאילו היה מבנה חוקי. הפונקציות הנפוצות הן
fprintf(3)
(או כל פונקציה אחרת שמשתמשת ב-FILE*)
readdir(3)
,
כי הקוד בדרך כלל לא בודק
fopen(3)
או
opendir(3)
השיחה באמת הצליחה.
דוגמה של readdir
:
pid: 25405, tid: 25405, name: crasher >>> crasher <<< signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xc r0 0000000c r1 00000000 r2 00000000 r3 3d5f0000 r4 00000000 r5 0000000c r6 00000002 r7 ff8618f0 r8 00000000 r9 00000000 sl 00000000 fp ff8618dc ip edaa6834 sp ff8617a8 lr eda34a1f pc eda618f6 cpsr 600d0030 backtrace: #00 pc 000478f6 /system/lib/libc.so (pthread_mutex_lock+1) #01 pc 0001aa1b /system/lib/libc.so (readdir+10) #02 pc 00001b35 /system/xbin/crasher (readdir_null+20) #03 pc 00001815 /system/xbin/crasher (do_action+976) #04 pc 000021e5 /system/xbin/crasher (main+100) #05 pc 000177a1 /system/lib/libc.so (__libc_init+48) #06 pc 00001110 /system/xbin/crasher (_start+96)
במקרה הזה, הסיבה הישירה לקריסה היא
pthread_mutex_lock(3)
ניסה לגשת לכתובת 0xc (מסגרת 0). אבל הדבר הראשון
pthread_mutex_lock
כן מפנה את state
של ה-pthread_mutex_t*
שניתנה לו. אם תסתכלו
source, אפשר לראות שרכיב מסוים נמצא בקיזוז 0 במבנה, מה שאומר
שהשדה pthread_mutex_lock
קיבל את הסמן הלא חוקי 0xc. מאת
מסגרת 1 אפשר לראות שהוא קיבל את הסמן הזה על ידי readdir
,
שמחלץ את השדה mutex_
מה-DIR*
שניתנו. במבט על המבנה הזה, אפשר לראות ש-mutex_
נמצאת
לקזז את sizeof(int) + sizeof(size_t) + sizeof(dirent*)
עם
struct DIR
, שבמכשיר 32 ביט הוא 4 + 4 + 4 = 12 = 0xc, כך
מצאת את הבאג: מצביע null הועבר על ידי readdir
של הקריאה החוזרת. בשלב הזה תוכלו להדביק את המקבץ בכלי המקבצים
איפה ב-Logcat זה קרה.
struct DIR { int fd_; size_t available_bytes_; dirent* next_; pthread_mutex_t mutex_; dirent buff_[15]; long current_pos_; };
ברוב המקרים אפשר לדלג על הניתוח הזה. כשל נמוך מספיק
בדרך כלל אפשר לדלג על מסגרות של libc.so
ומאשימים את קוד השיחה ישירות. אבל לא תמיד, ככה עושים את זה
וזה יהיה מקרה משכנע.
אפשר לשחזר מופעים של קריסה מהסוג הזה באמצעות crasher
fprintf-NULL
או crasher readdir-NULL
.
כישלון FORTIFY
כשל FORTIFY הוא מקרה מיוחד של ביטול שמתרחש כאשר ספריית C
מזהה בעיה שעשויה להוביל לנקודת חולשה באבטחה. ספריית C רבות
פונקציות מבוזרות; הם לוקחים ארגומנט נוסף שאומר להם
מה הגודל בפועל של מאגר הנתונים הזמני ובודקים בזמן הריצה אם הפעולה מתבצעת
שמנסים לבצע. הנה דוגמה שבה הקוד מנסה
ל-read(fd, buf, 32)
במאגר נתונים זמני שלמעשה הוא 10 בייטים בלבד
ארוך...
pid: 25579, tid: 25579, name: crasher >>> crasher <<< signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr -------- Abort message: 'FORTIFY: read: prevented 32-byte write into 10-byte buffer' r0 00000000 r1 000063eb r2 00000006 r3 00000008 r4 ff96f350 r5 000063eb r6 000063eb r7 0000010c r8 00000000 r9 00000000 sl 00000000 fp ff96f49c ip 00000000 sp ff96f340 lr ee83ece3 pc ee86ef0c cpsr 000d0010 backtrace: #00 pc 00049f0c /system/lib/libc.so (tgkill+12) #01 pc 00019cdf /system/lib/libc.so (abort+50) #02 pc 0001e197 /system/lib/libc.so (__fortify_fatal+30) #03 pc 0001baf9 /system/lib/libc.so (__read_chk+48) #04 pc 0000165b /system/xbin/crasher (do_action+534) #05 pc 000021e5 /system/xbin/crasher (main+100) #06 pc 000177a1 /system/lib/libc.so (__libc_init+48) #07 pc 00001110 /system/xbin/crasher (_start+96)
אפשר לשחזר מופע קריסה מהסוג הזה באמצעות crasher
fortify
.
פגיעה במקבץ זוהתה על ידי -fstack-protector
האפשרות -fstack-protector
של המהדר מוסיפה בדיקות
פועל עם מאגרי נתונים זמניים בערימה כדי להגן מפני עומס יתר של מאגר הנתונים הזמני. האפשרות הזו
מופעלת כברירת מחדל עבור קוד פלטפורמה אך לא עבור אפליקציות. כשאפשרות זו היא
מופעלת, המהדר מוסיף הוראות
פונקציה
פרולוג לכתיבת ערך אקראי ממש מעבר לערך המקומי האחרון בסטאק
אל פרק הזמן של הפונקציה כדי לקרוא אותה שוב ולבדוק שהיא לא השתנתה. אם המיקום
שהערך הזה השתנה, הוא הוחלף על ידי עומס על מאגר הנתונים הזמני, כך שה
קוראת ל-__stack_chk_fail
כדי לרשום הודעה ולבטל.
pid: 26717, tid: 26717, name: crasher >>> crasher <<< signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr -------- Abort message: 'stack corruption detected' r0 00000000 r1 0000685d r2 00000006 r3 00000008 r4 ffd516d8 r5 0000685d r6 0000685d r7 0000010c r8 00000000 r9 00000000 sl 00000000 fp ffd518bc ip 00000000 sp ffd516c8 lr ee63ece3 pc ee66ef0c cpsr 000e0010 backtrace: #00 pc 00049f0c /system/lib/libc.so (tgkill+12) #01 pc 00019cdf /system/lib/libc.so (abort+50) #02 pc 0001e07d /system/lib/libc.so (__libc_fatal+24) #03 pc 0004863f /system/lib/libc.so (__stack_chk_fail+6) #04 pc 000013ed /system/xbin/crasher (smash_stack+76) #05 pc 00001591 /system/xbin/crasher (do_action+280) #06 pc 00002219 /system/xbin/crasher (main+100) #07 pc 000177a1 /system/lib/libc.so (__libc_init+48) #08 pc 00001144 /system/xbin/crasher (_start+96)
ניתן להבדיל בין ההגדרה הזו לבין סוגים אחרים של ביטולים באמצעות נוכחות של
__stack_chk_fail
במעקב האחורי ובהודעת הביטול הספציפית.
אפשר לשחזר מופע קריסה מהסוג הזה באמצעות crasher
smash-stack
.
Seccomp SIGSYS מקריאת מערכת אסורה
רכיב seccomp
(ספציפית, seccomp-bpf) מגבילה את הגישה לקריאות מערכת. לקבלת מידע נוסף
מידע על seccomp למפתחי פלטפורמות, ראה את הפוסט בבלוג
Seccomp
סינון ב-Android O. שרשור שמפעיל שיחת מערכת מוגבלת
מקבלים אות SIGSYS עם הקוד SYS_SECCOMP. המספר להתקשרות במערכת יהיה
שמוצגת בשורת הסיבה, יחד עם הארכיטקטורה. חשוב לציין
שמספרי השיחות של המערכת משתנים בין ארכיטקטורות. לדוגמה,
שיחת המערכת של readlinkat(2)
היא מספר 305 ב-x86 אבל 267 ב-x86-64.
מספר השיחה שונה שוב בזרוע ובזרוע 64. כי שיחת מערכת
יכולים להיות הבדלים בין הארכיטקטורות, אבל בדרך כלל קל יותר להשתמש בדוח הקריסות
כדי לגלות איזו קריאת מערכת לא אושרה במקום לחפש את
מספר שיחת מערכת בכותרות.
pid: 11046, tid: 11046, name: crasher >>> crasher <<< signal 31 (SIGSYS), code 1 (SYS_SECCOMP), fault addr -------- Cause: seccomp prevented call to disallowed arm system call 99999 r0 cfda0444 r1 00000014 r2 40000000 r3 00000000 r4 00000000 r5 00000000 r6 00000000 r7 0001869f r8 00000000 r9 00000000 sl 00000000 fp fffefa58 ip fffef898 sp fffef888 lr 00401997 pc f74f3658 cpsr 600f0010 backtrace: #00 pc 00019658 /system/lib/libc.so (syscall+32) #01 pc 00001993 /system/bin/crasher (do_action+1474) #02 pc 00002699 /system/bin/crasher (main+68) #03 pc 0007c60d /system/lib/libc.so (__libc_init+48) #04 pc 000011b0 /system/bin/crasher (_start_main+72)
ניתן להבחין בין קריאות מערכת אסורות לקריסות אחרות לפי הנוכחות של
SYS_SECCOMP
בשורה של האות ואת התיאור בשורת הסיבה.
אפשר לשחזר מופע קריסה מהסוג הזה באמצעות crasher
seccomp
.
הפרה שקשורה לזיכרון לביצוע בלבד (Android 10 בלבד)
בזרוע 64 ב-Android 10 בלבד, פלחים להפעלה של מופו קבצים בינאריים וספריות לזיכרון הרצה בלבד (לא קריא) כשיטת הקשחה והגנה מפני מתקפות של שימוש חוזר בקוד. ההקלות האלה לא הייתה קשורות למיטיגציות אחרות, ולכן הן הוסרו.
כאשר קוד לא קריא, הוא גורם לקריאות מכוונות ולא מכוונות לקטעי זיכרון מסומנים
הפעלה בלבד כדי לזרוק SIGSEGV
עם הקוד SEGV_ACCERR
. יכול להיות
כתוצאה מבאג, נקודת חולשה, נתונים מעורבים עם קוד (למשל מאגר מילולי),
או בחינה עצמית מכוונת של הזיכרון.
המהדר מניח שהקוד והנתונים לא מתערבבים, אבל יכולות להיווצר בעיות כתוצאה מכתיבה ידנית
להרכיב. במקרים רבים ניתן לתקן את המצבים על ידי העברת הקבועים אל .data
.
אם נדרשת בחינה עצמית של הקוד בקטעים של קוד הפעלה,
mprotect(2)
לסימון הקוד כקריא, ולאחר מכן לסמן אותו כבלתי קריא אחרי
הפעולה הושלמה.
pid: 2938, tid: 2940, name: crasher64 >>> crasher64 <<< signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x5f2ced24a8 Cause: execute-only (no-read) memory access error; likely due to data in .text. x0 0000000000000000 x1 0000005f2cecf21f x2 0000000000000078 x3 0000000000000053 x4 0000000000000074 x5 8000000000000000 x6 ff71646772607162 x7 00000020dcf0d16c x8 0000005f2ced24a8 x9 000000781251c55e x10 0000000000000000 x11 0000000000000000 x12 0000000000000014 x13 ffffffffffffffff x14 0000000000000002 x15 ffffffffffffffff x16 0000005f2ced52f0 x17 00000078125c0ed8 x18 0000007810e8e000 x19 00000078119fbd50 x20 00000078125d6020 x21 00000078119fbd50 x22 00000b7a00000b7a x23 00000078119fbdd8 x24 00000078119fbd50 x25 00000078119fbd50 x26 00000078119fc018 x27 00000078128ea020 x28 00000078119fc020 x29 00000078119fbcb0 sp 00000078119fba40 lr 0000005f2ced1b94 pc 0000005f2ced1ba4 backtrace: #00 pc 0000000000003ba4 /system/bin/crasher64 (do_action+2348) #01 pc 0000000000003234 /system/bin/crasher64 (thread_callback+44) #02 pc 00000000000e2044 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+36) #03 pc 0000000000083de0 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64)
ניתן להבחין בין הפרות זיכרון להפעלה בלבד לבין קריסות אחרות לפי שורת הסיבה.
אפשר לשחזר מופע קריסה מהסוג הזה באמצעות crasher xom
.
זוהתה שגיאה על ידי fdsan
הכלי לחיטוי קבצים של fdsan ב-Android עוזר לזהות טעויות נפוצות באמצעות תיאורי קבצים כשימוש אחרי סגירה וסגירה כפולה. לצפייה fdsan תיעוד לפרטים נוספים על ניפוי באגים (ומניעה) של סוג השגיאות הזה.
pid: 32315, tid: 32315, name: crasher64 >>> crasher64 <<< signal 35 (), code -1 (SI_QUEUE), fault addr -------- Abort message: 'attempted to close file descriptor 3, expected to be unowned, actually owned by FILE* 0x7d8e413018' x0 0000000000000000 x1 0000000000007e3b x2 0000000000000023 x3 0000007fe7300bb0 x4 3033313465386437 x5 3033313465386437 x6 3033313465386437 x7 3831303331346538 x8 00000000000000f0 x9 0000000000000000 x10 0000000000000059 x11 0000000000000034 x12 0000007d8ebc3a49 x13 0000007fe730077a x14 0000007fe730077a x15 0000000000000000 x16 0000007d8ec9a7b8 x17 0000007d8ec779f0 x18 0000007d8f29c000 x19 0000000000007e3b x20 0000000000007e3b x21 0000007d8f023020 x22 0000007d8f3b58dc x23 0000000000000001 x24 0000007fe73009a0 x25 0000007fe73008e0 x26 0000007fe7300ca0 x27 0000000000000000 x28 0000000000000000 x29 0000007fe7300c90 sp 0000007fe7300860 lr 0000007d8ec2f22c pc 0000007d8ec2f250 backtrace: #00 pc 0000000000088250 /bionic/lib64/libc.so (fdsan_error(char const*, ...)+384) #01 pc 0000000000088060 /bionic/lib64/libc.so (android_fdsan_close_with_tag+632) #02 pc 00000000000887e8 /bionic/lib64/libc.so (close+16) #03 pc 000000000000379c /system/bin/crasher64 (do_action+1316) #04 pc 00000000000049c8 /system/bin/crasher64 (main+96) #05 pc 000000000008021c /bionic/lib64/libc.so (_start_main)
ניתן להבדיל בין ההגדרה הזו לבין סוגים אחרים של ביטולים באמצעות נוכחות של
fdsan_error
במעקב האחורי ובהודעת הביטול הספציפית.
אפשר לשחזר מופע של קריסה מהסוג הזה באמצעות
crasher fdsan_file
או crasher fdsan_dir
.
חקירת קובצי קריסה
אם אין קריסה ספציפית שאתם בודקים כרגע,
מקור הפלטפורמה כולל כלי לבדיקת debuggerd
שנקרא
קריסה. אם mm
בsystem/core/debuggerd/
תקבל
מקבלים גם crasher
וגם crasher64
בנתיב
כך שתוכלו לבדוק קריסות של 64 ביט). קריסה עלולה לקרוס
מספר דרכים מעניינות על סמך הארגומנטים בשורת הפקודה שמספקים.
כדי לראות את הבחירה שנתמכת כרגע, צריך להשתמש בcrasher --help
.
כדי להציג את החלקים השונים בחבילת הקריסה, בואו נעבור על זה תמונת מצב של קריסה לדוגמה:
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** Build fingerprint: 'Android/aosp_flounder/flounder:5.1.51/AOSP/enh08201009:eng/test-keys' Revision: '0' ABI: 'arm' pid: 1656, tid: 1656, name: crasher >>> crasher <<< signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr -------- Abort message: 'some_file.c:123: some_function: assertion "false" failed' r0 00000000 r1 00000678 r2 00000006 r3 f70b6dc8 r4 f70b6dd0 r5 f70b6d80 r6 00000002 r7 0000010c r8 ffffffed r9 00000000 sl 00000000 fp ff96ae1c ip 00000006 sp ff96ad18 lr f700ced5 pc f700dc98 cpsr 400b0010 backtrace: #00 pc 00042c98 /system/lib/libc.so (tgkill+12) #01 pc 00041ed1 /system/lib/libc.so (pthread_kill+32) #02 pc 0001bb87 /system/lib/libc.so (raise+10) #03 pc 00018cad /system/lib/libc.so (__libc_android_abort+34) #04 pc 000168e8 /system/lib/libc.so (abort+4) #05 pc 0001a78f /system/lib/libc.so (__libc_fatal+16) #06 pc 00018d35 /system/lib/libc.so (__assert2+20) #07 pc 00000f21 /system/xbin/crasher #08 pc 00016795 /system/lib/libc.so (__libc_init+44) #09 pc 00000abc /system/xbin/crasher Tombstone written to: /data/tombstones/tombstone_06 *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
אם מחפשים יומן, שורת הכוכביות עם הרווחים שימושית לקריסות מקוריות. המחרוזת "*** ***" מופיע לעתים רחוקות ביומנים אחרים בתחילת הקריסה המקורית.
Build fingerprint: 'Android/aosp_flounder/flounder:5.1.51/AOSP/enh08201009:eng/test-keys'
טביעת האצבע מאפשרת לזהות בדיוק באיזה גרסת build התרחשה הקריסה.
זאת בדיוק אותה מערכת של ro.build.fingerprint
לנכס.
Revision: '0'
הגרסה הקודמת מתייחסת לחומרה ולא לתוכנה. בדרך כלל
לא בשימוש, אבל יכול לעזור לכם להתעלם באופן אוטומטי מבאגים שידועים
נגרמו על ידי חומרה בעייתית. זה בדיוק כמו
מאפיין מערכת אחד (ro.revision
).
ABI: 'arm'
ה-ABI הוא אחד מהשניים, arm64 , x86 או x86-64. זה בעיקר
שימושי לסקריפט stack
שהוזכר למעלה, כדי שהוא ידע
באיזה כלי להשתמש.
pid: 1656, tid: 1656, name: crasher >>> crasher <<<
השורה הזו מזהה את השרשור הספציפי בתהליך שבו קרס. כאן זה היה התהליך, כך שמזהה התהליך ומזהה ה-thread תואמים. השם הפרטי הוא שם השרשור, והשם מוקף בתחילית >>>. וגם <<< הוא שם התהליך. עבור אפליקציה, שם התהליך הוא בדרך כלל שם החבילה המוגדר במלואו (כגון com.facebook.katana), שהוא שימושי כאשר שולחים באגים או מנסים למצוא את האפליקציה ב-Google Play. ה-PID ו- גם באיתור שורות היומן הרלוונטיות שקדמו לקריסה,
signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
השורה הזו מציינת איזה אות (SIGABRT) התקבל ופרטים נוספים על האופן שבו הוא
התקבל (SI_TKILL). האותות שדווחו על ידי debuggerd
הם
SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV ו-SIGTRAP. הערכים הספציפיים של האות
הקודים משתנים בהתאם לאות הספציפי.
Abort message: 'some_file.c:123: some_function: assertion "false" failed'
לא לכל הקריסות תופיע שורת הודעה, אבל הן יבוטלו. הדבר נאסף אוטומטית מהשורה האחרונה של פלט Logcat חמור ובמקרה של ביטול מכוון, סביר להניח שהדבר יוביל הסבר למה התוכנית הרגה את עצמה.
r0 00000000 r1 00000678 r2 00000006 r3 f70b6dc8 r4 f70b6dd0 r5 f70b6d80 r6 00000002 r7 0000010c r8 ffffffed r9 00000000 sl 00000000 fp ff96ae1c ip 00000006 sp ff96ad18 lr f700ced5 pc f700dc98 cpsr 400b0010
ב-Dump הרישום מוצג התוכן של המעבד (CPU) שנרשם בזמן התקבל אות. (הקטע הזה משתנה באופן כללי בין ממשקי ה-ABI). מידת היעילות הם תלויים בקריסה המדויקת.
backtrace: #00 pc 00042c98 /system/lib/libc.so (tgkill+12) #01 pc 00041ed1 /system/lib/libc.so (pthread_kill+32) #02 pc 0001bb87 /system/lib/libc.so (raise+10) #03 pc 00018cad /system/lib/libc.so (__libc_android_abort+34) #04 pc 000168e8 /system/lib/libc.so (abort+4) #05 pc 0001a78f /system/lib/libc.so (__libc_fatal+16) #06 pc 00018d35 /system/lib/libc.so (__assert2+20) #07 pc 00000f21 /system/xbin/crasher #08 pc 00016795 /system/lib/libc.so (__libc_init+44) #09 pc 00000abc /system/xbin/crasher
במעקב אחרי הקריסה אפשר לראות איפה היינו בזמן הקריסה.
העמודה הראשונה היא מספר המסגרת (תואם לסגנון של gdb שבו המסגרת העמוקה ביותר
הוא 0). ערכי ה-PC הם ביחס למיקום של הספרייה המשותפת, ולא
מאשר כתובות מוחלטות. העמודה הבאה כוללת את שם האזור הממופה.
(שהוא בדרך כלל ספרייה משותפת או קובץ הפעלה, אבל לא בטוח שהוא מתאים, למשל,
קוד שעבר הידור JIT). לבסוף, אם יש סמלים זמינים, זהו הסמל של המחשב
תואם מוצג, יחד עם ההיסט של הסמל הזה
בייטים. אפשר להשתמש במדיניות הזו בשילוב עם objdump(1)
כדי למצוא
את ההוראות המתאימות ל-Assmbler.
קריאת מצבות
Tombstone written to: /data/tombstones/tombstone_06
כאן אפשר לראות איפה מידע נוסף נכתב על ידי debuggerd
.
debuggerd
תשמור עד 10 מצבות,
והחלפה של מצבות קיימות לפי הצורך.
המצבה מכילה את אותו מידע כמו תמונת המצב של התאונה, בתוספת כמה
תוספות. לדוגמה, הוא כולל מעקבים לאחור עבור כל השרשורים (לא
רק השרשור שקורס), הנקודה הצפה מתעדכנת, מצבי ערימה גולמיים,
וזרמי זיכרון מסביב לכתובות ברישומים. התכונה המועילה ביותר היא גם
כולל מפת זיכרון מלאה (בדומה ל-/proc/pid/maps
).
הנה דוגמה עם הערות לגבי קריסת תהליך ARM בגרסת 32 סיביות:
memory map: (fault address prefixed with --->) --->ab15f000-ab162fff r-x 0 4000 /system/xbin/crasher (BuildId: b9527db01b5cf8f5402f899f64b9b121)
יש שני דברים שחשוב לציין כאן. הראשונה היא שהקידומת של השורה הזו עם "--->". המפות הכי שימושיות כשהקריסה לא רק אפסית ביטול ההפניה של הסמן. אם הכתובת שגויה, כנראה מדובר בוריאציה של ביטול הפניה של מצביע null. אחרת, הסתכלות במפות מסביב לפגמים לרוב יכולה לרמוז לכם על מה שקרה. כמה בעיות אפשריות שאותם ניתן לזהות באמצעות עיון במפות:
- קריאה/כתיבה מעבר לסוף של בלוק זיכרון.
- קריאה/כתיבה לפני ההתחלה של בלוק זיכרון.
- ניסיונות להריץ נתונים ללא קוד.
- סיכום של מקבץ תמונות.
- ניסיונות לכתוב לקוד (כמו בדוגמה שלמעלה).
הדבר השני שיש לציין הוא שקובצי הפעלה וקובצי ספריות משותפות
יש להציג את ה-BuildId (אם קיים) ב-Android 6.0 ואילך, כדי שתוכל לראות בדיוק
איזו גרסה של הקוד קרסה. הקבצים הבינאריים של הפלטפורמות כוללים BuildId לפי
ברירת מחדל החל מ-Android 6.0; NDK r12 ומעלה עובר אוטומטית
-Wl,--build-id
גם ל-linker.
ab163000-ab163fff r-- 3000 1000 /system/xbin/crasher ab164000-ab164fff rw- 0 1000 f6c80000-f6d7ffff rw- 0 100000 [anon:libc_malloc]
ב-Android, הערימה היא לא בהכרח אזור אחד. אזורי ערימה (heap)
יופיע בתווית [anon:libc_malloc]
.
f6d82000-f6da1fff r-- 0 20000 /dev/__properties__/u:object_r:logd_prop:s0 f6da2000-f6dc1fff r-- 0 20000 /dev/__properties__/u:object_r:default_prop:s0 f6dc2000-f6de1fff r-- 0 20000 /dev/__properties__/u:object_r:logd_prop:s0 f6de2000-f6de5fff r-x 0 4000 /system/lib/libnetd_client.so (BuildId: 08020aa06ed48cf9f6971861abf06c9d) f6de6000-f6de6fff r-- 3000 1000 /system/lib/libnetd_client.so f6de7000-f6de7fff rw- 4000 1000 /system/lib/libnetd_client.so f6dec000-f6e74fff r-x 0 89000 /system/lib/libc++.so (BuildId: 8f1f2be4b37d7067d366543fafececa2) (load base 0x2000) f6e75000-f6e75fff --- 0 1000 f6e76000-f6e79fff r-- 89000 4000 /system/lib/libc++.so f6e7a000-f6e7afff rw- 8d000 1000 /system/lib/libc++.so f6e7b000-f6e7bfff rw- 0 1000 [anon:.bss] f6e7c000-f6efdfff r-x 0 82000 /system/lib/libc.so (BuildId: d189b369d1aafe11feb7014d411bb9c3) f6efe000-f6f01fff r-- 81000 4000 /system/lib/libc.so f6f02000-f6f03fff rw- 85000 2000 /system/lib/libc.so f6f04000-f6f04fff rw- 0 1000 [anon:.bss] f6f05000-f6f05fff r-- 0 1000 [anon:.bss] f6f06000-f6f0bfff rw- 0 6000 [anon:.bss] f6f0c000-f6f21fff r-x 0 16000 /system/lib/libcutils.so (BuildId: d6d68a419dadd645ca852cd339f89741) f6f22000-f6f22fff r-- 15000 1000 /system/lib/libcutils.so f6f23000-f6f23fff rw- 16000 1000 /system/lib/libcutils.so f6f24000-f6f31fff r-x 0 e000 /system/lib/liblog.so (BuildId: e4d30918d1b1028a1ba23d2ab72536fc) f6f32000-f6f32fff r-- d000 1000 /system/lib/liblog.so f6f33000-f6f33fff rw- e000 1000 /system/lib/liblog.so
בדרך כלל, לספרייה משותפת יש שלוש רשומות סמוכות. אחד מהם קריא
קובץ הפעלה (קוד), אחד הוא לקריאה בלבד (נתונים לקריאה בלבד) והשני הוא קריאה-כתיבה
(נתונים שניתנים לשינוי). בעמודה הראשונה מוצגים טווחי הכתובות שמשמשים למיפוי,
העמודה השנייה הרשאות (בסגנון הרגיל של Unix ls(1)
),
בעמודה השלישית את ההיסט של הקובץ (בהקסדצימלי), העמודה הרביעית היא הגודל
של האזור (בהקסדצימלי), ובעמודה החמישית של הקובץ (או בשם האזור האחר).
f6f34000-f6f53fff r-x 0 20000 /system/lib/libm.so (BuildId: 76ba45dcd9247e60227200976a02c69b) f6f54000-f6f54fff --- 0 1000 f6f55000-f6f55fff r-- 20000 1000 /system/lib/libm.so f6f56000-f6f56fff rw- 21000 1000 /system/lib/libm.so f6f58000-f6f58fff rw- 0 1000 f6f59000-f6f78fff r-- 0 20000 /dev/__properties__/u:object_r:default_prop:s0 f6f79000-f6f98fff r-- 0 20000 /dev/__properties__/properties_serial f6f99000-f6f99fff rw- 0 1000 [anon:linker_alloc_vector] f6f9a000-f6f9afff r-- 0 1000 [anon:atexit handlers] f6f9b000-f6fbafff r-- 0 20000 /dev/__properties__/properties_serial f6fbb000-f6fbbfff rw- 0 1000 [anon:linker_alloc_vector] f6fbc000-f6fbcfff rw- 0 1000 [anon:linker_alloc_small_objects] f6fbd000-f6fbdfff rw- 0 1000 [anon:linker_alloc_vector] f6fbe000-f6fbffff rw- 0 2000 [anon:linker_alloc] f6fc0000-f6fc0fff r-- 0 1000 [anon:linker_alloc] f6fc1000-f6fc1fff rw- 0 1000 [anon:linker_alloc_lob] f6fc2000-f6fc2fff r-- 0 1000 [anon:linker_alloc] f6fc3000-f6fc3fff rw- 0 1000 [anon:linker_alloc_vector] f6fc4000-f6fc4fff rw- 0 1000 [anon:linker_alloc_small_objects] f6fc5000-f6fc5fff rw- 0 1000 [anon:linker_alloc_vector] f6fc6000-f6fc6fff rw- 0 1000 [anon:linker_alloc_small_objects] f6fc7000-f6fc7fff rw- 0 1000 [anon:arc4random _rsx structure] f6fc8000-f6fc8fff rw- 0 1000 [anon:arc4random _rs structure] f6fc9000-f6fc9fff r-- 0 1000 [anon:atexit handlers] f6fca000-f6fcafff --- 0 1000 [anon:thread signal stack guard page]
החל מ-Android 5.0, ספריית C כוללת את רוב האזורים הממופים האנונימיים שלה יש פחות אזורים מסתוריים.
f6fcb000-f6fccfff rw- 0 2000 [stack:5081]
אזורים בשם [stack:tid]
הם הערימות של הערך שצוין
שרשורים.
f6fcd000-f702afff r-x 0 5e000 /system/bin/linker (BuildId: 84f1316198deee0591c8ac7f158f28b7) f702b000-f702cfff r-- 5d000 2000 /system/bin/linker f702d000-f702dfff rw- 5f000 1000 /system/bin/linker f702e000-f702ffff rw- 0 2000 f7030000-f7030fff r-- 0 1000 f7031000-f7032fff rw- 0 2000 ffcd7000-ffcf7fff rw- 0 21000 ffff0000-ffff0fff r-x 0 1000 [vectors]
התצוגה של [vector]
או [vdso]
תלויה
של הארכיטקטורה, ARM משתמש ב-[vector]
, וכל שאר הארכיטקטורות משתמשות
[vdso]
.