היישום קורא גם את אותם נתחים מרובים פעמים.
013D0010 D4 9E BE BF 1C 1C 0B D4 C5 E7 11 B5 09 48 87 FA Ôž¾¿ÔÅçµ.H ‡ ú013D0020 29 4C 03 C9 DE 4A 2B 71 74 7F D2 48 E7 13 94 4E) LÉÞJ + qtÒHç ”N ... 013D3FF0 6A D1 55 92 E2 16 60 53 69 89 86 7D D9 D8 10 BC jÑU'â`Si ‰ †} Ùؼ013D4000 90 F3 D1 48 28 47 34 EC 39 36 EC 4D 69 2A 7D E5 óÑH (G4ì96ìMi *} å | _____._____ | | DWORD אחרון aka בדיקת - +
מראש>
שלבים ופרטים לפי סדר הגילוי:
פצל את קובץ ה- .dat בגושים של 16384 בתים וצור גם dump hex של כל קובץ לחיפוש והשוואה קלים. למען האמת, אני משתמש בלינוקס בחלק זה עם dd
, xxd -ps
, grep
, diff
וכו '
התחל את OllyDbg, פתח את היישום, אתר CreateFile
והגדר נקודת פריצה:
00401220 $ -FF25 18825000 JMP DWORD PTR DS: [<&kernel32.CreateFileA>; kernel32.CreateFileA
לחץ על F9
עד ששם הקובץ (ב- EAX
) הוא קובץ .dat. הגדר / הפעל נקודת שבירה ב- ReadFile
. F9
וכאשר הקריאה נעשית התחל לדרוך ולבדוק מה נעשה.
מסתכל על זה:
2.2:
לאחר קריאה זה שנה תחילה את המאגר על ידי שימוש בקיזוז כ"קסם "החל מ:
0045F5EC / $ 53 PUSH EBX; ALGO 2: אלגוריתם XOR - הקובץ פוסט נקרא .... 0045F6B6 \. C3 RETN; ALGO 2: RETN
לפחות שניים מהפעולות שבוצעו נראות libj_randl1 () ו- libj_randl2 (). (זה יהיה שלב 2.2 ברשימה לעיל.)
פשוט:
edx = כתובת זיכרון של bufferecx = קיזוז / 0x4000edi = edxebx = ecx * 0x9b9esi = dword אחרון למאגר & 0x7fffffffecx = 0i = 0; while (i < 0x3ffc) {/ * גודל המאגר - 4 * / מניפולציה במאגר}
כל השגרה מתורגמת ל- קוד C:
int xor_buf (uint8_t * buf, offset long, long buf_size) {int32_t eax; int32_t שפל; int32_t esi; ארוך אני; buf_size - = 4; ebx = (אופסט / 0x4000) * 0x9b9; / * אינטל int 32 * / esi = ((buf [buf_size + 3] << 24) | (buf [buf_size + 2] << 16) | (buf [buf_size + 1] << 8) | buf [0] & 0x7fffffff; עבור (i = 0; i < buf_size / * 0x3ffc * /; ++ i) {/ * libj_randl2 (sn) Ref. קישור למעלה. * / ebx = ((ebx% 0x0d1a4) * 0x9c4e) - ((ebx / 0x0d1a4) * 0x2fb3); אם (ebx < 0) {ebx + = 0x7fffffab; } / * libj_randl1 (sn) Ref. קישור למעלה. * / esi = ((esi% 0x0ce26) * 0x9ef4) - ((esi / 0x0ce26) * 0x0ecf);
אם (esi < 0) {esi + = 0x7fffff07; } eax = ebx - 0x7fffffab + esi; אם (eax < 1) {eax + = 0x7fffffaa; } / * שנה שלושה בתים הבאים. * / buf [i] ^ = (eax >> 0x03) & 0xff; אם (++ i < = buf_size) {buf [i] ^ = (eax >> 0x0d) & 0xff; } אם (++ i < = buf_size) {buf [i] ^ = (eax >> 0x17) & 0xff; }} להחזיר 0;}
ואז נוצר בדיקת בדיקה של המאגר שהתקבל, (פחות dword אחרון), ונבדק מול dword האחרון. כאן הוא משתמש במאגר מפלח BSS שנוצר בעת ההפעלה, שלב 1. מהרשימה למעלה . (קיזוז 0x00505000
+ 0x894
ושימוש באזור של 4 * 0x100
כיוון שהוא 256 שלמים של 32 סיביות). נראה כי מפת זרעים זו קבועה (לעולם לא נוצרה / שונתה מחדש) וניתן לדלג עליה אם לא רוצים לאמת את המאגר.
1.
נקודת קוד בפירוק ( הערות שלי.):
0045E614. 53 דחף EBX; ALGO 1: יצירת צ'קסום MAGICK BSS ... 0045E672. C3 RETN; ALGO 1: RETN
ניתן לפשט את הקוד למספרי BSS ב C כמו למשל :
int eax; / * INT NR 1, המספר הבא שנוצר לשמירה * / int i, j; int bss לא חתום [0x100] = {0}; / * קיזוז 00505894 * / עבור (i = 0; i < 0x100; ++ i) {eax = i << 0x18; עבור (j = 0; j < 8; ++ j) {if (eax & 0x80000000) {eax = (eax + eax) ^ 0x4c11db7; } אחר {eax << = 1; }} bss [i] = eax;}
2.3:
כי מערך ה- bss int משמש במאגר המנוהל ליצירת בדיקת בדיקה שאמורה להיות שווה למספר השלם האחרון בתאים 16384 שנקראו מהקובץ. (dword אחרון, זה שדילג עליו בשגרה ובדיקת XOR'ing.) . זה יהיה שלב 2.3 ברשימה שלמעלה.
char לא חתום * buf = מאגר קבצים מניפולטיבי; char לא חתום * bss = זיכרון dump 0x00505894 - 0x00505C90, או מהקוד לעיל = 0x13d0010; / * מיקום זיכרון למאגר. * / edx = 0x3ffc; / * גודל המאגר - 4 בתים (בדיקת בדיקה). * / ...
ביציאה ecx
שווה לסכום הבדיקה.
נקודת קוד בפירוק (הערות שלי.):
0045E5A8 / $ 53 PUSH EBX; אלגו 3: חישוב צ'קסום לאחר אלגוריתם 2 ... 0045E5E0 \. C3 RETN; ALGO 3: RETN (EAX = CHECKSUM == BUFFER LAST 4 BYTES)
מקוצר לשגרת C זה יכול להיות משהו כמו:
int32_t checksum ( מפה int32_t [0x100], uint8_t * buf, long len) {int i; int32_t k, cs = 0; עבור (i = 0; i < len; ++ i) {k = (cs >> 0x18) & 0xff; cs = map [buf [i] ^ k] ^ (cs << 0x08); } להחזיר cs;}
זה מסומן כדי להיות בסדר ואז סכום הבדיקה במאגר מוגדר כ: שני בתים פחות משמעותיים = 0, שני בתים משמעותיים ביותר מוגדרים לחלק מספר (מספר נתח בקובץ או מספר קריאה, (החל מ- 0)).
0045F9BF. C680 FC3F0000 >MOV BYTE PTR DS: [EAX + 3FFC], 0; הגדר שני בתים נמוכים יותר של בדיקת בדיקה ב- dat buf ל 00045F9C6. C680 FD3F0000 >MOV BYTE PTR DS: [EAX + 3FFD], 0; עוקב אחר הקודם 0045F9CD. 66: 8B4D F8 MOV CX, WORD PTR SS: [EBP-8]; הגדר את CX כדי לערום את ערך המצביע של addr EBP - 8 (מונה מסוג) 0045F9D1. 66: 8988 FE3F00>MOV WORD PTR DS: [EAX + 3FFE], CX; הגדר מאגר .dat בתים גבוהים יותר כמו CX.
כעת אחרי שכל זה נעשה ההעתקה בפועל של נתונים מתחילה באלגוריתמים עוד יותר. כאן העבודה האמיתית מתחילה . זיהוי סוגי נתונים, מבנים, איפה ומה וכו 'מצא כמה שגרות שחילצו שמות וכו'. אבל כל מה שהוא פיני לא עזר להקל על האחיזה;).
הנתונים שלמעלה יכולים להיות התחלה.
כמה נקודות פריצה שעשויות לעניין מלכתחילה:
Breakpoints Address Module פירוק פעיל תגובה 0045E5A8 Kirjaus5 מושבת PUSH EBX ALGO 3: חישוב בדיקת סכום לאחר אלגוריתם 20045E5E0 Kirjaus5 מושבת RETN ALGO 3: RETN (EAX = CHECKSUM == BUFFER Last 4 BYTES) 0045E614 Kirjaus5 Disabled PUSH EBX ALGO 1: CHERKSUMMER MAGIC BSS0045E672 Kirjaus5 Disabled RAG ALFO קרא ALGORITHM0045F6B6 Kirjaus5 מושבת RETN ALGO 2: RETN
כמה הערות:
שמור גיבוי של קובץ ה- .dat עובדים עם. אם תבטלו את היישום, הקובץ לעיתים נפגם מכיוון שהוא, כפי שצוין על ידי @blabb, כתבו נתונים חזרה לקובץ. נראה שגם קובץ ה- dat הוא חי ולכן הורדה חדשה שלו תביא לנתונים שונים.