ก่อนหน้านี้เคยพูดถึงเรื่อง Malware Analysis ก็หลายครั้ง ทั้งใน blog และส่วนตัว มาวันนี้จะกลับไปเล่าใหม่ตั้งแต่จุดเริ่มของการใช้งานไฟล์ Runtime ใน Windows นั่นคือไฟล์ EXE นั่นเอง ซึ่งพอพูดถึง EXE ไฟล์ก็ต้องไล่ต่อไปถึง DLL ที่ถูกเรียกมาใช้งาน ก็เลยเอามาเล่าใน blog เดียวเลยละกัน

DLL

EXE คือไฟล์ที่ถูกใช้งานมานานแสนนานใน Windows โดยโครงสร้างของไฟล์เหล่านี้จะอยู่ในโครงสร้างที่ชื่อว่า Portable Executable (PE) Format โดย PE Format นั้นถูกใช้ในหลายๆไฟล์ format ทั้ง (CPL, OCX, SYS และอื่นๆ) แต่ post นี้เราจะ focus ไปที่ DLL file ครับ

Export Function จะเป็นการอนุญาตให้ file อื่นสามารถนำ function ที่มีไปใช้ได้ เช่น ตัว ws2_32.dll ประกาศ export WSAStartup function ไว้ สำหรับใครก็ตามที่ต้องการสร้าง Windows Socket ก็ให้มาเรียกใช้งานได้ ซึ่งเมื่อ a.exe ต้องการสร้าง Windows Socket โดยใช้ function WSAStartup function ของ ws2_32.dll ตัว a.exe ก็จะร้องขอไปยัง Windows ซึ่งการ link Function จาก a.exe ไปยัง ws2_32.dll นั้นมีความเป็นไปได้ทั้ง static link, dynamic, runtime กล่าวคือ

  • static link จะเป็นการนำ code ของ WSAStartup Function จาก ws2_32.dll มาใส่แล้ว compile ทำให้พอเวลาเรียกก็จะเรียกในตัวเองเลย ไม่ต้องร้องขอ ws2_32.dll อะไรหลังจาก compile แล้ว
  • dynamic link จะเป็นการใส่ข้อมูลการเรียกของแต่ละ function ที่ import ที่ OS จำเป็นต้องหามาให้ใช้ไว้ใน PE Header ซึ่งข้อมูลส่วนนี้เรียกว่า Import Table
  • runtime คือตัว function และ library จะใช้ตอนที่ต้องการเท่านั้น ซึ่งสามารถทำได้ผ่าน LoadLibrary และ GetProcAddress function นั่นหมายความว่า ถ้า a.exe นั้นเป็นแบบ runtime link เมื่อ a.exe จะสร้าง Windows Socket ถึงจะเรียกหา ws2_32.dll เมื่อ load แล้ว ตัว GetProcAddress ก็จะเป็นค่าตำแหน่ง memory ในการ load library ที่เคยโหลดไว้นั่นเอง

Function ที่จะถูกนำไป import ได้ จำเป็นต้องประกาศไว้ก่อนเสมอ

ถ้าว่ากันจริงๆแล้ว EXE และ DLL จะมีความแตกต่างกันเพียงเล็กน้อยเท่านั้น อีกทั้งตัว flag ที่จะบ่งบอกว่าเป็น EXE หรือว่า DLL นั้นก็มีแค่ exported function เยอะหรือน้อยแค่นั้นเอง (EXE export funtion น้อย ส่วน DLL สร้างมาเพื่อเป็น function ให้กับตัวอื่นอยู่แล้ว ดังนั้นจึงมี export function เยอะ)

ทีนี้เวลาจะทำพวก malware analysis เราจะดูได้ง่ายๆว่า EXE หรือ malware นั้นทำอะไรได้บ้าง ก็ดูจาก import function เอาก็ได้ครับ เช่น การ require advapi32.dll นั่นหมายถึงการต้องการที่จะไปเปลี่ยนแปลง registry นั่นเอง เป็นต้น

หาก source code ของโปรแกรมนั้นซ้ำๆกันคือไม่มีการเปลี่ยนแปลงการเรียกใช้ function ตัว Import Table ก็มักจะถูกสร้างในทางเดิมซ้ำๆเช่นกัน (ในกรณีที่ถูก compile ด้วย compiler เดียวกัน) แต่ MD5 ของ PE นั้นอาจไม่เหมือนกัน เพราะเวลาซึ่งเป็น property ของไฟล์นั้นแตกต่างกันที่ส่วนของ generate time นั่นเอง แต่ Import Table Hash (Imphashes) นั้นจะยังคงเหมือนกัน ซึ่งรวมถึงการเปลี่ยนค่า parameter ภายใน source code ก็ไม่ได้ส่งผลต่อ Import Table Hash เช่นกัน เพราะเปลี่ยนแค่ parameter แต่ function ภายในยังคงเช่นเดิมนั่นเอง (ส่วนของ imphashes ถือเป็น signature หนึ่งในการตรวจจับ malware ของ Antivirus ต่างๆ)

