REVERSING WITH IDA FROM SCRATCH (P11)

Posted: April 18, 2019 in IDA Tutorials, REVERSING WITH IDA FROM SCRATCH (P11)
Tags: ,

Tôi không muốn nhồi nhét quá nhiều lý thuyết ngay từ đầu, vì vậy tôi đã lồng ghép và xen kẽ một số bài thực hành để các bạn không có cảm giác nhàm chán. Tuy nhiên, không vì thế mà chúng ta bỏ qua các kiến thức cơ bản, do đó trước khi tiếp tục các phần nâng cao hơn, phần này tôi sẽ cùng với các bạn xem xét một số cờ quan trọng trong ASM.

FLAGS

CARRY FLAG (CF)

Chúng ta đã tìm hiểu hoạt động của cờ CF (cờ nhớ) trong phần trước. Cờ này được kích hoạt trong quá trình tính toán của các số unsigned. Khi kết quả là số âm như ta đã gặp ở bài trước hoặc vượt quá mức biểu diễn tối đa trong trường hợp phép cộng. Hay nói cách khác, cờ CF được thiết lập là 1 khi có nhớ từ bit msb trong phép cộng hoặc có vay vào bit msb trong phép trừ.

Hãy quan sát các ví dụ sau thông qua trình debugger. Thực thi crackme.exe trong IDA thông qua debugger, ta dừng lại tại Entry Point của crackme (như đã cấu hình ở phần trước). Tại đây ta tiến hành patch lệnh như sau:

Khi patch như trên, chế độ graph của IDA sẽ bị lỗi, IDA tự động chuyển đổi về chế độ text. Để chuyển lại về chế độ graph, ta nhấn chuột phải tại địa chỉ entry point và chọn Create Function, sau đó nhấn phím space bar để chuyển lại về chế độ graph ban đầu. Tiếp theo, đặt lại giá trị cho thanh ghi EAX = 0xffffffff bằng cách nhấn chuột phải tại thanh ghi này và chọn Modify Value (E):

Sau khi thay đổi xong, nhấn F8 trace qua lệnh đã patch để thực hiện lệnh add này. Quan sát ta sẽ thấy cờ CF được kích hoạt do phép cộng bị tràn vượt quá số dương lớn nhất:

Điều tương tự xảy ra nếu chúng ta thực hiện patch lệnh bên dưới thành SUB EAX, EDX:

Sau đó, thay giá trị của EAX thành 0x25EDX thành 0x40. Nhấn F8 để thực hiện lệnh và quan sát xem liệu CF có được kích hoạt hay không? Chúng ta đã thấy rằng, nếu trừ hai số dương cho nhau và kết quả là số âm (Signed = -27 ~ 0xFFFFFFE5), cờ CF sẽ được bật lên:

Như vậy là cờ CF đã được kích hoạt. Nếu tôi tiếp tục cho thực hiện phép trừ này một lần nữa bằng cách nhấn chuột phải tại lệnh SUB và chọn Set IP, nhưng lần này tôi thay giá trị của thanh ghi EAX thành 0x100, giá trị của EDX vẫn giữ nguyên.

Tiếp theo F8 để trace qua lệnh:

Ta thấy cờ CF không được kích hoạt.

Như vậy, các quy tắc để bật cờ CF (carry) trong phép toán nhị phân/số nguyên là:

  • Cờ carry được bật nếu phép cộng hai số dẫn đến bit có trọng số lớn nhất bị đẩy ra ngoài – vượt quá khả năng biểu diễn. Ví dụ: 1111 + 0001 = 0000 (CF được bật)
  • Cờ carry được bật nếu phép trừ hai số dẫn tới việc cần phải vay vào bit có trọng số lớn nhất để trừ. Ví dụ: 0000 – 0001 = 1111 (CF được bật)

Trường hợp ngược lại, cờ CF không được bật (bằng 0):

  • 0111 + 0001 = 1000 (carry flag thiết lập bằng 0)
  • 1000 – 0001 = 0111 (carry flag thiết lập bằng 0)

OVERFLOW FLAG (OF)

