ช่องโหว่ JPEG of Death ทำงานอย่างไร?


94

ฉันได้อ่านเกี่ยวกับการใช้ประโยชน์จาก GDI + รุ่นเก่าบน Windows XP และWindows Server 2003 ที่เรียกว่า JPEG of death สำหรับโครงการที่ฉันกำลังทำอยู่

การใช้ประโยชน์สามารถอธิบายได้ดีในลิงค์ต่อไปนี้: http://www.infosecwriters.com/text_resources/pdf/JPEG.pdf

โดยทั่วไปไฟล์ JPEG จะมีส่วนที่เรียกว่า COM ที่มีฟิลด์ความคิดเห็น (อาจว่างเปล่า) และค่าสองไบต์ที่มีขนาดของ COM หากไม่มีความคิดเห็นขนาดคือ 2 ตัวอ่าน (GDI +) จะอ่านขนาดลบสองและจัดสรรบัฟเฟอร์ที่มีขนาดเหมาะสมเพื่อคัดลอกความคิดเห็นในฮีป การโจมตีเกี่ยวข้องกับการวางมูลค่า0ในสนาม GDI + หัก2นำไปสู่ค่า-2 (0xFFFe)ที่ได้รับการแปลงเป็นจำนวนเต็มไม่ได้ลงนามโดย0XFFFFFFFEmemcpy

โค้ดตัวอย่าง:

unsigned int size;
size = len - 2;
char *comment = (char *)malloc(size + 1);
memcpy(comment, src, size);

สังเกตว่าmalloc(0)ในบรรทัดที่สามควรส่งกลับตัวชี้ไปยังหน่วยความจำที่ไม่ได้ปันส่วนบนฮีป การเขียน0XFFFFFFFEไบต์ ( 4GB!!!!) จะไม่ทำให้โปรแกรมผิดพลาดได้อย่างไร สิ่งนี้เขียนเกินพื้นที่ฮีปและเข้าไปในพื้นที่ของโปรแกรมอื่น ๆ และระบบปฏิบัติการหรือไม่ แล้วจะเกิดอะไรขึ้น?

อย่างที่ฉันเข้าใจmemcpyมันก็แค่คัดลอกnตัวอักษรจากปลายทางไปยังต้นทาง ในกรณีนี้แหล่งที่ควรจะอยู่ในกองปลายทางในกองและเป็นn4GB


malloc จะจัดสรรหน่วยความจำจากฮีป ฉันคิดว่าการใช้ประโยชน์นั้นเสร็จสิ้นก่อน memcpy และหลังจากที่หน่วยความจำถูกจัดสรร
iedoc

เช่นเดียวกับหมายเหตุด้านข้าง: ไม่ใช่ memcpy สิ่งที่ส่งเสริมค่าให้เป็นจำนวนเต็มที่ไม่ได้ลงชื่อ (4 ไบต์) แต่เป็นการลบ
รอบ

1
อัปเดตคำตอบก่อนหน้าของฉันด้วยตัวอย่างสด mallocขนาดเอ็ดเพียง 2 0xFFFFFFFEไบต์มากกว่า ขนาดมหึมานี้ใช้สำหรับขนาดสำเนาเท่านั้นไม่ใช่สำหรับขนาดการจัดสรร
Neitsa

คำตอบ:


96

ช่องโหว่นี้เป็นช่องโหว่ที่ล้นออกมาอย่างแน่นอน

การเขียน 0XFFFFFFFE ไบต์ (4 GB !!!!) จะไม่ทำให้โปรแกรมผิดพลาดได้อย่างไร

อาจเป็นไปได้ แต่ในบางครั้งคุณมีเวลาใช้ประโยชน์ก่อนที่ความผิดพลาดจะเกิดขึ้น (บางครั้งคุณสามารถทำให้โปรแกรมกลับสู่การทำงานตามปกติและหลีกเลี่ยงความผิดพลาดได้)

เมื่อ memcpy () เริ่มทำงานสำเนาจะเขียนทับบล็อคฮีปอื่น ๆ หรือบางส่วนของโครงสร้างการจัดการฮีป (เช่นรายการว่างรายการไม่ว่าง ฯลฯ )

