Kỹ thuật dịch ngược x64 - Khái niệm cơ bản - Linux trên ARM64

1307
13-06-2018
Kỹ thuật dịch ngược x64 - Khái niệm cơ bản - Linux trên ARM64

Như bạn có thể đã biết rằng ARM cấp nguồn cho nhiều thiết bị hỗ trợ công suất thấp xung quanh chúng ta, bao gồm nhưng không giới hạn, điện thoại, thiết bị định tuyến, thiết bị IoT, v.v. Do đó, việc tìm hiểu sâu về cấu trúc này và tìm hiểu sự khác biệt so với cấu trúc x86 và x64 là hợp lý. Đối với bài đăng trên blog này, chúng tôi sẽ tập trung vào CPU ARM 64 bit vì hiện tại nó được sử dụng phổ biến nhất. Thiết lập của chúng tôi bao gồm Ubuntu 16.04 trên CPU ARM Cortex-A53, hỗ trợ cả tập lệnh 32 bit và 64 bit.

Trong các bài viết trước, chúng tôi đã dịch ngược một nhị phân C trong x64 Linux và Windows. Tuy nhiên, trong blog này,Bizfly Cloud sẽ dịch ngược cùng một chương trình, được viết lại trong C.

Kỹ thuật dịch ngược x64 - Khái niệm cơ bản - Linux trên ARM64 - Ảnh 1.

Biên dịch chương trình:

$ gcc crack_me.c -o crack_me


Thông tin nhị phân:

Kỹ thuật dịch ngược x64 - Khái niệm cơ bản - Linux trên ARM64 - Ảnh 2.

Tháo gỡ

