Thursday, December 16, 2010

Assembly พื้นฐาน

ในหัวข้อนี้ ผมจะพูดถึง Assembly ของ x86 โดยเอาเฉพาะที่จำเป็นสำหรับการเขียน exploit จะพยายามไม่ให้ยาวมากนะครับ

CPU Registers

ใน CPU จะมี registers ต่างๆ ขนาด 32 bits ที่ใช้เก็บข้อมูล สำหรับ ALU (Arithmetic Logic Unit) นำมาประมวลผล โดยมี register ที่สำคัญ มีดังนี้
  • EIP (Extended Instruction Pointer) ใช้สำหรับเก็บ address ของคำสั่งถัดไปที่จะถูกประมวลผล
  • EBP (Extended Base Pointer) ใช้สำหรับเก็บ address ล่างสุดของ frame ที่ทำงานอยู่ใน stack
  • ESP (Extended Stack Pointer) ใช้สำหรับเก็บ address บนสุดของ stack
  • EAX (Extended Accumulator Register), EBX (Extended Base Register), ECX (Extended Counter Register), EDX (Extended Data Register) ทั้ง 4 ตัวนี้ใช้สำหรับเก็บข้อมูลทั่วไป (General Purpose Registers)
  • ESI (Extended Source Index), EDI (Extended Destination Index) ใช้สำหรับคำสั่งที่ต้องการ indexing เช่น array, copy string แต่ในบางครั้ง ก็ถูกใช้เหมือนกับ register 4 ตัวข้างบน คือเก็บข้อมูลทั่วไป
General Purpose Registers (EAX, EBX, ECX, EDX) สามารถ access แบบ 16 bits และ 8 bits โดยแบ่งตามรูปข้างล่าง
ส่วน register ตัวอื่นๆ สามารถ access แบบ 16 bits ตามนี้ IP, BP, SP, SI, DI

Flags

Flags ใช้สำหรับบอกสถานะของผลลัพธ์ของคำสั่ง บางคำสั่งจะไม่มีการเปลี่ยนค่า Flags บางคำสั่งจะมีการเปลี่ยนบาง Flags โดยใน CPU นั้นมี Flags อยู่หลายตัว แต่ในที่นี้ ผมจะพูดเฉพาะ ZF (Zero Flag), SF (Sign Flag)
  • ZF เป็น flag ที่ถูก set เมื่อผลลัพธ์ของ operation เป็น 0
  • SF เป็น flag ที่ถูก set เมื่อผลลัพธ์ของ operation เป็นลบ

Assembly Language

คราวนี้ก็มาถึงตัว assembly เองแล้ว โดยตัว syntax เองก็จะมีหลักๆ อยู่ 2 แบบที่ใช้กัน คือ AT&T กับ Intel โดย
- ตัว AT&T syntax จะถูกใช้ใน GNU Assembler และส่วนมากจะเป็น default สำหรับ Linux
- ตัว Intel Syntax ก็จะเป็น Netwide Assembler (NASM) และ Windows assemblers ส่วนมากจะใช้ NASM

ทั้งสอง syntax ที่กล่าวนี้ จะมี syntax ที่ต่างกันบ้าง แต่เมื่อถูกเปลี่ยนเป็น machine code แล้ว ผลลัพธ์ที่ได้ก็จะเหมือนกัน โดยความแตกต่างหลักๆ ที่ต้องรู้

  • คำสั่งที่ต้องการ source กับ destination จะสลับกัน โดย AT&T จะใช้ source ข้างหน้า แต่ NASM จะใช้ destination ข้างหน้า คือ
    - AT&T: CMD <source>, <dest> <# comment>
    - NASM: CMD <dest>, <source> <; comment>
  • AT&T ใช้ % ข้างหน้า registers แต่ NASM ไม่ใช้
  • AT&T ใช้ $ ข้างหน้า immediate value แต่ NASM ไม่ใช้
  • AT&T จะมี suffix (ตัวต่อท้ายคำสั่ง) เพื่อระบุขนาดของ operand โดยใช้ l สำหรับ long (4 byte), w สำหรับ word (2 byte), b สำหรับ byte (สำหรับ GNU Assembler เราสามารถไม่ใส่ suffix ถ้าคำสั่งนั้นมี operand ที่ระบุขนาด) แต่ NASM จะมีเมื่อใช้กับการอ้างอิงที่อยู่ เช่น dword ptr, byte ptr
  • เรื่องการอ้างที่อยู่ memory โดย AT&T ใช้ () ส่วน NASM ใช้ [] และตำแหน่งของ index ก็จะต่างกัน จะพูดถึงอีกทีในเรื่องของ assembly command