ในบางจุดสำเนาจะพบหน้าที่ไม่ได้รับการจัดสรรและทริกเกอร์ AV (Access Violation) เมื่อเขียน GDI + จะพยายามจัดสรรบล็อกใหม่ในฮีป (ดูntdll! rtlAllocateHeap ) ... แต่ตอนนี้โครงสร้างฮีปสับสน

เมื่อถึงจุดนั้นการสร้างภาพ JPEG ของคุณอย่างระมัดระวังคุณสามารถเขียนทับโครงสร้างการจัดการฮีปด้วยข้อมูลที่ควบคุมได้ เมื่อระบบพยายามจัดสรรบล็อกใหม่ระบบอาจยกเลิกการลิงก์บล็อก (ฟรี) จากรายการฟรี

บล็อกได้รับการจัดการโดย (โดยเฉพาะ) การกระพริบ (ลิงก์ไปข้างหน้าบล็อกถัดไปในรายการ) และกะพริบ (ลิงก์ย้อนกลับบล็อกก่อนหน้าในรายการ) ตัวชี้ หากคุณควบคุมทั้งการกระพริบและการกะพริบคุณอาจมี WRITE4 ที่เป็นไปได้ (เขียนเงื่อนไขอะไร / ที่ไหน) ซึ่งคุณควบคุมสิ่งที่คุณสามารถเขียนและตำแหน่งที่คุณสามารถเขียนได้

เมื่อถึงจุดนั้นคุณสามารถเขียนทับตัวชี้ฟังก์ชัน (ตัวชี้SEH [Structured Exception Handlers]เป็นเป้าหมายของตัวเลือกในเวลานั้นย้อนกลับไปในปี 2004) และได้รับการเรียกใช้โค้ด

ดูโพสต์บล็อกกองปราบปรามการทุจริต: กรณีศึกษา

หมายเหตุ: แม้ว่าฉันจะเขียนเกี่ยวกับการหาประโยชน์โดยใช้ freelist แต่ผู้โจมตีอาจเลือกเส้นทางอื่นโดยใช้ข้อมูลเมตาของฮีปอื่น ("ข้อมูลเมตาของฮีป" เป็นโครงสร้างที่ระบบใช้เพื่อจัดการฮีปการกระพริบตาและการกะพริบเป็นส่วนหนึ่งของข้อมูลเมตาของฮีป) แต่ การหาประโยชน์จากการยกเลิกการลิงก์น่าจะเป็นวิธีที่ "ง่ายที่สุด" การค้นหาโดย Google สำหรับ "การหาประโยชน์จากฮีป" จะแสดงผลการศึกษามากมายเกี่ยวกับเรื่องนี้

สิ่งนี้เขียนเกินพื้นที่ฮีปและเข้าไปในพื้นที่ของโปรแกรมอื่น ๆ และระบบปฏิบัติการหรือไม่

ไม่เลย Modern OS ขึ้นอยู่กับแนวคิดของพื้นที่แอดเดรสเสมือนดังนั้นแต่ละกระบวนการจึงมีพื้นที่แอดเดรสเสมือนของตัวเองซึ่งช่วยให้สามารถระบุหน่วยความจำได้ถึง 4 กิกะไบต์บนระบบ 32 บิต (ในทางปฏิบัติคุณมีเพียงครึ่งหนึ่งของพื้นที่ที่อยู่ผู้ใช้ ส่วนที่เหลือเป็นของเคอร์เนล)

กล่าวโดยย่อกระบวนการไม่สามารถเข้าถึงหน่วยความจำของกระบวนการอื่นได้ (ยกเว้นว่าจะขอให้เคอร์เนลผ่านบริการ / API บางอย่าง แต่เคอร์เนลจะตรวจสอบว่าผู้เรียกมีสิทธิ์ที่จะทำเช่นนั้นหรือไม่)


ฉันตัดสินใจที่จะทดสอบช่องโหว่นี้ในช่วงปลายสัปดาห์นี้เพื่อให้เราได้รับความคิดที่ดีเกี่ยวกับสิ่งที่เกิดขึ้นแทนที่จะเป็นการคาดเดาเพียงอย่างเดียว ตอนนี้ช่องโหว่ดังกล่าวมีอายุ 10 ปีแล้วดังนั้นฉันจึงคิดว่ามันโอเคที่จะเขียนเกี่ยวกับเรื่องนี้แม้ว่าฉันจะไม่ได้อธิบายถึงส่วนการหาประโยชน์ในคำตอบนี้