Cờ OF (cờ tràn) cũng tương tự như cờ CF, nhưng đối với các tính toán liên quan đến số có dấu (signed). Cờ OF được thiết lập 1 khi xảy ra tràn, ngược lại nó bằng 0. Hiện tượng tràn gắn liền với một sự thật là phạm vi của các số biểu diễn trong máy tính có giới hạn. Ví dụ, phạm vi của các số thập phân có dấu có thể biểu diễn bằng một word 16 bit là từ -32768 đến 32767, với một byte 8 bit thì phạm vi là từ -128 đến 127. Đối với các số không dấu thì phạm vi từ 0 tới 65535 cho một word và từ 0 đến 255 cho một byte. Nếu kết quả của một phép tính nằm ngoài phạm vi thì hiện tượng tràn sẽ xảy ra và kết quả nhận được bị cắt bớt sẽ không phải là kết quả đúng. Xem xét một vài ví dụ dưới đây.

Giờ ta thay đổi EIP về lại câu lệnh ADD EAX, 1 và đặt lại giá trị của thanh ghi EAX thành 0x7fffffff (signed: 2147483647):

Sau đó nhấn F8 để thực hiện lệnh:

Chúng ta thấy rằng cờ OF đã được kích hoạt sau khi thực hiện lệnh. Đó là vì khi cộng 1 vào số dương 0x7fffffff, nếu xem xét đây là thao tác tính toán với số có dấu (signed) thì sẽ khiến kết quả sau khi thực hiện là số âm nhỏ nhất (signed: -2147483648) và dẫn đến kết quả sai:

Nếu thực hiện phép trừ EAX cho EDX với các giá trị như trên:

Cờ OF vẫn sẽ được kích hoạt bởi vì khi lấy số âm nhỏ nhất là 0x80000000 trừ đi 0x40 cho kết quả là một giá trị dương rất lớn (0x7FFFFFC0) và khiến cho kết quả của phép toán sai. Do đó, chúng ta có thể kết luận rằng cờ OF được kích hoạt khi có lỗi xảy ra trong quá trình tính toán với dấu. OF được bật khi bit có trọng số cao nhất (được xem là bit dấu) bị thay đổi bằng cách cộng hai số có cùng dấu (hoặc trừ hai số có dấu ngược nhau). Tràn không bao giờ xảy ra khi các dấu của hai toán hạng cộng là khác nhau (hoặc dấu của hai toán hạng trừ là giống nhau).

Như vậy, một số quy tắc để bật OF (overflow) trong phép toán nhị phân/số nguyên là:

  • Nếu tổng của hai số với bit dấu tắt tạo ra kết quả là một số với bit dấu bật, cờ “overflow” sẽ được bật. Ví dụ: 0100 + 0100 = 1000 (OF được bật)
  • Nếu tổng của hai số với bit dấu bật tạo ra kết quả là một số với bit dấu tắt, cờ “overflow” sẽ được bật. Ví dụ: 1000 + 1000 = 0000 (OF được bật)

SIGN FLAG (SF)

Cờ này khá đơn giản, nó được kích hoạt khi kết quả của việc tính toán là số âm, trong mọi trường hợp. Nó chỉ quan tâm tới kết quả của dấu mà không cần quan tâm kết quả tính toán đúng hay sai. Hay nói cách khác, cờ SF (cờ dấu) được thiết lập 1 khi bit msb của kết quả bằng 1, có nghĩa là kết quả là âm nếu ta làm việc vơi số có dấu.

Ví dụ như sau:

Kết quả của0x8000000 cộng 0x1 vẫn nằm trong dải số âm, là 0x8000001, vì vậy SF được kích hoạt. Chúng ta cũng thấy rằng OFCF không được kích hoạt vì không có lỗi trong quá trình tính toán của cả signed hoặc unsigned.

Rõ ràng, bộ xử lý khi thực hiện một lệnh liên quan tới tính toán hai thanh ghi, nó không hề biết các thanh ghi này là signed hay unsigned. Còn chúng ta có thể biết được là bởi vì ta thấy các lệnh nhảy có điều kiện ở phía dưới, ngược lại bộ xử lý không biết, do đó, trong bất kỳ hoạt động nào nó cũng sẽ xem xét các lệnh như thể là signed hoặc unsigned tại cùng một thời điểm và thay đổi các cờ cần thiết.