เนื่องจากเรากำลังเขียน exploit บน Linux ผมจะพูดถึง AT&T syntax เป็นหลัก

Assembly Commands

ในที่นี้ ผมจะพูดเฉพาะคำสั่งที่ผมคิดว่าสำคัญมากๆ ถ้าใครต้องการรู้เพิ่มเติม คงต้องหาอ่านเพิ่มเอาเองนะครับ

mov
คือการ copy (คัดลอก) ข้อมูลจาก source ไปยัง destination เช่น

movl $1234h, %eax
mov  %eax, %ebx   # GNU assembler สามารถเดาได้ว่าเป็น movl เพราะ ebx มีขนาด 4 bytes
movw %ax, %bx
movb %al, %bl
คำสั่งแรกคือ กำหนดค่าของ register EAX ให้เป็น 0x1234 (ใน assembly สามารถใช้ได้ทั้้ง 1234h และ 0x1234) ส่วนคำสั่งที่ 2 คือ กำหนดค่าของ EBX ให้เหมือน EAX ถ้าทำงานต่อกัน EBX ก็จะเป็นค่า 0x1234
ส่วนถ้า assembly นี้เขียนเป็น NASM syntax ก็จะเป็น
mov eax, 1234h
mov ebx, eax
mov bx, ax
mov bl, al

add, sub
ใช้สำหรับการบวกและลบ โดยนำค่าของ source ไปบวก/ลบ กับ destination แล้วเก็บผลลัพธ์ไว้ที่ destination เช่น

addl $1234h, %eax  # นำค่าที่อยู่ใน EAX บวก 0x1234 แล้วเก็บใน EAX
subl $1234h, %eax  # นำค่าที่อยู่ใน EAX ลบ 0x1234 แล้วเก็บใน EAX

xor, or, and
เป็น bitwise operation ของการทำ xor, or หรือ and ของ source กับ destination แล้วเก็บผลลัพธ์ไว้ที่ destination เช่น

xorl %eax, %eax  # xor ค่าของ EAX กับ EAX เป็นเทคนิคหนึ่ง ที่ทำให้ EAX เป็น 0
orl %ebx, %eax
andl %ebx, $ffh

push, pop
ใช้สำหรับ push กับ pop ค่าบน stack (ตำแหน่งบนสุดของ stack ดูได้จากค่า register ESP) เช่น

pushl $10h  # push ค่า 0x10 ลงใน stack
pushl %eax  # push ค่าของ EAX ลงใน stack
popl %ebx   # pop ค่าจาก stack เก็บใน EBX

cmp
ใช้สำหรับเปรียบเทียบค่า source กับ destination แล้ว set ค่า flag ต่างๆ ตามผลลัพธ์ เพื่อใช้สำหรับคำสั่ง jump ต่างๆ เช่น

cmpl $55, %eax

jne, je, jnz, jz, jmp
ใช้สำหรับ jump (กระโดด) ไปคำสั่งที่ตำแหน่งอื่นๆ โดยจะกระโดดหรือไม่ ขึ้นอยู่กับชนิดของ jump และค่าของ flag ต่างๆ แต่ในที่นี้ ผมจะไม่พูดถึง flag นะครับ เพราะจำยาก แต่ให้ดูที่ความหมายเอา โดยในตัวอย่างข้างล่างสมมติว่ามีการใช้ cmp ตามตัวอย่างข้างบน และค่า eax เป็น 10

