# Reversing with ida - Mở file bằng HXD.`PE..d†`(64-bit); `PE..L` (32-bit) - Khi load file mới vào ida thì mặc định ida sẽ không load kèm `PE header` hay `resource section`. Vậy nên để ida load được thì ta tích chọn manual load: ![](https://hackmd.io/_uploads/S1Q5_DH1T.png) - Để hiển thị địa chỉ bộ nhớ bên cạch các lệnh thì tại **Options** – **General**, tích chọn **Line prefixes**. Nếu muốn hiển thị các byte bên cạnh mỗi lệnh thì thay đổi giá trị của **Number of opcode bytes** thành số byte muốn xem - Để lưu cách setup thì vào **Windows** - **Save Desktop** và tích chọn **Default** để lưu vào cấu hình mặc định. - Sử dung `idc.get_bytes(address,number of bytes)` để lấy từng đó bytes từ địa chỉ input: ![](https://hackmd.io/_uploads/SkZ0lRr1T.png) Ngoài ra ta có thể vào **Execute script** để nhập lệnh python: ![](https://hackmd.io/_uploads/rkCf70BJT.png) Sau khi chạy script thì sẽ có file output ở cùng thư viện với file exe. - View > Open subviews > Patched Bytes (Ctrl+Alt+P) để có thể khôi phục lại các giá trị ban đầu: ![](https://hackmd.io/_uploads/Hyycc0Hy6.png) - `NEG ` chuyển đổi A -> -A - "JMP SHORT" là lệnh nhảy gồm 2 byte. Hướng nhảy chỉ định bởi byte thứ 2. (Byte t2 có gtri bao nhiêu thì sẽ nhảy từng đấy bước). Bước nhảy cao nhất là 0x7f - "JMP LOC_ADDRESS" là lệnh nhảy đến địa chỉ ở đằng sau tiền tố "loc_" - Các lệnh jump: ![](https://hackmd.io/_uploads/BJiDhYd16.png) ![](https://hackmd.io/_uploads/BkaF2tuyp.png) ![](https://hackmd.io/_uploads/S1X2hFuJT.png) **Phân tích CrueHead** - View > Open Subview > Segments, ta sẽ thấy các segments. Ở đây ta thấy khi ta ấn OK mà không chọn `manual load` nên IDA không nạp toàn bộ tệp mà chỉ tải 1 số sections quan trọng đầu tiên và được load tại địa chỉ 0x400000 ![](https://hackmd.io/_uploads/SyCh5lYy6.png) - READ(R ), WRITE(W), EXECUTE(X), DEBUGGER(D), LOADER(L). Các cột **D** trống vì khi ta gọi các section này ở chế độ debugger. - Để tạo snapshot giúp quay trở lại trạng thái trước khi thay đổi thì ta có thể ấn `Ctrl + Shift + W` hoặc chọn `Take database snapshot`. Nếu muốn restore thì vào **View > Database Snapshot Manager(Ctrl+Shift+T)**, chọn bản snapshot và nhấn **Restore** - Để tìm kiếm các strings trong IDA, chọn View > Open Subview > Strings (phím tắt là Shift+F12): - Trỏ vào địa chỉ và ấn X để tìm kiếm các đoạn code có sử dụng tới chuỗi này ![](https://hackmd.io/_uploads/S1-_2w6kp.png) - Vào menu Jump và chọn Mark Position (Alt+M) để về sau có thể quay lại địa chỉ đó bằng cách Jump > Jump To Marked Position(Ctrl + M) - Khi muốn thay đổi thành mảng thì ta nhấn chuột phải tại biến và chọn **Array**. Sau khi chuyển đổi xong ta sẽ được kết quả như dưới đây: ![](https://hackmd.io/_uploads/rk7yYuaka.png) ![](https://hackmd.io/_uploads/rk1iddpya.png) - Bên dưới có “s” cung cấp thông tin giá trị thanh ghi EBP đã lưu của hàm gọi (old frame) và “r” cung cấp thông tin về return address trước khi truy cập vào hàm. ## Debug - Nhấn G để nhảy đến địa chỉ bộ nhớ bất kì - Để xem được danh sách các Breakpoints đã đặt thông qua Breakpoint list (Ctrl+Alt+B) - Khi cmp thì nếu true thì các cờ bên dưới sẽ được bật theo lệnh tương ứng: ![](https://hackmd.io/_uploads/rkeOMt6J6.png) - Thay đổi giá trị của cờ hay thanh ghi thì ta ấn đúp hoặc chuột phải vào thanh ghi hoặc cờ muốn sửa ## CARRY FLAG (CF) - 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 thiết lập 1 khi xảy ra tràn, ngược lại nó bằng 0. - 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 kích hoạt khi kết quả của việc tính toán là số âm ## ZERO FLAG (ZF) - 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 - Khi ta muốn gộp các nhóm khối lệnh thì ta nhấn **Ctrl** và chọn lần lượt các khối cần nhóm. Sau đó, nhấn chuột phải trên một khối và chọn Group Nodes. Cuối cùng đặt tên cho node sau khi nhóm lại. - Còn khi muốn nhóm các câu lệnh thành 1 hàm thì ta nhấn chuột phải vào dòng lệnh và chọn **Set Type (nhấn Y)**. Khi đó IDA sẽ cố gắng khai báo lại hàm cùng với các tham số của hàm sao cho tường minh nhất - `SHL` tương đương với nhân với 2 ## IpyIDA - [IpyIDA](https://github.com/eset/ipyida). Sau khi tải xong thì vào **Edit > Plugins > IpyIDA**. - Để tra cứu tính năng ta ấn phím **?** - Để xóa thông tin phía trên nhấn phím **ESC** - Nhập tên module + **?** thì sẽ cung cấp cho ta thông tin nhanh về module đó: ![reference link](https://hackmd.io/_uploads/SkdQt661a.png) - **%hist** để hiện thị thông tin lịch sử về các lệnh: ![](https://hackmd.io/_uploads/rJht9p6yT.png) - **%edit** sẽ mở ứng dụng notepad của Windows. Còn **%edit x-y** sẽ mở một notepad chứa các lệnh đã gõ nằm trong khoảng đó: ![](https://hackmd.io/_uploads/ryRBjpa1a.png) - **%history -n** thêm số dòng bên cạnh các câu lệnh đã sử dụng: ![](https://hackmd.io/_uploads/BJAKjapJp.png) [Nhiều câu lệnh khác](http://ipython.org/ipython-doc/3/index.html) - Khi ta dùng plugin này có thể sử dụng lệnh **%edit** để lưu các lệnh thành python script và thực thi thông qua **File-Script file** cũng ra kết quả tương tự. - Ta dùng hàm `ScreenEA()` để lấy địa chỉ hiện tại của con trỏ. Lệnh `idc.GetDisasm (address)` sẽ cung cấp cho chúng ta lệnh ASM tại nơi con trỏ đang đứng - Với câu lệnh `idc.GetOpnd`, ta có thể lấy được thông tin về toán hạng đầu tiên hoặc thứ hai của câu lệnh. - Đoạn code dưới đây thực hiện in ra tên của hàm hiện tại: ![](https://hackmd.io/_uploads/H1pby0aJT.png) - Tên của tất cả các hàm được liệt kê thông qua đoạn code sau: ![](https://hackmd.io/_uploads/rJ8fkCp1T.png) - Các câu lệnh bên trong hàm: ![](https://hackmd.io/_uploads/ByQ7yATJa.png) ## File Packed - Là file được ẩn code gốc của chương trình và lưu lại bằng cách áp dụng kĩ thuật nén hoặc mã hóa để không bị reverse một cách dễ dàng. - Nó được chèn thêm 1 đoạn section nhận code đã mã hóa và giải mã trong bộ nhớ và lưu code vào bất kì section nào hoặc chính nó và cuối cùng nhảy tới code gốc. ![](https://hackmd.io/_uploads/H1StVNAJp.png) - Để thực hiện reverse file packed thì khi load vào ida ta chọn **Manual load** và bỏ lựa chọn **Create imports segment** - Khi ta tìm được ra **OEP(Original Entry Point)** thì ta đặt breakpoint ở lệnh `jmp` đến đó và debug. Khi debug đến lệnh nhảy thì nhấn `F8`. - Để chuyển được sang chế độ graph thì ta chọn **Reanalyze program**: ![](https://hackmd.io/_uploads/r1_-zsAJ6.png) - Cách 2 ta đặt 1 breakpoint để dừng khi dòng lệnh đó được thực thi. Do ta không biết được chương trình sẽ chạy dòng nào trước nên ta chỉnh size của breakpoint thành size của code gốc: ![](https://hackmd.io/_uploads/Sy9fmNgx6.png) - Sau khi code gốc không confbij ẩn thì chúng ta tiến hành dump để lưu thành 1 file bằng cách dùng IDC Script: ``` static main() { auto fp, ea; fp = fopen("dumped.bin", "wb"); for (ea = 0x400000; ea < 0x40b200; ea++) fputc(Byte(ea), fp); } ``` - Do ImageBase là 0x400000 và section cuối cùng của file là OVERLAY ở địa chỉ 40B2000: ![](https://hackmd.io/_uploads/SJOQ9tLxa.png) - Để chạy thì ta vào **File-> Script command(Shift+F2)** và dán script vào, sau đó ấn Run. ![](https://hackmd.io/_uploads/Hy5niF8l6.png) - Sau khi run chúng ta được file dumped.bin. rồi ta đổi đuôi file thành .exe: ![](https://hackmd.io/_uploads/HySj2FIg6.png) - Sau khi đổi tên không có icon mặt cười như file gốc. Vậy để fix thì ta mở file bằng PEditor: ![](https://hackmd.io/_uploads/B1ZHx9Iea.png) - sau đó ta ấn vào section-> chọn section bất kì -> dumpfixer: ![](https://hackmd.io/_uploads/SkHig58xT.png) ![](https://hackmd.io/_uploads/BJxRlqUgT.png) Lúc này đã khôi phục lại được icon - Do khi ta dump file thì vô tình khiến cho giá trị tại IAT(Import Address Table) là địa chỉ của hàm API chứ không phải là offset để cộng với ImageBase để tìm tên của hàm API. Nên để fix thì ta dùng Scylla: ![](https://hackmd.io/_uploads/B1xunQcLxp.png) Ở phần đầu ta chọn process của file bị packed.Và thay đổi OEP thành 0x401000 sau đó ấn IAT Autosearch: ![](https://hackmd.io/_uploads/BkywVqUgp.png) Sau đó ấn ok-> Get Imports: ![](https://hackmd.io/_uploads/ByLnEqIgp.png) Lúc này ta thấy lỗi ở 000031AC nên ta xem trong packed file tương ứng với gì: ![](https://hackmd.io/_uploads/H1awqlvgT.png) - Sau khi nhấn vào `unk_72798010` và nhấn **C** và **P** thì ta được như sau: ![](https://hackmd.io/_uploads/SyjPsxwxT.png) - Ấn đúp chuột vào invalid API tên Scylla để sửa invalid API. - ![](https://hackmd.io/_uploads/BJLnTgvg6.png) Sau đó ấn fixdump để lưu file đã fix. - Khi ta thấy Scylla detect nhầm invalid API thì ta nhấn chuột phải và chọn Cut Thunk để loại bỏ giá trị Invalid này ra khỏi IAT. ### Tìm OEP bằng phương pháp PUSHAD-POPAD - Đặt breakpoint sau lệnh pushad - Debug đến breakpoint đó - Vào địa chỉ của thanh ghi **ESP** - Đặt breakpoint tại đó và chỉnh thành lệnh Read và Write - Nhấn F9 - Sau đó tìm tới lệnh nhảy tới địa chỉ OEP và đặt breakpoint và jmp vào như bth ### Fix lỗi không run được file sau khi Fix Dump - Load vào IDA, vào phần HEADER - Patch để sửa trường **Dll Characteristics** = 0 - Biến var_4 này để bảo vệ stack không bị overflow: ![](https://hackmd.io/_uploads/SJLV-XwgT.png) - **_security_cookie** là một giá trị ngẫu nhiên Ta dùng **Greatis WinDowse** để có thể xác định được Control ID của texbox, cửa sổ để khi reverse ta có thể biết được đâu là code của text box đó Nhấn **Y** để set type cho hàm ## Vulnerabilities - Để tìm hàm main thì ta tìm kiếm các tham số argc hoặc argv trong cửa sổ Names - Khi dùng hàm gets_s để copy input là 1 số có dấu thì chương trình sẽ truyền vào số không dấu tức số dương cực lớn sẽ được truyền vào hàm => gây tràn ### BinDiff - Vào plugin Bindiff -> Diff Database...-> tìm database của file muốn so sánh - Ở cột Similarity trong cửa sổ **Matched Functions**. Nếu giá trị trả về = 1 thì 2 hàm giống nhau còn nếu < 1 thì có sự khác biệt - Khi thấy điểm thương đồng <1 thì ta nhấn chuột phải và chọn View Flowgraphs(Ctrl+E) - Khối nào Highlight màu xanh là code giống nhau còn màu vàng là khác nhau Tại cột Change sẽ cung cấp cho ta thông tin mô tả sự khác biệt giữa hai hàm được so khớp: (I): có sự khác biệt hoặc thay đổi về số lượng lệnh. (J): có sự khác biệt trong rẽ nhánh. ( C): có thay đổi trong lệnh call. (E): Entrypoint không khớp hoặc khác nhau. (G): có thay đổi về cấu trúc trong hàm, ví dụ số lượng basic blocks. Bật Stack Pointer trong phần general để thấy được sự thay đổi của esp ## Struct - Lệnh mov sẽ không làm thay đổi giá trị của esp - Ta có thể lợi dụng Overbuffer để điền luôn những vùng nhớ tiếp theo để đạt được mục đích - Tab **Local Types** giúp chỉnh sửa và thêm các struct theo định dạng C++. - Thông thường, các buffer sẽ được truy xuất thông qua lệnh LEA - Để gộp thành 1 struct thì đánh dấu các biến cần gộp, sau đó chọn meun Edit > Create struct from selection - Để chọn structure offset cho biến thì ta nhấn ‘T’ (Structure offset) - Để tạo struct, vào tab Structures và nhấn **Insert** - Tạo trường trong struct thì nhấn **D**, muốn đổi kích thước thì nhấn D nhiều lần. Sau đó nhấn chuột phải và chọn **Expand struct type… (Ctrl+E).** để thay đổi kích thước của struct - Để đổi struct thì ta nhấn chuột phải vào struct muốn thay đổi và chọn **Convert to struct*** - Để gán struct thành kiểu struct khác thì ta nhấn ALT + Q (Edit > Struct var…) Để nhận dạng module cần thiết thì ta truy cập menu Debugger > Debugger windows > Module list> Tìm tên module > Analyze module > Load debug symbols - Run until return (Ctrl+F7) ## DEP ### ROP (Return-Oriented Programming) - Nhảy tới code gọi là **gadgets** -> gọi các hàm API cho phép thay đổi quyền thực thi trên stack hoặc heap -> nhảy tới đó để thực thi - Phương pháp chung: input giá trị vào các thanh ghi -> nhảy tới 1 gadgets **PUSHAD - RET** - Các lệnh call tới API thường sẽ thông qua offset **(off_)** - Khi cần tìm opcode nào đó thì vào **Search > sequence of bytes (Alt + B)** Để tìm kiếm các đoạn code có lệnh `ret` ở cuối thì ta nhấn chuột phải tại module cần tìm -> Search gadgets - Muốn tính giá trị của CANARY(giá trị ngẫu nhiên) tại thời điểm thực thi thì: `CANARY = _security_cookie xor EBP` - SEH (Structural Exception Handling) chứa các danh sách liên kết. Khi có ngoại lệ, hệ điều hành sẽ duyệt tới danh sách đó. - Để kiểm soát exception: (1) một con trỏ tới “Bản ghi đăng ký ngoại lệ (Exception Registration Record)” hiện tại (SEH) (2) một con trỏ tới “Bản ghi đăng ký ngoại lệ tiếp theo (Next Exception Registration Record)” (nSEH). - Debugger > Debugger windows > SEH list ta sẽ thấy SEH của từng cấu trúc trong Stack: ![](https://hackmd.io/_uploads/S1pKu8gWa.png) - Nhấn Alt + I để mở **search immediate value** - SafeSEH bảo vệ exceptional handler chain bằng cách bổ sung thêm một bảng xử lý ngoại lệ an toàn - Alt + L để đánh dấu điểm bắt đầu kết hợp với phím G để nhảy tới địa chỉ mong muốn. -