Khó hiểu logic trong bom nhị phân giai đoạn 3


8

Tôi có chương trình lắp ráp sau đây từ phòng thí nghiệm bom nhị phân. Mục tiêu là xác định từ khóa cần thiết để chạy nhị phân mà không kích hoạt explode_bombchức năng. Tôi đã nhận xét phân tích của tôi về lắp ráp cho chương trình này nhưng tôi gặp khó khăn trong việc chắp nối mọi thứ lại với nhau.

Tôi tin rằng tôi có tất cả thông tin tôi cần, nhưng tôi vẫn không thể thấy logic cơ bản thực tế và do đó tôi bị mắc kẹt. Tôi sẽ đánh giá rất cao bất kỳ sự giúp đỡ!

Sau đây là chương trình tự tháo rời:

0x08048c3c <+0>:     push   %edi
   0x08048c3d <+1>:     push   %esi
   0x08048c3e <+2>:     sub    $0x14,%esp
   0x08048c41 <+5>:     movl   $0x804a388,(%esp)
   0x08048c48 <+12>:    call   0x80490ab <string_length>
   0x08048c4d <+17>:    add    $0x1,%eax
   0x08048c50 <+20>:    mov    %eax,(%esp)
   0x08048c53 <+23>:    call   0x8048800 <malloc@plt>
   0x08048c58 <+28>:    mov    $0x804a388,%esi
   0x08048c5d <+33>:    mov    $0x13,%ecx
   0x08048c62 <+38>:    mov    %eax,%edi
   0x08048c64 <+40>:    rep movsl %ds:(%esi),%es:(%edi)
   0x08048c66 <+42>:    movzwl (%esi),%edx
   0x08048c69 <+45>:    mov    %dx,(%edi)
   0x08048c6c <+48>:    movzbl 0x11(%eax),%edx
   0x08048c70 <+52>:    mov    %dl,0x10(%eax)
   0x08048c73 <+55>:    mov    %eax,0x4(%esp)
   0x08048c77 <+59>:    mov    0x20(%esp),%eax
   0x08048c7b <+63>:    mov    %eax,(%esp)
   0x08048c7e <+66>:    call   0x80490ca <strings_not_equal>
   0x08048c83 <+71>:    test   %eax,%eax
   0x08048c85 <+73>:    je     0x8048c8c <phase_3+80>
   0x08048c87 <+75>:    call   0x8049363 <explode_bomb>
   0x08048c8c <+80>:    add    $0x14,%esp
   0x08048c8f <+83>:    pop    %esi
   0x08048c90 <+84>:    pop    %edi
   0x08048c91 <+85>:    ret  

Khối sau chứa phân tích của tôi

  5 <phase_3>
  6 0x08048c3c <+0>:     push   %edi // push value in edi to stack
  7 0x08048c3d <+1>:     push   %esi // push value of esi to stack
  8 0x08048c3e <+2>:     sub    $0x14,%esp // grow stack by 0x14 (move stack ptr -0x14 bytes)
  9 
 10 0x08048c41 <+5>:     movl   $0x804a388,(%esp) // put 0x804a388 into loc esp points to
 11 
 12 0x08048c48 <+12>:    call   0x80490ab <string_length> // check string length, store in eax
 13 0x08048c4d <+17>:    add    $0x1,%eax // increment val in eax by 0x1 (str len + 1) 
 14 // at this point, eax = str_len + 1  = 77 + 1 = 78
 15 
 16 0x08048c50 <+20>:    mov    %eax,(%esp) // get val in eax and put in loc on stack
 17 //**** at this point, 0x804a388 should have a value of 78? ****
 18 
 19 0x08048c53 <+23>:    call   0x8048800 <malloc@plt> // malloc --> base ptr in eax
 20 
 21 0x08048c58 <+28>:    mov    $0x804a388,%esi // 0x804a388 in esi 
 22 0x08048c5d <+33>:    mov    $0x13,%ecx // put 0x13 in ecx (counter register)
 23 0x08048c62 <+38>:    mov    %eax,%edi // put val in eax into edi
 24 0x08048c64 <+40>:    rep movsl %ds:(%esi),%es:(%edi) // repeat 0x13 (19) times
 25 // **** populate malloced memory with first 19 (edit: 76) chars of string at 0x804a388 (this string is 77 characters long)? ****
 26 
 27 0x08048c66 <+42>:    movzwl (%esi),%edx // put val in loc esi points to into edx