jne 5   # Jump if Not Equal คือ 55 ไม่เท่ากับ 10 ดังนั้นก็จะ jump 
je 5    # Jump if Equal คือ 55 ไม่เท่ากับ 10 ดังนั้นจะไม่ jump
jnz 5   # Jump if Not Zero คือถ้า zero flag ไม่ถูก set ซึ่งจะเหมือนกัน jne
jz 5    # Jump if Zero คือถ้า zero flag ถูก set ซึ่งจะเหมือนกัน je
jmp 5   # jump โดยไม่มีเงื่อนไข
การ jump จะมีทั้งแบบ absolute address (คือระบุว่าจะไปที่ address ไหน) และ relative address (คือระบุว่าจะไปข้างหน้าหรือข้างหลังจากตำแหน่งปัจจุบันเท่าไร) โดยตัวอย่างที่ผมเขียนมา เป็นแบบ relative ทั้งหมด

inc, dec
ใช้สำหรับเพิ่มค่า (+1) หรือลดค่า (-1) ใน register เช้น

inc %eax
dec %ebx

lea
ย่อมาจาก load effective address ใช้สำหรับคำนวณค่า address ของ source แล้วเก็บที่ destination คำสั่งนี้หลายๆ คน จะสับสนกับ mov โดย mov ใช้สำหรับ copy ค่าที่อยู่ใน address ของ source สมมติว่าค่าใน EAX เป็น 0xdeadbee0 และค่าที่อยู่ใน address 0xdeadbee4 คือ 8

leal 4(%eax),%ebx  # คำนวณค่า address ของ source ได้ 0xdeadbeee4 แล้วเก็บที่ EBX
แต่ถ้าเป็น mov
movl 4(%eax),%ebx  # เอาค่า address ที่คำนวณได้ แล้วไปดึงค่าที่ address นั้น (คือ 8) แล้วเก็บที่ EBX

int
ใช้สำหรับเรียก interrupt handler ในการเขียน exploit บน linux ตัวที่จะได้ใช้บ่อย คือค่า 0x80 ซึ่งใช้สำหรับเรียก system call เช่น

int $0x80

nop
คือ no operation (ไม่มีการทำงาน) ใช้สำหรับบอกว่าไม่ต้องทำอะไร คล้ายๆ กับบรรทัดที่มี semicolon เฉยๆ ใน C ตัวนี้ผมจะพูดถึงประโยชน์ทีหลัง เมื่อมีการใช้งาน และขอให้จำด้วยว่ามีค่าเป็น 0x90

ในหัวข้อนี้ ที่ผมเขียนมาทั้งหมดเกี่ยวกับ assembly จริงๆ แล้วยังไม่พอที่จะมาใช้จริงๆ แค่ให้พอที่จะถูๆไถๆไปได้ สำหรับเรื่อง assembly ผมแนะนำให้ฝึกมากกว่า ไม่ต้องไปนั่งท่องจำอะไร ได้ใช้สักพักก็จะจำได้เอง ใครที่ยังไม่ค่อยเข้าใจก็อ่านหัวข้อต่อไปก่อนเลยนะครับ มันเป็นเรื่องที่เกี่ยวกัน และจะได้ฝึก assembly ด้วย น่าจะช่วยให้เข้าใจได้มากขึ้น


Reference:
- Gray Hat Hacking
- Using Assembly Language in Linux - http://asm.sourceforge.net/articles/linasm.html
- x86 Instruction Set Reference - http://siyobik.info/index.php?module=x86
- X86 Opcode and Instruction Reference - http://ref.x86asm.net/

8 comments:

  1. แจ่มครับ แต่ยากได้เครื่องมือในการทำอ่ะครับ พอจะมีแนะนำบ้างมั้ยครับ

    ReplyDelete
  2. อยากได้เครื่องมือเขียนเหมือนกันครับช่วยแนะนำด้วยครับ

    ขอบคุณครับ

    ReplyDelete
  3. ในที่สุดวิชา ไมโคร โปรเซสเซอร์และ computer systems ก็ได้มาใช้ซะที

    ReplyDelete
  4. TIS-100 ลองเล่นดูครับ เกมในsteam เป็นเกมเกี่ยวกับภาษา assembly

    ReplyDelete
  5. ขอบคุณคับ ได้ความรู้เยอะมาก

    ReplyDelete