Vì các lệnh nhảy có điều kiện phụ thuộc vào cờ, chương trình sẽ nhìn vào kết quả của các cờ CF (trong phép tính unsigned) hoặc OF (trong phép tính signed) để từ đó đưa ra quyết định nhảy. Ví dụ, nếu có một lệnh JB (lệnh này là unsigned), do đó nó sẽ chỉ nhìn vào cờ CF và không quan tâm đến cờ OF ngay cả khi cả hai cờ này đều được kích hoạt.

Như vậy, trách nhiệm thuộc về người lập trình, người có quy ước về kết quả. Nếu đang làm việc với số có dấu thì chỉ có cờ OF đáng quan tâm trong khi cờ CF có thể bỏ qua, ngược lại khi làm việc với số không dấu thì cờ quan trọng là CF chứ không phải là OF.

ZERO FLAG (ZF)

Cờ này không phụ thuộc vào dấu

Nó được kích hoạt khi:

  • Phép so sánh (sử dụng một phép trừ) khi cả hai toán hạng đều bằng nhau.
  • Khi tăng hoặc giảm và kết quả là bằng không, hoặc trong một phép trừ mà kết quả có được bằng 0.

Chúng ta có thể chứng minh điều đó:

Ta thiết lập EAX có giá trị 0xffffffff và cộng thêm 1 vào EAX. Điều gì sẽ xảy ra?

Chúng ta thấy rằng cờ ZF được kích hoạt vì kết quả bằng 0 và nếu ta xem xét cả số unsigned thì cờ CF cũng được kích hoạt vì có tràn khi cộng 1 vào số dương lớn nhất. Trong khi đó, cờ OF không được kích hoạt bởi vì cả hai đều là số có dấu, -1 + 1 = 0 và không có lỗi. Cờ SF cũng không kích hoạt vì kết quả không phải là số âm.

Chúng ta thấy trạng thái các cờ rất quan trọng. Hãy xem liệu lệnh nhảy có điều kiện kế tiếp có xảy ra hay không? Ta patch lệnh SUB EAX, EDX như trên và bên dưới là lệnh nhảy JB 0x401018:

Sau đó gán EAX = 0x40EDX = 0x2, nhấn F8 để thực hiện lệnh SUB:

Mũi tên màu đỏ sẽ nhấp nháy vì thanh ghi EAX lớn hơn thanh ghi EDX, do đó lệnh nhảy sẽ không thực hiện. Ta quan sát các cờ.

JB là một lệnh nhảy unsigned và chỉ nhảy nếu cờ CF được kích hoạt. Rõ ràng ở đây cờ CF không được kích hoạt vì quá trính tính toán là chính xác giữa hai số dương cho ra kết quả là số dương, có nghĩa số đầu tiên lớn hơn số thứ hai, như vậy sẽ không thực hiện nhảy.

Nhưng nếu chúng ta thay đổi EAX thành 0x40EDX thành 0x80 và thử lặp lại lệnh trừ một lần nữa:

Trong trường hợp này, vì EAX nhỏ hơn EDX, lệnh nhảy JB sẽ được thực hiện và đi theo hướng của mũi tên màu xanh lá cây.

Vì khi lệnh JB nhìn vào cờ CF, nó sẽ nhảy vì cờ CF đã được bật. Vì kết quả của một thao tác unsigned là số âm và đã gây ra lỗi. Cờ SF cũng được kích hoạt vì kết quả là âm, còn cờ OF không được kích hoạt.

Lệnh JB sẽ nhảy căn cứ theo trạng thái của cờ CF nhưng nếu tôi thay đổi thành lệnh JL.

Trong trường hợp này, hướng thực hiện thay đổi và đi theo mũi tên màu xanh lá cây bởi vì toán hạng đầu tiên nhỏ hơn toán hạng thứ hai, nhưng lệnh nhảy JL căn cứ vào cờ nào?

Chúng ta thấy rằng lệnh JL sẽ thực hiện nếu cờ SF khác 0. Trong trường hợp này cờ SF = 1, do vậy sẽ nhảy và cũng là logic vì toán hạng đầu tiên nhỏ hơn toán hạng thứ hai. Lệnh SUB tương tự cách hoạt động của lệnh so sánh CMP, chỉ khác là SUB lưu kết quả còn CMP thì không.