***** // at this point, edx should contain the string at 0x804a388?
 28 
 29 0x08048c69 <+45>:    mov    %dx,(%edi) // put val in dx to loc edi points to
***** // not sure what effect this has or what is in edi at this point
 30 0x08048c6c <+48>:    movzbl 0x11(%eax),%edx // edx = [eax + 0x11]
 31 0x08048c70 <+52>:    mov    %dl,0x10(%eax) // [eax + 0x10] = dl
 32 0x08048c73 <+55>:    mov    %eax,0x4(%esp) // [esp + 0x4] = eax
 33 0x08048c77 <+59>:    mov    0x20(%esp),%eax // eax = [esp + 0x20]
 34 0x08048c7b <+63>:    mov    %eax,(%esp) // put val in eax into loc esp points to
***** // not sure what effect these movs have
 35 
 36 // edi --> first arg
 37 // esi --> second arg
 38 // compare value in esi to edi
 39 0x08048c7e <+66>:    call   0x80490ca <strings_not_equal> // store result in eax
 40 0x08048c83 <+71>:    test   %eax,%eax 
 41 0x08048c85 <+73>:    je     0x8048c8c <phase_3+80>
 42 0x08048c87 <+75>:    call   0x8049363 <explode_bomb>
 43 0x08048c8c <+80>:    add    $0x14,%esp
 44 0x08048c8f <+83>:    pop    %esi
 45 0x08048c90 <+84>:    pop    %edi
 46 0x08048c91 <+85>:    ret 

Cập nhật:

Khi kiểm tra các thanh ghi trước khi String_not_equal được gọi, tôi nhận được như sau:

eax            0x804d8aa        134535338
ecx            0x0      0
edx            0x76     118
ebx            0xffffd354       -11436
esp            0xffffd280       0xffffd280
ebp            0xffffd2b8       0xffffd2b8
esi            0x804a3d4        134521812
edi            0x804f744        134543172
eip            0x8048c7b        0x8048c7b <phase_3+63>
eflags         0x282    [ SF IF ]
cs             0x23     35
ss             0x2b     43
ds             0x2b     43
es             0x2b     43
fs             0x0      0
gs             0x63     99

và tôi nhận được mã giả đã tháo rời sau đây bằng Hopper:

nhập mô tả hình ảnh ở đây

Tôi thậm chí đã thử sử dụng cả số được tìm thấy trong eax và chuỗi được xem trước đó làm từ khóa của tôi nhưng cả hai đều không hoạt động.


3
Tôi đánh giá cao việc bạn đăng một bài bình luận tốt về việc tháo gỡ và ghi chú chi tiết về những gì bạn nghĩ rằng mã này làm. Nếu chỉ có nhiều người sẽ nỗ lực hơn khi đăng câu hỏi về phòng thí nghiệm bom ...
fuz

2
rep movslsao chép các từ khóa 32 bit từ địa chỉ này %esisang địa chỉ khác %edi, tăng cả 4 lần mỗi lần, số lần bằng ecx. Hãy nghĩ về nó như là memcpy(edi, esi, ecx*4). Xem felixcloutier.com/x86/movs:movsb:movsw:movsd:movsq (đó là movsdký hiệu Intel).
Nate Eldredge

2
Vì vậy, nó không phải là 19 byte mà là 19 * 4 = 76 byte.
Nate Eldredge

@NateEldredge ah Tôi thấy điều đó có ý nghĩa bởi vì chuỗi tại vị trí bộ nhớ 0x80490abcó độ dài 77. Và cảm ơn rất nhiều về liên kết!
scy8

@fuz cảm ơn bạn, thật không may, nó vẫn chưa giúp tôi ghép mọi thứ lại với nhau
scy8

Câu trả lời:


3

Hàm này tạo một bản sao đã sửa đổi của một chuỗi từ bộ lưu trữ tĩnh, vào một bộ đệm malloced.


