REVERSING WITH IDA FROM SCRATCH (P16)

Posted: June 25, 2019 in IDA Tutorials, REVERSING WITH IDA FROM SCRATCH (P16)
Tags: ,

Trước khi tiếp tục tìm hiểu thêm về các chủ đề khác, chúng ta sẽ thực hành thêm một vài bài tập unpack với các packer khác. Trong phần này target sẽ là UnPackMe_ASPack 2.2 (https://mega.nz/#!7XJ33I6D!DvNo6dNeCeyTDoXpSM9zeZWIi1kpALs26oCNd2tCUbY)

Load file bị packed vào IDA:

Ta dừng lại tại Entry Point của file, tại đó bắt đầu bằng một lệnh PUSHAD. Lệnh PUSHAD này thực hiện lưu toàn bộ giá trị hiện thời của các thanh ghi vào Stack theo thứ tự như sau:

Trực quan hơn các bạn có thể xem ví dụ minh họa dưới đây:

Ngược lại với PUSHAD, ta có lệnh POPAD, là lệnh lấy các giá trị từ stack và lưu lại vào các thanh ghi theo thứ tự như mô tả bên dưới (giá trị của thanh ghi ESP trên Stack sẽ được bỏ qua. Thay vào đó nó sẽ được tăng lên sau khi mỗi thanh được pop ra).

Đối với các trình packer đơn giản, hầu hết chúng đều bắt đầu với lệnh PUSHAD để lưu trạng thái ban đầu của các thanh ghi khi bắt đầu và sử dụng POPAD để khôi phục lại các giá trị đã lưu, trước khi nhảy tới OEP để thực hiện code của chương trình đã “rã code” hoàn toàn trong bộ nhớ.

Nhờ vào dấu hiệu này, chúng ta có thể dễ dàng tìm thấy OEP bằng cách sử dụng phương pháp PUSHAD-POPAD. Tuy nhiên, với các trình packer tiên tiến hơn về sau, các nhà phát triển đã nhận ra được điểm yếu này và tránh sử dụng những câu lệnh trên.

Vậy phương pháp này là như thế nào? Chúng ta hãy cùng xem xét với file đã load.

Trước tiên, chúng ta cần lựa chọn trình debugger và chạy nó. Cách nhanh nhất là tại Debugger > Select Debugger, chọn Local Win32 Debugger (hoặc bạn nào dùng IDA 7+ thì là Local Windows Debugger). Nhưng bây giờ, để thực hành chúng ta sẽ thực hiện thông qua Python. Ta có thể gõ từng câu lệnh một tại thanh Python của IDA hoặc sử dụng plugin mà chúng ta đã cài đặt là IpyIDA sẽ tiện lợi hơn. Tôi sẽ sử dụng plugin này để minh họa:

Trước tiên, tôi import idc, sau đó khi gõ idc.Load và nhấn TAB, plugin sẽ cung cấp cho tôi các hàm có liên quan. Ở đây tôi chọn idc.LoadDebugger. Trong trường hợp của chúng ta, ta phải chọn win320 cho local debugger (1 là dành cho remote debugger). Sau khi gõ lệnh, ta có kết quả trả về là True như sau:

Như trên hình, ta thấy rằng nó đã được chọn, nếu lặp lại lệnh này một lần nữa ta sẽ nhận được FALSE vì đã hoạt động rồi.

Phương pháp PUSHAD dựa trên việc thực hiện lệnh PUSHAD và trong lệnh tiếp theo, ta tìm các thanh ghi đã được lưu vào Stack và sau đó đặt một breakpoint để dừng trình gỡ lỗi khi nó cố gắng phục hồi giá trị các thanh ghi bằng lệnh POPAD, ngay trước khi nhảy tới OEP sau khi giải nén xong mã gốc.

Vì vậy, nhấn F2 để đặt một breakpoint tại lệnh bên dưới lệnh PUSHAD, ta sẽ dừng lại tại lệnh này sau khi cho thực thi chương trình. (Ở đây lệnh PUSHA tương tự như PUSHAD).

Nếu bạn muốn thực hiện thao tác trên bằng Python thì có thể gõ lệnh sau:

Bằng lệnh trên, ta đã đặt một breakpoint từ Python. Tham số đầu tiên là địa chỉ ta muốn đặt bp, tham số thứ hai là kích thước của breakpoint và tham số thứ ba là kiểu bp. Trong trường hợp này, ta muốn dừng thực thi của chương trình thông qua software bp, do đó truyền vào là BPT_SOFT hoặc 0.

Chúng ta đã lựa chọn được Debugger ở bước trước, và cũng đã đặt breakpoint. Bây giờ, ta khởi động trình debugger để buộc nó dừng lại tại breakpoint đã đặt. Rất đơn giản bằng cách nhấn F9 hoặc từ Python, ta gõ lệnh sau:

Với lệnh này, trình debugger mà ta lựa chọn sẽ khởi chạy, và nếu tất cả mọi thứ đều ngon lành, ví dụ trong trường hợp này, nó sẽ dừng lại tại Breakpoint mà chúng ta đã đặt tại địa chỉ 0x46B002:

Bây giờ, quan sát giá trị các thanh ghi đã được lưu vào cửa sổ Stack, ta sẽ đặt một bp ở dòng đầu tiên để dừng lại ở đó, vì đó là nơi các giá trị của các thanh ghi được lưu bởi PUSHAD sẽ được khôi phục lại bằng POPAD.

Như vậy, chúng ta phải đặt BP tại 0x19FF64 (trong trường hợp trên máy của tôi). Chuyển con trỏ để lựa chọn cửa sổ Disassembly và sau đó nhấn vào mũi tên nhỏ bên cạnh thanh ghi ESP, ta sẽ tới đây:

Bằng cách nhấn mũi tên bên cạnh thanh ghi như thế IDA sẽ đi tới địa chỉ này tại màn hình Disassembly. Từ đó, chúng ta có thể đặt Breakpoint bằng cách nhấn F2, nhưng chúng ta sẽ phải cấu hình lại bp là vì trong trường hợp này cần phải sử dụng On Read and Write chứ không phải On execution, bởi ta muốn dừng lại khi nó phục hồi hoặc đọc giá trị chứ không phải là thực thi mã tại đó.

Khi nhấn F2, cửa sổ Breakpoint settings sẽ hiện ra cho phép ta cấu hình như sau:

Để kiểm tra breakpoint đã đặt có chính xác không, ta xem tại Debugger > Breakpoints > Breakpoint List:

Tại cửa sổ Breakponts ở trên, ta có thể nhấn chuột phải và chọn Edit để thay đổi cấu hình breakpoint mà chúng ta muốn.

Vậy ta có thể đặt bp tương tự như đã làm thông qua Python được không? Hoàn toàn được nhé:

Với câu lệnh trên thì tham số đầu tiên là địa chỉ cần đặt bp, tham số thứ hai là kích thước của bp và tham số 3 kiểu breakpoint cần đặt. Trong trường hợp này Read/Write Access như mô tả trong bảng trên. Do đó, nếu tôi gõ lệnh, một breakpoint tương tự sẽ được thiết lập như khi ta thực hiện bằng tay.

Bây giờ vô hiệu hóa các BP đã đặt trước đó trong danh sách các Breakpoints bằng cách nhấp chuột phải và chọn Disable hoặc từ Python gõ lệnh:

Với tham số thứ hai bằng 1, tức là ta kích hoạt nó, còn bằng 0 thì tức là ta tắt nó. Kết quả tại màn hình danh sách các bp như sau (màu xanh tức là bp đã bị disable):

Tiếp theo nhấn F9 để tiếp tục hoặc gõ lệnh sau:

Nhấn OK ta sẽ dừng lại tại đây:

Ta thấy chương trình dừng lại ngay sau lệnh POPAD khi nó khôi phục các thanh ghi và cũng thấy rằng từ Stub này nó sẽ nhảy tới địa chỉ OEP tại 0x4271b0 thông qua cặp lệnh PUSH & RET (tương tự như lệnh JMP). Do vậy, ta trace code để thực hiện các lệnh này cho đến khi tới được OEP như hình dưới đây:

Tại OEP, cho IDA phân tích lại chương trình giống như đã làm trong bài viết trước. Kết quả có được như sau:

Lúc này thì toàn bộ code của chương trình đã được unpack hoàn toàn trên bộ nhớ, công việc tiếp theo là dump chương trình. Ta phải tìm địa chỉ ImageBase và địa chỉ cuối cùng trong section cuối cùng của file thực thi. Trong cửa sổ Segments, tôi thấy ImageBase0x400000 và địa chỉ kết thúc là 0x46e000:

Thay vì sử dụng idc script đã đề cập ở phần 15, phần này tôi sẽ sử dụng một script tương tự như vậy nhưng viết bằng Python. Nội dung của script đơn giản như sau:

Đoạn code python sử dụng để dump file như trên hình, tôi lưu nó với tên là ipython_dump.py. Sau đó, thực thi script này thông qua File-> Script File. Sau khi thực hiện xong ta sẽ thấy có tập tin dumped.bin được tạo ra. Tiếp theo ta sẽ sử dụng trình PE Editor để fix lại file dump này (tại cửa sổ Section Table Viewer, nhấn chuột phải và chọn Dump Fixer):

Kết quả file sẽ lấy lại được icon như hình dưới đây:

Phần dump file là coi như xong. Tiếp là công việc rebuild lại IAT. Mở Scylla 0.98 và chọn process của file mà hiện ta đang dừng lại tại OEP:

Thay thế bằng OEP là 0x4271B0, nhấn IAT Autosearch (nhấn Yes để chấp nhận sử dụng ) và tiếp theo là Get Imports. Nếu nhấn Show Invalid, ta sẽ thấy có một loạt các APIs lỗi. Thử xem liệu Scylla có thể khắc phục các hàm invalid này một cách tự động hay không?

Chúng ta thấy rằng Scylla không thể sửa được, vì vậy ta sẽ thực hiện bằng tay.

Như trên hình, chúng ta thấy hàm API đầu tiên tại 0x460818, đây là hàm hợp lệ, còn trên đó là các giá trị không hợp lệ. Bắt đầu với địa chỉ không hợp lệ đầu tiên tại 0x4600ec.

Bạn thấy rằng nội dung không trỏ đến bất kỳ địa chỉ hợp lệ nào và nếu bạn nhấn CTRL + X sẽ không tìm thấy tham chiếu nào:

Trong khi ở các hàm API chuẩn sẽ có các tham chiếu sử dụng tới hàm API đó. Ví dụ:

Do vậy, các địa chỉ này không phải thuộc IAT, ta sẽ xóa chúng.

Nếu như tôi nhấn clear và nhấn lại IAT Autosearch một lần nữa, nhưng tuy nhiên lúc này tôi không đồng ý chọn Advanced mode, ta thấy Scylla sẽ tìm được đầy đủ các thông tin về IAT. Chỉ có duy nhất một vị trí invalid cần phải xác minh lại:

Căn cứ vào vị trí invalid ta tìm được hàm API bị thiếu là:

Tiến hành fix lại hàm này trong Scylla:

Sau khi fix xong, bước cuối cùng lựa chọn Fix Dump để repair lại dump file:

Sau khi sửa xong, ta có thể thực thi file một cách bình thường:

Phần 16 này xin được kết thúc tại đây. Tôi gửi kèm một bài tập nhỏ để các bạn thực hành, nhiệm vụ của các bạn là unpack file PACKED_PRACTICA_1.exe (https://mega.nz/#!jGQHDQpA!tRHdeG_z96GwEqY3ZDh0j0XEE0Ja01lIYbEEhufIVT8). Hẹn gặp các bạn ở phần 17!

Image result for unpackme 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. Yin Yang says:

    Xin hỏi có bài unpack PC Guard ver 5.xx không?

  2. kienmanowar says:

    Không nhé bạn!

  3. Yin Yang says:

    Cám ơn bạn đã trả lời.

  4. Yin Yang says:

    Còn bên dùng Ollydbg thì sao hả bạn?

  5. kienmanowar says:

    @Yin Yang: Ý bạn muốn hỏi về gì?

  6. Yin Yang says:

    https://mega.nz/#!664RDYrA!UbOMfWrixAQw5pQa_lCd0C7G2YvTDXuukE_3m3j9mUg

    Câu hỏi của mình ở trong link trên. Cám ơn Bạn đã hỏi.

  7. kienmanowar says:

    @Ying Yang: Vậy bạn tự research thôi 😀

  8. Yin Yang says:

    Cám ơn Bạn đã trà lời. Tiếp tục “mò” vậy… 🙂

  9. kienmanowar says:

    Gửi bạn cái ảnh tham khảo để có hướng mò:
    unpacked

    Regards,

  10. Yin Yang says:

    WoW, mình có theo mấy cái tuts4you dùng mấy cái Ctrl+F9 để trace OEP được (exception at 0x10032349, 0x1003256A và 0x10032A23). Nhưng sau khi dump file rồi dùng ImpREC để sửa IAT thì 80% là errors, stuck. Cho nên tình cờ kiếm được site này, mà không có bài nào hướng dẫn về unpack PC guard ver 5.xx. Bạn có thể upload ảnh rõ hơn được không. Mắt tuổi nhá nhem rồi nên không tỏ cho lắm, cám ơn nhiều…:)

  11. Shin says:

    Em chào anh 😀 Lại là em đây. Em đang phát triển 1 con AV nhưng hơi bí chỗ bắt sự kiện download file (từ Chrome, Zalo, Telegram, …) Như các AV trên thị trường hiện làm là mỗi khi mình download 1 file thì chúng sẽ ngay lập tức check được có phải file độc hại hay không. Anh có biết có cách nào để phát hiện khi nào có file mới được download không ạ? Em cảm ơn anh.

  12. Butch says:

    cho e hỏi là ví dụ mấy bài hồi trước của anh ấy ( có mấy vấn đề cần hỏi ) cmt ớ dưới bài có được a có trả lời k a

  13. kienmanowar says:

    Liên quan bài nào thì em comment bài đó, có thời gian thì anh sẽ trả lời, còn không thì bạn nào đó đi ngang qua có lòng “chắc” sẽ trả lời 🙂

    Regards,

  14. Yin Yang says:

    Cuối cùng, mình cũng unpackage bằng cách dùng script https://imgur.com/k1oCrYV, cám ơn đã giúp hint. Bây giờ mình muốn patch dll đó để xài máy khác mà không phụ thuộc vào pass và các files khác. Nhất là function LTINITIALIZEFROMINI lúc nào cũng return “true” or “1”. Xin hỏi có khả thi không?
    Bạn có paypal không? Mình chưa bao giờ dùng vietcombank.

  15. kienmanowar says:

    @Yin Yang: Chúc mừng bạn nhé!
    Tôi không đào sâu thêm nữa nên không có ý kiến :D. Nhưng cơ bản, mọi lý thuyết đều có thể trở thành thực tế, chỉ là cách chúng ta làm sao để biến nó thành thực tế mà thôi. Các hàm sau khi thực thi thường trả về kết quả cho thanh ghi EAX hoặc một thanh ghi dùng chung nào đó, nên việc return true là có thể thực hiện được.

    Regards,

  16. Vỹ says:

    Cảm ơn anh vì tất cả những gì anh chia sẻ

  17. kienmanowar says:

    Thanks em! Hi vọng những gì anh viết giúp ích được cho em.

    Regards,

  18. lph77 says:

    chào anh, em lại có 1 câu hỏi phiền anh giải đáp:
    Việc khôi phục lại được IAT có phải chỉ để giúp file gốc có thể thực thi nhằm debug hay còn ý nghĩa nào khác không, cảm ơn anh

  19. kienmanowar says:

    Chào em,
    Khôi phục IAT nhằm đảm bảo file có thể thực thi được một cách bình thường trên mọi hệ điều hành. Và khi nó thực thi được thì em mới debug được bình thường.

    Regards,

  20. n0tduck says:

    Đọc bài viết xong bắt tay vào replicate những gì anh hướng dẫn làm em có cảm giác mình vừa chơi cheat lol

  21. kienmanowar says:

    @n0duck: Cheat là sao em :D?

  22. n0tduck says:

    Em đùa tí 😀 như kiểu em vừa xem xong sách giải rồi mới làm toán vậy :))))
    Mà anh ơi, em ko tìm thấy string “if you unpack it…” là do tác giả đã obfuscate rồi hả anh?

  23. n0tduck says:

    Ý em là trong file đã unpacked

  24. kienmanowar says:

    À, tác giả sẽ load data từ resource name là “A6F2D1FB” vào memory, sau đó sử dụng loop để thực hiện decode ra string.

  25. Trần Trung Kiên says:

    tại sao trong vòng for của script py, tăng giữa mỗi lần lặp là 4 vậy anh? còn bên script idc của bài trước thì chỉ là ++

  26. kienmanowar says:

    @Trần Trung Kiên:
    Chào em,

    Hai script có cùng một mục đích là ghi toàn bộ nội dung file đang debug ra disk. Ở idc thì là đọc byte nào ghi luôn byte đấy. Còn ở python thì pack lại từng dword (4 byte) và lưu vào biến, nên mới có tăng 4 là thế.

    Câu hỏi của em hoàn toàn em có thể tự tìm câu trả lời bằng cách kiểm tra từng câu lệnh thông qua Python/IDC bar, hoặc Script Command (Shift+F2)

    Regards,

  27. z4huntr says:

    anh ơi cho em hỏi với ạ tại sao lại đặt breakpoint tại địa chỉ 0x19FF64 (ESP) ạ

  28. mixue says:

    cho em hoi la windowdefender bao file :UnPackMe_ASPack 2.2 chua PWS:Win32/Zbot!ml.

  29. kienmanowar says:

    Chào em,
    ASPack vốn là một packer nổi tiếng từ trước đây, ngoài việc sử dụng để pack các phần mềm nó còn được attacker sử dụng để pack mã độc nữa. Do đó, các AV sẽ nhận diện nó như là một dấu hiệu nguy hiểm. Tốt nhát, để không phải lo lắng, em nên thực hành các bài viết của anh trên môi trường ảo hóa sử dụng VMWare hoặc VirtualBox.

    Regards!

Leave a comment

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