Articles

คำสั่ง Assembly (MASM) พื้นฐานที่ถามกันบ่อย

Written by pspn on . Posted in Writec0de

ในบทความนี้เราจะมาดู คำสั่งในภาษา Assembly สำหรับ MASM ที่ถูกถามกันบ่อยๆ  เพื่อเพิ่มความเข้าใจของเราให้มากขึ้น  ยกตัวอย่างเช่น ADDR กับ OFFSET ต่างกันอย่างไร  และจะใช้เมื่อไหร่?  ใน Assembly มีตัวแปรแบบ Structure หรือไม่และ If-then-else ในภาษา Assembly เป็นอย่างไร  เราจะได้มาทำความเข้าใจกันในบทความนี้ครับ   ไปดูต่อกันเลย

ADDR and OFFSET

OFFSET operator จะคืนค่ากลับมาเป็นตำแหน่งของตัวแปร ใช้สำหรับระบุตำแหน่งของตัวแปรแทนที่จะเป็นค่าของตัวแปร:

  1. MyVar     db        77h       ;  byte-sized variable called MyVar initialised to 77h
  2. .code
  3. mov eax, MyVar               ;  copies 77h into eax
  4. mov ebx, offset MyVar      ;  copies memory address where 77h stored into ebx

นอกจากนั้น OFFSET ยังสามารถใช้ในการส่งค่าตำแหน่งของตัวแปรไปยังฟังก์ชั้นต่างในการใช้งานคำสั่ง invoke   มันใช้งานได้เฉพาะตัวแปรที่เป็น Global ที่ประกาศไว้ในส่วนของ .data หรือ .data?   มันไม่สามารถใช้กับตัวแปรที่ประกาศตอนใช้งาน proc ที่ใช้คำสั่ง LOCAL   ซึ่งไม่มีค่า offset เพราะมันถูกสร้างบน stack ขณะรันโปรแกรม (runtime) ADDR operator  จะมาแก้ปัญหานี้  มันถูกใช้โดยเฉพาะกับฟังก์ชัน invoke เพื่อส่งค่าตำแหน่งของตัวแปรไปยังฟังก์ชัน  สำหรับ Goble variables  มันจะแปลงเป็นคำสั่ง push  ซึ่งเหมือนกันกับที่ OFFSET ใช้

  1. push GlobalVar

อย่างไรก็ตาม สำหรับตัวแปรแบบ Local   ADDR จะแปลงคำสั่งเป็น:

  1. lea eax, LocalVar          ;  load effective address of LocalVar into eax

(สิ่งสำคัญที่ต้องจำไว้ก็คือ เมื่อมีการใช้ addr กับตัวแปรแบบ local   eax จะถูกใช้งาน  ดังนั้นค่า eax จะไม่ว่างให้ใช้งานสำหรับ Procedure นั้นๆ) lea eax, LocalVar  โดยปกติแล้วจะเท่ากับคำสั่ง  mov eax, offset LocalVar  แต่ใช้งาน 1 cpu cycle  mov...offset เตรียมไว้สำหรับกรณีอื่น ที่ไม่ใช้ตัวแปรแบบ local Square Brackets [] โดยปกติจะใช้แสดงค่า (เนื้อหา) ของตัวแปรที่ตรงกับตำแหน่งที่ระบุ  อย่างไรก็ตาม Syntax ของ MASM แตกต่างเล็กน้อยจาก assembler เจ้าอื่นๆในกรณีนี้   ใน MASM คำสั่งทั้งหมดนี้จะสร้างคำสั่งออกมาเหมือนกัน

  1. mov eax,1
  2. mov eax,[1]
  3. mov eax, DWORD PTR 1
  4. mov eax, DWORD PTR [1]