Điều này có vẻ kỳ lạ. Các mallockích thước phụ thuộc vào strlen+1, nhưng memcpykích thước là một hằng số thời gian biên dịch? Việc dịch ngược của bạn rõ ràng cho thấy địa chỉ đó là một chuỗi ký tự nên có vẻ như điều đó tốt.

Có lẽ việc tối ưu hóa bị bỏ lỡ đã xảy ra do một string_length()chức năng tùy chỉnh có thể chỉ được xác định trong một mục khác .c(và quả bom đã được biên dịch mà không tối ưu hóa thời gian liên kết để đặt nội tuyến chéo). Vì vậy, size_t len = string_length("some string literal");không phải là hằng số thời gian biên dịch và trình biên dịch đã phát ra một cuộc gọi đến nó thay vì có thể sử dụng độ dài không đổi đã biết của chuỗi.

Nhưng có lẽ họ đã sử dụng strcpytrong nguồn và trình biên dịch đã thực hiện nội tuyến đó như là một rep movs. Vì rõ ràng là sao chép từ một chuỗi ký tự, độ dài là hằng số thời gian biên dịch và nó có thể tối ưu hóa một phần công việc strcpythường phải làm. Thông thường nếu bạn đã tính độ dài thì nên sử dụng memcpythay vì strcpytính toán lại một cách nhanh chóng, nhưng trong trường hợp này, nó thực sự giúp trình biên dịch tạo mã tốt hơn cho phần đó so với việc họ chuyển giá trị trả về string_lengthcho memcpy, một lần nữa bởi vì string_lengthkhông thể nội tuyến và tối ưu hóa đi.


   <+0>:     push   %edi // push value in edi to stack
   <+1>:     push   %esi // push value of esi to stack
   <+2>:     sub    $0x14,%esp // grow stack by 0x14 (move stack ptr -0x14 bytes)

Nhận xét như thế là dư thừa; các hướng dẫn đã nói rằng. Điều này đang lưu hai thanh ghi được bảo toàn cuộc gọi để chức năng có thể sử dụng chúng trong nội bộ và khôi phục chúng sau này.

Nhận xét của bạn về sublà tốt hơn; vâng, phát triển ngăn xếp là ý nghĩa ngữ nghĩa cấp cao hơn ở đây. Hàm này dành một số không gian cho người dân địa phương (và cho hàm args được lưu trữ movthay vì pushed).


Các rep movsdbản sao 0x13 * 4 byte, tăng ESI và EDI để chỉ qua phần cuối của khu vực được sao chép. Vì vậy, một movsdhướng dẫn khác sẽ sao chép 4 byte khác tiếp giáp với bản sao trước đó.

Mã thực sự sao chép 2 cái khác, nhưng thay vì sử dụng movsw, nó sử dụng movzwtải từ và movcửa hàng. Điều này làm cho tổng số 78 byte được sao chép.

  ...
      # at this point EAX = malloc return value which I'll call buf
<+28>:    mov    $0x804a388,%esi            # copy src = a string literal in .rodata?
<+33>:    mov    $0x13,%ecx
<+38>:    mov    %eax,%edi                  # copy dst = buf
<+40>:    rep movsl %ds:(%esi),%es:(%edi)   # memcpy 76 bytes and advance ESI, EDI

<+42>:    movzwl (%esi),%edx
<+45>:    mov    %dx,(%edi)        # copy another 2 bytes (not moving ESI or EDI)
 # final effect: 78-byte memcpy

Trên một số (nhưng không phải tất cả) CPU, sẽ rất hiệu quả nếu chỉ sử dụng rep movsbhoặc rep movswvới số lượng thích hợp, nhưng đó không phải là thứ mà trình biên dịch đã chọn trong trường hợp này. movzxaka AT & T movzlà một cách tốt để thực hiện tải hẹp mà không bị phạt đăng ký một phần. Đó là lý do tại sao trình biên dịch làm điều đó, vì vậy họ có thể viết một thanh ghi đầy đủ mặc dù họ sẽ chỉ đọc 8 hoặc 16 bit thấp của reg đó với một hướng dẫn lưu trữ.

Sau khi sao chép một chuỗi ký tự thành buf, chúng ta có một byte tải / lưu trữ sao chép một ký tự buf. Hãy nhớ tại thời điểm này EAX vẫn đang chỉ vào buf, mallocgiá trị trả về. Vì vậy, nó tạo ra một bản sao sửa đổi của chuỗi ký tự.