การวางแผน

งานที่ยากที่สุดคือการค้นหา Windows XP ที่มีเฉพาะ SP1 เหมือนในปี 2004 :)

จากนั้นฉันดาวน์โหลดภาพ JPEG ที่ประกอบด้วยพิกเซลเดียวเท่านั้นดังที่แสดงด้านล่าง (ตัดเพื่อความกะทัดรัด):

File 1x1_pixel.JPG
Address   Hex dump                                         ASCII
00000000  FF D8 FF E0|00 10 4A 46|49 46 00 01|01 01 00 60| ÿØÿà JFIF  `
00000010  00 60 00 00|FF E1 00 16|45 78 69 66|00 00 49 49|  `  ÿá Exif  II
00000020  2A 00 08 00|00 00 00 00|00 00 00 00|FF DB 00 43| *          ÿÛ C
[...]

ภาพ JPEG ประกอบด้วยเครื่องหมายไบนารี (ซึ่งเป็นส่วนที่อยู่ภายใน) ในภาพด้านบนFF D8คือเครื่องหมาย SOI (Start Of Image) ในขณะที่FF E0ตัวอย่างเช่นเป็นเครื่องหมายแอปพลิเคชัน

พารามิเตอร์แรกในส่วนของเครื่องหมาย (ยกเว้นเครื่องหมายบางตัวเช่น SOI) เป็นพารามิเตอร์ความยาวสองไบต์ซึ่งเข้ารหัสจำนวนไบต์ในส่วนของเครื่องหมายรวมทั้งพารามิเตอร์ความยาวและไม่รวมเครื่องหมายสองไบต์

ฉันเพิ่งเพิ่มเครื่องหมาย COM (0x FFFE) หลัง SOI เนื่องจากเครื่องหมายไม่มีคำสั่งที่เข้มงวด

File 1x1_pixel_comment_mod1.JPG
Address   Hex dump                                         ASCII
00000000  FF D8 FF FE|00 00 30 30|30 30 30 30|30 31 30 30| ÿØÿþ  0000000100
00000010  30 32 30 30|30 33 30 30|30 34 30 30|30 35 30 30| 0200030004000500
00000020  30 36 30 30|30 37 30 30|30 38 30 30|30 39 30 30| 0600070008000900
00000030  30 61 30 30|30 62 30 30|30 63 30 30|30 64 30 30| 0a000b000c000d00
[...]

ความยาวของส่วน COM ถูกตั้งค่า00 00เพื่อทริกเกอร์ช่องโหว่ ฉันยังฉีด 0xFFFC ไบต์หลังเครื่องหมาย COM ด้วยรูปแบบที่เกิดซ้ำซึ่งเป็นตัวเลข 4 ไบต์ในฐานสิบหกซึ่งจะเป็นประโยชน์เมื่อ "ใช้ประโยชน์" ช่องโหว่

การแก้จุดบกพร่อง

ดับเบิลคลิกที่ภาพทันทีจะก่อให้เกิดข้อผิดพลาดในเชลล์ของ Windows (aka "explorer.exe") ในบางส่วนในฟังก์ชั่นที่มีชื่อว่าgdiplus.dllGpJpegDecoder::read_jpeg_marker()

ฟังก์ชันนี้เรียกใช้สำหรับเครื่องหมายแต่ละตัวในรูปภาพเพียงแค่: อ่านขนาดเซ็กเมนต์ของเครื่องหมายจัดสรรบัฟเฟอร์ที่มีความยาวเป็นขนาดเซ็กเมนต์และคัดลอกเนื้อหาของส่วนไปยังบัฟเฟอร์ที่จัดสรรใหม่นี้

นี่คือจุดเริ่มต้นของฟังก์ชัน:

.text:70E199D5  mov     ebx, [ebp+arg_0] ; ebx = *this (GpJpegDecoder instance)
.text:70E199D8  push    esi
.text:70E199D9  mov     esi, [ebx+18h]
.text:70E199DC  mov     eax, [esi]      ; eax = pointer to segment size
.text:70E199DE  push    edi
.text:70E199DF  mov     edi, [esi+4]    ; edi = bytes left to process in the image

eaxรีจิสเตอร์ชี้ไปที่ขนาดเซ็กเมนต์และediเป็นจำนวนไบต์ที่เหลือในรูปภาพ