MyVariable และ [MyVariable] ทั้งสองหมายถึงค่าของ MyVariable โปรแกรมเมอร์หลายคนใช้ square bracket กับตัวแปรเพื่ออ้างถึงค่าของตัวแปร  ซึ่งทำให้โค้ดอ่านง่าย และง่ายต่อการพอร์ตไป assemblers อื่น จากที่กล่าวมาข้างต้น offset MyVariable และ addr MyVariable ทั้งสองหมายถึงตำแหน่งของ MyVariable เมื่อใช้งานกับรีจีสเตอร์  Square brackets ทำให้เกิดควมแตกต่างเพื่อแสดงถึงตำแหน่งของหน่วยความจำ (Memory address)

  1. mov ebx,eax        ;  copies the value in eax into ebx.
  2. mov ebx,[eax]      ;  copies the value at the memory address in eax into ebx
  3. mov [ebx],eax      ;  copies the value in eax into memory at the address in ebx.

The PTR operator (h3) PTR operator เป็นตัวบอกให้ assembler ทราบถึงขนาดของข้อมูลในแต่ในขณะนั้น  ที่ซึ่งขนาดของข้อมูลไม่สามารถได้มาโดยอัตโนมัติ  ตัวอย่างเช่น:

  1. MOV [eax], 0

นี่เป็นคำสั่งที่ไม่สามารถใช้งานได้  เพราะว่าตัวจัดการกับหน่วยความจำ [eax] ไม่สามารถบ่งบอกถึงขนาดหน่วยความจำได้  ถ้าเราต้องการคัดลอกค่า 0 (zero) ไปยังหน่วยความจำแบบ BYTE ที่ตำแหน่ง [eax]  เราต้องใช้ BYTE PRT เพื่อบอกให้ assembler รู้ว่าเราจะใช้หน่วยความจำเท่าำไหร่  เพื่อให้เข้าใจมากขึ้นไปดูตัวอย่างกัน

  1. mov bl, [eax]

Compiler รู้ว่า 'bl' เท่ากับ 8 bits  เรากำลังสั่งให้ processor เก็บค่าที่อยู่ในตำแหน่งที่เก็บอยู่ใน eax (eax เก็บตำแหน่งของ Memory ปลายทาง)   conpilre จะรู้ขอบเขตคือ 8 bits โดยทราบจากรีจีสเตอร์ 'bl' ที่ปลายทาง  จึงทำให้ไม่มีปัญหา

  1. mov bl, byte ptr [eax]

คำสั่งนี้เหมือนกันทุกประการกับข้างบน  ซึ่งมันไม่จำเป็นแต่เพื่อให้อ่านง่ายยิ่งขึ้น   ทำไมเราต้องใช้ BYPT PTR  หรือ WORD PTR ตามด้วย "at-the-address-of" ([...]) statement? คำตอบดูได้จากต่อไปนี้:

  1. mov [ebx], 3

นี่เป็นการหนีเสือปะจรเข้สำหรับ compiler  คำสั่งข้างบนหมายถึง "move  3 ไปยังตำแหน่งหน่วยความจำที่เก็บอยู่ใน ebx" compiler มีการคิดเป็น bits และต้องรู้ว่า 3 เป็น BYTE, WORD, หรือ DWORD เป็นต้น มันไม่มีทางรู้ว่าจะใช้กี่หน่วยความจำกี่ bits เพื่อใช้ในการเก็บค่าที่ได้   เพื่อทำให้มันกระจ่างมากขึ้น ใช้ BYTE PTR ในการเก็บค่า 3 ไว้ในหน่วยความจำ 1 Byte ในตำแหน่งที่กำหนด   เช่นเดียวกันกัีบ WORD PTR จะใช้หน่วยความจำ 2 Bytes ในการเก็บค่า 3 :

  1. mov byte ptr [ebx], 3

Structures การประกาศตัวแปรประเภท Structure:

  1. Example STRUCT
  2. field1
  3. field2
  4. field3
  5. Example ENDS

