ใครช่วยอธิบายว่ารหัสประกอบต่อไปนี้ทำอะไรได้บ้าง?
int 0x80
ใครช่วยอธิบายว่ารหัสประกอบต่อไปนี้ทำอะไรได้บ้าง?
int 0x80
คำตอบ:
ผ่านการควบคุมเพื่อขัดจังหวะเวกเตอร์ 0x80
ดูhttp://en.wikipedia.org/wiki/Interrupt_vector
บน Linux มีลักษณะที่นี้system_call
มันถูกนำมาใช้ในการจัดการ แน่นอนในระบบปฏิบัติการอื่นอาจหมายถึงสิ่งที่แตกต่างไปจากเดิมโดยสิ้นเชิง
int 0x80
เป็นชนิดพิเศษของcall
ฟังก์ชันในเคอร์เนล (เลือกโดยeax
)
int
หมายถึงการขัดจังหวะและตัวเลข0x80
คือหมายเลขขัดจังหวะ การขัดจังหวะจะถ่ายโอนโฟลว์โปรแกรมไปยังใครก็ตามที่จัดการการขัดจังหวะนั้นซึ่งจะถูกขัดจังหวะ0x80
ในกรณีนี้ ใน Linux 0x80
ตัวจัดการขัดจังหวะคือเคอร์เนลและใช้เพื่อเรียกระบบไปยังเคอร์เนลโดยโปรแกรมอื่น
เคอร์เนลได้รับแจ้งเกี่ยวกับระบบที่เรียกโปรแกรมที่ต้องการสร้างโดยตรวจสอบค่าในรีจิสเตอร์%eax
(ไวยากรณ์ AT&T และ EAX ในไวยากรณ์ของ Intel) การเรียกระบบแต่ละครั้งมีข้อกำหนดที่แตกต่างกันเกี่ยวกับการใช้รีจิสเตอร์อื่น ๆ ยกตัวอย่างเช่นค่า1
ใน%eax
หมายถึงการเรียกระบบของexit()
และค่าในการเก็บค่าของรหัสสถานะสำหรับ%ebx
exit()
จำไว้ว่า0x80
= 80h
=128
คุณสามารถดูได้ที่นี่นั่นINT
เป็นเพียงหนึ่งในหลาย ๆ คำสั่ง (จริงๆแล้วคือการแสดงภาษาแอสเซมบลี (หรือฉันควรจะพูดว่า 'ช่วยในการจำ') ที่มีอยู่ในชุดคำสั่ง x86 นอกจากนี้คุณยังสามารถค้นหาข้อมูลเพิ่มเติมเกี่ยวกับการเรียนการสอนนี้พบคู่มือ Intel ของตัวเองที่นี่
สรุปจาก PDF:
INT n / INTO / INT 3 - Call to Interrupt Procedure
คำสั่ง INT n สร้างการเรียกไปยังตัวจัดการการขัดจังหวะหรือตัวจัดการข้อยกเว้นที่ระบุด้วยตัวถูกดำเนินการปลายทาง ตัวถูกดำเนินการปลายทางระบุเวกเตอร์ตั้งแต่ 0 ถึง 255 ซึ่งเข้ารหัสเป็นค่ากลางที่ไม่ได้ลงนาม 8 บิต คำสั่ง INT n เป็นเครื่องมือช่วยจำทั่วไปสำหรับการเรียกใช้ซอฟต์แวร์ที่สร้างขึ้นไปยังตัวจัดการขัดจังหวะ
อย่างที่คุณเห็น0x80คือตัวถูกดำเนินการปลายทางในคำถามของคุณ ณ จุดนี้ CPU รู้ว่าควรรันโค้ดบางตัวที่อยู่ใน Kernel แต่รหัสอะไร? ที่กำหนดโดย Interrupt Vector ใน Linux
การขัดจังหวะซอฟต์แวร์ DOS ที่มีประโยชน์ที่สุดอย่างหนึ่งคือการขัดจังหวะ 0x21 ด้วยการเรียกใช้พารามิเตอร์ที่แตกต่างกันในรีจิสเตอร์ (ส่วนใหญ่เป็น ah และ al) คุณสามารถเข้าถึงการดำเนินการต่างๆของ IO เอาต์พุตสตริงและอื่น ๆ
ระบบ Unix และอนุพันธ์ส่วนใหญ่ไม่ใช้ซอฟต์แวร์ขัดจังหวะยกเว้นการขัดจังหวะ 0x80 ซึ่งใช้ในการโทรระบบ สิ่งนี้ทำได้โดยการป้อนค่า32 บิตที่สอดคล้องกับฟังก์ชันเคอร์เนลในรีจิสเตอร์ EAX ของโปรเซสเซอร์จากนั้นเรียกใช้ INT 0x80
โปรดดูที่สิ่งนี้ซึ่งแสดงค่าอื่น ๆ ในตารางตัวจัดการขัดจังหวะ
ดังที่คุณเห็นตารางชี้ให้ CPU ดำเนินการเรียกระบบ คุณสามารถค้นหาตารางระบบ Linux โทรที่นี่
ดังนั้นโดยการย้ายค่า 0x1 ไปที่ EAX register และเรียก INT 0x80 ในโปรแกรมของคุณคุณสามารถทำให้กระบวนการดำเนินการโค้ดใน Kernel ซึ่งจะหยุด (ออก) กระบวนการทำงานปัจจุบัน (บน Linux, x86 Intel CPU)
ต้องไม่สับสนระหว่างฮาร์ดแวร์ขัดจังหวะกับซอฟต์แวร์ขัดจังหวะ นี่คือคำตอบที่ดีมากสำหรับเรื่องนี้
นอกจากนี้ยังเป็นแหล่งที่ดี
int 0x80
เรียกระบบ i386 Linux ABI นั้นคล้ายกับ DOS int 0x21
ABI มาก ใส่หมายเลขโทรลงในรีจิสเตอร์ (AH สำหรับ DOS, EAX สำหรับ Linux) และอาร์กิวเมนต์อื่น ๆ ในรีจิสเตอร์อื่น ๆ จากนั้นรันคำสั่งการขัดจังหวะซอฟต์แวร์ ความแตกต่างที่สำคัญคือสิ่งที่ระบบเรียกให้คุณทำ (เข้าถึงฮาร์ดแวร์โดยตรงใน DOS แต่ไม่ใช่ Linux) ไม่ใช่วิธีที่คุณเรียกใช้
/usr/include/x86_64-linux-gnu/asm/unistd_64.h
ตัวอย่างการเรียกระบบ Linux ที่รันได้น้อยที่สุด
Linux ตั้งค่าตัวจัดการขัดจังหวะเพื่อ0x80
ให้ดำเนินการเรียกระบบซึ่งเป็นวิธีสำหรับโปรแกรม userland ในการสื่อสารกับเคอร์เนล
.data
s:
.ascii "hello world\n"
len = . - s
.text
.global _start
_start:
movl $4, %eax /* write system call number */
movl $1, %ebx /* stdout */
movl $s, %ecx /* the data to print */
movl $len, %edx /* length of the buffer */
int $0x80
movl $1, %eax /* exit system call number */
movl $0, %ebx /* exit status */
int $0x80
รวบรวมและเรียกใช้ด้วย:
as -o main.o main.S
ld -o main.out main.o
./main.out
ผลลัพธ์: โปรแกรมพิมพ์ไปยัง stdout:
hello world
และออกอย่างหมดจด
คุณไม่สามารถตั้งไสขัดจังหวะของคุณเองโดยตรงจาก userland เพราะคุณมีเพียงแหวนที่ 3 และลินุกซ์ป้องกันคุณจากการทำเช่นนั้น
GitHub อัปสตรีม ทดสอบบน Ubuntu 16.04
ทางเลือกอื่นที่ดีกว่า
int 0x80
ถูกแทนที่ด้วยทางเลือกที่ดีกว่าสำหรับการโทรระบบ: อันดับแรกตามsysenter
ด้วย VDSO
x86_64 มีใหม่syscall
การเรียนการสอน
ดูเพิ่มเติม: "int 0x80" หรือ "syscall" ดีกว่าอย่างไร
ตัวอย่าง 16 บิตขั้นต่ำ
ก่อนอื่นเรียนรู้วิธีสร้างระบบปฏิบัติการ bootloader ขั้นต่ำและเรียกใช้บน QEMU และฮาร์ดแวร์จริงตามที่ฉันได้อธิบายไว้ที่นี่: https://stackoverflow.com/a/32483545/895245
ตอนนี้คุณสามารถทำงานในโหมดจริง 16 บิต:
movw $handler0, 0x00
mov %cs, 0x02
movw $handler1, 0x04
mov %cs, 0x06
int $0
int $1
hlt
handler0:
/* Do 0. */
iret
handler1:
/* Do 1. */
iret
สิ่งนี้จะทำตามลำดับ:
Do 0.
Do 1.
hlt
: หยุดดำเนินการสังเกตว่าโปรเซสเซอร์ค้นหาตัวจัดการตัวแรกที่แอดเดรส0
อย่างไรและตัวที่สองที่4
: นั่นคือตารางของตัวจัดการที่เรียกว่าIVTและแต่ละรายการมี 4 ไบต์
ตัวอย่างน้อยที่สุดที่ทำ IO บางตัวเพื่อให้ตัวจัดการมองเห็นได้
ตัวอย่างโหมดป้องกันน้อยที่สุด
ระบบปฏิบัติการสมัยใหม่ทำงานในโหมดป้องกันที่เรียกว่า
การจัดการมีตัวเลือกมากขึ้นในโหมดนี้ดังนั้นจึงซับซ้อนกว่า แต่จิตวิญญาณก็เหมือนกัน
ขั้นตอนสำคัญคือการใช้คำแนะนำ LGDT และ LIDT ซึ่งจะชี้ที่อยู่ของโครงสร้างข้อมูลในหน่วยความจำ (ตาราง Interrupt Descriptor) ที่อธิบายตัวจัดการ
int 0x80 คือคำสั่งภาษาแอสเซมบลีที่ใช้เพื่อเรียกใช้การเรียกระบบใน Linux บนโปรเซสเซอร์ x86 (เช่นที่รองรับ Intel)
คำสั่ง "int" ทำให้เกิดการขัดจังหวะ
คำตอบง่ายๆ: อินเทอร์รัปต์พูดง่ายๆคือเหตุการณ์ที่ขัดขวางซีพียูและบอกให้รันงานเฉพาะ
คำตอบโดยละเอียด :
CPU มีตาราง Interrupt Service Routines (หรือ ISR) ที่เก็บไว้ในหน่วยความจำ ในโหมดจริง (16 บิต) สิ่งนี้จะถูกจัดเก็บเป็นIVTหรือI nterrupt V ector T able โดยทั่วไป IVT จะอยู่ที่0x0000:0x0000
(ที่อยู่จริง0x00000
) และเป็นชุดของที่อยู่ออฟเซ็ตเซกเมนต์ที่ชี้ไปที่ ISR ระบบปฏิบัติการอาจแทนที่รายการ IVT ที่มีอยู่แล้วด้วย ISR ของตัวเอง
(หมายเหตุ: ขนาดของ IVT ถูกกำหนดไว้ที่ 1024 (0x400) ไบต์)
ในโหมดป้องกัน (32 บิต) ซีพียูจะใช้ IDT IDT เป็นโครงสร้างความยาวตัวแปรที่ประกอบด้วยตัวบอก (หรือที่เรียกว่าประตู) ซึ่งบอก CPU เกี่ยวกับตัวจัดการขัดจังหวะ โครงสร้างของตัวบ่งชี้เหล่านี้มีความซับซ้อนมากกว่ารายการออฟเซ็ตแบบง่ายของ IVT นี่คือ:
bytes 0, 1: Lower 16 bits of the ISR's address.
bytes 2, 3: A code segment selector (in the GDT/LDT)
byte 4: Zero.
byte 5: A type field consisting of several bitfields.
bit 0: P (Present): 0 for unused interrupts, 1 for used interrupts.*
bits 1, 2: DPL (Descriptor Privilege Level): The privilege level the descriptor (bytes 2, 3) must have.
bit 3: S (Storage Segment): Is 0 for interrupt and trap gates. Otherwise, is one.
bits 4, 5, 6, 7: GateType:
0101: 32 bit task gate
0110: 16-bit interrupt gate
0111: 16-bit trap gate
1110: 32-bit interrupt gate
1111: 32-bit trap gate
* IDT อาจมีขนาดแปรผัน แต่ต้องเป็นลำดับเช่นถ้าคุณประกาศ IDT ของคุณเป็น 0x00 ถึง 0x50 คุณต้องมีการขัดจังหวะทุกครั้งตั้งแต่ 0x00 ถึง 0x50 ระบบปฏิบัติการไม่จำเป็นต้องใช้ทั้งหมดดังนั้นบิตปัจจุบันจึงช่วยให้ซีพียูจัดการกับการขัดจังหวะอย่างเหมาะสมที่ระบบปฏิบัติการไม่ต้องการจัดการ
เมื่อเกิดการขัดจังหวะ (ไม่ว่าจะโดยทริกเกอร์ภายนอก (เช่นอุปกรณ์ฮาร์ดแวร์) ใน IRQ หรือโดยint
คำสั่งจากโปรแกรม) CPU จะดัน EFLAGS จากนั้น CS และตามด้วย EIP (สิ่งเหล่านี้จะถูกเรียกคืนโดยอัตโนมัติโดยiret
คำสั่งขัดจังหวะการส่งคืน) ระบบปฏิบัติการมักจะจัดเก็บข้อมูลเพิ่มเติมเกี่ยวกับสถานะของเครื่องจัดการการขัดจังหวะคืนค่าสถานะเครื่องและดำเนินการต่อ
ในระบบปฏิบัติการ * NIX จำนวนมาก (รวมถึง Linux) การเรียกใช้ระบบจะถูกขัดจังหวะ โปรแกรมใส่อาร์กิวเมนต์ให้กับการเรียกระบบในรีจิสเตอร์ (EAX, EBX, ECX, EDX ฯลฯ .. ) และโทรขัดจังหวะ 0x80 เคอร์เนลได้ตั้งค่า IDT ให้มี interrupt handler บน 0x80 แล้วซึ่งเรียกเมื่อได้รับ interrupt 0x80 จากนั้นเคอร์เนลจะอ่านอาร์กิวเมนต์และเรียกใช้ฟังก์ชันเคอร์เนลตามนั้น อาจจัดเก็บผลตอบแทนใน EAX / EBX การเรียกระบบส่วนใหญ่ถูกแทนที่ด้วยคำแนะนำsysenter
และsysexit
(หรือsyscall
และsysret
บน AMD) ซึ่งช่วยให้เข้าสู่วงแหวน 0 ได้เร็วขึ้น
การขัดจังหวะนี้อาจมีความหมายที่แตกต่างกันในระบบปฏิบัติการอื่น อย่าลืมตรวจสอบเอกสารประกอบ
eax
ใช้สำหรับจำนวน syscall asm.sourceforge.net/intro/hello.html
ดังที่กล่าวไว้มันทำให้การควบคุมข้ามไปขัดจังหวะเวกเตอร์ 0x80 ในทางปฏิบัติสิ่งนี้หมายความว่า (อย่างน้อยใน Linux) คือการเรียกระบบถูกเรียกใช้ การเรียกระบบและอาร์กิวเมนต์ที่แน่นอนถูกกำหนดโดยเนื้อหาของรีจิสเตอร์ ตัวอย่างเช่นสามารถเรียก exit () ได้โดยตั้งค่า% eax เป็น 1 ตามด้วย "int 0x80"
มันบอกให้ซีพียูเปิดใช้งานอินเทอร์รัปต์เวกเตอร์ 0x80 ซึ่งบน Linux OSes คือการขัดจังหวะการโทรระบบที่ใช้ในการเรียกใช้ฟังก์ชันระบบเช่นopen()
ไฟล์และอื่น ๆ
int คืออะไรนอกจากการหยุดชะงักกล่าวคือโปรเซสเซอร์จะหยุดการดำเนินการปัจจุบันไว้
0x80 ไม่ใช่แค่การเรียกระบบหรือการเรียกเคอร์เนล กล่าวคือฟังก์ชันระบบจะถูกเรียกใช้งาน
ในการเป็น 0x80 ที่เฉพาะเจาะจงหมายถึง rt_sigtimedwait / init_module / restart_sys ซึ่งแตกต่างกันไปในแต่ละสถาปัตยกรรม
ดูรายละเอียดเพิ่มเติมได้ที่ https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md