จากนั้นโค้ดจะอ่านขนาดเซ็กเมนต์โดยเริ่มจากไบต์ที่สำคัญที่สุด (ความยาวคือค่า 16 บิต):

.text:70E199F7  xor     ecx, ecx        ; segment_size = 0
.text:70E199F9  mov     ch, [eax]       ; get most significant byte from size --> CH == 00
.text:70E199FB  dec     edi             ; bytes_to_process --
.text:70E199FC  inc     eax             ; pointer++
.text:70E199FD  test    edi, edi
.text:70E199FF  mov     [ebp+arg_0], ecx ; save segment_size

และไบต์ที่มีนัยสำคัญน้อยที่สุด:

.text:70E19A15  movzx   cx, byte ptr [eax] ; get least significant byte from size --> CX == 0
.text:70E19A19  add     [ebp+arg_0], ecx   ; save segment_size
.text:70E19A1C  mov     ecx, [ebp+lpMem]
.text:70E19A1F  inc     eax             ; pointer ++
.text:70E19A20  mov     [esi], eax
.text:70E19A22  mov     eax, [ebp+arg_0] ; eax = segment_size

เมื่อเสร็จแล้วขนาดเซ็กเมนต์จะถูกใช้เพื่อจัดสรรบัฟเฟอร์ตามการคำนวณนี้:

จัดสรรขนาด = segment_size + 2

ทำได้โดยรหัสด้านล่าง:

.text:70E19A29  movzx   esi, word ptr [ebp+arg_0] ; esi = segment size (cast from 16-bit to 32-bit)
.text:70E19A2D  add     eax, 2 
.text:70E19A30  mov     [ecx], ax 
.text:70E19A33  lea     eax, [esi+2] ; alloc_size = segment_size + 2
.text:70E19A36  push    eax             ; dwBytes
.text:70E19A37  call    _GpMalloc@4     ; GpMalloc(x)

ในกรณีของเราเป็นขนาดของกลุ่มเป็น 0 ขนาดจัดสรรสำหรับบัฟเฟอร์คือ 2 ไบต์

ช่องโหว่เกิดขึ้นทันทีหลังจากการจัดสรร:

.text:70E19A37  call    _GpMalloc@4     ; GpMalloc(x)
.text:70E19A3C  test    eax, eax
.text:70E19A3E  mov     [ebp+lpMem], eax ; save pointer to allocation
.text:70E19A41  jz      loc_70E19AF1
.text:70E19A47  mov     cx, [ebp+arg_4]   ; low marker byte (0xFE)
.text:70E19A4B  mov     [eax], cx         ; save in alloc (offset 0)
;[...]
.text:70E19A52  lea     edx, [esi-2]      ; edx = segment_size - 2 = 0 - 2 = 0xFFFFFFFE!!!
;[...]
.text:70E19A61  mov     [ebp+arg_0], edx

โค้ดเพียงแค่ลบขนาด segment_size (ความยาวเซ็กเมนต์คือค่า 2 ไบต์) จากขนาดเซ็กเมนต์ทั้งหมด (0 ในกรณีของเรา) และลงท้ายด้วยจำนวนเต็มน้อย: 0 - 2 = 0xFFFFFFFE

จากนั้นโค้ดจะตรวจสอบว่ามีไบต์เหลือให้แยกวิเคราะห์ในรูปภาพ (ซึ่งเป็นจริง) จากนั้นข้ามไปที่สำเนา:

.text:70E19A69  mov     ecx, [eax+4]  ; ecx = bytes left to parse (0x133)
.text:70E19A6C  cmp     ecx, edx      ; edx = 0xFFFFFFFE
.text:70E19A6E  jg      short loc_70E19AB4 ; take jump to copy
;[...]
.text:70E19AB4  mov     eax, [ebx+18h]
.text:70E19AB7  mov     esi, [eax]      ; esi = source = points to segment content ("0000000100020003...")
.text:70E19AB9  mov     edi, dword ptr [ebp+arg_4] ; edi = destination buffer
.text:70E19ABC  mov     ecx, edx        ; ecx = copy size = segment content size = 0xFFFFFFFE
.text:70E19ABE  mov     eax, ecx
.text:70E19AC0  shr     ecx, 2          ; size / 4
.text:70E19AC3  rep movsd               ; copy segment content by 32-bit chunks

