שְׁאֵלָה:
אין טבלת סמלים דינמית אך רזולוציה של השיטה מספריות משותפות אינה פועלת
Kartoch
2014-03-08 03:45:15 UTC
view on stackexchange narkive permalink

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

  $ > objdump -tT crackme-01crackme-01: פורמט קובץ elf32-i386objdump: crackme-01: לא אובייקט דינמי TABLE SYMBOL: ללא סמלים טבלת סימבולים דינמית: ללא סמלים  

אך עדיין רזולוציית הספרייה הדינמית קיימת, למשל לפני השיחה ל strcmp:

  0x08048330 ב ?? () ... 0xb7ff2420 ב- _dl_runtime_resolve () מ /lib /ld-linux.so.20xb7fec020 ב- _dl_fixup () מ /lib/ld-linux.so.20xb7ff6678 ב- __x86.get_pc_thunk.bx () מ / lib / ld- linux.so.20xb7fec033 ב- _dl_fixup () מ /lib/ld-linux.so.20xb7fe7600 ב- _dl_lookup_symbol_x () מ /lib/ld-linux.so.20xb7fe6df9 ב- do_lookup_x () מ /lib/ld-linuxed.so.20x7 ב- _dl_name_match_p () מ /lib/ld-linux.so.2...0xb7ff5d74 ב- strcmp () מ /lib/ld-linux.so.2

השאלה שלי היא איך טבלת הסמלים מוסתרת מ- readelf אך עדיין משתמשים בה במהלך הביצוע?

שְׁלוֹשָׁה תשובות:
perror
2014-03-08 06:48:17 UTC
view on stackexchange narkive permalink

למעשה, הם כנראה השתמשו בתוכנת sstrip מהחבילה ElfKicker. על פי קובץ sstrip README :

sstrip הוא כלי עזר קטן שמסיר את התוכן בסוף קובץ ELF שאינו חלק מתמונת הזיכרון של התוכנית.

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

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

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

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

ללא הפשטה

בואו ניקח בחשבון תוכנית (דומה לזו שנבדקה בשאלה) עם אין הפשטת הכל.

  $ > objdump -tT ./crackme./crackme: פורמט קובץ elf32-i386 לוח SYMBOL: 08048134 ld .interp 00000000 .interp08048148 ld .note.ABI-tag 00000000 .note.ABI-tag080 ld .note.gnu.build-id 00000000 .note.gnu.build-id0804818c ld .gnu.hash 00000000 .gnu.hash
080481ac ld .dynsym 00000000 .dynsym0804822c ld .dynstr 00000000 .dynstr ... 080497dc g .bss 00000000 _end08048390 g F .text 00000000 _start080485f8 g O .rodata 00000004 _fp_hw080497d8 g .bss 00000000 __. _Jv_RegisterClasses080497d8 גרם O. נתונים 00000000 .מוסתרים __TMC_END__00000000 w * UND * 00000000 _ITM_registerTMCloneTable080482f4 g F .init 00000000 _initDYNAMIC SYMBOL TABLE: 00000000 DF * UND * 00000000 GLIBC_200000000 0 printf00000000 DF * UND * 00000000 GLIBC_2.0 מערכת00000000 w D * UND * 00000000 __gmon_start__00000000 DF * UND * 00000000 GLIBC_2.0 __libc_start_main080485fc g DO .rodata 00000004 בסיס _IO_stdin_used  

סטריפ עם רצועה

  $ רצועת > ./crackme-striped$> objdump -tT ./crackme-striped ./crackme-striped: תבנית קובץ elf32-i386 לוח SYMBOL: ללא סמלים טבלה של סימנול * * * 00000000 GLIBC_2.0 strcmp00000000 DF * UND * 00000000 GLIBC_2.0 read00000000 DF * UND * 00000000 GLIBC_2.0 printf00000000 DF * UND * 00000000 GLIBC_2.0 system00000000 w D * UND * 00000000 __gmon_start__00000000 DF * UND * 00000000 GLIBC_2.0 __libc_start .rodata 00000004 בסיס _IO_stdin_used  

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

הפשטה עם פס

