Lý do cho các địa chỉ "lạ" như main+0
, main+1
, main+3
, main+6
và như vậy, là bởi vì mỗi lệnh chiếm một số biến của byte. Ví dụ:
main+0: push %ebp
là hướng dẫn một byte để hướng dẫn tiếp theo là main+1
. Mặt khác,
main+3: and $0xfffffff0,%esp
là hướng dẫn ba byte để hướng dẫn tiếp theo sau đó là main+6
.
Và, vì bạn hỏi ý kiến tại sao movl
dường như nhận một số byte thay đổi, giải thích cho điều đó như sau.
dài Hướng dẫn không chỉ phụ thuộc vào opcode (như movl
) mà còn các phương thức giải quyết cho toán hạng cũng như (những điều opcode đang hoạt động trên). Tôi đã không kiểm tra đặc biệt cho mã của bạn nhưng tôi nghi ngờ sự hướng dẫn
movl $0x1,(%esp)
có lẽ là ngắn hơn vì không có bù đắp tham gia - nó chỉ sử dụng esp
như địa chỉ. Trong khi đó, một cái gì đó như:
movl $0x2,0x4(%esp)
đòi hỏi tất cả những gì movl $0x1,(%esp)
làm, cộng một byte thêm cho bù đắp 0x4
.
Trong thực tế, đây là một phiên debug hiển thị những gì tôi muốn nói:
Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.
c:\pax> debug
-a
0B52:0100 mov word ptr [di],7
0B52:0104 mov word ptr [di+2],8
0B52:0109 mov word ptr [di+0],7
0B52:010E
-u100,10d
0B52:0100 C7050700 MOV WORD PTR [DI],0007
0B52:0104 C745020800 MOV WORD PTR [DI+02],0008
0B52:0109 C745000700 MOV WORD PTR [DI+00],0007
-q
c:\pax> _
Bạn có thể thấy rằng chỉ lệnh thứ hai với một bù đắp thực sự là khác nhau để là người đầu tiên mà không có nó. Đó là một byte dài hơn (5 byte thay vì 4 byte, để giữ khoảng trống) và thực sự có mã hóa khác nhau c745
thay vì c705
.
Bạn cũng có thể thấy rằng bạn có thể mã hóa lệnh đầu tiên và thứ ba theo hai cách khác nhau nhưng về cơ bản chúng cũng thực hiện tương tự.
Các hướng dẫn and $0xfffffff0,%esp
là một cách để buộc esp
phải được trên một ranh giới cụ thể. Điều này được sử dụng để đảm bảo sự liên kết thích hợp của các biến. Nhiều bộ nhớ truy cập trên các bộ vi xử lý hiện đại sẽ hiệu quả hơn nếu chúng tuân thủ các quy tắc căn chỉnh (chẳng hạn như giá trị 4 byte phải được căn chỉnh với ranh giới 4 byte). Một số bộ vi xử lý hiện đại thậm chí sẽ gây ra lỗi nếu bạn không tuân thủ các quy tắc này.
Sau hướng dẫn này, bạn được đảm bảo rằng esp
nhỏ hơn hoặc bằng giá trị trước đó và được liên kết với ranh giới 16 byte.
Tiền tố gs:
chỉ có nghĩa là để sử dụng thanh ghi gs
phân đoạn để truy cập bộ nhớ hơn là mặc định.
Hướng dẫn mov %eax,-0xc(%ebp)
nghĩa là lấy nội dung của thanh ghi ebp
, trừ 12 (0xc
) và sau đó đặt giá trị eax
vào vị trí bộ nhớ đó.
Giải thích về mã. Hàm function
của bạn về cơ bản là một không lớn. Việc lắp ráp tạo ra được giới hạn để thiết lập khung stack và teardown, cùng với một số kiểm tra tham nhũng khung stack trong đó sử dụng các vị trí bộ nhớ %gs:14
được đề cập ở trên.
Nó tải giá trị từ vị trí đó (có thể giống như 0xdeadbeef
) vào khung ngăn xếp, thực hiện công việc của mình, sau đó kiểm tra ngăn xếp để đảm bảo nó không bị hỏng.
Công việc của nó, trong trường hợp này, là không có gì. Vì vậy, tất cả những gì bạn thấy là công cụ quản trị hàm.
Thiết lập ngăn xếp xảy ra giữa function+0
và function+12
. Tất cả mọi thứ sau đó là thiết lập mã trả về trong eax
và rách xuống khung ngăn xếp, bao gồm cả kiểm tra tham nhũng.
Tương tự, main
bao gồm thiết lập khung ngăn xếp, đẩy thông số cho function
, gọi function
, rách khung ngăn xếp và thoát.
Comments đã được chèn vào mã bên dưới:
0x08048428 <main+0>: push %ebp ; save previous value.
0x08048429 <main+1>: mov %esp,%ebp ; create new stack frame.
0x0804842b <main+3>: and $0xfffffff0,%esp ; align to boundary.
0x0804842e <main+6>: sub $0x10,%esp ; make space on stack.
0x08048431 <main+9>: movl $0x3,0x8(%esp) ; push values for function.
0x08048439 <main+17>: movl $0x2,0x4(%esp)
0x08048441 <main+25>: movl $0x1,(%esp)
0x08048448 <main+32>: call 0x8048404 <function> ; and call it.
0x0804844d <main+37>: leave ; tear down frame.
0x0804844e <main+38>: ret ; and exit.
0x08048404 <func+0>: push %ebp ; save previous value.
0x08048405 <func+1>: mov %esp,%ebp ; create new stack frame.
0x08048407 <func+3>: sub $0x28,%esp ; make space on stack.
0x0804840a <func+6>: mov %gs:0x14,%eax ; get sentinel value.
0x08048410 <func+12>: mov %eax,-0xc(%ebp) ; put on stack.
0x08048413 <func+15>: xor %eax,%eax ; set return code 0.
0x08048415 <func+17>: mov -0xc(%ebp),%eax ; get sentinel from stack.
0x08048418 <func+20>: xor %gs:0x14,%eax ; compare with actual.
0x0804841f <func+27>: je <func+34> ; jump if okay.
0x08048421 <func+29>: call <_stk_chk_fl> ; otherwise corrupted stack.
0x08048426 <func+34>: leave ; tear down frame.
0x08048427 <func+35>: ret ; and exit.
Tôi nghĩ lý do cho %gs:0x14
có thể thấy rõ từ trên cao, nhưng chỉ trong trường hợp, tôi sẽ xây dựng ở đây.
Nó sử dụng giá trị này (một sentinel) để đặt trong khung ngăn xếp hiện tại để, một cái gì đó trong hàm làm điều gì đó ngớ ngẩn như viết 1024 byte vào mảng 20 byte được tạo trên ngăn xếp hoặc trong trường hợp của bạn:
char buffer1[5];
strcpy (buffer1, "Hello there, my name is Pax.");
thì trọng điểm sẽ được ghi đè và kiểm tra vào cuối của hàm sẽ phát hiện rằng, gọi hàm thất bại để cho bạn biết, và sau đó có thể hủy để tránh bất kỳ vấn đề khác.
Nếu nó đặt 0xdeadbeef
vào stack và điều này đã được đổi thành cái gì khác, sau đó một xor
với 0xdeadbeef
sẽ tạo ra một giá trị khác không được phát hiện trong các mã với hướng dẫn je
.
Các bit có liên quan được diễn giải ở đây:
mov %gs:0x14,%eax ; get sentinel value.
mov %eax,-0xc(%ebp) ; put on stack.
;; Weave your function
;; magic here.
mov -0xc(%ebp),%eax ; get sentinel back from stack.
xor %gs:0x14,%eax ; compare with original value.
je stack_ok ; zero/equal means no corruption.
call stack_bad ; otherwise corrupted stack.
stack_ok: leave ; tear down frame.
Bạn quên thẻ bài tập về nhà. –
Đối với mỗi điểm trong số này, bạn có thể giải thích câu trả lời của bạn là gì, và sau đó nếu bạn tắt nó có thể được làm rõ. –
không có điều này là không có bài tập về nhà .. – Adi