ข้อมูลโค้ดด้านบนแสดงให้เห็นว่าขนาดของสำเนาคือ 0xFFFFFFFE ชิ้น 32 บิต บัฟเฟอร์ต้นทางถูกควบคุม (เนื้อหาของรูปภาพ) และปลายทางคือบัฟเฟอร์บนฮีป

เขียนเงื่อนไข

สำเนาจะทริกเกอร์ข้อยกเว้นการละเมิดการเข้าถึง (AV) เมื่อมาถึงส่วนท้ายของหน้าหน่วยความจำ (อาจมาจากตัวชี้ต้นทางหรือตัวชี้ปลายทาง) เมื่อ AV ถูกทริกเกอร์ฮีปจะอยู่ในสถานะที่มีช่องโหว่แล้วเนื่องจากสำเนาได้เขียนทับบล็อกฮีพที่ตามมาทั้งหมดแล้วจนกว่าจะพบเพจที่ไม่ได้แมป

สิ่งที่ทำให้สามารถใช้ประโยชน์จากข้อผิดพลาดนี้ได้ก็คือ 3 SEH (Structured Exception Handler; นี่คือการลอง / ยกเว้นในระดับต่ำ) กำลังจับข้อยกเว้นในส่วนนี้ของโค้ด อย่างแม่นยำยิ่งขึ้น SEH แรกจะคลายสแต็กเพื่อให้มันกลับมาเพื่อแยกวิเคราะห์เครื่องหมาย JPEG อื่นดังนั้นจึงข้ามเครื่องหมายที่ทำให้เกิดข้อยกเว้นโดยสิ้นเชิง

หากไม่มี SEH รหัสจะทำให้ทั้งโปรแกรมล้มเหลว ดังนั้นโค้ดจะข้ามเซ็กเมนต์ COM และแยกวิเคราะห์ส่วนอื่น ดังนั้นเราจึงกลับไปGpJpegDecoder::read_jpeg_marker()ที่ส่วนใหม่และเมื่อรหัสจัดสรรบัฟเฟอร์ใหม่:

.text:70E19A33  lea     eax, [esi+2] ; alloc_size = semgent_size + 2
.text:70E19A36  push    eax             ; dwBytes
.text:70E19A37  call    _GpMalloc@4     ; GpMalloc(x)

ระบบจะยกเลิกการเชื่อมโยงบล็อกจากรายการฟรี มันเกิดขึ้นที่โครงสร้างข้อมูลเมตาถูกเขียนทับโดยเนื้อหาของรูปภาพ ดังนั้นเราจึงควบคุมการยกเลิกการเชื่อมโยงด้วยข้อมูลเมตาที่ควบคุมได้ รหัสด้านล่างในที่ใดที่หนึ่งในระบบ (ntdll) ในตัวจัดการฮีป:

CPU Disasm
Address   Command                                  Comments
77F52CBF  MOV ECX,DWORD PTR DS:[EAX]               ; eax points to '0003' ; ecx = 0x33303030
77F52CC1  MOV DWORD PTR SS:[EBP-0B0],ECX           ; save ecx
77F52CC7  MOV EAX,DWORD PTR DS:[EAX+4]             ; [eax+4] points to '0004' ; eax = 0x34303030
77F52CCA  MOV DWORD PTR SS:[EBP-0B4],EAX
77F52CD0  MOV DWORD PTR DS:[EAX],ECX               ; write 0x33303030 to 0x34303030!!!

ตอนนี้เราสามารถเขียนสิ่งที่เราต้องการที่เราต้องการ ...


3

เนื่องจากฉันไม่ทราบรหัสจาก GDI สิ่งที่อยู่ด้านล่างเป็นเพียงการคาดเดา

สิ่งหนึ่งที่นึกถึงคือพฤติกรรมหนึ่งที่ฉันสังเกตเห็นในบางระบบปฏิบัติการ (ฉันไม่รู้ว่า Windows XP มีสิ่งนี้หรือไม่) คือเมื่อจัดสรรด้วย / ใหม่mallocคุณสามารถจัดสรรได้มากกว่า RAM ของคุณตราบใดที่ คุณไม่ได้เขียนถึงความทรงจำนั้น

นี่เป็นพฤติกรรมของเคอร์เนลลินุกซ์

จาก www.kernel.org:

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

ในการเข้าสู่หน่วยความจำที่มีถิ่นที่อยู่จะต้องทริกเกอร์ข้อบกพร่องของเพจ