Bây giờ, hãy kích hoạt GDB bằng nhị phân và bắt đầu tiến hành phân tích. Lưu ý rằng tôi đang sử dụng GEF (https://github.com/hugsy/gef) với GDB, do đó, lời nhắc của tôi sẽ trông giống như gef> chứ không phải gdb>. Hãy bắt đầu bằng cách tháo gỡ hàm chính.

$ gdb ./crack_me


gef> disas main


Kỹ thuật dịch ngược x64 - Khái niệm cơ bản - Linux trên ARM64 - Ảnh 3.

Sự chú ý của chúng tôi chuyển trực tiếp đến hàm <check_pass><main 64>, nhưng trước khi đến đó, bạn có thể muốn dành một chút thời gian và hiểu ý nghĩa của các chỉ dẫn này. Bạn có thể đọc thêm trên tài liệu của ARM (https://developer.arm.com/docs/100069/latest/a64-general-instructions).

Sau đây là một số hướng dẫn quan trọng đối với phân tích của chúng tôi.

b - chèn nhánh cho nhãn, như câu lệnh jmp

bl - chèn nhánh có liên kết cho nhãn, như câu lệnh call

b.ne - chèn nhánh cho nhãn nếu không bằng nhau, như câu lệnh jne

b.eq - chèn nhánh cho nhãn nếu bằng nhau, như câu lệnh je

Hãy đi sâu vào assembly code. Việc đánh số đề cập đến các phần được đánh dấu trong đầu ra tháo gỡ GDB.

    1. Trong địa chỉ 0x4007bc, <main 4>, thanh ghi con trỏ stack (sp) được di chuyển đến thanh ghi x29. Sau đó, chúng tôi nhận thấy các đối số hàm chính được truy cập từ thanh ghi x29. Lưu ý offset (độ dời) 28 của thanh ghi x29 chứa argc và offset 16 chứa argv ( là input password của chúng tôi). Khi so sánh giá trị argc, nếu bằng 0x2, chúng tôi chèn nhánh (b.eq - chèn nhánh nếu bằng nhau) cho <main 52>.

    2. Ba dòng tiếp theo <main 52>, <main 56><main 60> mở rộng chiều dài của chuỗi argv từ 16 đến 24 (16 0x8 = 24) và được tham chiếu bằng thanh ghi x0.

    3. Sau đó, chúng tôi gọi (bl - chèn nhánh với liên kết) tới hàm <check_pass>. Bây giờ hãy tháo gỡ hàm <check_pass>.

gef> disas check_pass


Kỹ thuật dịch ngược x64 - Khái niệm cơ bản - Linux trên ARM64 - Ảnh 4.

4. Trong địa chỉ 0x400738, <check_pass 8>, chuỗi argv mới được sao chép được sao chép từ thanh ghi x0 sang thanh ghi x29 với offset 24. Sau đó chúng tôi thấy một số hoạt động canary stack, từ <check_pass 12> đến tận <check_pass 24>. Cái gì đó đang được lưu trữ trên thanh ghi x29 với offset 56 từ địa chỉ 0x411048, mà sau đó được so sánh ở phần cuối của hàm từ <check_pass 96> trở đi cho đến <check_pass 124>.

5. Quay trở lại phần thân của hàm <check_pass>, chúng tôi thấy rằng từ <check_pass 32> trở đi, một cái gì đó đang được truy cập từ 0x4008d0 và được lưu trữ vào thanh ghi x29 với offset 0x28 (40), có lẽ là mật khẩu bí mật chăng?

6. Sau đó, từ <check_pass 60> trở đi, thanh ghi x1 chỉ định dữ liệu được sao chép gần đây từ 0x4008d0 và thanh ghi x0 chỉ định chuỗi argv trong thanh ghi x29 với offset 24, tiếp theo là lệnh gọi hàm strcmp (chuỗi so sánh giữa x0 & x1) . Giá trị trả về của hàm strcmp được lưu trữ trong thanh ghi đa năng w0 16bit. Nếu các chuỗi bằng nhau, thì w0 được đặt thành 0x0, nếu không thì nó được đặt thành 0x1. Trở lại hàm <main>

7. Giá trị trả về của hàm <check_pass> được lưu trong thanh ghi w0, được sao chép vào thanh ghi x29 với offset 44. Sau đó, tại <main 76> chúng tôi so sánh giá trị của thanh ghi w0 để xem nó có bằng 0x1 hay không. Nếu không, chúng tôi nhảy (b.ne - chèn nhánh nếu không bằng nhau) tới <main 100>, điều này sẽ cho chúng tôi một thông báo thành công, và cuối cùng là thoát khỏi chương trình.

Bây giờ chúng tôi sẽ bắt đầu chương trình với một mật khẩu sai. Nhưng trước đó, chúng tôi phải thêm điểm ngắt, tại câu lệnh so sánh tại <main 76>.

gef> break *0x400804


gef> run pass123


Kỹ thuật dịch ngược x64 - Khái niệm cơ bản - Linux trên ARM64 - Ảnh 5.

Chúng tôi đã đạt đến điểm ngắt tại câu lệnh so sánh tại 0x400804, <main 76>. Ngoài ra, hãy quan sát rằng giá trị của thanh ghi x00x1. Tuy nhiên, con trỏ x0 bằng không ngoài thanh ghi w0 32 bit phụ, x0 chứa giá trị trả về của hàm <check_pass>. Nhưng dựa trên giả thuyết từ phân tích, giá trị này phải là 0x0 cho chương trình để hiển thị cho chúng tôi thông báo thành công.

Hãy để chúng tôi thay đổi giá trị của nó…

gef> set $x0=0x0


Kỹ thuật dịch ngược x64 - Khái niệm cơ bản - Linux trên ARM64 - Ảnh 6.

Bây giờ chúng ta hãy tiếp tục thực thi.

gef> continue


Kỹ thuật dịch ngược x64 - Khái niệm cơ bản - Linux trên ARM64 - Ảnh 7.

Lời kết

Hóa ra giả thuyết của chúng tôi là chính xác. Thay đổi giá trị của x0 từ 0x1 thành 0x0 đã thực hiện được thủ thuật. Điều đó có nghĩa là thủ thuật này sẽ luôn kiểm tra xem liệu w0 có được đặt thành 0x1 để hiển thị thông báo không chính xác hay không. Chúng tôi biết được điều này từ mã nguồn của chương trình. Do đó, quay trở lại hàm <check_pass>, chúng tôi đã nhận thấy một cái gì đó đang được sao chép từ địa chỉ 0x4008d0. Hãy kiểm tra một chút.

Kỹ thuật dịch ngược x64 - Khái niệm cơ bản - Linux trên ARM64 - Ảnh 8.

Ở đây nó không giống như bất kỳ hướng dẫn lắp ráp hợp lệ nào, nhưng việc lặp lại 53 là đáng ngờ, còn 41 là 'A' trong hệ thập lục phân. Cái này trông giống như một chuỗi liên tục. Hãy nhìn kỹ hơn. Gộp 10 dòng nữa từ địa chỉ 0x4008d0

Kỹ thuật dịch ngược x64 - Khái niệm cơ bản - Linux trên ARM64 - Ảnh 9.

Nhìn vào 0x4008d00x4008d4, chúng tôi có thể thấy rằng nó là một chuỗi 8 bit little-endian. Hãy thử giải mã nó xem…

Kỹ thuật dịch ngược x64 - Khái niệm cơ bản - Linux trên ARM64 - Ảnh 10.

Kết quả, ở đây chúng tôi có mật khẩu ban đầu "PASSWORD".

Kỹ thuật dịch ngược x64 - Khái niệm cơ bản - Linux trên ARM64 - Ảnh 11.

Đây chỉ là một ví dụ khác về phân tích các tệp nhị phân với GDB, nhưng trong một kiến trúc khác biệt. Tiếp tục, chúng tôi sẽ giải quyết các chương trình phức tạp hơn, với các kiến trúc không phổ biến và các tệp nhị phân khác thường.

Link gốc: https://niiconsulting.com/checkmate/2018/04/reverse-engineering-x64-basics-linux-on-arm64/

Theo Bizfly Cloud chia sẻ

>> Có thể bạn quan tâm: Kỹ thuật dịch ngược x64 cho người mới bắt đầu - Linux

BizFly Cloud là nhà cung cấp dịch vụ điện toán đám mây với chi phí thấp, được vận hành bởi VCCorp.

BizFly Cloud là một trong 4 doanh nghiệp nòng cốt trong "Chiến dịch thúc đẩy chuyển đổi số bằng công nghệ điện toán đám mây Việt Nam" của Bộ TT&TT; đáp ứng đầy đủ toàn bộ tiêu chí, chỉ tiêu kỹ thuật của nền tảng điện toán đám mây phục vụ Chính phủ điện tử/chính quyền điện tử.

Độc giả quan tâm đến các giải pháp của BizFly Cloud có thể truy cập tại đây.

DÙNG THỬ MIỄN PHÍ và NHẬN ƯU ĐÃI 3 THÁNG tại: Manage.bizflycloud

TAGS: linux
SHARE