PE File

ย้อนกลับมาดู PE File กันซักหน่อย อย่างที่บอกไปแล้วว่าทั้ง EXE และ DLL นั้นเป็น PE File ทั้งคู่ โดย PE File นั้นถูกใช้ทั้งใน (x86 และ x64) ตัว PE File format นั้นมี data structure ที่จำเป็นต่อ Windows OS loader เพื่อใช้ในการรัน code โดยมาจาก Microsoft Common Object File Format (COFF). PE File นั้นจะมี 2 ส่วนหลักๆคือ

  1. Header
  2. Section

ซึ่งแต่ละส่วนก็จะถูกแบ่งย่อยไปอีก

DOS Header (DOS MZ Header + DOS Stub):

DOS Header จะเป็นส่วน 64 byte แรกของทุกๆ PE file โดย e_magic คือส่วน magic number ซึ่งเป็นส่วนที่ใช้ในการประกาศ MS-DOS-compatible file type. โดย MS-DOS-compatible executable files จะเซ็ทค่านี้เป็น 0x54AD ถ้าดูเป็น 2 byte แรกก็คือ 4D 5A ซึ่งถ้าดูในลักษณะของ ASCII characters ก็คือ MZ นั่นเอง

ส่วน DOS Stub จริงๆแล้วเป็นแค่ string ที่เขียนว่า “This program cannot be run in DOS mode.” เท่านั้น

ทีนี้ DOS Header จริงๆแล้วเป็นส่วนหนึ่งโครงสร้างที่เรียกว่า IMAGE_DOS_HEADER

ซึ่งส่วนสุดท้ายของ IMAGE_DOS_HEADER นั่นคือ e_Ifanew คือตำแหน่งที่  60 โดย e_Ifanew นั้นเป็นตัวเก็บ file offset ของ PE header.

PE File Header

PE File Header จะมี field ที่เก็บข้อมูลต่างๆไว้ โดย

  • PE signature (first 4 bytes contains the number 4550h > ” PE “)
  • IMAGE_FILE_MACHINE ขนาด 2 byte กำหนดลักษณะการทำงานของไฟล์
    • ( 14C h) สำหรับ Intel 80386 processor (x86)
    • (14Dh) สำหรับ Intel 80486 processor
    • (14Eh) สำหรับ Intel Pentium processor
  • กำหนดจำนวน sections มีขนาด 2 byte
  • timestamp ‘TimeDateStamp’ มีขนาด 32bit (4 byte)
  • ‘PointerToSymbolTable’ 32 bit value
  • Number Of Symbols ใช้สำหรับการ debug
  • Size Of Optional Header’ (16 bit) กำหนดลักษณะการ load ของ PE File โดยจะมีบอกพวกตำแหน่งของ address ของ code, ขนาดของ stack ที่ต้องจอง, ขนาดของ data segment และอื่นๆ
  • ‘Characteristics’ ขนาด 16 bit เก็บ flag ต่างๆของ libraries และ object files

The Section Table

Section Table จะอยู่ต่อจาก optional header โดยตำแหน่งใน section จะถูกคำนวณจากค่า byte แรกหลังจาก header ซึ่งทำให้เราต้องใช้ส่วน size of optional header โดยแต่ละ section header จะมีขนาด 40 byte

Name1: An 8-byte null-padded UTF8 encoding string. This is can be null.

VirtualSize: The actual size of the section’s data in bytes. This may be less than the size of the section on disk.

SizeOfRawData: The size of section’s data in the file on the disk.

PointerToRawData: This is so useful because it is the offset from the file’s beginning to the section’s data.

Characteristics: This flag describes the characteristics of the section.

The PE File Section

ส่วนนี้จะเก็บ content ของไฟล์รวมถึง code, data และ resource ต่างๆ โดยทุกส่วนจะมี header และ body

  • .text section หรือ CODE คือส่วนที่เป็น executable code นั่นหมายความว่าส่วนนี้จะต้องมีสิทธิ์เป็น readonly กับ execute
  • .bss: ส่วนที่เป็น data ที่ยังไม่ได้ทำอะไร
  • .data และ .rdata ส่วน read-only data บน file system, เช่น strings และค่าคงที่ เป็นต้น
  • .rsrc เป็นส่วน resource section, พวก icons และ images
  • .edata section เก็บ export directory สำหรับ application หรือ DLL.
  • .idata section เก็บพวก imported functions

เมื่อไฟล์ PE ถูกรันจะกระทำดังนี้

  1. Read data structure ของไฟล์ PE
  2. หา information และตำแหน่งของ library code ที่จำเป็นในการทำงานของไฟล์ พวกการ import function ต่างๆ
  3. จองพื้นที่ spec สำหรับการ load section ทั้งหมด

Source::