0x008 reverse1.exe ทบทวนสรุปการมอง PE Files

0x008 reverse1.exe ทบทวนสรุปการมอง PE Files
Blog นี้จะเป็นการเขียนทบทวน สรุปเป็นหัวข้อ และ short note สั้นๆเอาไว้
ก่อนที่จะเข้าสู่เรื่องต่อไป
ยังคงใช้แนวคิดเดิมคือ "เรารู้มากหรือรู้น้อยแค่ไหนกับ file นี้"

อธิบายคำศัพท์เปิดหัวก่อนเลย
::: Decompiler (ดีคอมไพเลอร์)

    หน้าที่: แปลงโค้ดเครื่อง (Machine Code) หรือไบนารีกลับเป็นโค้ดภาษาระดับสูง (High-Level Language) เช่น C, Java, Python
ตัวอย่างเช่น
    >> IDA Pro (กับ Hex-Rays Decompiler): พยายามอย่างแรงกล้า ที่จะแปลง malware.exe จาก Assembly มาเป็น โค้ด C-like ให้ผู้ใช้วิเคราะห์ได้ง่ายที่สุด
    >> Ghidra: สร้างโดย NSA ก็เก่งในเรื่อง static analysis นะ และโดดเด่นมาทางรีเวิร์ส Windows Driver (*.sys) แต่ไม่เข้ามือผมเท่าไหร่ 555
    >> dnSpy: สำหรับ .NET Applications (เช่น app.exe ที่คอมไพล์ด้วย C#)

    ! ข้อจำกัด: โค้ดที่ได้อาจไม่เหมือนต้นฉบับ 100% เนื่องจากข้อมูลบางส่วนหายไประหว่างการคอมไพล์




::: Debugger (ดีบักเกอร์)

    หน้าที่: ใช้ติดตามและวิเคราะห์การทำงานของโปรแกรมขณะรัน เช่น ดูค่าตัวแปร, ตั้งจุดหยุด (Breakpoint), แก้ไขหน่วยความจำ
ตัวอย่างเช่น
    >> x64dbg: ติดตามการทำงานของ malware.exe ดูการเขียน Registry เป็นต้น , โคตรมันส์ตัวนี้
    >> WinDbg: ดีบัก Windows Kernel (ntoskrnl.exe) หรือ BSOD โคตรลึกตัวนี้ Windows native เน้นๆ
    >> OllyDbg: ตัวนี้ก็ดีแต่เก่าไปหน่อย ใครเคยใช้มาเล่าบ้างครับ

    ! ใช้เมื่อ: ต้องการหาบั๊กหรือวิเคราะห์พฤติกรรมโปรแกรมแบบเรียลไทม์ หรือพูดง่ายๆ รันจริง แล้วดู ทีละสเต็ปๆ



::: Disassembler (ดิสแอสเซมเบลอร์)

    หน้าที่: แปลงโค้ดเครื่องเป็น Assembly Language (ภาษาระดับต่ำ)
ตัวอย่างเช่น
    >> IDA Freeware: เปิด reverse1.exe เพื่อดูฟังก์ชันต่างๆ เป็นตัวเริ่มต้น reverse ของใครหลายๆคน ผมด้วย แต่ตอนนี้ไปที่ rizin ก่อน 555
    >> rizin: นั่นแหละ เรากำลังทำกันอยู่เลย
    >> Binary Ninja: อีกตัวนึง ที่มีทั้งฟรีและจ่ายเงิน

    ! ต่างจาก Decompiler: กระบวนการนี้มักจะไม่พยายามแปลงกลับเป็นภาษาระดับสูง แต่แสดงผลเป็น Assembly เป็นหลัก ก็คือ rizin ที่พวกเรากำลังใช้อยู่นี่เอง




::: Disassembly (ดิสแอสเซมบลี)

    ความหมาย: กระบวนการ แปลงโค้ดเครื่อง → Assembly (ผลลัพธ์ที่ได้อาจเป็น Text File หรือแสดงใน Tool)
ตัวอย่างเช่น
    >> Process Hacker: อาจะมีคนเคยใช้ ตัวอย่างเช่น ใช้ดู Memory ของ chrome.exe แล้ว Disassemble เพื่อหา Injection Code
    >> PE Explorer: ตัวอย่างเช่น เปิด python310.dll มันจะ แสดง Assembly ของฟังก์ชัน PyRun_SimpleString ก็คือกระบวนการดิสแอสเซมบลี




::: Linker (ลิงเกอร์)

    หน้าที่: รวม Object Files (.obj/.o) จากคอมไพเลอร์เป็นไฟล์ที่สามารถ Executable หรือสร้างเป็นไลบรารี
ตัวอย่างเช่น
    >> Visual Studio's link.exe: รวม .obj ของโปรแกรม C++ กลายมาเป็น app.exe
    >> MinGW's ld.exe: ลิงก์ไฟล์จาก GCC เป็น reverse1.exe ที่ผมเคยเขียนเล่าไปใน blog ก่อนหน้า

    เกี่ยวข้องกับ: การแก้ไข Symbol (ชื่อฟังก์ชัน/ตัวแปร) และจัดสรรหน่วยความจำ




::: Loader (โหลดเดอร์)

    หน้าที่: โหลดโปรแกรมจากดิสก์เข้า RAM และเตรียมสภาพแวดล้อมก่อนรัน (เช่น จัดสรร Memory, Resolve Dynamic Links)
ตัวอย่างเช่น
    >> Windows Loader (ntdll.dll): โหลด explorer.exe พร้อม DLL ที่จำเป็น (เช่น user32.dll)
    >> DLL Hijacking: แทนที่ version.dll ในโฟลเดอร์โปรแกรมเพื่อโหลดโค้ดปลอม



::: Pack (แพ็ก)

    กระบวนการ: บีบอัดหรือเข้ารหัสไฟล์ เพื่อลดขนาดหรือป้องกันการวิเคราะห์ (เช่น UPX, Themida)
ตัวอย่างเช่น
    >> UPX: บีบอัด reverse1.exe เพื่อเน้นไปในเรื่องการลดขนาดไฟล์
    >> Themida: แพ็ก reverse1.exe เพื่อเน้นไปในเรื่องป้องกันการ Crack



::: Unpack (อันแพ็ก):

    กระบวนการ: แกะไฟล์ที่ถูกแพ็กกลับสู่รูปแบบเดิมก่อนวิเคราะห์ด้วย Debugger/Disassembler มักใช้ใน Malware Analysis หรือการป้องกัน Reverse Engineering
ตัวอย่างเช่น
    >> unpack.py (ด้วย PEfile): แกะ packed_malware.exe ที่น่าจะใช้ VMProtect เข้ามา



เข้าสู่เนื้อหาอีกมุมกันเลยครับ

Paragraphe 1 : PE File Structure

0x008-06-PE-binary-detailed-structure

1.1 PE File มาจากคำว่า Portable Execution สำหรับภาพโครงสร้างผมอ้างอิงมาจาก https://github.com/corkami/pics ซึ่งน่าจะอธิบายได้ละเอียดที่สุด แต่ภาพมีขนาดใหญ่ และเป็นภาษาอังกฤษ หากใครอยากเน้นๆ ก็คลิกเข้าไปดูได้ครับ แนะนำเลยอันนี้ ละเอียดดีครับ
1.2 เครื่องมือวิเคราะห์ rizin ของเรา เปิด PE File ได้หลักๆ 2 แบบ 1 แบบปกติคือ Parse PE อำนวยความสะดวกให้ 2 เปิดแบบ raw byte แบบดิบๆ เลือกเอาตามจุดประสงค์การเปิด ส่วนการเปิดแบบ debug / write mode ผมขอข้ามไปนะครับ
1.3 Header คือ properties ว่าไฟล์นี้มีอะไร , section คือสิ่งที่บอกว่า file นี้ "จะทำอะไร" บ้าง
1.4 ทบทวนการดู RVA / Offset ให้เป็น จาก ImageBase การเปิดไฟล์ที่ต่างรูปแบบกันก็จะทำให้มองเห็นข้อมูลที่ต่างกัน

0x008-01-open-file-in-rizin

Paragraph 2 : สิ่งที่ "คงที่" ในโครงสร้าง PE File

2.1 PE File ที่รันได้จะมี DOS Header (MZ) และชี้ไปยัง **NT Header (PE\0\0) เสมอ
2.2 Section .text จะประมวลผล Machine Code เสมอ ใช้คำสั่งตามลำดับ memory address ต่ำไปสูง การขยับไปแต่ละตำแหน่งในการรันจริง มันจะมีการชี้ตำแหน่งนั้นๆ เพื่อรันคำสั่งไปเรื่อยๆ สิ่งนั้นคือ Instruction Pointer ถ้าเป็น 64bit คือ RIP ไม่ใช่ แสดงความเสียใขด้วยนะเฟ้ย
2.3 Section .rdata จะประมวลผลข้อมูลที่ อ่านได้อย่างเดียว "เขียนไม่ได้" โดยทั่วไป .rdata เป็น read-only แต่ควรตรวจสอบ permission ใน section ด้วย (เช่นจาก iS)
2.4 มี API Windows Call เสมอ ต้องมี ใช้คำสั่ง ii เพื่อดู ถ้าเป็นไฟล์ PE ที่ใช้ API แบบธรรมดา จะเห็น function ครบ แต่ถ้าเป็น malware อาจ call API แบบหลบซ่อน ทำให้ต้อง cross-ref และ trace เพิ่ม
2.5 มองเห็นการสร้าง stack , push , pop , return ได้จากแต่ละฟังก์ชั่น
2.6 ใช้ ii~data กรอง API ที่เกี่ยวกับคำว่า ‘data’ จาก import

2.7 มี .text แปลว่ามีโค้ดคำสั่ง (instruction) มีคำสั่ง แปลว่ามีการ import , มีการ import แปลว่ามี API ให้ Call ใช้ , มี API ให้ call ใช้แปลว่ามีฟังก์ชั่น , มีฟังก์ชั่นแปลว่ามี Stack frame, มี Stack frame แปลว่ามี Stack base , มี Stack base แปลว่ามี Thread , มี Thread แปลว่ามี process , มี process แปลว่ามีการ โหลด PE เข้า memory อย่างถูกต้อง (execute), PE โหลดได้เพราะโครงสร้างถูกต้อง วงจรเบื้องต้นนี้จะเป็นรูปแบบคงที่เสมอ

0x008-02-main-func-stack

Paragraph 3 : สิ่งที่ "ไม่มีทางเห็นได้ใน Static Analysis"

3.1 ตำแหน่ง Stack pointer ใน instruction address เรามองเห็น แต่ ค่าจริงที่จะเกิดขึ้น (rsp , rbp) เราจะมองไม่เห็นเพราะไม่ได้รันจริง
3.2 โค้ดบางโค้ดที่ malware ทำการ obfuscated มา
3.3 ไม่เห็นพฤติกรรมลึกๆ เช่น การสร้างไฟล์ หรือการเขียน registry ของ Windows (ต้องดูตอนรันเท่านั้น)
3.4 CFG (Control Flow Graph) ของ Rizin ไม่ always ถูก 100% ยิ่งถ้าโค้ดถูก obfuscated มา ให้คิดไว้เลย อย่าหวัง อย่าคาดหวังเกินไปเพื่อน

Paragraph 4 : rizin reverse engineer typically tasks.

4.1 เปิดไฟล์ให้ถูกจุดประสงค์ รู้ให้ได้ก่อนเปิด , ถ้าไม่รู้ก็ปิดแล้วเปิดใหม่ แค่นั้น

4.2 ? เพื่อดู help , พิมพ์คำสั่ง แล้วตามด้วย ? เพื่อดู help ของคำสั่งนั้นๆ เช่น a? และเติม ? เข้าไปอีกเพื่อให้ rizin ช่วยยกตัวอย่างได้ เช่น a??

4.3 entrypoint ของไฟล์ ยังไม่ใช่ main function เปิดไฟล์แบบธรรมดา เราจะอยู่ที่ entrypoint เสมอ
4.4 pdf คือ print disassembly function แสดงออกมาว่าฟังก์ชั่นที่เรายืนอยู่ มี assembly ยังไง (ใช้บ่อยมาก)
4.5 axl เชื่อมโยง calling ว่า function อะไรเรียกอะไร , ax? ดูเลยมีทุกอย่างเกี่ยวกับ cross references ไว้ไล่ฟังก์ชั่น call
4.5 p คือคำสั่ง print สิ่งต่างๆออกมา d คือ disassembly , x คือ hex , f คือ function
4.6 i มาจาก information , ii = import information, iH , Information of Header แปลตามตัว เพิ่มเติมก็ i?
4.7 s คือการเดินทาง (seek) ไปตามที่ต่างๆ อยากเข้าไป address ไหนเพื่อดูฟังก์ชั่นอะไรใน address นั้นก็ s ไป เพิ่มเติมก็ s?
4.8 VVv คือ visual graph ที่ช่วยเราไล่ logic control flow (CFG) ได้ง่ายขึ้น ฝึกซักหน่อย อารมณ์คล้ายๆ tmux/vim กด q เพื่อ ออกจาก VV
4.9 ax , axl คือจุดเริ่มต้นที่ดีมาก โดยเฉพาะสำหรับผม พาให้เราไปทบทวนหน้าที่ของ API ที่ปรากฎในลำดับต่อไป
4.10 rizin มี debugger นะ เปิดไฟล์แบบ รันได้ แต่อย่าเพิ่งรีบเลย แนะนำไป x64dbg ดีกว่า เขาเกิดมาเพื่อสิ่งนี้
4.11 คำสั่ง แสลช / คือการค้นหาสิ่งต่างๆใน rzin เพิ่มเติม /? เพื่อดู , แน่นอน การเข้าใจโครงสร้าง PE ก่อนจะช่วยเรามากก่อนจะค้นหาอะไร
4.12 ตัวหนอน ~ คือ grep ของ rizin (เป็น internal grep) ใช้บ่อย มากกกกก เพิ่มเติมก็ ~? ดูเอาจะพบ regex อีก ดีจัดๆ เวลาจัดการ text manipulation เยอะๆ
4.13 คัดแยกว่า pe file นี้มีการใช้ thread มั้ยด้วย "หา API ว่ามีการใช้ thread มั้ย" หลักคิดคือ ลิสต์ API ที่ import มาก่อน จากนั้นค่อย grep เอาเฉพาะคำว่า Thread เช่น ii~Thread , แต่ว่าให้ระวังนิดนึงคือ การใช้ ii~Thread เป็นการหา ‘API ที่เกี่ยวกับ thread ที่ import เข้ามา’ ไม่ได้แปลว่ามีการเรียก thread จริงหรือไม่ ต้อง cross-ref หรือดู dynamic เพิ่มด้วย

0x008-03-rizin-help-command

ให้ดู help ชัดๆ อีกครั้ง อันนี้ช่วยได้มากเลยครับ ผมเองก็ไม่มีทางจำได้หมดต้องเปิดบ่อยๆ

0x008-05-rizin-help-command

Paragraph 5 : shape หลักคิดอีกครั้ง

5.1 รวบรวมสิ่งที่เรารู้ให้มากที่สุดด้วยวิธีอื่นๆเพิ่มเติม เพื่อลดเวลา
5.2 ตี scope ให้แคบลง ว่าเราสนใจอะไร ไม่ต้องรู้ assembly ทุกบรรทัด
5.3 รู้ limitation ของ rizin , รู้ limitation ของ static Analysis อย่าฝืนเกินไป
5.4 ใช้ Cutter ช่วยถ้ายังไม่ถนัด ผมมักใช้ในการบันทึกรูปไว้สื่อสารกับคนอื่น และผมรู้นะว่าใช้แล้วมันดี แต่ผมไม่ค่อยเลือกใช้ว่ะ เพราะมันช้ากว่า CLI ฮ่าๆ
5.5 ทำสิ่งเล็กๆ ให้คล่องๆ ในปริมาณมากๆ

0x008-04-rizin-cutter-gui

เจอกัน blog ต่อไป ขอบคุณทุกท่านที่ติดตามครับ