REVERSING WITH IDA FROM SCRATCH (P15)

Posted: June 5, 2019 in IDA Tutorials, REVERSING WITH IDA FROM SCRATCH (P15), Uncategorized
Tags: ,

Vài dòng lan man:

Mới đây Sẻ đệ (yeuchimse) đóng Blog, làm tôi cũng nghĩ hay thôi, dăm ba cái Blog, view cũng lèo tèo, viết lách vừa tốn thời gian chỉnh sửa, chụp choẹt…. chắc cũng đóng nốt cho nhanh...

Anh em trong Gờ-rúp “kín” thi thoảng lại: “Già rồi, viết lách cái gì. Định kiếm fame đến bao giờ!!“. Thôi thì …..

………..

…………†

Dành cho những bạn nào chưa nghe bản guitar solo này, hãy xem cách Steve Vai “nựng” đàn để tìm cho mình những cảm hứng riêng

Trong phần trước, tôi đã cho các bạn thấy một vài phương pháp để có thể phát hiện và tới được OEP trong một file bị packed. Phần này, chúng ta sẽ tiếp tục với hai bước còn thiếu mà tôi đã đề cập, đó là: dump filerebuild IAT.

Dump file

Các bạn có thể thực hành lại để tới OEP và Reanalyze chương trình, sau khi thực hiện xong ta có mọi thứ sẵn sàng cho việc Dump file. Hành động dump file có thể hiểu một cách “chân phương” là sau khi tới được OEP, toàn bộ code gốc của chương trình cùng các hàm APIs đã được “bung” đầy đủ trên memory, lúc này ta thực hiện thao tác dump để lưu thành một file.

Ở đây, tôi sẽ sử dụng một IDC Script để thực hiện công việc này (không phải là Python script). Nội dung của script như sau:

static main()
{
	auto fp, ea;
	fp = fopen("dumped.bin", "wb");
	for (ea = 0x400000; ea < 0x40b200; ea++)
		fputc(Byte(ea), fp);
}

Trong script trên, chúng ta thực hiện dump từ địa chỉ ImageBase0x400000 tới địa chỉ lớn nhất mà ta thấy trong tab IDA Segments. Đó chính là section cuối cùng của file thực thi, trong trường hợp này là section OVERLAY, tại địa chỉ 0x40b200.

Copy&Paste script trên vào Notepad và lưu lại với tên là dumper.idc. Quay lại IDA và cho thực thi script này thông qua File > Script file  (IDA hỗ trợ cả Python và IDC scripts). Kết quả tôi có được tập tin dumped.bin như hình dưới:

Tôi tạo một bản sao của file đã dump ra và đổi tên nó thành một tập tin có đuôi mở rộng .exe:

Các bạn để ý thì thấy rằng, file sau khi đổi tên không hề có icon mặt cười như ở file gốc. Để fix được thiếu sót này các bạn có thể sử dụng chương trình PE Editor v1.7 (download tại đây: https://mega.nz/#!uGpHkKTD!bkxqBmj2Ib0FC6QDln7Cf1DnXSqRazinTuXNbAdFCjE). Chạy chương trình, load file exe và sau đó chọn sections:

Tại màn hình chứa các thông tin về Sections:

Nhấp chuột phải vào một section bất kì và chọn Dumpfixer, tương tự như hình dưới đây:

Bước dumpfixer này thực chất là thiết lập cho Raw_size = Virtual_sizeRaw_offset = Virtual_offset. Kết quả có được như sau:

OK, như vậy là đã lấy lại được icon của file. Tạm thời ở thời điểm này, file đã dump ra được tốt rồi, ít nhất là icon đã hiển thị đúng, nhưng tuy nhiên file này vẫn chưa thể thực thi được bình thường bởi vì cần phải sửa cả IAT nữa.

Rebuild IAT

IAT là gì?

IAT viết đầy đủ là Import Address Table, là một bảng nằm trong file thực thi và được sử dụng khi chương trình hoạt động. Bảng này lưu tất cả địa chỉ của các hàm được import, từ đó chương trình có thể sử dụng để chạy trên bất kỳ máy nào (chính xác là trên bất kỳ môi trường OS nào).

Nếu IAT là đầy đủ và chính xác thì khi ta đưa tập tin thực thi cho một người khác, IAT sẽ được điền đầy đủ các giá trị tương ứng (địa chỉ các hàm APIs) trên máy đó, bất kể hệ điều hành hoạt động trên máy đó là gì, tính tương thích sẽ được duy trì và chương trình sẽ thực thi được một cách bình thường.

Nghĩa là, IAT sẽ luôn nằm ở một vị trí nhất định trong mỗi tệp thực thi, và sẽ có các vị trí cố định cho mỗi hàm để điền vào. Các bạn nhớ lại phần trước tôi có so sánh (chưa giải thích kĩ) sự khác biệt giữa hình ảnh từ tệp tin bị packed khi ta tới được OEP (trước khi thực hiện việc dump) với file gốc ban đầu:

Cả hai đều hiển thị địa chỉ 0x403238 và dường như chúng có cùng nội dung. Bây giờ chúng ta mở lại file gốc:

Quan sát ở bên dưới thấy có địa chỉ file offset (trên ổ đĩa) là 0x1038. Tôi dùng trình Hex Editor là HxD để load file gốc và tìm tới địa chỉ offset này:

Tại HxD, ta thấy nội dung tại offset 0x10380x355e. Nếu tôi cộng giá trị 0x355e vào địa chỉ ImageBase0x400000, tôi có kết quả là 0x40355e. Vậy có thông tin gì lưu tại địa chỉ mà ta vừa tính toán được? Để làm rõ hơn, tôi cho IDA load lại file gốc cùng với tùy chọn Manual Load, mục đích để IDA nạp tất cả các sections của tập tin thực thi:

Sau khi chấp nhận cho tải tất cả các sections, đợi khi IDA phân tích toàn bộ file xong, ta đi đến địa chỉ 0x40355e. Ở bên phải lúc này ta thấy được tên của hàm API là:GetModuleHandleA

Tương tự đối với các ví trí khác mà tôi highlight làm ví dụ như hình dưới:

Như vậy, từng giá trị trên sẽ được cộng với địa chỉ ImageBase để từ đó tìm ra tên của các hàm tương ứng. Và cũng từ các tên hàm đó sẽ lấy được các địa chỉ tương ứng của từng hàm trong máy lúc runtime. Ví dụ, như trong trường hợp này nó sẽ tìm địa chỉ của hàm GetModuleHandleA() trên máy của chúng ta và sửa lại giá trị 5e 35 bằng địa chỉ thật của hàm API (ví dụ: 757116D0  kernel32.GetModuleHandleA)

Đó chính là lý do tại sao một tệp tin thực thi có thể chạy trên bất kỳ máy nào, bởi vì nó sẽ luôn luôn tìm tên của API tương ứng trong mỗi mục của IAT và tìm ra địa chỉ hợp lệ của hàm tương ứng với từng máy khác nhau khi nó thực thi. Và đó cũng là lý do tại sao trên bất kỳ máy nào nếu tôi thực hiện một lời gọi hàm như sau:

CALL [0x403238]

Chương trình sẽ luôn luôn hoạt động bởi vì 0x403238 là mục IAT của hàm GetModuleHandleA(), nội dung thay đổi chính là địa chỉ hàm mà hệ điều hành sẽ lưu lại bằng cách chỉnh sửa lại giá trị ban đầu 5e 35 (trỏ đến chuỗi tên của hàm sau khi cộng thêm ImageBase).

Giữ nguyên hai màn hình IDA đang mở (một cho file bị packed và đang dừng lại ở OEP; còn 1 cho file gốc), ta mở thêm một IDA thứ ba và load file đã dumped mà tôi vừa đổi tên là:dumped.exe.

Chuyển tới địa chỉ của IAT là 0x403238:

Quan sát bên dưới, ta thấy địa chỉ file offset lúc này là 0x3238, địa chỉ này không khớp với file gốc bởi sau khi thực hiện dumpfixer đã thay đổi kích thước trên đĩa (Raw Size) bằng với kích thước ảo (Virtual size), dẫn tới địa chỉ offset cũng thay đổi, cho nên địa chỉ của hàm API GetModuleHandleA() cũng vì thế mà thay đổi theo.

Sử dụng HxD mở file dump để kiểm tra, tìm tới offset 0x3238. Kết quả ta thấy như sau:

Chúng ta thấy rằng giá trị tại đó là địa chỉ của một hàm API, chứ không phải là một offset để cộng với ImageBase nhằm tìm tên của hàm API nữa. Do vậy, ta có thể hiểu rằng khi chương trình chạy, hệ thống sẽ lấy được địa chỉ chính xác của hàm API và lưu địa chỉ này ở đó, vậy nên khi thực hiện dump chương trình thì địa chỉ của hàm GetModuleHandleA() cũng sẽ được lưu kèm theo file đã dump. Để kiểm tra ta có thể tới địa chỉ của hàm này trong IDA như hình dưới:

Vậy vấn đề ở đây là gì?

Khi ta thực thi file dump, hệ thống sẽ tìm kiếm IAT và lấy ra giá trị này, sau đó cộng với địa chỉ ImageBase để từ đó tìm ra tên của hàm, cuối cùng sẽ tìm địa chỉ của hàm … Nhưng vì khi ta thực hiện dump file đã vô tình đã phá vỡ nguyên tắc này, do giá trị được lưu cuối cùng chính là địa chỉ thật của hàm API, và bởi vậy chương trình sẽ bị crash khi khởi chạy vì nó không thể điền vào IAT chính xác.

Để giải quyết được vấn đề này ta cần phải tìm một cách để sửa lại IAT và khôi phục lại tất cả các offset – trỏ vào các chuỗi chứa tên của các hàm API. Công việc này nếu làm bằng tay sẽ rất oải, do số lượng hàm API rất nhiều, vậy nên chúng ta sẽ sử dụng một công cụ hỗ trợ có tên là Scylla:

https://forum.tuts4you.com/files/file/576-scylla-imports-reconstruction/

Chạy ứng dụng này. Tại chỗ Attach to an active process, tôi chọn process của file bị packed:

Nhớ rằng bạn vẫn phải giữ nguyên màn hình của IDA đang dừng lại tại OEP.

Tại Scylla, thay đổi ​OEP​ thành giá trị chính xác là 0x​401000:

Tiếp theo nhấn​ ​IAT​ ​Autosearch, một bảng thông báo xuất hiện:

Điều này có nghĩa là, IAT bắt đầu tại địa chỉ 0x403184 và có kích thước là 0x108. Nhấn OK để chấp nhận, sau đó nhấn Get Imports, ta có kết quả như sau:

Các bạn thấy rằng Scylla đã lấy được tất cả thông tin trừ 1 giá trị khi ta nhấn nhấn Show Invalid. Mặt khác, chúng ta thấy rằng offset 0x3238 trong packed file tương ứng với GetModuleHandleA(), như vậy là Scylla đã làm việc rất tốt:

Tại packed file, đi tới địa chỉ API mà Scylla thông báo là không hợp lệ tại 0x403208 (kết quả có thể khác ở trên máy của các bạn), để xem tại đó có thông tin gì:

IDA cung cấp thông tin là unk_6E33AC50, nghĩa là nó chưa nhận biết được code tại đây. Đi tới địa chỉ này:

Ta tới thư viện apphelp.dll, nhấn C để chuyển đổi các bytes tại đây thành code, tạo hàm và cho Reanalyze lại chương trình:

Kết quả ta có được code thuộc thư viện apphelp.dll sẽ gọi tới hàm API GetDC(). Nhấp đúp chuột tại offset szGetdcWndPDcP, ta tới đây:

Như vậy, hàm API GetDC() thuộc thư viện user32.dll. Do đó, quay trở lại màn hình của Scylla, nhấp đúp chuột invalid API này và chọn như hình bên dưới để sửa lại:

Nhấn OK để sửa và như vậy ta sẽ có được các hàm API hợp lệ. Nếu ta bấm Show Suspect, Scylla sẽ cung cấp các APIs mà nó còn nghi ngờ, để xác minh ta lại quay lại IDA và đi đến các địa chỉ 0x4031BC, 0x40322C0x403278 (kết quả này có thể khác trên máy của các bạn):

Thông tin mà IDA cung cấp như sau:

Kết quả thu được là chính xác hoàn toàn rồi, cuối cùng là nhấn nút Fix Dump:

Scylla sẽ lưu file đã fix với tên mới là dumped_SCY.exe. Nhấp đúp vào để chạy thử ta thấy crackme chạy ok như file gốc:

Chúng ta thấy rằng khi mở file đã fix ở trên trong IDA, ta thấy nó bắt đầu từ OEP 0x401000 và tên của APIs đã được fix hoàn toàn và trông không khác gì với bản gốc:

Như vậy qua bài viết này, tôi đã hướng dẫn các bạn thực hiện xong toàn bộ quá trình unpack một file được pack bằng một packer dễ. Phần tiếp theo ta sẽ xem xét một vài ví dụ khó hơn.

Phần này xin được kết thúc tại đây. Hẹn … à mà thôi! Khi nào có hứng tôi đưa lên tiếp…..

Image result for brain dump 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. Mong anh luôn giữ được lửa để ra tiếp các phần mới 😀

  2. john says:

    hello.thx.but how can i find this article in english language?

  3. lph77 says:

    hi anh, anh cho em được hỏi có phải trong mọi trường hợp file bị pack, cách unpack là tìm đến địa chỉ code ban đầu sẽ được load ra như ở đây là tại địa chỉ 401000 hay không

  4. kienmanowar says:

    Hi lph77,

    Nếu được như thế là đẹp nhất. Tuy nhiên, có những trường hợp tới địa chỉ gọi là near OEP, tuy nhiên nếu có được full IAT và fix file ổn thì vẫn có thể chạy được bình thường.

    Regards,

  5. ripsolo says:

    @@ ôi sao a lại thế, bọn em còn cần anh mà @@. em theo a đến IDA đây nè.hihi :), truyền cảm hứng tiếp anh ơi :). p/s cơ mà sao em cũng thích Rock, thần tượng Slash (Guns N’ Roses) ^^, thích luôn RE , phải chăng nhưng ai thích Rock đều có chút không bình thường phải ko đại ca :))

  6. kienmanowar says:

    @ripsolo: Phần đa những người nghe nhạc Rock thường là người sống nội tâm, thẳng tính và đôi khi thích nổi loạn :D.

    Mình anh thì cũng chỉ là một ngọn nến phập phù thôi em :D. Dăm ba cái bài viết dạo là do anh thích viết và muốn viết để chia sẻ, đơn giản nó chỉ thế thôi. Chứ ngồi lọ mọ viết lách, chụp choẹt, chỉnh sửa … rồi đọc lại .. thế này đâu ai trả lương đâu em :)))

    Regards,

  7. Lan Vu says:

    Anh cho em hỏi là nếu em muốn dump một file dll trên IDA nhưng lúc debug nó được load bằng 1 file exe khác nên trong tab Segmentation sẽ có rất nhiều các dll khác nữa, vậy có cách nào để em xác định được địa chỉ và dump chỉ riêng file dll đó không ạ? Em đã thử dump các segment có tên giống tên dll mà em muốn nhưng có vẻ điều đó chưa đúng. Em cảm ơn ạ!!

  8. kienmanowar says:

    Chào em,

    Anh vẫn chưa hiểu ý của em lắm! Nếu em đã biết được Dll được load bởi một exe khác thì phải xác định được Dll đó load ở địa chỉ base nào chứ nhỉ? Còn không em có thể sử dụng các công cụ khác để dump như ProcessHacker hoặc Scylla để pick DLL từ một file exe.

    Regards,

  9. lanleft says:

    Dạ vâng,

    Chắc là do em chưa tìm hiểu kỹ đã hỏi. Em cảm ơn anh nhiều!! Blog của anh rất hữu ích ạ.

  10. kienmanowar says:

    Không có gì em! Ai cũng đều có những câu hỏi tương tự như em thôi.
    Hi vong blog của anh giúp ích được cho sở thích / công việc hiện tại của em.

    Regards,

  11. n0tduck says:

    Hi anh,

    Cảm ơn anh vì bài post, em thắc mắc tại bước dumpfixer , ngoài việc để lấy lại icon thì có tác dụng gì nữa ko? Vì khi em cố ý bỏ qua bước này và nhảy trực tiếp sang rebuild IAT thì chương trình ko chạy được.

    Và tại bước dumpfixer sau khi fix, dùng PeInternals kiểm tra IMAGE_SECTION_HEADER UPXO thì Pointer To Raw Data là 1000, trước đó là 0, em goo-fu vài đường thì ko thấy mô tả làm sao PE Editor làm được chuyện này. Suy nghĩ mãi cũng ko ra vì trước giờ sau khi compile thì trường PTRD nó là mặc định thôi 😐 , em tham khảo link bên dưới cũng ko thấy nhắc làm sao thay đổi trường này.
    https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#section-table-section-headers

    Sincerely,

  12. kienmanowar says:

    Chào em,

    Thông tin em hỏi nó liên quan tới kiến thức về PE file là map và unmap. Bình thường, khi pe file của em nằm trên disk thì sẽ sử dụng các trường Raw offset và Raw size để tìm kiếm thông tin liên quan tới các sections. Khi thực thi, thì windows loader sẽ làm nhiệm vụ mapping file từ disk lên memory dựa theo thông tin về ImageBase, Virtual offset, Virtual size, Relocation, Section Align ..

    Khi em debug & unpack, thì em đang làm việc với file đã được mapping lên Virtual memory (relative với base address). Do đó, khi em dump file ra disk nó sẽ giữ thông tin của các sections được mapped, dẫn đến việc em truy xuất thông tin về Raw offset sẽ không chính xác. Cho nên, bước dumpfixer như anh đề cập, thực chất là việc Unmapping, thiết lập cho Raw_size = Virtual_size và Raw_offset = Virtual_offset.

    Regards,

  13. n0tduck says:

    Hi anh,

    Em đang làm 1 bài tập unpacking ngoài lề bài viết dựa theo những kiến thức do anh hướng dẫn. Sau khi dump file, unmapping, fixing IAT thì section .data bị thiếu mất Image_SCN_MEM_WRITE dẫn đến lỗi “The instruction at 0xXXXXXX referenced memory at 0xXXXXXXX. The memory could not be written. Em sữa bằng cách thêm “memory write” Section Characteristics của .data. Em muốn google nhưng ko biết tìm từ đâu hết anh -_-

    Thân,

  14. Cong Trung says:

    Hi anh,

    Em đang làm 1 bài unpack UPX. Sau khi em dump ra và dùng scylla để fix thì còn 1 hàm là ___guard_check_icall_fptr. Sau khi tra gg thì thuộc ntdll!LdrpValidateUserCallTarget nhưng khi em search trong SysWOW64\ntdl.dll thì lại không có LdrpValidateUserCallTarget.

    Anh có thể chỉ cho em tại sao lại bị trường hợp như vậy được không ạ?

  15. Cong Trung says:

    Dạ vâng, em cám ơn anh

  16. Spoilsport says:

    Dù hơi muộn, nhưng em cũng muốn cảm ơn anh ạ!! Em mới học Reverse, những kiến thức chia sẻ tâm huyết như này thật sự đã giúp những sinh viên như em rất nhiều!!

  17. kienmanowar says:

    Tks em! Rất vui khi những gì anh chia sẻ trên blog này giúp ích được cho công việc học tập của tụi em!

    Regards,

Leave a comment

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