לבסוף, בואו נסתכל מה קורה בעת שימוש ב sstrip .

  $ > sstrip ./crackme-sstriped$> objdump -tT ./crackme-sstriped ./crackme-sstriped: פורמט הקובץ elf32-i386objdump: ./crackme-sstriped: לא דינמי objectSYMBOL TABLE: ללא סמלים TABLE SYMBOL DYNAMIC: ללא סמלים  

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

  0x8048390 <_start>: xor% ebp,% ebp 0x8048392 <_start + 2>80: pop48 esi 0 <_start + 3>: mov% esp,% ECX 0x8048395 <_start + 5>: ו 0xfffffff0 $,% esp 0x8048398 <_start + 8>: לדחוף% EAX 0x8048399 <_start + 9>:% לדחוף esp 0x804839a <_start + 10>: לדחוף% edx 0x804839b <_start + 11>: לדחוף 0x80485e0 0x80483a0 $ <_start + 16>: לדחוף 0x8048570 0x80483a5 $ <_start + 21>: לדחוף% ECX 0x80483a6 <_start + 22>: לדחוף% ESI 0x80483a7 <_start + 23>: לדחוף 0x8048490 $ 0x80483ac <_start + 28>: שיחת 0x8048380 <__libc_start_main @ plt> 0x80483b1 <_start + 33>: hlt   p re> 

ואז, רצועה ep :

  0x8048390: xor% ebp,% ebp0x8048392: pop% esi0x8048393: mov% esp,% ecx0x8048395: ו- $ 0 xfffffff0,% esp0x8048398: push% eax0x8048399: push% esp0x804839a: push% edx0x804839b: push $ 0x80485e00x80483a0: push $ 0x8048570
0x80483a5: לדחוף% ecx0x80483a6: לדחוף% esi0x80483a7: לדחוף $ 0x80484900x80483ac: להתקשר 0x8048380 <__libc_start_main @ plt>0x80483b1: hlt 
>
  0x8048390: xor% ebp,% ebp0x8048392: pop% esi0x8048393: mov% esp,% ecx0x8048395: ו- $ 0xfffffff0,% esp0x8048398: לדחוף% eax0x8048399: לדחוף% esp0x8048xa: 0808039a: 080803a דחוף $ 0x80485700x80483a5: push% ecx0x80483a6: push% esi0x80483a7: push $ 0x80484900x80483ac: התקשר 0x80483800x80483b1: hlt 

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

  $ > readelf -l סוג קובץ Elf עם פסי crackme הוא EXEC (קובץ הפעלה) נקודת כניסה 0x8048390 יש 8 כותרות תוכנית, החל מ קיזוז 52 כותרות התוכנית: סוג קיזוז VirtAddr PhysAddr FileSiz MemSiz Flg Align PHDR 0x000034 0x08048034 0x08048034 0x00100 0x00100 RWE 0x4 INTERP 0x000134 0x08048134 0x08048134 0x00013 0x00013 RWE 0x1000 RWE 0x1000 LOAD 0x0006b4 0x080496b4 0x080496b4 0x00124 0x00128 RWE 0x1000 DYNAMIC 0x0006c0 0x080496c0 0x080496c0 0x000e8 0x000e8 הערה 0x4 RWE 0x000148 0x08048148 0x08048148 0x00044 0x00044 0x4 RWE GNU_EH_FRAME 0x000600 0x08048600 0x08048600 0x00024 0x00024 RWE 0x4 GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x10 סעיף מיפוי המגזר: ייתכן שחלקים המגזר. .. 00 01. אינטרפ 02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini. rodata .eh_frame_hdr .eh_frame
03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss 04 .dynamic 05 .not.ABI-tag .note.gnu.build-id 06 .eh_frame_hdr 07  