การกำหนด instance ของตัวแปรแบบ structure ที่ยังไม่มีการกำหนดค่าเริ่มต้น:

  1. .data?
  2. MyStruct1 Example

หรือ กำหนดค่าเริ่มต้นของตัวแปร การเข้าถึง Field แบบโดยตรง:

  1. mov eax,Mystruct2.field2      ;  copy 67 into eax
  2.  

การเข้าถึง Field ทางอ้อม:

  1. mov ebx,offset MyStruct2       ;ebx contains pointer to MyStruct2 structure

Syntax1:

  1. mov eax,[ebx].Example.field2

Syntax2:

  1. mov eax,[ebx.Example.field2]

Syntax3:

  1. mov eax,[Example ptr eax].field2

Syntax4:ใข้ ASSUME directive:

  1. assume eax:ptr Example
  2. mov [eax].field2
  3. assume eax:nothing          ;  always remember to un-assume when finished.

High-Level Constructs in MASM

MASM อนุญาตให้เราใช้งานคำสังภาษา C ระดับสูง เพื่อทำให้โค้ดเราอ่านง่าย assembler จะแปลงโค้ดเหล่านี้เป็น คำสั่ง assembly ทั่วไป  ก่อนที่จะแปลงไปเป็นออปเจ็คโค้ด(object code)

Relational Operators

เราสามารถใช้คำสั่งในการเปรียบเทียบ สำหรับ .IF, .REPEAT, และ .WHILE ได้เหมือนกับที่ใช้ในภาษา C  MASM จะแปลงคำสั่งเหล่านี้เป็น compare, test, และ conditional jump  ซึ่งประกอบไปด้วย:
condition


เงื่อนไขที่ไม่มี operators test สำหนับ  nonzero (พวกที่ไม่เท่ากับ 0) เหมือนกันที่ใช้ใน C:
.WHILE (x) is the same as .WHILE (x != 0) - while x is nonzero
.WHILE (!x) is the same as .WHILE (x == 0) - while x is zero

เราสามารถใช้ flag names (ZERO?, CARRY?, OVERFLOW?, SIGN?, and PARITY?) เป็นตัวดำเนินการได้ด้วยเหมือนกัน ตัวอย่างเช่น .WHILE (CARRY?)  ค่าของ carry flag เพื่อเป็นตัวกำหนดผลของเงื่อนไข

 

Register Preservation

Win32 ฟังก์ชั่น ที่เราเรียกใช้งาน (call) จะทำการจอง "segment registers" และรีจีสเตอร์ EBX, EDI, ESI และ EBP   ตรงกันข้าม ECX และ EDX จะยังไม่มีเก็บค่าจนกว่าจะ Return จาก Win32 ฟังก์ชั่น  EAX  จะไม่ถูกจอง  ซึ่งบ่อยครั้งมันจะถูกใช้ในการเก็บค่าที่คืนค่ามาจากฟังก์ชั่น (return value)    ดังนั้น ถ้าเรามีข้อมูลที่สำคัใน EAX, ECX หรือ EDX เราจะต้องเก็บมันไว้ที่อืนก่อน (เช่น เก็บมันไว้บน Stack) ที่จะเรียกใช้งาน API ฟังก์ชั่น

เช่นเดียวกับ ถ้าเราใช้ EBX, EDI, ESI หรือ EBP ใน Callback ฟังก์ชั่น  อย่าลืมที่จะคืนค่า(Restore) ของพวกมันก่อนที่จะส่งการควบคุมให้ก้ับ windows  นี่ไม่ได้หมายความว่า เราไม่สามารถใช้รีจีสเตอร์เหล่านี้ได้ เพี่ยงแต่ให้เรามั่นใจว่าเราได้คืนค่า (Restore) พวกมันก่อนที่จะส่งการควบคุมกลับไปยัง windows

เรียบเรียงจาก: Win32 Assembler Coding โดย Goppit

blog comments powered by Disqus