Kết luận của bài viết này là không nhất thiết phải nhìn vào cờ để biết điều gì sẽ xảy ra với các nhảy có điều kiện, nó thuộc về hoạt động nội bộ bên trong. Chúng ta chỉ cần nhớ rằng, nếu hai toán hạng bằng nhau, lệnh JZ sẽ thực hiện. Nếu toán hạng đầu nhỏ hơn và là unsigned, thì sẽ nhảy nếu nó là lệnh JB. Còn nếu toán hạng đầu nhỏ hơn nhưng ở kiểu signed thì sẽ nhảy nếu là lệnh JL. Như vậy, ta chỉ cần quan sát cột thứ ba trong bảng signedunsigned là đủ. Tuy nhiên, sẽ vẫn là tốt hơn nếu chúng ta có cái nhìn chi tiết hơn :P.

Hẹn gặp lại các bạn ở phần 12!

Image result for we dig deeper funny

Xin gửi lời cảm ơn chân thành tới thầy Ricardo Narvaja!

m4n0w4r

Ủng hộ tác giả

Nếu bạn cảm thấy những gì tôi chia sẻ trong bài viết là hữu ích, bạn có thể ủng hộ bằng “bỉm sữa” hoặc “quân huy” qua địa chỉ:

Tên tài khoản: TRAN TRUNG KIEN
Số tài khoản: 0021001560963
Ngân hàng: Vietcombank

Comments
  1. Cuối cùng anh ra phần 11 😀 Em đợi gần tháng rồi. Em tưởng anh có post bên Medium nữa, nhưng vẫn lâu lâu qua đây check.

  2. kienmanowar says:

    Anh push cả hai, vì có người thích nền tảng này, có người lại thích nền tảng khác.
    Với lại medium cũng hỗ trợ import trực tiếp theo link, nên có chỉnh sửa thì cũng ít 🙂

    Regards,

  3. Nguyễn Nhật Tân says:

    Dạ, không hiểu sao bài này em không nhận được thông báo lẫn cả từ email của Medium luôn, bỏ sót mấy hôm nay 😀

  4. Shin says:

    Loạt bài về IDA của a thật sự rất hay. Em hóng từ phần :D.
    À tiện đây e muốn xin phép a cho e sử dụng bài PE_File của a để làm tài liệu tham khảo không a? E đang muốn viết bài về PE một cách tổng quan hơn để giúp cho người đọc dễ hình dung cũng như là tiếp cận ạ

  5. kienmanowar says:

    @Shin: Hoàn toàn được em ơi!! Em cứ sử dụng làm tài liệu tham chiếu và nếu bài của em viết xong, gửi qua anh để anh đẩy Blog.

  6. Shin says:

    Vâng oke a. E cũng chỉ chỉnh sửa lại những phần mà e cảm thấy là người mới bắt đầu sẽ cảm thấy khó hiểu thôi ạ. E cảm ơn a 😀

  7. lph77 says:

    hi anh, em đang đọc loạt bài ida này của anh, sau đó em có đọc thêm bai về ollydug thì em phân vân không biết ida vs ollydbg khác nhau thế nào, ida rõ ràng có tính năng debug tương tự olly ngoài ra giao diện còn trực quan, dễ nhìn hơn. olly cũng không được support nữa, vậy cho em được hỏi hiện tại có cần thiết học thêm olly không, hai các khác nhau những gì. cảm ơn anh

  8. kienmanowar says:

    @lph77: Câu hỏi của em khá hay 😀
    Mặc dù IDA có hỗ trợ Debugger và nếu debug nhiều thì thấy giao diện của nó cũng ổn, cũng như hỗ trợ nhiều tính năng mà OllyDbg chưa tối ưu. Tuy nhiên, không phải tự nhiên mà người ta lại đi xây dựng các trình debugger riêng như Ollydbg/ x64dbg/ Windbg. Mỗi công cụ sẽ có điêm mạnh/ yếu bổ sung cho nhau. Thế nên việc biết sử dụng những trình Debugger khác ngoài Debugger mà IDA hỗ trợ là điều cần thiết.

    Regards,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.