<+48>:    movzbl 0x11(%eax),%edx
<+52>:    mov    %dl,0x10(%eax)             # buf[16] = buf[17]

Có lẽ nếu nguồn không đánh bại sự lan truyền liên tục, với mức tối ưu hóa đủ cao, trình biên dịch có thể đã đặt chuỗi cuối cùng vào .rodatanơi bạn có thể tìm thấy nó, tầm thường hóa giai đoạn bom này. : P

Sau đó, nó lưu trữ các con trỏ như stack args để so sánh chuỗi.

<+55>:    mov    %eax,0x4(%esp)               # 2nd arg slot = EAX = buf
<+59>:    mov    0x20(%esp),%eax              #  function arg = user input?
<+63>:    mov    %eax,(%esp)                  # first arg slot = our incoming stack arg
<+66>:    call   0x80490ca <strings_not_equal>

Cách "gian lận": nhìn vào kết quả thời gian chạy với GDB

Một số phòng thí nghiệm bom chỉ cho phép bạn chạy bom trực tuyến, trên một máy chủ thử nghiệm, sẽ ghi lại vụ nổ. Bạn không thể chạy nó dưới GDB, chỉ sử dụng tháo gỡ tĩnh (như objdump -drwC -Mintel). Vì vậy, máy chủ thử nghiệm có thể ghi lại số lần bạn thất bại. ví dụ như CS 3330 tại cs.virginia.edu mà tôi đã tìm thấy với google, nơi tín dụng đầy đủ yêu cầu ít hơn 20 vụ nổ.

Việc sử dụng GDB để kiểm tra bộ nhớ / thanh ghi một phần thông qua một chức năng giúp việc này dễ dàng hơn rất nhiều so với chỉ làm việc từ phân tích tĩnh, trên thực tế tầm thường hóa chức năng này trong đó đầu vào duy nhất chỉ được kiểm tra ở cuối. ví dụ, chỉ cần nhìn vào những gì các đối số khác đang được thông qua strings_not_equal. (Đặc biệt nếu bạn sử dụng GDB's jumphoặc set $pc = ...các lệnh để bỏ qua kiểm tra vụ nổ bom.)

Đặt điểm dừng hoặc một bước duy nhất ngay trước cuộc gọi đến strings_not_equal. Sử dụng p (char*)$eaxđể coi EAX là a char*và hiển thị cho bạn chuỗi C (0 chấm dứt) bắt đầu từ địa chỉ đó. Tại thời điểm đó, EAX giữ địa chỉ của bộ đệm, như bạn có thể thấy từ cửa hàng đến ngăn xếp.

Sao chép / dán kết quả chuỗi đó và bạn đã hoàn tất.

Các giai đoạn khác có nhiều đầu vào số thường không dễ dàng với phô mai với trình gỡ lỗi và yêu cầu ít nhất một số phép toán, nhưng các giai đoạn trong danh sách liên kết yêu cầu bạn phải có một chuỗi các số theo đúng thứ tự để duyệt qua danh sách cũng trở nên tầm thường nếu bạn biết cách sử dụng trình gỡ lỗi để thiết lập các thanh ghi để so sánh thành công khi bạn truy cập chúng.


Cảm ơn bạn rất nhiều! Cuối cùng tôi cũng hiểu.
mlz7

Điều này cũng xóa tan sự nhầm lẫn của tôi! Cảm ơn bạn!
scy8

2

rep movslsao chép các từ khóa 32 bit từ địa chỉ này %esisang địa chỉ khác %edi, tăng cả 4 lần mỗi lần, số lần bằng %ecx. Hãy nghĩ về nó như là memcpy(edi, esi, ecx*4).

Xem https://felixcloutier.com/x86/movs:movsb:movsw:movsd:movsq (đó là Movsd trong ký hiệu Intel).

Vì vậy, đây là sao chép 19*4=76byte.


cảm ơn, tôi cũng có một số điều khác mà tôi hơi bối rối (được liệt kê ở phía dưới) nếu bạn có thể giúp tôi hiểu rõ hơn về chúng
scy8
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.