Archive for October, 2019


Phần 24 này tôi sẽ thực hành với file IDA2.exe đã gửi kèm ở phần trước. Cách làm với IDA2.exe cũng tương tự như cách đã thực hiện với file IDA1.exe, chỉ khác là trong file này có hai biến cần kiểm tra điều kiện, nếu thỏa mãn cả hai thì chúng ta sẽ tới được đoạn code hiển thị thông báo “you are a winnner man je\n”:

Như trên hình ta thấy biến var_14 được so sánh với giá trị 0x71727374h còn biến var_C được so sánh với giá trị 0x91929394, do đó ta phải tìm cách để có thể thay đổi giá trị các biến này. Trước tiên, tôi sẽ đổi tên chúng thành cookiecookie_2 để dễ dàng nhận biết trong code của chương trình:

Sau khi đổi tên xong, chọn lần lượt từng biến này và nhấn “X”:

Ta thấy rằng các biến này duy nhất được truy cập thông qua lệnh LEA trước khi thực hiện hàm printf(). Lệnh LEA sẽ lấy thông tin địa chỉ của các biến để in ra màn hình, bên dưới là câu lệnh CMP làm nhiệm vụ so sánh. Như vậy là giá trị của các biến này không hề bị thay đổi trong hàm main().

Do đó, theo suy luận tương tự như phần trước, ta phải thay đổi giá trị của các biến này để có thể tới được thông báo hiển thị “…winner…”. Mà như đã thấy ở trên hình thì các biến này đều được so sánh với một giá trị cụ thể do chương trình mặc định sẵn. Vì vậy, để có thể thay đổi các biến thì khả năng sẽ phải thực hiện overflow lên vùng buffer, để từ đó có thể ghi đè giá trị ta mong muốn lên các biến này.

Bên cạnh các biến cookie mà ta vừa phân tích ở trên, để ý một chút các bạn sẽ thấy trong file IDA2.exe này còn có thêm một biến khác là var_10. Ban đầu, biến này được khởi gán bằng 0:

Nhấn ‘X’ để xem biến này còn được sử dụng ở những chỗ nào khác:

Theo kết quả trên hình, ta thấy ngoài lệnh khởi gán nó bằng 0 thì biến này còn được truy cập ở hai vị trí khác nữa thông qua lệnh LEA. Chúng ta sẽ xem xét từng chỗ một, nhưng trước tiên ta thực hiện việc điều chỉnh lại các tham số được truyền cho các hàm APIs để cho dễ nhìn hơn. Áp dụng kỹ thuật đã làm với file IDA1.exe, kết quả có được như sau:

Sau khi thay đổi xong các bạn sẽ thấy code nhìn rõ ràng hơn nhiều so với ban đầu, qua đó ta cũng hiểu được cách mà chương trình truyền tham số cho hàm APIs. OK, giờ quay lại vấn đề lúc trước, ta kiểm tra các đoạn code khác có truy cập tới biến var_10:

Phân tích đoạn code trên hình, đầu tiên thông qua lệnh LEA thì eax sẽ có được địa chỉ của biến, sau đó thực hiện tăng nội dung hay tăng giá trị của nó lên 1 thông qua lệnh INC. Sau đó, lấy lại địa chỉ của biến một lần nữa và truyền nó như một tham số của hàm printf(). Như vậy, hàm printf() sẽ in ra địa chỉ của biến chứ không phải là giá trị của biến.

Dựa vào thông tin chương trình sẽ in ra màn hình dòng thông báo “flag %x”, tôi đổi tên biến var_10 thành flag. Tuy nhiên, sau quá trình phân tích thì thấy rằng biến này không có ảnh hưởng gì tới quá trình hoạt động của chương trình.

Tóm lại, chúng ta chỉ cần nghiên cứu biến Buffer vì các biến ở trên biến này được sinh ra trong quá trình compile chương trình, được sử dụng làm các biến tạm thời chứ không có tác động đến chương trình:

Nhấp đúp vào biến Buffer, ta sẽ chuyển tới màn hình cung cấp thông tin biểu diễn Stack của hàm main() như hình dưới đây:

Rõ ràng, biến Buffer này là một nơi được dành riêng trong bộ nhớ, được sử dụng để lưu thông tin mà người dùng nhập vào từ bàn phím thông qua giao diện console. Biến này được truyền vào là một tham số của hàm gets():

Để kiểm tra kích thước của buffer này ta nhấn chuột phải và chọn Array, IDA sẽ cung cấp thông tin mà nó phân tích được:

Như vậy Buffer có kích thước gồm 68 phần tử, mỗi phần tử là 1 byte. Ta nhấn OK để đồng ý chuyển đổi, kết quả có được như hình:

Với kết quả có được, ta biết rằng đầu tiên phải điền đầy buffer với 68 ký tự, và do hạn chế của hàm gets nên chúng ta có thể overflow buffer này và ghi đè thêm 4 bytes vào biến cookie nằm bên dưới của buffer. Tiếp theo là ghi đè lên 4 bytes của biến flag (một dword (dd)) và cuối cùng là 4 bytes của biến cookie_2:

Tổng kết lại: user_input= 68 *”A”+ cookie + flag + cookie2. Với thông tin như vậy, chúng ta có thể xây dựng một python script như sau:

from subprocess import *
import struct
p = Popen([r'IDA2.exe', 'f'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)

cookie=struct.pack("<L",0x71727374)
cookie2=struct.pack("<L",0x91929394)
flag=struct.pack("<L",0x90909090)

print " Attach to the debugger and press Enter\n"
raw_input()

user_input=68 *"A"+ cookie + flag + cookie2
p.stdin.write(user_input)

testresult = p.communicate()[0]

print user_input
print(testresult)

Kiểm tra thử script, tôi có được kết quả như sau:

Như vậy là thành công!

Image result for deal with expert funny

Trong phần 24 này tôi gửi kèm thêm 2 file IDA3.exeIDA4.exe (https://mega.nz/#!bOxGWISb!kYzE2SsWPyo0YQg5tL72qy14ntuMlic02ML5bg7-GD0) để các bạn thực hành như một bài tập về nhà.

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

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


Trong phần 23 này tôi sẽ giải quyết câu hỏi đặt ra đối với file IDA1.exe đã được gửi kèm trong phần 22. Đầu tiên, load file vào IDA để phân tích, ta sẽ dừng lại tại hàm main():

Tại đây, bạn cuộn chuột xuống dưới sẽ nhìn thấy chuỗi “you are a winnner man je\n“. Đó chính là nơi mà chúng ta cần phải tới được. Tôi đổi màu cho block này để tiện cho việc nhận diện một cách dễ dàng.

Quan sát bên trên một chút, ta thấy rằng để có thể tới được đoạn code hiển thị chuỗi mong muốn thì phải vượt qua được đoạn code thực hiện so sánh giữa biến var_C với một hằng số 0x51525354. Nhấn chuột phải tại giá trị 0x51525354, IDA sẽ cung cấp cho chúng ta một loạt các lựa chọn chuyển đổi, ở đây chúng ta sẽ lựa chọn chuyển thành các kí tự ‘QRST’:

Các bạn để ý một chút sẽ dễ dàng có thể nhận ra, file IDA1.exe được biên dịch không sử dụng cơ chế CANARY protection, vì nếu có áp dụng thì ở của đầu hàm nó sẽ phải đọc dữ liệu từ một địa chỉ thuộc section data vào thanh ghi eax và đem xor với thanh ghi ebp, kết quả được lưu vào biến nằm ngay trên giá trị của ebp đã được lưu vào Stack trước đó. Trước khi kết thúc hàm biến này sẽ được đọc ra để kiểm tra lại. Tóm lại file IDA1.exe không áp dụng Stack Canary, ta sẽ phân tích tĩnh bố trí các biến và tham số tại Stack của hàmmain(), nhấp đúp vào bất kỳ biến nào ta sẽ có kết quả như sau:

Tại đây, ta thấy rằng biến duy nhất xuất hiện trong Stack, ngay bên trên giá trị của EBP được lưu, đó là biến var_C. Chọn biến này và nhấn “X”, sẽ thấy nó được sử dụng để so sánh với chuỗi “QRST”. Căn cứ trên kết quả so sánh sẽ đi tới đoạn code hiển thị thông báo “winner” hay là không:

Tóm lại, file này không áp dụng CANARY, vì như ta thấy rõ ràng trong trường hợp này thì đây là một lệnh kiểm tra thuộc mã nguồn của chương trình. CANARY không thể “trộn” lẫn nằm xen kẽ với các quyết định rẽ nhánh trong mã của chương trình, nó phải được bổ sung bởi chính trình biên dịch, và nằm ở bên ngoài code ban đầu.

Cho nên để tránh nhầm lẫn biến var_C với CANARY, ta có thể đổi tên nó thành var_Decision.

Như trên hình, biến này được sử dụng hai lần trong hàm main(). Vậy làm thế nào chúng ta có thể thay đổi giá trị của biến var_Decision để khiến cho chương trình rẽ nhánh đi tới đoạn code hiển thị thông báo “winner”?

Khi các bạn quan sát kĩ vào các dòng lệnh asm thì điều sẽ khiến bạn chú ý là có các biến được truy cập thông qua thanh ghi EBP và có những biến khác được truy cập thông qua thanh ghi ESP:

Toàn bộ các lệnh khởi tạo ban đầu được thêm vào bởi trình biên dịch nhằm thiết lập thanh ghi ESP nằm trên các biến cục bộ và buffer. Lệnh SUB ESP, 0xB8 làm nhiệm vụ dành ra không gian phục vụ cho các biến cục bộ và buffer được khai báo bên trong thân hàm main(). Để biết cụ thể thì các bạn có thể debug để hiểu rõ hơn. Bản thân IDA có một trợ giúp rất hữu ích là “Stack Pointer”, hỗ trợ hiển thị sự thay đổi của thanh ghi ESP kể từ điểm bắt đầu của hàm (giá trị tại đầu hàm được thiết lập bằng 0).

Kết quả tại code của chương trình sẽ xuất hiện thêm thông tin như hình dưới đây:

Quan sát trên hình các bạn sẽ thấy sự tác động của mỗi lệnh lên ngăn xếp bắt đầu từ đầu hàm, sau khi thực hiện PUSH EBP, thanh ghi ESP sẽ bị giảm đi 4, do vậy dòng lệnh thứ hai có thêm số 004 ở bên phải, hàm ý là stack pointer thay đổi. Tuy nhiên, ở câu lệnh thứ hai là MOV EBP, ESP, vì đây chỉ là một lệnh MOV nên lệnh này không làm thay đổi Stack, do vậy giá trị của ESP vẫn giữ nguyên, thanh ghi thay đổi là EBP.

Đó là lý do tại sao dòng lệnh thứ ba có cùng giá trị là 004 vì lúc này chưa thay đổi ESP. Như vậy, ta thấy rằng ở thời điểm này giá trị ESPEBP là giống nhau và thanh EBP từ đây có thể được sử dụng để truy xuất các biến cục bộ cũng như các tham số của hàm. Dòng lệnh thứ 3 thực hiện trừ ESP đi 0xB8, do vậy kết quả sẽ là giá trị 0xBC (so với điểm bắt đầu) và vì EBP vẫn ở mức 004 nên chênh lệch giữa hai thanh ghi EBPESP sẽ là 0xB8.

Quan sát kĩ hơn thì thấy khoảng cách này không thay đổi nữa ngay cả khi thực hiện các lời gọi hàm call __allocacall ___main (được tự động thêm vào trong quá trình biên dịch chương trình). Tóm lại, khoảng cách giữa EBPESP0xB8, và đây chính là khoảng không gian dành cho các biến cục bộ và buffer được khai báo trong thân của hàm main().

Tiếp tục đọc code, các bạn thấy có biến được IDA đặt tên là var_9c. Kiểm tra toàn bộ hàm main() thì thấy biến này không được sử dụng cho việc gì khác, vì vậy nó có thể là một biến tạm thời được tạo bởi trình biên dịch, ta sẽ đặt tên nó là Temp để dễ nhận biết:

Bên dưới biến Temp chúng ta có biến Buffer:

Nhấn chuột phải tại biến Buffer này và chuyển đổi sang kiểu array, ta sẽ được kết quả là một biến Buffer có kích thước là 140 phần tử, mỗi phần tử có kích thước 1 byte:

Nếu chúng ta có thể làm tràn biến Buffer bằng cách đẩy vào dữ liệu nhiều hơn 140 bytes, như vậy sẽ gây ra Buffer Overflow, lúc đó ta có thể ghi đè giá trị lên biến var_Decision và nếu có thể được sẽ ghi đè dữ liệu lên cả giá trị EBP đã được lưu và đè lên cả địa chỉ trở về (r). Điều này tùy thuộc lượng dữ liệu mà ta có thể nhồi vào biến Buffer.

Tiếp tục quá trình reversing, điểm gây chú ý tiếp theo liên quan tới trình biên dịch, đó là là cách nó truyền các tham số cho hàm. Ở target này, thay vì sử dụng lệnh PUSH để lưu các tham số vào Stack (như vẫn thường gặp), thì ở đây ta thấy chương trình lưu trực tiếp thông qua lệnh MOV. Các bạn quan sát lệnh printf, rõ ràng trong trường hợp này printf có một đối số duy nhất là địa chỉ của chuỗi “you are a winner man je\n” và không có tham số nào khác:

Ta thấy chương trình truy xuất địa chỉ của chuỗi trên vì có tiền tố offset ở phía trước và lưu vào biến có tên Format trong ngăn xếp. Nhưng cụ thể là ở đâu trong Stack? Lúc này chương trình không sử dụng thanh ghi EBP để truy cập đến tham số của hàm mà sử dụng chính thanh ghi ESP, tuy nhiên ký hiệu (esp+0B8h+Format) hơi khó hiểu, nếu chúng ta nhấn chuột phải tại đây IDA sẽ cung cấp cho ta thông tin thay thế như sau:

Nếu ta chọn cách biểu diễn như IDA gợi ý:

Qua việc chuyển đổi trên, ta có thể hiểu bản chất của câu lệnh mov sẽ thực hiện việc lưu địa chỉ của chuỗi vào trong nội dung thanh ghi ESP (đó chính là đỉnh của Stack), để sử dụng nó như một tham số của hàm. Do vậy, chương trình thay vì sử dụng lệnh PUSH thì nó sử dụng lệnh MOV để truyền tham số cho hàm, và theo logic thì lệnh PUSH sẽ thay làm đổi giá trị của ESP, trong khi lệnh MOV thì không, các bạn có thể thấy được thông qua giá trị 0xBC ngay trước hàm printf():

Cách truyền tham số này cũng được áp dụng đối với tất cả các hàm APIs khác mà IDA1.exe sử dụng. Áp dụng cách thức chuyển đổi tương tự như đã làm ở trên, ta sẽ có được các đoạn mã lệnh asm nhìn dễ hiểu hơn:

Tiếp tục quá trình phân tích, hàm printf() đầu tiên nhận ba tham số truyền vào cho nó. Vì sao lại biết được là có 3 tham số? Là bởi vì hàm in ra màn hình chuỗi sử dụng các giá trị để thay thế cho format string: “buf:%08x cookie:%08x \ n”. Các giá trị “%08x” sẽ được thay thế bằng hai tham số được truyền trước đó tại [esp+8][esp+4]:

Cách truyền tham số cho hàm thường sẽ là từ phải qua trái. Do đó, tham số thứ 3 của hàm printf() là địa chỉ của biến var_Decision, có được thông qua lệnh LEA và lưu tại thanh ghi EAX. Sau đó lưu địa chỉ này vào vị trí [ESP+8] trên Stack. Tham số thứ hai là địa chỉ của biến Buffer, cũng thực hiện theo cách tương tự nhưng được lưu vào bị trí [ESP+4] trên Stack và tham số đầu tiên được lưu tại [ESP] là địa chỉ của chuỗi “buf: %08x cookie: %08x\n”.

Nếu chạy trực tiếp file này tại màn hình console, ta sẽ thấy chuỗi được in ra màn hình cùng với địa chỉ của cả hai biến:

Tiếp theo, chương trình sử dụng hàm gets() để đọc các kí tự từ stdin (standard input) và lưu vào biến Buffer. Ta để ý ở đây số lượng kí tự nhập vào bằng bàn phím là tùy ý mà không có bất kỳ giới hạn nào, tức là ta có thể nhập vào nhiều hơn 140 ký tự mà không có vấn đề gì:

Đoạn code trên sẽ tương ứng với mã giả C như sau: gets(Buffer);

Vì vậy, giả sử nếu ta ghi vào Buffer này 140 chữ ‘A’ và kèm theo sau đó là chuỗi “TSRQ” (theo kiểu little endian), chương trình sẽ nhảy tới đoạn code hiển thị thông báo “winner” vì khi đó biến var_Decision nằm ngay bên dưới Buffer, sẽ được ghi đè bởi giá trị “QRST”. Kiểm tra bằng tay trước khi chúng ta thực hiện tự động bằng script:

Thực hiện lệnh tại Python bar của IDA, sau đó copy chuỗi được sinh ra:

“AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATSRQ”

Chạy chương trình và dán chuỗi này vào, kết quả có được như sau:

Kết quả đúng như mong đợi, ta tiến hành tạo script để chạy tự động, script này cũng giống như script đã tạo ở phần trước trước, chỉ khác ở này chỉ truyền một input cho chương trình thôi:

from subprocess import *
p = Popen([r'IDA1.exe', 'f'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)

print "Attach to the debugger and press Enter\n"
raw_input()

user_input="A" *140 + "TSRQ\n"
p.stdin.write(user_input)

testresult = p.communicate()[0]

print user_input
print(testresult)

Kiểm tra kết quả sau khi thực hiện script:

Phần 23 đến đây là kết thúc. Tôi gửi các bạn bài tập IDA2.exe (https://mega.nz/#!jXpgGSKJ!jx8p41yH5Drqq78fKonMqVQhmQf2BqwbJh9aqHDwZ_o) để các bạn thực hành, nó tương tự như IDA1.exe.

Image result for that was easy funny gif office space

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

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


Ở phần 22 này chúng ta sẽ tìm hiểu một chút về các công cụ bổ trợ cho IDA trong việc việc so sánh sự khác nhau giữa hai binary.

Nếu các bạn là dân dev, hay dân văn phòng suốt ngày lọ mọ với một mớ code/ tài liệu thì chắc sẽ chẳng lạ gì với việc tự làm “bằng cơm” hoặc sử dụng công cụ chuyên dụng để so sánh sự khác nhau/ sai khác trong cùng một source code hoặc giữa cùng một tài liệu tại những thời điểm khác nhau.

Tôi lấy ví dụ về việc so sánh giữa hai file văn bản: bên trái là file gốc vs bên phải là file đã chỉnh sửa (nhưng chữ sửa trong file để font màu trắng). Nếu không dùng tool mà làm “bằng cơm” để kiểm tra sự thay đổi, tôi đố các bạn tìm được hell-yes-onion-head-emoticon

Với các file binary cũng vậy, công cụ differ cũng sẽ cho ta biết sự khác biệt/ thay đổi giữa hai phiên bản của cùng một chương trình, các công cụ này sẽ cố gắng thực hiện phân tích, so khớp các hàm trong chương trình và đưa ra kết qủa về những hàm có sự thay đổi và thay đổi ở đâu.

Ta thấy rõ ràng để thực hiện công việc này không phải là dễ dàng, đặc biệt là khi có những thay đổi lớn từ phiên bản này sang phiên bản khác, sự thay đổi này có thể đến từ việc cập nhật các bản vá bảo mật để giải quyết các lỗ hổng tồn tại trong chương trình, hoặc có thể là những cải tiến mới trong tính năng của chương trình, v.v… Với dân chuyên crack soft hoặc tìm hiểu cracking thì việc so sánh giữa file gốc và file đã patch có thể giúp cho họ tìm hiểu được cách patch của các cracker khác. Với dân chuyên nghiên cứu exploit thì việc làm này có thể giúp họ biết được một bản vá bảo mật có giải quyết được triệt để lỗi hay không? Hay là patch lỗi này lại sinh ra lỗi khác có thể khai thác được.

Tính tới thời điểm hiện tại, cá nhân tôi biết được có 3 công cụ differ sử dụng kết hợp với IDA. Trong bài viết này, tôi sẽ giới thiệu lần lượt các công cụ này và tùy cảm nhận của từng người mà lựa chọn cho mình công cụ phù hợp hoặc sử dụng kết hợp.

Note: Thời điểm tôi chỉnh sửa lại bài viết này thì Bindiff đã ra phiên bản 5 (https://www.zynamics.com/software.html); Diaphora cũng được Joxean Koret cập nhật liên tục để có thể làm việc với các phiên bản IDA 6.9 tới 7.3

1. BinDiff

Công cụ phải nói tới đầu tiên chính là BinDiff, mục tiêu của nó là một công cụ hỗ trợ so sánh các tập tin nhị phân nhằm giúp các chuyên gia nghiên cứu lỗ hổng có thể nhanh chóng tìm thấy sự khác biệt và tương đồng trong mã chương trình được phân rã ở dạng câu lệnh asm. Công cụ này được phát triển bởi Zynamics, công ty này sau đó được Google mua lại vào năm 2011. Phiên bản mà tôi sử dụng tại thời điểm của bài viết này là BinDiff v4.3. Thông tin chi tiết về BinDiff có thể tìm đọc tại đây: https://www.zynamics.com/software.html

Các bạn download về và tiến hành cài đặt. Việc cài đặt BinDiff dễ như các bạn cài Win dạo, khác mỗi là bạn không phải kiếm cr@ck thôi, chú ý là trên máy cần phải cài đặt Java Runtime Enviroment (JRE) trước nhé. BinDiff 4.3 hiện chỉ hỗ trợ cho IDA 6.x, chưa có plugin cho 7.x (Note: hiện bản BinDiff 5 đã hỗ trợ cho IDA 7.x).

Để minh họa cho việc sử dụng BinDiff, tôi sẽ sử dụng các file .idb có sẵn: một file có vuln và một file đã được fix. Mở IDA lên và load database của file có vuln (VULNERABLE_o_NO.idb) vào:

Sau khi IDA load xong database của file có vuln, truy cập menu Edit > Plugins và chọn BinDiff 4.3:

Cửa sổ BinDiff sẽ xuất hiện như hình dưới:

Tiếp theo chọn Diff Database… và tìm tới database của file đã fix vuln để so sánh:

BinDiff sẽ chạy và phân tích hai databse này:

Sau khi thực hiện xong, BinDiff sẽ hiển thị kết quả cho chúng ta. Có thể các cửa sổ của BinDiff tại IDA của các bạn sẽ không giống như tôi, các bạn có thể thực hiện drag & drop để lựa chọn một chế độ xem phù hợp nhất:

Tab mà chúng ta quan tâm là “Matched Functions”:

Tại đây, các ban sẽ thấy cột đầu tiên (similarity) cung cấp kết quả về sự giống nhau giữa các hàm, Theo kinh nghiệm của nhiều người đã dùng BinDiff thì nếu giá trị trả về bằng 1.00 thì có nghĩa là hai hàm đó hoàn toàn giống nhau, không có thay đổi gì, ngược lại nếu giá trị này < 1.00 thì có nghĩa là cùng một hàm nhưng đã có sự thay đổi, khác biệt. Để dễ dàng và thuận tiện thì chúng ta chỉ việc nhấp chuột vào đầu cột đó để sắp xếp chúng theo kết quả từ khác nhau đến giống nhau. Tương tự như hình:

Theo kết quả có được như trên hình, ta thấy rằng ở đây chỉ có một điểm tương đồng nhỏ hơn 1. Nhấn chuột phải tại đó và chọn View Flowgraphs hoặc nhấn phím tắt là Ctrl+E:

Lúc này, plugin sẽ sẽ gọi tới bindiff.jar trong thư mục cài đặt của BinDiff để hiển thị FlowGraphs của hai hàm có sự thay đổi ở trên:

Như trên hình, ta thấy các khối được highlight bằng màu xanh, đó là những khối có code giống nhau, còn khối được highlight bằng màu vàng là khối có sự thay đổi và những câu lệnh thay đổi cũng được BinDiff highlight bằng những màu khác nhau để ta có thể dễ dàng nhận ra:

Như trên hình, các bạn sẽ nhận thấy được sự thay đổi ở lệnh nhảy, và chúng ta cũng đã biết việc đổi từ lệnh nhảy JLE thành JBE là cách để chương trình tránh bị dính lỗi Buffer Overflow, do vậy nếu trong một chương trình mà ta có cả phiên bản bị lỗi và phiên bản đã được vá lỗi, sau khi so sánh và nhìn vào các hàm đã thay đổi thì chúng ta sẽ phải thực hiện reverse lại hàm đó để xem nó có thực sự là lỗ hổng của chương trình hay không.

Một trong những ưu điểm của BinDiff so với hai công cụ sắp được đề cập là ngoài việc hỗ trợ chế độ đồ họa tương tác rất tốt, khả năng parse trực tiếp các file .idb mà không cần phải tạo ra các file trung gian thì nó còn cung cấp khả năng tìm kiếm rất tiện lợi, qua đó ta có thể tìm kiếm địa chỉ và bất kỳ đoạn text nào. Ví dụ, tôi thực hiện tìm kiếm chuỗi “cmp”:

Bên cạnh đó, BinDiff hỗ trợ sao chép địa chỉ của khối có thay đổi, qua đó ta có thể đi tới được khối này trong IDA bằng việc nhấn G và dán địa chỉ đã copy vào:

BinDiff cũng cung cấp một cửa sổ nhỏ tương tự như cửa sổ Graph Overview của IDA để giúp dễ dàng di chuyển, quan sát các hàm cùng danh sách các khối đã được phân rã:

Như các bạn thấy BinDiff cung cấp cho chúng ta một giao diện cùng các tính năng hữu ích, với những file đơn giản như trong ví dụ này thì các bạn thấy có vẻ dễ dàng, tuy nhiên với các ứng dụng lớn và phức tạp hơn thì sẽ không ngon ăn như thế này đâu.

“Dễ thế này thì Đông Lào người ta đi săn bug hết!!”

Do BinDiff hiện tại chỉ hỗ trợ IDA 6.x, với phiên bản IDA 7.x thì phải sử dụng BinExport (https://github.com/google/binexport) để xuất ra file và import vào Bindiff để so sánh. Phiên bản BinExport mà tôi đang sử dụng được build bởi bạn Ngôn Nguyễn (aka @computerline). Cách thức thực hiện như sau, đầu tiên load db của file có lỗi (VULNERABLE_o_NO.idb) vào IDA 7.x. Sau khi load xong, vào Edit > Plugins chọn BinExport:

Giao diện của BinExport sẽ xuất hiện, chọn BinExport v2 Binary Export:

Sau đó lưu lại với tên bất kì với phần mở rộng là .BinExport (ví dụ: VULNERABLE_o_NO.BinExport)

Tiếp theo, dùng IDA load db của file đã fix lỗi (NO_VULNERABLE.idb) và cũng thực hiện tương tự như trên, lưu thành file có tên là NO_VULNERABLE.BinExport:

Sau khi có hai file được export bằng BinExport, chạy trực tiếp Bindiff và load hai file này vào để so sánh:

Sau khi nhấn Diff thì bên tab Workspace sẽ hiển thị tên hai file được so sánh và tab Overview sẽ cung cấp kết quả so sánh:

Nhấp đúp vào tên hai file được so sánh tại tab Workspace, BinDiff sẽ hiển thị kết quả chi tiết:

Tiếp theo, chọn Matched Functions, sắp xếp lại theo mức độ tương đồng (Similarity):

Cuối cùng, nhấn chuột phải tại hàm có sự khác biệt, chọn Open Flow Graph và phân tích kết quả trả về:

2. Turbodiff

Công cụ cho phép so sánh binary tiếp theo là TurboDiff. Đây là một plugin được code bởi tác giả Nicolas Economou (@NicoEconomou), là đồng nghiệp trước đây với thầy Ricardo Narvaja tại Core Security. Các bạn có thể download plugin này tại: https://www.coresecurity.com/corelabs-research/open-source-tools/turbodiff, tuy nhiên đây là phiên bản cũ. Phiên bản mà thầy Ricardo Narvaja sử dụng là phiên bản mới hơn. Việc cài đặt rất dễ, chỉ việc chép file turbodiff.plw vào thư mục plugins của IDA là xong.

Tương tự như đã làm với BinDiff, nhưng khác chút là ta load database NO_VULNERABLE.idb của file đã fix lỗi trước. Sau khi IDA load xong, vào menu Edit > Plugins và chọn Turbodiff:

Để so sánh được thì TurboDiff cần phải lấy thông tin từ file .idb:

Lựa chọn “take info from this idb” và nhấn OK, turbodiff sẽ phân tích và tạo ra 2 file có đuôi mở rộng là .dis và .ana:

Sau đó, mở database của file có lỗi và cũng làm tương tự như trên:

Khi phân tích xong, ta tiếp tục chọn turbodiff một lần nữa, nhưng lần sẽ thực hiện so sánh bằng cách chọn “compare with…” và nhấn OK:

Chọn file cần so sánh là db của file đã được fix lỗi:

Giữ nguyên cấu hình mặc định của turbodiff và nhấn OK:

Ta sẽ có được kết quả như sau:

Tại tab “Turbodiff results” ta nhấn CTRL + F và tìm kiếm từ khóa là “changed” hoặc “suspicious” để hiển thị những chỗ thay đổi:

Sau khi có được kết quả như hình, nhấp đúp vào đó, turbo diff sẽ sử dụng wingrap32 để hiển thị flow graph:

Như các bạn thấy turbodiff đã cung cấp thông tin về khối lệnh có sự thay đổi. Tương tự như Bindiff, turbodiff cũng sử dụng một mã màu để biểu diễn cho tỉ lệ thay đổi, màu xanh lá cây được sử dụng cho các khối có những thay đổi ít, màu vàng sẽ dùng cho các khối có thay đổi nhiều và màu đỏ sử dụng cho các khối được thêm vào. Rõ ràng, về mặt đồ họa và khả năng tương tác thì không thể so sánh được với Bindiff, nhưng bù lại là tốc độ của Turbodiff thực sự rất nhanh. Hiệu năng là điều rất đáng quan tâm nếu áp dụng với các file có kích thước lớn và nó không hiển thị quá màu mè như Bindiff. Một điểm khác nữa với Bindiff là Turbodiff phải parse database của IDA thành các file trung gian rồi mới so sánh.

3. Diaphora

Công cụ so sánh binary cuối cùng mà tôi giới thiệu với các bạn là diaphora, đây là một plugin do Joxean Koret (@matalaz) viết bằng Python. Joxean Koret là tác giả của cuốn sách “The Antivirus Hacker’s Handbook” và hình như có quen biết với anh Quỳnh khi làm việc ở Coseinc. Để sử dụng được diaphora các bạn có thể download tại đây: https://github.com/joxeankoret/diaphora. Tính đến thời điểm hiện tại diaphora không còn hỗ trợ IDA 6.8 nữa, mà chỉ làm việc với IDA Pro 6.9, 6.95 và 7.0.

Tương tự như đã làm với các công cụ trước, đầu tiên ta load db của file đã fix lỗi vào IDA trước. Do là dạng python script, nên để chạy được diaphora ta vào File > Script File…

Tìm tới thư mục chứa file diaphora.py và lựa chọn file này để chạy:

Màn hình Diaphora sẽ xuất hiện, ta có thể lựa chọn đường dẫn để lưu file SQLite db cho file hiện tại đang phân tích bởi IDA hoặc giữ nguyên đường dẫn mà Diaphora thiết lập:

Sau đó nhấn OK để Diaphora tiến hành công việc phân tích và lưu thành file sqlite:

Thực hiện tương tự với file có lỗi:

Sau khi thực hiện xong hai bước trên ta sẽ có được 2 file sqlite để phục vụ cho việc so sánh. Tiếp theo, ta mở lại diaphora.py (lúc này IDA đang mở db của file có lỗi):

Ở phần “SQLite database to diff against”, ta tìm đến file sqllite được tạo ra trước đó của file đã fix lỗi. Chọn file này và nhấn Open:

Cuối cùng nhấn OK để Diaphora tiến hành xử lý vá so sánh. Diaphora sẽ hỏi như hình dưới, chỉ việc nhấn Yes là xong:

Sau khi compare xong, diaphora sẽ hiển thị 2 tab mới tại IDA với tên là Best matchesPartial matches. Tại tab Best matches là các hàm giống nhau hoàn toàn và không thay đổi, nên ta không cần quan tâm tới tab này:

Tab Partial matches sẽ cung cấp cho chúng ta thông tin về các hàm có khả năng khác nhau:

Ta thấy diaphora đã tìm được hai hàm bị thay đổi như trên. Về cơ bản, chủ quan mà nói tôi thấy diaphora cho kết quả hên xui so với Bindiff và Turbodiff, nếu chạy với IDA7 thì không ra được Partial maches, chạy với IDA 6.8 thì mới ra được kết quả mong muốn. Ngoài ra, do được viết bằng python nên diaphora chạy khá lâu, theo tôi thì nó chạy lâu nhất, gặp phải db lớn là xác định ngồi đợi. Để xem được sự thay đổi ở một hàm nào đó, nhấn chuột phải tại hàm đó và chọn chế độ Diff assembly in a graph:

Kết quả có được như hình dưới đây:

Chế độ graph này của diaphora nhìn có vẻ tốt hơn Turbodiff một chút, nhưng cũng không cho ta khả năng tương tác như ở Bindiff, khối quan trọng đã thay đổi được highlight bằng màu đỏ, còn màu vàng là khối có những thay đổi nhỏ, ví dụ như ta đổi tên của một biến. Ngoài ra, diaphora còn cung cấp tùy chọn khác là Diff pseudo code, sử dụng Hexrays decompiler đi kèm với IDA để xây dựng lại mã nguồn của file thực thi. Kết quả có được khi lựa chọn tùy chọn này như hình dưới đây:

Phần 22 đến đây là kết thúc nhé các bạn!

Image result for diff funny

Trong phần này tôi cung cấp một file IDA1.exe (https://mega.nz/#!mHAU1AaS!Zm2g_pXoiCDWiKwSdRJsJ0tYNTRjc6JSkBJyr6h7j0g). Tôi muốn các bạn phân tích nó và xem liệu nó có lỗi hay không và nếu có thì bạn thực hiện khai thác buffer overflow làm sao để thay đổi luồng thực thi của chương trình để từ đó hiển thị thông báo You are a winner man.

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

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