ואז הגרסה שעברה עם strook:

  $ > readelf -l ./crackme-sstriped סוג קובץ Elf הוא EXEC (קובץ הפעלה) כניסה נקודה 0x8048390 ישנם 8 כותרות תכנית המתחילות בקיזוז 52 כותרות התוכנית: סוג אופסט VirtAddr PhysAddr FileSiz MemSiz Flg Align PHDR 0x000034 0x08048034 0x08048034 0x00100 0x00100 RWE 0x4 INTERP 0x000134 0x08048134 0x08013x 0x08013 0.2] טען 0x000000 0x08048000 0x08048000 0x006b4 0x006b4 RWE 0x1000 LOAD 0x0006b4 0x080496b4 0x080496b4 0x00124 0x00128 RWE 0x1000 DYNAMIC 0x0006c0 0x080496c0 0x080496c0 0x000e8 0x000e8 הערה 0x4 RWE 0x000148 0x08048148 0x08048148 0x00044 0x00044 GNU 0x4 RWE _EH_FRAME 0x000600 0x08048600 0x08048600 0x00024 0x00024 RWE 0x4 GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x10  

כפי שניתן לראות, שם הסעיפים הוסר גם (כפי שהוכרז ב- READ. / p>

שים לב, החלת sstrip על הפעלה שעברה upx הופכת את ההפעלה הסופית לבלתי שמישה (ניסיתי).

אני לא חושב שסטריפ מסיר סמלים דינמיים.
האם האחרון בכלל רץ? האם אתה יכול לעשות את זה 'read-l'?
למעשה, מעולם לא לקחתי את הזמן לבחון כיצד זה עובד באופן פנימי. אבל, בהחלט כדאי לחפור אותו.
אוקיי, אז הוא שומר על הערך DYNAMIC עם הסמלים. אבל נראה ש- objdump הוא מטומטם מדי (הא הא) להתמודד עם זה.
אני חושב שזה אפילו יותר גרוע מזה. זה `libbfd` עצמו שלא יכול להתמודד עם הגדרות מסוג זה.
מצטער לסתור אותך @perror, אך הסמלים הדינמיים אינם מוסרים. טבלת החלקים מוסרת, ו- 'objdump' ו- 'readelf' מסתמכים על החלקים כדי למצוא את דרכם דרך הבינארי (אם זה באמצעות 'libbfd'). אך המקטעים אינם נחוצים ולמעשה אינם משמשים את המקשר הדינמי (בדרך כלל משהו כמו `ld.so`). 'readelf -d' יציג ערך 'SYMTAB' שמצביע על טבלת הסמלים שניתן לפרש בצורה שפויה יחד עם הערכים הדינמיים 'STRTAB', 'JMPREL' ו- 'RELA'.
@Celelibi: אין בעיה לסתור אותי! (במיוחד אם אתה צודק!). מה שאתה אומר יכול להסביר הרבה. אני צריך לחפור קצת יותר בתהליך טעינת הספרייה הדינמית ואז (ולסמוך על פחות 'objdump'). תודה רבה על הערתך.
Igor Skochinsky
2014-03-08 04:44:33 UTC
view on stackexchange narkive permalink

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

UPX לוקח קובץ הפעלה (סטטי או דינמי), דוחס את הכותרת והקטעים שלו. ומוסיף בדל פריקה קטן. ההפעלה המתקבלת נראית כמו מערכת סטטית למערכת ההפעלה.

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

עריכה : כפי שמוצג על ידי perror, יתכן שהקובץ לא ארוז בפועל אלא פשוט היה לו טבלת החלקים מופשטת. אמנם זה לא משפיע על יכולת ההפעלה שלו אבל הוא שובר כלים רבים, כולל, ככל הנראה, objdump ו- readelf . מומלץ לנסות את הכלי Dumper File Extensive (EFD) שיכול להדפיס את טבלת הסמלים הדינמית גם אם טבלת החלקים הופשטה.

EDIT2 : נראה שבכל זאת readelf יכול לטפל בקבצים כאלה. נסה להריץ readelf -D -s <file.elf> . (ניסיתי תחילה את --dyn-syms אבל זה לא עבד.)

Jason Geffner
2014-03-08 03:59:39 UTC
view on stackexchange narkive permalink

ההבדל הוא כדלקמן:

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

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



שאלה ותשובה זו תורגמה אוטומטית מהשפה האנגלית.התוכן המקורי זמין ב- stackexchange, ואנו מודים לו על רישיון cc by-sa 3.0 עליו הוא מופץ.
Loading...