โดยทั่วไปคุณต้องทำให้หน่วยความจำสกปรกก่อนที่จะได้รับการจัดสรรบนระบบจริง:

  unsigned int size=-1;
  char* comment = new char[size];

บางครั้งมันจะไม่ทำการจัดสรรจริงใน RAM (โปรแกรมของคุณจะยังไม่ใช้ 4 GB) ฉันรู้ว่าฉันเคยเห็นพฤติกรรมนี้บน Linux แต่ฉันไม่สามารถทำซ้ำได้ในขณะนี้ในการติดตั้ง Windows 7 ของฉัน

เริ่มต้นจากพฤติกรรมนี้สถานการณ์ต่อไปนี้เป็นไปได้

ในการทำให้หน่วยความจำนั้นมีอยู่ใน RAM คุณต้องทำให้มันสกปรก (โดยทั่วไปคือ memset หรือบางส่วนเขียนไป):

  memset(comment, 0, size);

อย่างไรก็ตามช่องโหว่นี้ใช้ประโยชน์จากบัฟเฟอร์ล้นไม่ใช่ความล้มเหลวในการจัดสรร

กล่าวอีกนัยหนึ่งถ้าฉันต้องมีสิ่งนี้:

 unsinged int size =- 1;
 char* p = new char[size]; // Will not crash here
 memcpy(p, some_buffer, size);

สิ่งนี้จะนำไปสู่การเขียนหลังจากบัฟเฟอร์เนื่องจากไม่มีสิ่งที่เรียกว่าหน่วยความจำต่อเนื่อง 4 GB

คุณไม่ได้ใส่อะไรลงใน p เพื่อทำให้หน่วยความจำทั้ง 4 GB สกปรกและฉันไม่รู้ว่าmemcpyทำให้หน่วยความจำสกปรกทั้งหมดในคราวเดียวหรือแค่ทีละหน้า (ฉันคิดว่ามันเป็นทีละหน้า)

ในที่สุดมันจะจบลงด้วยการเขียนทับสแต็กเฟรม (Stack Buffer Overflow)

ช่องโหว่ที่เป็นไปได้อีกประการหนึ่งคือหากรูปภาพถูกเก็บไว้ในหน่วยความจำเป็นอาร์เรย์ไบต์ (อ่านทั้งไฟล์ในบัฟเฟอร์) และขนาดของความคิดเห็นถูกใช้เพื่อข้ามข้อมูลที่ไม่สำคัญไปข้างหน้า

ตัวอย่างเช่น

     unsigned int commentsSize = -1;
     char* wholePictureBytes; // Has size of file
     ...
     // Time to start processing the output color
     char* p = wholePictureButes;
     offset = (short) p[COM_OFFSET];
     char* dataP = p + offset;
     dataP[0] = EvilHackerValue; // Vulnerability here

ดังที่คุณได้กล่าวไว้หาก GDI ไม่ได้จัดสรรขนาดนั้นโปรแกรมจะไม่มีวันผิดพลาด


4
นั่นอาจเป็นระบบ 64 บิตโดยที่ 4GB ไม่ใช่เรื่องใหญ่ (พูดถึงพื้นที่แอดเดส) แต่ในระบบ 32 บิต (ดูเหมือนจะมีช่องโหว่เช่นกัน) คุณไม่สามารถจองพื้นที่แอดเดรส 4GB ได้เพราะนั่นจะมีทั้งหมด! ดังนั้นmalloc(-1U)จะล้มเหลวแน่นอนกลับมาNULLและmemcpy()จะพัง
rodrigo

9
ฉันไม่คิดว่าบรรทัดนี้เป็นจริง: "ในที่สุดมันก็จะเขียนลงในที่อยู่ของกระบวนการอื่น" โดยปกติกระบวนการหนึ่งไม่สามารถเข้าถึงหน่วยความจำของอีกกระบวนการหนึ่งได้ ดูประโยชน์ MMU
chue x

@MMU ประโยชน์ใช่คุณพูดถูก ฉันตั้งใจจะบอกว่าจะข้ามขอบเขตฮีปปกติและเริ่มเขียนทับสแต็กเฟรม ฉันจะแก้ไขคำตอบของฉันขอบคุณที่ชี้ให้เห็น
MichaelCMS
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.