Archive for the ‘IDA Pro section’ Category


Lâu rồi không viết lách gì, blog mốc hết cả!!

Trong bài này tôi sẽ giới thiệu với các bạn về các công cụ bổ trợ cho IDA trong 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 🙂

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 (tham khảo từ bài viết của thầy Ricardo Narvaja), 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.

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ới nhất tính đến 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 hiện chỉ hỗ trợ cho IDA 6.x, chưa có plugin cho 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:

Hết!

“Bạn nào thấy dở thì nhớ Follow, nhấn Like để ủng hộ nhé!” ← Tôi học câu này từ mấy kênh trên YouTube

 

 

 

Advertisements

Mới đây, tại hội thảo BotConf2017, các chuyên gia của Avast đã giới thiệu tới cộng đồng công cụ decompiler với tên gọi là RetDec (Retargetable Decompiler). Theo các chuyên gia chia sẻ, sau bảy năm phát triển, Avast quyết định open-source công cụ này nhằm trợ giúp cho cộng đồng an ninh mạng trong cuộc chiến chống lại các phần mềm độc hại. Thông qua công cụ này sẽ cho phép những người hoạt động trong lĩnh vực phân tích/nghiên cứu có thể biết được hoạt động của một ứng dụng, mà không cần phải thực thi.

Hiện tại, RetDec hỗ trợ các tính năng sau:

  • Supported file formats: ELF, PE, Mach-O, COFF, AR (archive), Intel HEX, and raw machine code.
  • Supported architectures (32b only): Intel x86, ARM, MIPS, PIC32, and PowerPC.
  • Static analysis of executable files with detailed information.
  • Compiler and packer detection.
  • Loading and instruction decoding.
  • Signature-based removal of statically linked library code.
  • Extraction and utilization of debugging information (DWARF, PDB).
  • Reconstruction of instruction idioms.
  • Detection and reconstruction of C++ class hierarchies (RTTI, vtables).
  • Demangling of symbols from C++ binaries (GCC, MSVC, Borland).
  • Reconstruction of functions, types, and high-level constructs.
  • Integrated disassembler.
  • Output in two high-level languages: C and a Python-like language.
  • Generation of call graphs, control-flow graphs, and various statistics.
  • IDA plugin that allows decompilation of files directly from the IDA disassembler.

Thông tin chi tiết có thể xem thêm tại blog: https://blog.avast.com/avast-open-sources-its-machine-code-decompiler

RetDec hỗ trợ plugin để có thể sử dụng với IDA. Mô hình hoạt động của plugin này như sau:

Plugin hỗ trợ phiên bản IDA từ 6.6 trở lên (không làm việc với IDA 7.x). Tải plugin tại đây: https://retdec.com/idaplugin/. Việc cài đặt để sử dụng rất đơn giản, trong bài viết này tôi minh họa sử dụng plugin với IDA 6.8.

Các bước cài đặt Plugin:

  1. Chép file retdec.plw vào thư mục plugin của IDA (/plugins).
  2. Mặc định, plugin này đăng ký phím tắt là Ctrl-D. Nếu đã sử dụng phím tắt này cho một plugin khác hoặc muốn sử dụng một phím tắt khác, cần phải sửa đổi tệp cấu hình plugin của IDA ( /plugins/plugins.cfg). Bên cạnh đó, plugin này hỗ trợ nhiều chế độ decompile: selective (decompile hàm tại vị trí con trỏ); full (decompile toàn bộ binary), do vậy khuyến nghị cấu hình plugins.cfg như sau:

Thực hiện Decompile:

RetDec hỗ trợ hai chế độ decompile:

  • Remote API Decompilation: sử dụng API được cấp bởi RetDec, API này sẽ kết nối và gửi dữ liệu lên máy chủ của RetDect. Quá trình decompile sẽ do máy chủ của RetDect thực hiện và trả kết quả về khi decompile xong.
  • Local decompilation: Ở chế độ này, phải cài đặt RetDec trên máy và đảm bảo plugin có thể truy cập tới file decompile.sh.

Remote API Decompilation: Với chế độ này các bước cấu hình cơ bản như dưới đây:

  • Đăng kí một tài khoản miễn phí tại https://retdec.com. Sau khi kích hoạt tài khoản, vào phần quản lý account để tạo API key tương tự như hình dưới:

  • Cấu hình Retdec plugin trong IDA để sử dụng API key đã tạo ra ở bước trên:

Kết quả chạy plugin sau khi cấu hình thành công:


Local decompilation: Để sử dụng chế độ này thì việc cấu hình có phức tạp hơn chút. Các bước cơ bản như sau:

  • Phiên bản mới nhất là của RetDec là v3.0. Tùy vào phiên bản của OS mà lựa chọn bản cài đặt phù hợp tại đây. Sau khi tải về, bung nén vào một thư mục tùy ý. Ví dụ:

Lưu ý: Nhớ bổ sung đường dẫn tới thư mục bin vào system PATH của Windows.

  • Sau khi cài đặt xong, kiểm tra thử xem có chạy được không. Nếu kết quả như hình là ok:

  • Cấu hình lại plugin sử dụng chế độ Local decompilation như sau:

  • Kết quả có được sau khi tiến hành decompile như sau:

Cơ bản là như vậy, chi tiết các chức năng khác có thể đọc thêm tại https://retdec.com và trong tài liệu userguide đi kèm với plugin.

End!

m4n0w4r

 


Blog này tôi xây dựng với tinh thần chia sẻ những kiến thức mà tôi biết trong quá trình nghiên cứu, làm việc và học hỏi. Do vậy, tôi cũng hi vọng các bạn vẫn thường xuyên ghé Blog của tôi cũng có được tinh thần như thế36

Gần đây, hasherezade trên trang twitter cá nhân của mình có đăng tải một crackme do cô ấy viết dành cho @Malwarebytes. Là một lập trình viên và là người nghiên cứu độc lập nhưng đặc biệt quan tâm tới lĩnh vực InfoSec, Hasherezade dành nhiều thời gian với công việc phân tích mã độc và chia sẻ thông tin về những mối nguy hại cho cộng đồng thông qua trang blog cá nhân hoặc qua Malwarebytes Team.

Trong cộng đồng cybersecurity nhung nhúc toàn nam giới, việc xuất hiện những bóng hồng tài năng như cô ấy quả thật rất hiếm. Cá nhân tôi cảm giác một mình cô ấy cân cả team ở Malwarebytes 🙂 . Tôi thích cô ấy bởi thái độ làm việc nghiêm túc, ngoài ra còn vì một lý do đặc biệt khác, là một người nghiên cứu độc lập có lẽ cô ấy chưa biết tới các cụm từ “chuyên gia bảo mật tự phong”, “nhà ngoại cảm bảo mật” … Nếu không biết các cụm từ này thật quả là đáng tiếc!! Hi vọng nếu có cơ hội du lịch qua dải đất hình chữ S này hoặc qua màn ảnh nhỏ cũng được, cô ấy có thể cập nhật các cụm từ trên vào vốn từ của mình…Tôi đảm bảo tương lai sẽ rộng mở hơn, báo chí sẽ săn đón nhiều hơn …4

Download crackme tại đường link trên: https://www.hybrid-analysis.com/sample/4ba96615dd4f38d5bf75c192c6bee81ecac595fda911d6974739557118eda032?environmentId=100

Thử chạy crackme, nhận được lời giới thiệu: challenge này được tạo ra dành cho những người làm công việc liên quan tới phân tích mã độc. Khi hoàn thành toàn bộ các nhiệm vụ sẽ tìm được flag có dạng là flag{…}. Challenge này có rất nhiều màn cần phải vượt qua để tới được đích cuối cùng.

Tiếp theo load thử crackme vào một trình debugger bất kì (ví dụ: OllyDBG/x64dbg), được trang bị sẵn các plugin chống các cơ chế anti-debug. Chạy thử crackme trong debugger, mục đích nhằm để kiểm tra xem crackme có thực thi được bình thường không hay là có sử dụng các cơ chế anti-debug nào khác nữa:

Như đã thấy trên hình, crackme vẫn thực thi được hoàn toàn bình thường. Giờ dựa vào chuỗi “I am so sorry, you failed! :(“ để tìm ngược lại đoạn code check liên quan. Trước khi load crackme vào IDA, kiểm tra thêm các thông tin cơ bản khác.

Compiler: Microsoft Visual C/C++(2012)[-]. Như vậy là crackme không bị pack.

Crypto:

Crackme có sử dụng kĩ thuật anti-debug, sử dụng các hàm API liên quan tới mã hóa thuộc thư viện ADVAPI32.dll, có khả năng sử dụng cả Base64.

Unicode String:

ANSI String:

Căn cứ vào thông tin của string thì khả năng crackme có kiểm tra để kết nối internet, tạo payload khác và thực thi payload này.

Quá trình thu thập các thông tin để có cái nhìn tổng quan về crackme này cơ bản đã xong. Để tìm hiểu và phân tích kĩ hơn, tiến hành load crackme này vào IDA. IDA sẽ thực hiện quá trình phân tích tự động cho tới khi cửa sổ Output window xuất hiện thông báo: “The initial autoanalysis has been finished”, có nghĩa là quá trình phân tích của IDA đã hoàn tất. Chuyển tới cửa số Strings Window (Shift+F12), tìm chuỗi “I am so sorry, you failed! 😦“:

Nhấp đúp chuột tại chuỗi này sẽ tới địa chỉ .rdata:00420A38 tại màn hình IDA View:

Sử dụng chức năng Cross-References của IDA để tìm ra vùng code gọi tới chuỗi này:

Với kết quả trên hình, biết được chuỗi này chỉ được sử dụng ở một nơi duy nhất, đó là tại hàm main() của crackme. Nhấn đúp chuột tại địa chỉ có được, tôi sẽ tới hàm main() của crackme:


Stage1 — Get the final binary

1.1. Khôi phục URL bị mã hóa

Quan sát tại hàm main(), sau khi gọi các hàm printf() để in ra màn hình các thông tin giới thiệu về crackme, tôi thấy có lệnh call sub_004014F0() tại địa chỉ 0x00401972 (tôi đã đổi tên thành GetDecryptedUrl()), sau hàm call này sẽ có quá trình kiểm tra để rẽ nhánh như trên hình. Thanh ghi al sẽ lưu kết quả trả về sau khi gọi hàm này, nếu <> 0 (hay True) thì sẽ nhảy tới nhánh “right_track”. Do đó, cần phải phân tích nhiệm vụ của hàm call này. Tổng thể toàn bộ code tại sub_004014F0() như dưới đây:

Tôi sẽ đi lần lượt từ đầu, đầu tiên sẽ gặp lệnh rdtsc, lệnh này hay được malware áp dụng để anti-debug. Sau khi thực hiện lệnh này, kết quả được lưu vào thanh ghi eax (chứa 32 bit thấp) và edx (chứa 32 bit cao). Các giá trị này sau đó được lưu vào biến để sử dụng sau:

Tiếp theo, tại địa chỉ 00401510 sẽ gọi tới sub_004019D0 (detect_debugger()) làm nhiệm vụ phát hiện xem crackme có đang bị debug không thông qua các API IsDebuggerPresent() & CheckRemoteDebuggerPresent():

Đọc kĩ đoạn code trên sẽ thấy mục đích của sub này không phải để anti-debug, mà thông qua việc kiểm tra xem có đang bị debug hay không để thiết lập giá trị cho một mảng, tôi đặt tên là global_bytes_array[]. Kết thúc quá trình kiểm tra, mảng sẽ được gán giá trị đầu tiên là 0x81B22A94, sau đó index của mảng sẽ được tăng dần để phục vụ lưu giá trị tiếp theo.

Tại địa chỉ 00401520 sẽ gọi tới sub_00401A50() (au_re_RaiseException()) nhằm thực hiện RaiseException:

Sau đoạn code trên, mảng sẽ được gán giá trị thứ hai là 0x18E309F0. Tiếp theo, tại địa chỉ 00401525 gọi tới sub_ 00401B00() (check_hw_registers()), mục đích của sub này sẽ kiểm tra xem có đặt hardware breakpoint hay không bằng cách kiểm tra giá trị các thanh ghi Dr0; Dr1; Dr2; Dr3. Nếu không đặt hw bp thì sẽ không gán giá trị vào mảng, do đó cần phải đặt hw bp để có được giá trị được gán tiếp theo cho mảng.

Với việc có thiết lập hw bp, mảng sẽ được gán giá trị thứ ba là 0xEFF4652B. Tại đỉa chỉ 0040152A, ta sẽ gặp sub_00401C20() (check_PEB_flags()). Sub này làm nhiệm vụ kiểm tra NtGlobalFlag & BeingDebugged trong cấu trúc PEB, nếu các trường này có giá trị non-zero thì sẽ thực hiện gán giá trị cho mảng. Đây cũng là cách anti-debug hay gặp ở malware. Tuy nhiên, ở đây ta thấy mục đích của crackme này rõ ràng không phải là để anti-debug:

Như vậy, sau quá trình kiểm tra, mảng của chúng ta sẽ được gán giá trị thứ tư là 0xBA521C56. Sau đoạn code kiểm tra flags trong PEB, tại địa chỉ 00401531 gọi tới sub_00402730() (find_blacklisted_devices()).

Sub này sẽ build một danh sách các checksum cho các blacklist device, sau đó sử dụng API QueryDosDeviceA() để lấy toàn bộ các thông tin về tên các MS-DOS device, và lưu tại buffer là lpTargetPath. Sau đó sẽ duyệt danh sách này, tính toán một giá trị check_sum của từng device name và so sánh với danh sách device đã thiết lập từ trước. Nếu như tìm thấy có blacklist device thì mới gán giá trị cho mảng. Do trên máy tôi không có blacklist device nào tương ứng, do đó tôi phải patch tại lệnh nhảy để chuyển tới code gán giá trị cho mảng:

Kết quả ta sẽ có được giá trị thứ tư gán vào mảng là 0x2CBE186A. Tiếp theo, tới sub_00402880() (anti_virtualBox()) tại địa chỉ 00401536, sub này sẽ kiểm tra xem ta có đang phân tích crackme trong môi trường VirtualBox hay không?

Do tôi không sử dụng VirtualBox nên phải patch để tới đoạn code gán giá trị cho mảng. Tới đây, ta có giá trị thứ 5 được gán vào mảng là 0xF5C1D288. Tiếp tục tới địa chỉ 0040153D, tại đây gọi tới sub_00402B70() (find_blacklisted_modules()).

Sub này cũng build một danh sách các checksum cho các module (nhìn vào đây thì chịu không rõ là module nào được tạo checksum).

Tiếp theo sẽ thực hiện quá trình tìm kiếm các blacklist module thông qua các hàm API GetCurrentProcessId(), CreateToolhelp32Snapshot(), Module32First(), Module32Next(). Nếu tìm thấy mới thực hiện gán giá trị cho mảng:

Do trên máy tôi, không có module nào thỏa mãn nên tôi cũng patch để tới đoạn code gán giá trị thứ 6 cho mảng là 0x8005F916.

Tại địa chỉ 00401544, gọi tới sub_00402DE0() (find_blacklisted_processes()). Sub này build một danh sách các checksum cho các process, thực hiện quá trình tìm kiếm các blacklist process thông qua các hàm API CreateToolhelp32Snapshot(), Process32First(), Process32Next(). Nếu tìm thấy sẽ tiến hành gán giá trị cho mảng.

Tiếp tục patch cờ ZF để tới đoạn code gán giá trị thứ 7 cho mảng là 0xFB18EAD7. Cuối cùng tới đoạn code lấy lại các giá trị timestamp của lệnh rdtsc để sử dụng cho sub_00401BC0() (timing_based_detection()) tại địa chỉ 00401555.

Đây là một cách mà các malware thường hay áp dụng để phát hiện debugger. Tiến hành patch để tới đoạn code gán giá trị cuối cùng cho mảng là 0x82CF939D. Sau khi vượt qua được hết các quá trình kiểm tra trên, tôi có được mảng với toàn bộ các giá trị được gán như sau:

Đoạn code tiếp theo khởi gán các giá trị cho một mảng (tôi đặt là szEncrypted_Url), sau đó sẽ thực hiện quá trình giải mã để lấy plaintext url và vào szUrl. Quá trình giải mã URL có sự tham gia của mảng đã gán ở trên, do vậy khả năng đây có thể là key để phục vụ việc giải mã. URL sau khi được giải mã sẽ được tính checksum để so sánh với giá trị mặc định là 0x3B47B2E6. Cơ bản tại sub_004031C0() (decrypt_url()) sẽ thực hiện các công việc dưới đây.

Sử dụng API CryptAcquireContextW() để lấy handle với các tham số sau:

Sau khi lấy được handle sẽ sử dụng API CryptCreateHash() để khởi tạo và trả về một handle sử dụng cho việc hash dữ liệu bằng các hàm CryptHashData() và CryptHashSessionKey(). Thuật toán được sử dụng để phục vụ hash dữ liệu là SHA_256:

Thực hiện hash dữ liệu với hàm API CryptHashData(), tham số pbData trỏ tới mảng bytes đã được khởi gán ở trên. Như vậy, khi thực hiện xong, hàm này sẽ cập nhật một giá trị hash dựa trên mảng byte đã có:

Tại máy tôi, giá trị hash được cập nhật có giá trị sau:

Dựa vào hash có được, sử dụng hàm API CryptDeriveKey() để tạo ra một key phục vụ cho việc giải mã dữ liệu:

Key AES 128 bit cuối cùng được tạo ra:

Với key được tạo ra như trên, thực hiện giải mã encrypted_URL để có link gốc:

Nếu giải mã thành công ta sẽ có được URL chính xác như sau:

Với URL được giải mã đúng sẽ qua được phần kiểm tra check sum với giá trị mặc định là 0x3B47B2E6h. Cùng với đó, tôi sẽ không tới đoạn code in ra màn hình thông báo: “I am so sorry, you failed! :(\n”. Truy xuất URL trên, tôi nhận thấy đây là một dữ liệu rất dài bị mã hóa bằng Base64:


1.2. Giải mã và dump file PE mới

Đi sâu vào phân tích call sub_401690() tại địa chỉ 004019A5. Tại đây, đầu tiên sẽ kiểm tra xem kết nối Internet có sẵn sàng hay không bằng hàm API InternetGetConnectedState(). Nếu không sẽ in ra màn hình thông báo: “I need internet!\n”.

Tiếp theo, thực hiện cấp phát một vùng nhớ với toàn các bytes 0 với số lượng (0x1FA0Eu*1) bytes. Vùng nhớ này sẽ được sử dụng để chứa dữ liệu tải về từ URL ở trên:

get_base64_data_from_url() sử dụng các API InternetOpenA(), InternetOpenUrlA(), InternetReadFile() với user_agent là ‘Mal-Zilla’. Thực hiện download toàn bộ nội dung từ URL và lưu vào vùng nhớ đã cấp phát. Download xong sẽ in ra màn hình thông báo: “You are on the right track!\n” và tính toán lại kích thước thật sự của vùng dữ liệu đã download là 0xC20B bytes, đồng thời cấp phát vùng nhớ khác toàn bytes 0 với kích thước thật này. Thực hiện giải mã toàn bộ khối dữ liệu Base64 đã download và lưu vào vùng nhớ mới được cấp phát:

Vùng buffer sau khi decrypt vẫn ở dạng bị mã hóa:

crackme tiếp tục cấp phát một vùng nhớ mới, sử dụng API RtlDecompressBuffer() để giải nén buffer ở trên (CompressedBufferSize = 0xC20B):

Vùng buffer (trên máy tôi) sau khi được giải nén có kết quả như hình dưới, với kích thước sau giải nén FinalUncompressedSize = 0xE400:

Với vùng buffer có được này tôi thấy vẫn chưa có thông tin gì cụ thể cả, nhưng nhận thấy một điểm đặc biệt là có rất nhiều chuỗi “malwarebytes” được lặp đi lặp lại tại vùng buffer này. Tiếp tục phân tích code phía dưới tôi nhận ra một số điểm khá thú vị:

  1. Thực hiện khởi tạo một vùng nhớ mới.
  2. Gọi sub_00406B20() (Get_Clipboard_Data()): sử dụng các hàm API IsClipboardFormatAvailable(), OpenClipboard(), GetClipboardData() để copy dữ liệu của clipboard vào vùng nhớ đã cấp phát.
  3. Gọi sub_004011A0() (xor_decrypt_new_PE()): sử dụng vùng nhớ chứa dữ liệu clipboard làm xor key giải mã để vùng buffer trên.
  4. Kiểm tra xem vùng buffer được giải mã có phải là PE file hay không thông qua vdấu hiệu “MZ”. Nếu không đúng sẽ hiện thị thông báo “Better luck next time!”, “Nope :(” bằng API MessageBoxA().
  5. Dựa trên dấu hiệu so sánh với “MZ”, tôi thực hiện lấy mẫu để xor với vùng buffer trên và đi đến kết luận, xor key dùng để giải mã chính là “malwarebytes”.

Tiến hành copy chuỗi “malwarebytes” vào clipboard và thực hiện đoạn code trên để lấy được PE file:

Lúc này, ta có thể dump toàn bộ vùng nhớ này bằng OllyDumpEx plugin và save lại với tên mới (ví dụ: mb_crackme_dump.exe):

Thử chạy file vừa dump xem có thực thi được không, nhận được thông báo:

Tới đây có thể dừng lại để chuyển sang phân tích binary của Stage2.

Tuy nhiên, nếu tiếp tục quá trình phân tích sau khi vùng buffer được giải mã chính xác như trên, tôi gặp một kĩ thuật thường thấy khi phân tích malware đó là process hollowing. Trước khi thực hiện process hollowing, crackme sử dụng API ExpandEnvironmentStringsW() để tạo một command đánh lừa như sau: %SystemRoot%\system32\rundll32.exe secret.dll,#1 .

Lên quan đến kĩ thuật hollowing, cụ thể ở crackme sẽ thực hiện một số bước như sau:

  • Khởi tạo một instance mới của một tiến trình hợp lệ (ở chế độ CREATE_SUSPENDED) thông qua API CreateProcess().

PROCESS_INFORMATION: là struct chứa thông tin liên quan tới ProcessId & ThreadId để giúp xác định tiến trình được tạo. Ví dụ trên máy tôi:

  • Lấy thông tin về context của hThread ứng với tiến trình vừa được khởi tạo ở trên thông qua API GetThreadContext(hThread, &Context).
  • Từ thông tin về context có được, truy xuất tới PEB để lấy ImageBaseAddress của tiến trình (baseAddress = (DWORD *) contx.Ebx+8). Sử dụng API ReadProcessMemory(hProcess, (LPCVOID)(Context.Ebx + 8), &Buffer, 4u, NULL) để kiểm tra toàn bộ dữ liệu tại base address và vùng nhớ với kích thước được chỉ định có thể truy cập để đọc dữ liệu hay không, nếu không, và nếu không thể truy cập thì hàm sẽ bị lỗi.
  • Sử dụng VirtualAllocEx()/VirtualAlloc() để cấp phát một vùng nhớ mới có quyền “PAGE_EXECUTE_READWRITE” dùng để lưu code của PE đã giải mã.
  • Tiến hành copy toàn bộ dữ liệu từ vùng PE buffer đã được giải mã ở trên vào vùng nhớ mới được tạo ra:

  • Sau đó lấy thực hiện việc lấy Entry Point, lưu vào _Eax, sử dụng các API SetThreadContext() và ResumeThread() để thực thi process từ EP.

Note: Nếu như thực hiện toàn bộ quá trình trên, thì PE file này sẽ thực thi dưới process là rundll32.exe với một tham số giả là “secret.dll, #1” (đánh lừa chúng ta như kiểu nó đang load file dll là secret.dll và gọi tới hàm được export dll có thứ tự là #1).

Như vậy, để phân tích được PE file đã giải mã thì có thể thực hiện:

  • Cách 1: Sau khi file đã giải mã hoàn toàn, thực hiện dump file như tôi đã làm ở trên.
  • Cách 2: Sử dụng Process Hacker, dump file từ memory (nhớ lưu thông tin địa chỉ base address. Ví dụ trong hình là 0xd0000). Sau đó, dùng công cụ pe_unmapper của chính hasherezade để fix file đã dump:

  • Cách 3: trước khi thực hiện API SetThreadContext(), tìm trong Context địa chỉ của EP, sau đó dùng Process Hacker để patch bytes tại entry point thành 0xEB 0xFE để tạo infinite loop (lưu ý nhớ lại byte gốc của EP), sau khi patch xong cho ResumeThread() để process thực thi bình thường và rơi vào vòng lặp vô tận, tiến hành attach tiến trình mới này vào một trình debugger khác để debug tiếp:


2. Stage 2 — Get the final flag

Có được binary mới bằng phương pháp dump ở trên với kích thước 57.0 KB (58,368 bytes). Khi chạy tôi nhận được thông báo “You failed 😦 . Better luck next time”. Load file mới vào IDA, tìm chuỗi này để dò ngược lại code. Tôi tới được hàm main() của file.

Tại main(), đầu tiên sẽ gặp sub_004010C0() (get_api_address()) có nhiệm vụ lấy địa chỉ của các hàm API “NtQueueApcThread”, “ZwSetInformationThread”, “ZwCreateThreadEx”, “RtlCreateUserThread”, thuộc thư viện ntdll.dll.

Sử dụng API GetModuleFileNameA() để lấy đường dẫn đầy đủ của file đang phân tích. Dựa trên đường dẫn này thực tính ra một giá trị checksum. Dùng API ExpandEnvironmentStringsA() để thiết lập một đường dẫn khác là %SystemRoot%\\system32\\rundll32.exe (C:\Windows\system32\rundll32.exe). Với đường dẫn này cũng thực hiện tính giá trị checksum khác.

Sau khi qua đoạn kiểm đường dẫn trên, tới đoạn code mà binary sử dụng hàm API EnumWindows() để tìm toàn bộ các top-level windows hiện có trên màn hình bằng cách truyền handle tới từng window, thông qua một callback function đã được định nghĩa trước. Ở đây, giá trị được truyền cho hàm callback là mã hash (0x3C5FE025) của một window nào đó mà tôi không biết.

Code tại hàm callback EnumFunc() thực hiện việc lấy ClassName của các window, sau đó tính checksum dựa trên ClassName này, so sánh với giá trị mặc định là (0x3C5FE025). Nếu bằng nhau thì sẽ ẩn cửa sổ có ClassName tương ứng, lấy process_id, qua đó lấy ra open hanle bằng API OpenProcess():

Ở đây tôi dùng một trick đế lấy handle của một Window khác (ví dụ của Calc.exe) và truyền handle này vào cho hàm API GetClassNameA():

Khi đó, ClassName sẽ chứa tên Class của ứng dụng (Calc.exe):

Patch đoạn so sánh checksum để tới đoạn code lấy process id, và open handle của calc.exe. Tiếp theo tới đoạn code lấy giá trị của PEB.BeingDebugged Flag (nếu bị debug sẽ có giá trị là 1), sau đó lấy 4 bytes đầu tiên của một vùng nhớ (tôi đã đổi thành shell_code) để thực hiện tính toán thông qua vòng lặp xor. Các bytes sau khi được giải mã được gán lại vào shell_code, tính checksum cho vùng shell_code có độ dài 0x177 bytes và so sánh với giá trị checksum mặc định là 0xCA1C7FCF. Nếu không bằng thì sẽ hiện thông báo: “You failed :(\nBetter luck next time!”, “Stage 2”.

Đoạn code tiếp theo sẽ thực hiện inject toàn bộ shell_code vào victim_process thông qua handle đã có được ở trên. Sử dụng ZwCreateSection() để tạo section có thuộc tính PAGE_EXECUTE_READWRITE = 0x40, NtMapViewOfSection() để maps section được tạo vào trong vùng nhớ của process hiện tại. Sau đó copy toàn bộ shell_code vào section đã được map thực hiện map vào process mà ta lấy được handle ở trên (của tôi là handle của calc.exe). Điều này đồng nghĩa với việc thực hiện inject toàn bộ shell_code vào calc.exe.

Kết quả sau khi đã thực hiện việc inject thành công:

Cuối cùng, tính toán một giá trị random bằng GetTickCount() % 3. Tùy thuộc vào kết quả trả về bằng 0, 1 hay 2 mà gọi các hàm liên quan tới thread khác nhau để thực thi shellcode.

Nếu thực hiện thành công, tôi có được flag 11:

Cảm ơn haserezade! Đây là một challenge thú vị với nhiều kĩ thuật khác nhau hay được sử dụng bởi malware, vừa tầm để những người không phải “chuyên gia bảo mật” như tôi nghiên cứu, học hỏi thêm để nâng cao kĩ năng.29

Hết!

m4n0w4r

(Bài viết có chỗ nào sai/thiếu sót mong bạn đọc góp ý)

“Khắc ghi sâu trong tim từng nét mi đường mày
Đong đầy nỗi nhớ nhung vào bức họa
Thấm nhuộm cả sắc mực tràn
Sách ngàn chữ cùng đều ố vàng
Đêm tĩnh mịch, rèm thưa đã mờ mờ sáng
Phất tay áo lên điệu múa trong mộng bỗng thật bồi hồi
Lòng dần dâng tràn nỗi niềm tương tư
Nàng quyến luyến rơi hoa lê
Lặng yên họa, hồng nhan đợi ai quay về
Trống vắng, người ấy cứ dần dần mà hao gầy …
Hương vị môi son ấy…
Vén rèm châu lên là vì ai?
Cớ sao vẫn không thấy người
Trăng khuya sáng vằng vặc, tình này thật khó thay…
Mưa phùn khẽ giăng giăng buổi sớm ngày đầu xuân
Bâng khuâng gọi lộc non thức tỉnh
Nghe tiếng gió nhẹ thổi bên tai
Than dòng nước chảy, thương cánh hoa rơi
Là ai ở nơi mây khói, gảy tiếng đàn đưa…”

Image result for vén rèm châu

[IDA Plug] Keypatch

Posted: September 16, 2016 in IDA Pro section, Keypatch, Uncategorized
Tags: ,

Keypatch is a plugin of IDA Pro for Keystone Assembler Engine.Keypatch consists of 3 tools inside.

  • Patcher & Fill Range: these allow you to type in assembly to directly patch your binary.
  • Assembler: this interactive tool let you enter assembly & get back instruction encoding.

Keypatch is confirmed to work on IDA Pro version 6.4, 6.6, 6.8, 6.9, 6.95 but should work flawlessly on older versions. If you find any issues, please report.

Keypatch offers some nice features:

  • Cross-architecture: support Arm, Arm64 (AArch64/Armv8), Hexagon, Mips, PowerPC, Sparc, SystemZ & X86 (include 16/32/64bit).
  • Cross-platform: work everywhere that IDA works, which is on Windows, MacOS, Linux.
  • Based on Python, so it is easy to install as no compilation is needed.
  • User-friendly: automatically add comments to patched code, and allow reverting (undo) modification.
  • Open source under GPL v2.

keypatch_patcher2

Version 2.0.1

@aquynh aquynh released this 20 hours ago

  • Fix an off-by-one bug in Fill-Range function.
  • Log more information when padding NOP

Download: https://github.com/keystone-engine/keypatch/archive/2.0.1.zip


Contributed By Check Point Software Technologies LTD.

Features

1. Seamless synchronization of labels, function names, comments and global variables (w/wo demangling)

  • Synchronization modes
    • On demand
    • On rename (update on-the-fly)
  • Supports image base-independent synchronization

2. Dynamic dumping of debugged process memory regions

It can be useful in the following cases:

  • When debugged process has extracted/temporary/injected module which doesn’t appear in modules list
  • When it doesn’t have a valid PE header
  • When it have corrupted import table, etc.

3. Python scripting

We support the following list of debug backends for now:

Latest release: v_1_1_0_3

[-] anti-dubug: removed hardcoded classname/window name
[*] OllyDbg20: updated SWIG SDK-wrapper compatibility with win types
[*] dbg backends FIX: fixed trucation of RPC request
[*] dbg backends FIX: now we send packet length for each RPC call for additional message check


Viết linh tinh ……too_sad

Cơ bản, nhiều người có cùng suy nghĩ chung là không sử dụng IDA trong việc unpack file và theo tôi quan điểm này không có gì sai, vì như đã biết việc unpack thường sử dụng các trình debugger như OllyDBG/Immunity kết hợp với các công cụ fix PE file (LordPE; ImpREC, Scylla…). Tuy nhiên, trong bài viết này chúng ta sẽ thử dùng IDA để unpack một unpackme đơn giản, không sử dụng các biện pháp bảo vệ cao cấp, mục đích chủ yếu là để biết được cách thức thực hiện như thế nào với IDA.

Công cụ sử dụng:

  • DIE v1.01 (Detect It Easy)
  • IDA 6.8
  • Peditor v1.7 (yoda&M.o.D)
  • Scylla v0.9.8

Tải Unpackme tại: https://www.hex-rays.com/products/ida/support/tutorials/unpack_pe/test00.exe

Trước tiên, dùng DIE để kiểm tra sơ bộ unpackme:

2016-04-16_16-15-00

Fig.1

File được pack bằng MEW(SE v1.0), đoán là bản Standard Edition. Gồm 2 sections, section đầu có tên là MEW và section thứ hai có tên rất loằng ngoằng:

2016-04-16_16-18-47

Fig.2

Thông tin về các hàm APIs được unpackme sử dụng:

2016-04-16_16-20-04

Fig.3

OK thông tin cơ bản đã có. Tiếp theo mở IDA và load unpackme:

2016-04-16_16-05-50

Fig.4

Giữ nguyên các tùy chọn mặc định tại phần Options, nhấn OK để tiếp tục, ta nhận được thông báo sau:

2016-04-16_16-11-15

Fig.5

Thông báo này cho biết, section có ký tự loằng ngoằng bên dưới section MEW đã bị cắt bỏ các offsets và IDA sẽ chỉ đọc 0x7A1 bytes. Bỏ qua cảnh báo này, nhấn OK để tiếp tục, một thông báo nữa xuất hiện cho biết import của file đã bị hủy, điều này có nghĩa là khả năng lớn là file đã bị pack hoặc đã bị chỉnh sửa khiến cho việc phân tích trở nên khó khăn:

2016-04-16_16-25-47

Fig.6

Nhấn OK, để tiếp tục, IDA sẽ dừng lại tại đây:

2016-04-16_16-32-25

Fig.7

Quan sát tại màn hình IDA, lúc này ta thấy trước các byte đều xuất hiện cụm “dd” hay “db”. Điều này có nghĩa là IDA không nhận diện được code nên nó diễn giải dữ liệu dưới dạng dword (dd), word(dw), byte(db). IDA cho phép chúng ta thay đổi kiểu dữ liệu bằng cách nhấn phím tắt D:

2016-04-16_16-50-10

Fig.8

Để cấu hình các kiểu dữ liệu chọn Options > Setup data types… hoặc nhấn Alt+D:

2016-04-16_16-52-43

Fig.9

Đây là các thiết lập mặc định của IDA để chuyển đổi giữa các kiểu byte, word và dword khi nhấn D, tuy nhiên ta cũng có thể cấu hình thêm các loại dữ liệu khác nếu cần thiết. Trong trường hợp này của unpackme, ta cần không thực hiện việc thay đổi kiểu dữ liệu nữa mà thực hiện chuyển đổi các dữ liệu này sang các lệnh assembly. Để thực hiện việc chuyển đổi này, IDA hỗ trợ phím tắt C hoặc vào Edit > Code:

2016-04-16_17-11-40

Fig.10

Kết quả có được như sau:

2016-04-16_17-13-25

Fig.11

Ta thấy rằng, dữ liệu đã được chuyển thành một lệnh nhảy tới địa chỉ thuộc section đầu tiên, bắt đầu từ 400000 và kết thúc tại 401000. Vì IDA đã không nhận diện được code chuẩn ngay từ đầu nên lệnh nhảy này cũng không cho ta nhiều thông tin, chọn địa chỉ 400158 và nhấn Enter để follow cũng không tới được địa chỉ đó. Nếu như chúng ta sử dụng trình debugger như OllyDBG thì hoàn toàn có thể tới được địa chỉ trên:

2016-04-16_17-28-16

Fig.12

Tuy nhiên, mục tiêu của bài viết là sử dụng IDA để thực hiện nên ta sẽ làm theo cách khác. Ta sẽ thay đổi cách load file với tùy chọn Manual load như đã thấy trong phần Options của IDA. Mở lại unpackme, lựa chọn như hình minh họa:

2016-04-16_17-35-49

Fig.13

Tùy chọn Manual load cho phép ta quyết định sections nào sẽ được load vào IDA thay vì để IDA tự động load toàn bộ, việc bỏ tùy chọn Create imports segment sẽ loại bỏ cảnh báo của IDA về việc IAT bị hủy. Sau khi cấu hình như trên, nhấn OK để tiếp tục:

2016-04-16_17-42-20

Fig.14

IDA yêu cầu nhập địa chỉ của ImageBase. Ta giữ nguyên không thay đổi, nhấn OK để tiếp tục, IDA sẽ yêu cầu xác nhận việc nạp các sections và file header:

2016-04-16_17-44-24

Fig.15

2016-04-16_17-44-24

Fig.16

2016-04-16_17-46-15

Fig.17

Nhấn Yes để xác nhận, sau khi IDA load xong, nhấn C để chuyển dữ liệu sang code:

2016-04-16_17-54-27

Fig.18

Như đã thấy trên hình, địa chỉ 400158 đã được IDA nhận dạng là một location cụ thể, nhấn đúp chuột vào địa chỉ này ta tới vùng code bắt đầu tại địa chỉ đó:

2016-04-16_17-57-47

Fig.19

Tại đây được IDA nhận là Header code, nhấn P để tạo function hoặc chọn Edit > Functions > Create function…

2016-04-16_20-06-06

Fig.20

Nhấn space bar để chuyển sang Graphic mode. Quan sát tại màn hình Graphic ta thấy có rất nhiều lệnh call và lệnh nhảy, các lệnh call đều liên quan đến thanh ghi ebx. Thanh ghi ebx được khởi tạo bởi hai lệnh mov esi, 40601Ch & mov ebx, esi:

2016-04-16_20-19-37

Fig.21

Quan sát giá trị tại 0x40601C xem có thông tin gì:

2016-04-16_20-23-40

Fig.22

Nhấn D để chuyển đổi kiểu dữ liệu về giá trị dword:

2016-04-16_20-25-34

Fig.23

Chuyển tới địa chỉ 0x400130, quan sát tại đây có thể thấy rằng vùng code thể hiện thông tin của các sections tương ứng với thông tin mà ta thấy được khi xem bằng DIE:

2016-04-16_20-36-44

Fig.24

Tên của section thứ nhất (“MEW”) thì rõ ràng rồi, nhưng tên của section thứ hai thì nhìn rối rắm và dường như nó đã được encrypt hoặc có thể chứa thông tin gì đó. Chọn tên section này và thử nhấn C để chuyển sang mã lệnh, ta thấy có lệnh được ẩn trong chuỗi tên:

2016-04-16_20-46-51

Fig.25

Ta thấy có hai lệnh nhảy tới hai địa chỉ là 400108+7 và 400108+4, các địa chỉ này đều nằm giữa chuỗi tên của section thứ nhất, để có thông tin cụ thể về các địa chỉ này ta cần phải undefine vùng địa chỉ chứa tên của section thứ nhất. Chọn địa chỉ 0x400108 và nhấn phím U:

2016-04-16_21-05-16

Fig.26

Sau khi undefine, ta thấy tên của section thứ nhất chính xác chỉ từ 0x400108 đến 0x40010B, còn các vùng từ 0x40010C tới 0x40010F là vùng dữ liệu được IDA đánh dấu là unk (unknown). Để chỉnh sửa lại cho chính xác, nhấn C để chuyển đổi vùng đó thành code:

2016-04-16_21-19-48

Fig.27

Sau khi chuyển đổi thành công thì đoạn code tại 0x400130 cũng thay đổi theo:

2016-04-16_21-23-31

Fig.28

Thông tin thu được khá hữu ích rồi, tuy nhiên để tiếp tục ta phải sử dụng tới việc debug. Lựa chọn trình debugger như hình:

2016-04-16_21-32-57

Fig.29

Quay lại lệnh nhảy tới sub_400158, nhấn F2 để đặt một breakpoint:

2016-04-16_21-34-46

Fig.30

Sau khi đặt bp xong, nhấn F9 để run unpackme, ta sẽ dừng lại tại bp:

2016-04-16_21-40-13

Fig.31

Nhấn F7 để trace tới sub_400158:

2016-04-16_21-41-43

Fig.32

2016-04-16_21-43-10

Fig.33

Tại màn hình debug của IDA, nhấn Shift+F7 để mở cửa sổ Segments. Quan sát tại cửa sổ này ta thấy thông tin về các sections của file. Thông thường, dưới thông tin về PE header sẽ là section .text (ở đây có thể là section MEW(đã bị packer đổi tên)):

2016-04-16_22-01-59

Fig.34

Theo thông tin cửa sổ Segments cung cấp thì section MEW bắt đầu từ 0x401000 và kết thúc tại 0x406000, vậy size của section này là 0x5000. Tiếp theo, mở cửa sổ Breakpoint lists (Ctrl+Alt+B) để thiết lập một bp tương tự như ta đặt memory bp trong OllyDBG. Tại cửa sổ Breakpoints, nhấn Ins để thiết lập thêm một bp như sau:

2016-04-17_1-24-04

Fig.35

Với thông tin như trên, ta sẽ thiết lập một bp tại section đầu tiên (thường là section chứa OEP), nơi mà code sẽ được ghi vào để xem code được decompress tại section đó như thế nào. Ở đây ta chọn section MEW tại 0x401000 và kích thước là 0x5000 để yêu cầu dừng lại khi đạt tới kích thước đã thiết lập. Sau khi đặt xong, nhấn F9 để thực thi:

2016-04-16_22-28-09

Fig.36

Sau khi dừng lại tại bp, ta thấy code đã được decompress xong. Bước tiếp theo ta sẽ tiến hành dump toàn bộ file. Dựa trên thông tin từ màn hình Segments cung cấp, ta sẽ dump file bắt đầu từ 0x400000 tới 0x407004. Để dump được ta sử dụng idc script sau:

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

Tại IDA, nhấn Shift+F2 để mở cửa sổ thực thi script, copy&paste đoạn script trên vào, sau đó nhấn Run để thực hiện:

2016-04-17_1-36-22

Fig.37

Kết quả, ta có file được dump ra là test00_dump.bin nằm cùng thư mục của unpackme:

2016-04-17_1-40-01

Fig.38

Sử dụng PEditor để mở file vừa được dump:

2016-04-17_1-42-02

Fig.39

Nhấn sections để xem thông tin về các section, chuột phải tại section MEW và chọn:

2016-04-17_1-44-59

Fig.40

Sau khi dump xong, đổi .bin thành .exe:

2016-04-17_1-48-53

Fig.41

Bước cuối cùng của quá trình unpack chính là fix IAT. Mở Scylla và chọn process test00.exe. Chỉnh lại OEP thành 0x401000, sau đó nhấn IAT Autosearch:

2016-04-17_1-53-28

Fig.42

OK Scylla đã tìm thấy thông tin của IAT, tiếp theo nhấn Get Imports để nhận các APIs:

2016-04-17_1-56-59

Fig.43

Tìm thấy 19 hàm APIs mà unpackme sử dụng, không có invalid. Nhấn Fix Dump để thực hiện fix cho file test00_dump.exe:

IDA_19funcs

Fig.44

OK, vậy là quá trình rebuild thành công, Scylla tạo ra file mới là test00_dump_SCY.exe. Chạy file đã fix kết quả như sau:

2016-04-17_2-02-57

Fig.45

Vậy là quá trình unpack đã thành công!!

2016-04-21_18-33-26


Link: https://forum.tuts4you.com/topic/37666-crackme-find-the-flag-by-extremecoders/

Cái crackme này lúc đầu download về, cho vào IDA analyse, nhìn đống messy code xong thấy ngơ luôn, như kiểu thanh niên ngáo đá muốn phá đảo thế giới ảo ….emo_popo_choler. Sau đọc được bài write-up của kao (http://lifeinhex.com/solving-find-the-flag-crackme-by-extreme-coders/), thấy dị dị nên note lại. sexy_girl.

Load crackme vào IDA, quan sát code tại _main thấy như sau:

2015-11-05_9-39-22Đặt BP tại lệnh popa:

2015-11-05_9-53-19Tiến hành debug, dừng lại tại bp, quan sát thấy vùng code đã được giải mã như sau:

2015-11-05_9-57-35Cuộn chuột xuống dưới để xem code đã được giải mã hết chưa thì lại bắt gặp vòng lặp xor nữa beated và vùng code bị encrypted ở dưới còn rất nhiều:

2015-11-05_10-03-252015-11-05_10-04-38Việc đặt BP để decrypt code bằng tay sẽ mất nhiều thời gian và không hiệu quả. Cách hiệu quả nhất là dùng script để thực hiện việc giải mã một cách tự động. Script thực hiện quá trình giải mã như sau:

/* IDC Script: this script automate decrypt all encrypted code */
//declare local variables
auto ea;
auto addr;
auto size;
auto xorval;
auto x;
auto b;
 
ea = MinEA(); //set ea to the lowest address used in the program (ea = 401000h)
ea = FindBinary(ea, SEARCH_DOWN , "E8 00 00 00 00 5E 81 C6"); // search for the specified byte. If not found return BADADDR. Ex: First found at 40103Bh
while (ea != BADADDR)
{
   addr = ea + 5 + 0x16;      //get adrr of encrypted code (ex: addr = 40103Bh + 5 + 0x16 = 401056h)
   size = Dword(ea + 0xD);    //get size of encrypted code at ea (40103Bh), ex: size = 204h
   xorval = Byte(ea + 0x15);  //get xor key, ex: xorval = 86h

   Message(form("Encrypted code parameters: start=%x size=%x key=%x\n", addr, size, xorval));
   for (x=0; x<size; x++)
   {
      b = Byte(addr + x);
      PatchByte(addr + x, b^xorval);
   }
   ea = FindBinary(ea +1, SEARCH_DOWN, "E8 00 00 00 00 5E 81 C6"); //next found, ex: ea = 401277h
}

Toàn bộ đoạn script trên thực hiện việc tìm kiếm vùng code liên quan đến việc giải mã, sau đó trích xuất các thông tin liên quan tới vùng code bị encrypted, gồm địa chỉ (ea), kích thước (size), key mã hóa (xorval). Sau khi có đủ thông tin, thực hiện việc giải mã.

Để thực thi script, reanalyse lại crackme trong IDA, tại _main nhấn Shift-F2, nhập script và cho thực thi:

2015-11-05_10-24-53Sau khi toàn bộ code đã được giải mã xong, thực hiện tái tạo lại hàm _main do ban đầu đã bị sai: .text:00401056 _main endp ; sp-analysis failed.

2015-11-05_10-50-56Phân tích sơ bộ và đặt tên lại một số biến, ta có được thông tin sau:

2015-11-05_11-01-16Sau đó Flag nhập vào sẽ được sử dung để tính toán và so sánh, ví dụ như các đoạn code sau:

2015-11-05_11-04-562015-11-05_11-07-24Mã giả có được bằng Hexrays:

v12 = 0x88;
  do
  {
    --v12;
    *(_BYTE *)(v12 + 0x401333) ^= 0x26u;
  }
  while ( v12 );
  size_EncryptCode4 = 0x88;
  do
  {
    --size_EncryptCode4;
    *(_BYTE *)(size_EncryptCode4 + 0x4013F3) ^= 0x8Eu;
  }
  while ( size_EncryptCode4 );
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[23]
     + enteredString[26]
     + enteredString[14]
     + enteredString[24]
     + enteredString[17]
     + enteredString[12]
     + enteredString[10]
     + enteredString[15]
     + enteredString[4]
     + enteredString[2]
     + enteredString[6]
     + enteredString[1]
     + enteredString[29]
     + enteredString[11]
     + 2 * (enteredString[9] + enteredString[19]) == 1927 )
    ++iResult;
  v14 = 0x88;
  do
  {
    --v14;
    *(_BYTE *)(v14 + 0x4013F3) ^= 0x8Eu;
  }
  while ( v14 );
  size_EncryptCode5 = 0x17;
  do
  {
    --size_EncryptCode5;
    *(_BYTE *)(size_EncryptCode5 + 0x4014B3) ^= 0x29u;
  }
  while ( size_EncryptCode5 );

Tóm lại, đếm sơ sơ thì có khoảng gần 100 vùng code tính toán và kiểm tra kiểu như trênextreme_sexy_girl. Lẫn lộn trong đống code này là các đoạn code chịu trách nhiệm encryp/decrypt.

Toàn bộ code có được của hàm _main bằng HexRays:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  FILE *v3; // eax@1
  HMODULE handle_kernel32; // eax@1
  FARPROC addr_Sleep; // esi@1
  signed int iResult; // eax@1
  char enteredString[32]; // [sp+10h] [bp-A0h]@1
  char ProcName[6]; // [sp+30h] [bp-80h]@1
  char ModuleName[9]; // [sp+38h] [bp-78h]@1
  char def_string2[17]; // [sp+44h] [bp-6Ch]@1
  char def_string1[61]; // [sp+58h] [bp-58h]@1
  char v13; // [sp+95h] [bp-1Bh]@1
  char str_Result_Msg[19]; // [sp+98h] [bp-18h]@201

  *(_DWORD *)enteredString = 0;
  *(_DWORD *)&enteredString[4] = 0;
  *(_DWORD *)&enteredString[8] = 0;
  *(_DWORD *)&enteredString[12] = 0;
  *(_DWORD *)&enteredString[16] = 0;
  *(_DWORD *)&enteredString[20] = 0;
  *(_DWORD *)&enteredString[24] = 0;
  *(_DWORD *)&enteredString[28] = 0;
  def_string1[0] = '-';
  def_string1[1] = '-';
  def_string1[2] = '-';
  def_string1[3] = '-';
  def_string1[4] = '-';
  def_string1[5] = '-';
  def_string1[6] = '-';
  def_string1[7] = '-';
  def_string1[8] = '-';
  def_string1[9] = '-';
  def_string1[51] = '-';
  def_string1[52] = '-';
  def_string1[53] = '-';
  def_string1[54] = '-';
  def_string1[55] = '-';
  def_string1[56] = '-';
  def_string1[57] = '-';
  def_string1[58] = '-';
  def_string1[59] = '-';
  def_string1[60] = '-';
  def_string1[10] = 'F';
  def_string1[11] = 'i';
  def_string1[12] = 'n';
  def_string1[13] = 'd';
  def_string1[14] = ' ';
  def_string1[15] = 't';
  def_string1[16] = 'h';
  def_string1[17] = 'e';
  def_string1[18] = ' ';
  def_string1[19] = 'f';
  def_string1[20] = 'l';
  def_string1[21] = 'a';
  def_string1[22] = 'g';
  def_string1[23] = ':';
  def_string1[24] = ' ';
  def_string1[25] = 'B';
  def_string1[26] = 'y';
  def_string1[27] = ' ';
  def_string1[28] = 'E';
  def_string1[29] = 'x';
  def_string1[30] = 't';
  def_string1[31] = 'r';
  def_string1[32] = 'e';
  def_string1[33] = 'm';
  def_string1[34] = 'e';
  def_string1[35] = 'C';
  def_string1[36] = 'o';
  def_string1[37] = 'd';
  def_string1[38] = 'e';
  def_string1[39] = 'r';
  def_string1[40] = 's';
  def_string1[41] = ',';
  def_string1[42] = ' ';
  def_string1[43] = 'N';
  def_string1[44] = 'o';
  def_string1[45] = 'v';
  def_string1[46] = ' ';
  def_string1[47] = '2';
  def_string1[48] = '0';
  def_string1[49] = '1';
  def_string1[50] = '5';
  v13 = '\0';
  puts(def_string1);
  puts("\n\n");
  def_string2[0] = 'E';
  def_string2[1] = 'n';
  def_string2[2] = 't';
  def_string2[3] = 'e';
  def_string2[4] = 'r';
  def_string2[5] = ' ';
  def_string2[6] = 'y';
  def_string2[7] = 'o';
  def_string2[8] = 'u';
  def_string2[9] = 'r';
  def_string2[10] = ' ';
  def_string2[11] = 'f';
  def_string2[12] = 'l';
  def_string2[13] = 'a';
  def_string2[14] = 'g';
  def_string2[15] = ':';
  def_string2[16] = '\0';
  puts(def_string2);
  puts("\n");
  v3 = (FILE *)sub_404DF4();
  fgets(enteredString, 31, v3);
  ModuleName[0] = 'k';
  ModuleName[1] = 'e';
  ModuleName[2] = 'r';
  ModuleName[3] = 'n';
  ModuleName[4] = 'e';
  ModuleName[5] = 'l';
  ModuleName[6] = '3';
  ModuleName[7] = '2';
  ModuleName[8] = '\0';
  handle_kernel32 = GetModuleHandleA(ModuleName);
  ProcName[0] = 'S';
  ProcName[1] = 'l';
  ProcName[2] = 'e';
  ProcName[3] = 'e';
  ProcName[4] = 'p';
  ProcName[5] = '\0';
  addr_Sleep = GetProcAddress(handle_kernel32, ProcName);
  ((void (__stdcall *)(signed int))addr_Sleep)(750);
  iResult = 0;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[26]
     + enteredString[20]
     + enteredString[10]
     + enteredString[7]
     + 3 * enteredString[14]
     + 2
     * (enteredString[23]
      + enteredString[24]
      + enteredString[17]
      + enteredString[21]
      + enteredString[12]
      + enteredString[18]) == 2024 )
    iResult = 1;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[23]
     + enteredString[26]
     + enteredString[14]
     + enteredString[24]
     + enteredString[17]
     + enteredString[12]
     + enteredString[10]
     + enteredString[15]
     + enteredString[4]
     + enteredString[2]
     + enteredString[6]
     + enteredString[1]
     + enteredString[29]
     + enteredString[11]
     + 2 * (enteredString[9] + enteredString[19]) == 1927 )
    ++iResult;
  if ( enteredString[14] + enteredString[10] == 148 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[14]
     + enteredString[10]
     + enteredString[11]
     + enteredString[3]
     + enteredString[25]
     + 2 * (enteredString[22] + enteredString[27]) == 741 )
    ++iResult;
  if ( enteredString[24] + enteredString[29] == 229 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[26]
     + enteredString[20]
     + enteredString[21]
     + enteredString[12]
     + enteredString[7]
     + enteredString[9]
     + enteredString[15]
     + enteredString[4]
     + enteredString[1]
     + enteredString[29]
     + enteredString[11]
     + enteredString[3]
     + enteredString[27]
     + enteredString[25]
     + enteredString[5]
     + enteredString[8]
     + enteredString[16]
     + 2 * (enteredString[6] + enteredString[19]) == 2133 )
    ++iResult;
  if ( enteredString[14]
     + enteredString[17]
     + enteredString[7]
     + enteredString[15]
     + enteredString[11]
     + enteredString[8] == 619 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[26]
     + enteredString[21]
     + enteredString[12]
     + enteredString[7]
     + enteredString[11]
     + enteredString[16]
     + enteredString[28]
     + 2 * enteredString[23] == 996 )
    ++iResult;
  if ( enteredString[24]
     + enteredString[17]
     + enteredString[6]
     + enteredString[1]
     + enteredString[29]
     + 2 * enteredString[21] == 717 )
    ++iResult;
  if ( enteredString[24]
     + enteredString[21]
     + enteredString[12]
     + enteredString[18]
     + enteredString[6]
     + enteredString[29]
     + enteredString[11]
     + enteredString[8]
     + enteredString[16]
     + 3 * enteredString[26]
     + 2
     * (enteredString[0]
      + enteredString[17]
      + enteredString[2]
      + enteredString[19]
      + enteredString[5]
      + enteredString[28]
      + enteredString[13]) == 2308 )
    ++iResult;
  if ( enteredString[20]
     + enteredString[17]
     + enteredString[21]
     + enteredString[7]
     + enteredString[9]
     + enteredString[1]
     + enteredString[19]
     + enteredString[11]
     + enteredString[27]
     + enteredString[8]
     + enteredString[28]
     + 3 * (enteredString[14] + enteredString[3])
     + 2 * (enteredString[0] + enteredString[23] + enteredString[18] + enteredString[6]) == 2347 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[26]
     + enteredString[20]
     + enteredString[24]
     + enteredString[10]
     + enteredString[9]
     + enteredString[6]
     + enteredString[11]
     + enteredString[25]
     + 2 * (enteredString[23] + enteredString[28]) == 1041 )
    ++iResult;
  if ( enteredString[23] + enteredString[6] + enteredString[27] == 184 )
    ++iResult;
  if ( enteredString[23]
     + enteredString[18]
     + enteredString[9]
     + enteredString[19]
     + enteredString[11]
     + enteredString[25]
     + enteredString[5]
     + enteredString[16]
     + 2 * enteredString[1] == 1076 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[26]
     + enteredString[20]
     + enteredString[24]
     + enteredString[10]
     + enteredString[7]
     + enteredString[4]
     + enteredString[2]
     + enteredString[6]
     + enteredString[19]
     + enteredString[11] == 1040 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[23]
     + enteredString[14]
     + enteredString[18]
     + enteredString[10]
     + enteredString[4]
     + enteredString[6]
     + enteredString[1]
     + enteredString[19]
     + enteredString[11]
     + enteredString[8]
     + enteredString[16]
     + enteredString[28]
     + 5 * enteredString[21]
     + 3 * enteredString[13]
     + 2 * (enteredString[26] + enteredString[24]) == 2393 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[23]
     + enteredString[26]
     + enteredString[24]
     + enteredString[9]
     + enteredString[15]
     + enteredString[29]
     + enteredString[11]
     + enteredString[25] == 901 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[18]
     + enteredString[10]
     + enteredString[7]
     + enteredString[9]
     + enteredString[29]
     + enteredString[11]
     + enteredString[28]
     + 3 * enteredString[26] == 893 )
    ++iResult;
  if ( enteredString[18] + enteredString[11] + enteredString[3] == 308 )
    ++iResult;
  if ( enteredString[22]
     + enteredString[14]
     + enteredString[20]
     + enteredString[12]
     + enteredString[10]
     + enteredString[4]
     + enteredString[2]
     + enteredString[19]
     + enteredString[29]
     + enteredString[5]
     + enteredString[8]
     + enteredString[13]
     + 2 * enteredString[18] == 1350 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[26]
     + enteredString[14]
     + enteredString[20]
     + enteredString[17]
     + enteredString[7]
     + enteredString[9]
     + enteredString[1]
     + enteredString[19]
     + enteredString[5]
     + enteredString[8]
     + 2 * (enteredString[15] + enteredString[29] + enteredString[28]) == 0x62C )
    ++iResult;
  if ( enteredString[14]
     + enteredString[20]
     + enteredString[24]
     + enteredString[12]
     + enteredString[18]
     + enteredString[10]
     + enteredString[15]
     + enteredString[6]
     + enteredString[29]
     + enteredString[3]
     + enteredString[25]
     + 3 * enteredString[21]
     + 2 * (enteredString[0] + enteredString[7] + enteredString[4]) == 0x81B )
    ++iResult;
  if ( enteredString[23]
     + enteredString[26]
     + enteredString[24]
     + enteredString[17]
     + enteredString[21]
     + enteredString[18]
     + enteredString[10]
     + enteredString[6]
     + enteredString[11]
     + enteredString[5]
     + enteredString[8]
     + enteredString[13]
     + 3 * enteredString[22]
     + 2 * (enteredString[7] + enteredString[9] + enteredString[27]) == 0x6B9 )
    ++iResult;
  if ( enteredString[11] + enteredString[28] == 141 )
    ++iResult;
  if ( enteredString[26]
     + enteredString[24]
     + enteredString[17]
     + enteredString[12]
     + enteredString[7]
     + enteredString[4]
     + enteredString[29]
     + enteredString[5]
     + enteredString[16]
     + enteredString[13]
     + 2 * (enteredString[22] + enteredString[15] + enteredString[25]) == 0x60E )
    ++iResult;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[20]
     + enteredString[17]
     + enteredString[18]
     + enteredString[15]
     + enteredString[2]
     + enteredString[1]
     + enteredString[19]
     + enteredString[27]
     + enteredString[5]
     + enteredString[16]
     + enteredString[13]
     + 2
     * (enteredString[14]
      + enteredString[24]
      + enteredString[10]
      + enteredString[7]
      + enteredString[6]
      + enteredString[11]
      + enteredString[25]) == 0x9B4 )
    ++iResult;
  if ( enteredString[22]
     + enteredString[17]
     + enteredString[10]
     + enteredString[5]
     + enteredString[16]
     + 2 * (enteredString[20] + enteredString[24]) == 0x324 )
    ++iResult;
  if ( enteredString[7]
     + enteredString[4]
     + enteredString[2]
     + enteredString[27]
     + enteredString[25]
     + enteredString[5]
     + enteredString[8]
     + enteredString[16]
     + enteredString[13]
     + 3 * (enteredString[22] + enteredString[24] + enteredString[9])
     + 2 * (enteredString[0] + enteredString[1] + enteredString[28] + 2 * enteredString[10]) == 0x941 )
    ++iResult;
  if ( enteredString[23]
     + enteredString[17]
     + enteredString[12]
     + enteredString[27]
     + enteredString[16]
     + 2 * enteredString[9] == 0x2C0 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[26]
     + enteredString[24]
     + enteredString[10]
     + enteredString[7]
     + enteredString[4]
     + enteredString[6]
     + enteredString[1]
     + enteredString[29]
     + enteredString[11]
     + enteredString[3]
     + enteredString[27]
     + enteredString[5]
     + enteredString[8]
     + enteredString[13]
     + 3 * enteredString[20]
     + 2 * (enteredString[22] + enteredString[14] + enteredString[15]) == 0x83E )
    ++iResult;
  if ( enteredString[26]
     + enteredString[14]
     + enteredString[21]
     + enteredString[18]
     + enteredString[9]
     + enteredString[15]
     + enteredString[2]
     + enteredString[29]
     + enteredString[3]
     + enteredString[5] == 968 )
    ++iResult;
  if ( enteredString[24]
     + enteredString[12]
     + enteredString[4]
     + enteredString[1]
     + enteredString[19]
     + enteredString[3]
     + enteredString[16]
     + 2 * (enteredString[26] + enteredString[21] + enteredString[6] + enteredString[5]) == 1356 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[21]
     + enteredString[9]
     + enteredString[15]
     + enteredString[29]
     + enteredString[3]
     + enteredString[5]
     + 3 * enteredString[23]
     + 2 * (enteredString[12] + enteredString[7] + enteredString[19] + enteredString[27]) == 0x74D )
    ++iResult;
  if ( enteredString[26]
     + enteredString[14]
     + enteredString[18]
     + enteredString[7]
     + enteredString[9]
     + enteredString[2]
     + enteredString[1]
     + enteredString[3]
     + enteredString[5]
     + enteredString[8]
     + enteredString[28]
     + 2
     * (enteredString[0]
      + enteredString[20]
      + enteredString[17]
      + enteredString[21]
      + enteredString[15]
      + enteredString[6]
      + enteredString[19]
      + enteredString[25]
      + enteredString[13]) == 2769 )
    ++iResult;
  if ( enteredString[22]
     + enteredString[23]
     + enteredString[10]
     + enteredString[15]
     + enteredString[19]
     + enteredString[11]
     + enteredString[8]
     + enteredString[28]
     + 2
     * (enteredString[0]
      + enteredString[24]
      + enteredString[1]
      + enteredString[3]
      + enteredString[27]
      + 2 * (enteredString[26] + enteredString[7])) == 2147 )
    ++iResult;
  if ( enteredString[23]
     + enteredString[14]
     + enteredString[20]
     + enteredString[24]
     + enteredString[17]
     + enteredString[19]
     + enteredString[11]
     + enteredString[3]
     + enteredString[25]
     + enteredString[13]
     + 2
     * (enteredString[0]
      + enteredString[21]
      + enteredString[9]
      + enteredString[2]
      + enteredString[6]
      + enteredString[1]
      + enteredString[16]) == 2450 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[26]
     + enteredString[24]
     + enteredString[17]
     + enteredString[21]
     + enteredString[18]
     + enteredString[10]
     + enteredString[9]
     + enteredString[19]
     + enteredString[27]
     + enteredString[25]
     + 3 * (enteredString[11] + enteredString[13])
     + 2 * (enteredString[12] + enteredString[28] + 2 * (enteredString[1] + enteredString[3])) == 2755 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[23]
     + enteredString[26]
     + enteredString[20]
     + enteredString[21]
     + enteredString[12]
     + enteredString[7]
     + enteredString[9]
     + enteredString[2]
     + enteredString[1]
     + enteredString[11]
     + enteredString[5]
     + enteredString[16]
     + 3 * (enteredString[6] + enteredString[25])
     + 2 * (enteredString[24] + enteredString[18] + enteredString[10] + enteredString[15] + enteredString[28]) == 2561 )
    ++iResult;
  if ( enteredString[21] + enteredString[4] + enteredString[19] + enteredString[5] + 2 * enteredString[14] == 642 )
    ++iResult;
  if ( enteredString[20]
     + enteredString[24]
     + enteredString[17]
     + enteredString[4]
     + enteredString[27]
     + enteredString[25]
     + enteredString[8]
     + enteredString[16]
     + 3 * enteredString[19]
     + 2 * (enteredString[26] + enteredString[14] + enteredString[7] + enteredString[13] + 2 * enteredString[29]) == 0x920 )
    ++iResult;
  if ( enteredString[20]
     + enteredString[24]
     + enteredString[12]
     + enteredString[18]
     + enteredString[10]
     + 3 * enteredString[3]
     + 2
     * (enteredString[17]
      + enteredString[21]
      + enteredString[9]
      + enteredString[15]
      + enteredString[2]
      + enteredString[1]
      + enteredString[19]
      + enteredString[29]
      + enteredString[11]
      + enteredString[8]) == 2925 )
    ++iResult;
  if ( enteredString[23]
     + enteredString[20]
     + enteredString[24]
     + enteredString[10]
     + enteredString[15]
     + enteredString[6]
     + enteredString[19]
     + enteredString[29]
     + enteredString[3]
     + enteredString[8]
     + enteredString[16]
     + enteredString[28]
     + 3 * enteredString[7]
     + 2
     * (enteredString[14]
      + enteredString[17]
      + enteredString[12]
      + enteredString[9]
      + enteredString[1]
      + enteredString[25]
      + enteredString[13]) == 2956 )
    ++iResult;
  if ( enteredString[22]
     + enteredString[26]
     + enteredString[24]
     + enteredString[7]
     + enteredString[15]
     + enteredString[2]
     + enteredString[11]
     + enteredString[3]
     + enteredString[25]
     + enteredString[5]
     + enteredString[13]
     + 2 * (enteredString[20] + enteredString[21] + enteredString[12] + enteredString[18] + enteredString[8]) == 2050 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[9]
     + enteredString[11]
     + enteredString[25]
     + enteredString[5]
     + 2 * enteredString[22] == 628 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[26]
     + enteredString[20]
     + enteredString[12]
     + enteredString[2]
     + enteredString[6]
     + enteredString[29]
     + enteredString[11]
     + enteredString[3]
     + enteredString[25]
     + enteredString[5]
     + enteredString[16]
     + 2 * (enteredString[22] + enteredString[9] + enteredString[19] + 2 * enteredString[28]) == 1842 )
    ++iResult;
  if ( enteredString[24] + enteredString[10] + enteredString[9] + enteredString[11] + enteredString[16] == 491 )
    ++iResult;
  if ( enteredString[22]
     + enteredString[23]
     + enteredString[24]
     + enteredString[17]
     + enteredString[12]
     + enteredString[6]
     + enteredString[29]
     + enteredString[27]
     + enteredString[5]
     + enteredString[16]
     + enteredString[28]
     + 3 * enteredString[4]
     + 2
     * (enteredString[21]
      + enteredString[15]
      + enteredString[2]
      + enteredString[1]
      + enteredString[3]
      + enteredString[25]) == 2557 )
    ++iResult;
  if ( enteredString[26] + enteredString[7] + enteredString[27] + 3 * enteredString[2] == 474 )
    ++iResult;
  if ( enteredString[23]
     + enteredString[14]
     + enteredString[21]
     + enteredString[12]
     + enteredString[7]
     + enteredString[15]
     + enteredString[2]
     + enteredString[11]
     + enteredString[3]
     + enteredString[8]
     + 2 * (enteredString[18] + enteredString[6] + enteredString[27] + enteredString[28]) == 1472 )
    ++iResult;
  if ( enteredString[17]
     + enteredString[7]
     + enteredString[2]
     + enteredString[25]
     + enteredString[5]
     + enteredString[13]
     + 3 * enteredString[28] == 723 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[23]
     + enteredString[14]
     + enteredString[24]
     + enteredString[21]
     + enteredString[12]
     + enteredString[10]
     + enteredString[7]
     + enteredString[15]
     + enteredString[19]
     + enteredString[29]
     + enteredString[3]
     + enteredString[16]
     + 3 * (enteredString[6] + enteredString[1])
     + 2 * (enteredString[18] + enteredString[25]) == 2304 )
    ++iResult;
  if ( enteredString[20]
     + enteredString[24]
     + enteredString[17]
     + enteredString[21]
     + enteredString[10]
     + enteredString[15]
     + enteredString[2]
     + enteredString[6]
     + enteredString[1]
     + enteredString[19]
     + enteredString[3]
     + enteredString[25]
     + enteredString[8]
     + enteredString[28]
     + 3 * enteredString[12]
     + 2 * (enteredString[22] + enteredString[26] + enteredString[18] + enteredString[4]) == 2234 )
    ++iResult;
  if ( enteredString[22] + enteredString[12] + enteredString[10] + enteredString[4] + enteredString[29] == 463 )
    ++iResult;
  if ( enteredString[21] + enteredString[18] == 211 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[23]
     + enteredString[21]
     + enteredString[18]
     + enteredString[7]
     + enteredString[4]
     + enteredString[11]
     + enteredString[8]
     + enteredString[28]
     + enteredString[13]
     + 3 * enteredString[12]
     + 2 * (enteredString[26] + enteredString[9] + enteredString[29] + enteredString[27]) == 2008 )
    ++iResult;
  if ( enteredString[20]
     + enteredString[12]
     + enteredString[18]
     + enteredString[15]
     + enteredString[29]
     + enteredString[5]
     + enteredString[28]
     + 3 * enteredString[10]
     + 2 * (enteredString[14] + enteredString[25]) == 1228 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[24]
     + enteredString[21]
     + enteredString[9]
     + enteredString[4]
     + enteredString[2]
     + enteredString[1]
     + enteredString[11]
     + enteredString[3]
     + enteredString[5]
     + enteredString[8]
     + enteredString[28] == 1191 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[14]
     + enteredString[20]
     + enteredString[24]
     + enteredString[15]
     + enteredString[4]
     + enteredString[1]
     + enteredString[5]
     + enteredString[16]
     + 3 * enteredString[29]
     + 2 * (enteredString[2] + enteredString[8]) == 1691 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[23]
     + enteredString[14]
     + enteredString[17]
     + enteredString[12]
     + enteredString[10]
     + enteredString[9]
     + enteredString[15]
     + enteredString[19]
     + enteredString[3]
     + 2
     * (enteredString[21]
      + enteredString[7]
      + enteredString[1]
      + enteredString[27]
      + enteredString[8]
      + 2 * enteredString[26]) == 2070 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[17]
     + enteredString[18]
     + enteredString[10]
     + enteredString[2]
     + enteredString[19]
     + enteredString[11]
     + enteredString[28]
     + 3 * (enteredString[14] + enteredString[20] + enteredString[1])
     + 2
     * (enteredString[26] + enteredString[7]
                          + enteredString[4]
                          + enteredString[3]
                          + enteredString[25]
                          + enteredString[5]) == 2776 )
    ++iResult;
  if ( enteredString[20]
     + enteredString[10]
     + enteredString[2]
     + enteredString[1]
     + enteredString[19]
     + enteredString[11]
     + enteredString[3]
     + enteredString[27]
     + enteredString[16]
     + enteredString[28]
     + 3 * (enteredString[14] + enteredString[12])
     + 2 * (enteredString[0] + enteredString[24] + enteredString[4]) == 2169 )
    ++iResult;
  if ( enteredString[22]
     + enteredString[7]
     + enteredString[29]
     + enteredString[11]
     + enteredString[27]
     + enteredString[16]
     + enteredString[28]
     + enteredString[13]
     + 2 * (enteredString[23] + enteredString[6] + enteredString[25] + enteredString[5]) == 1394 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[14]
     + enteredString[24]
     + enteredString[21]
     + enteredString[18]
     + enteredString[4]
     + enteredString[6]
     + enteredString[29]
     + enteredString[3]
     + 3 * enteredString[2]
     + 2 * (enteredString[17] + 2 * enteredString[25]) == 1928 )
    ++iResult;
  if ( enteredString[0] + enteredString[23] + enteredString[24] + enteredString[25] + enteredString[5] == 514 )
    ++iResult;
  if ( enteredString[26]
     + enteredString[17]
     + enteredString[21]
     + enteredString[19]
     + enteredString[25]
     + enteredString[16]
     + enteredString[13] == 700 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[26]
     + enteredString[14]
     + enteredString[17]
     + enteredString[18]
     + enteredString[15]
     + enteredString[2]
     + enteredString[19]
     + enteredString[27]
     + enteredString[25]
     + enteredString[13]
     + 2 * enteredString[20] == 1184 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[23]
     + enteredString[20]
     + enteredString[24]
     + enteredString[12]
     + enteredString[15]
     + enteredString[4]
     + enteredString[29]
     + enteredString[3]
     + enteredString[5]
     + 2 * enteredString[11] == 1273 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[26]
     + enteredString[14]
     + enteredString[21]
     + enteredString[12]
     + enteredString[4]
     + enteredString[6]
     + enteredString[1]
     + enteredString[19]
     + enteredString[29]
     + enteredString[5]
     + enteredString[16] == 1192 )
    ++iResult;
  if ( enteredString[22]
     + enteredString[21]
     + enteredString[12]
     + enteredString[15]
     + enteredString[11]
     + enteredString[8]
     + enteredString[28]
     + enteredString[13]
     + 2 * enteredString[14] == 913 )
    ++iResult;
  if ( enteredString[24] + enteredString[18] + enteredString[10] + enteredString[25] + 2 * enteredString[20] == 555 )
    ++iResult;
  if ( enteredString[26] + enteredString[1] + enteredString[16] == 257 )
    ++iResult;
  if ( enteredString[12]
     + enteredString[18]
     + enteredString[7]
     + enteredString[9]
     + enteredString[15]
     + enteredString[4]
     + enteredString[3]
     + enteredString[13]
     + 2 * (enteredString[22] + enteredString[14] + enteredString[10] + enteredString[19] + enteredString[27]) == 1561 )
    ++iResult;
  if ( enteredString[20]
     + enteredString[17]
     + enteredString[18]
     + enteredString[6]
     + enteredString[1]
     + enteredString[19]
     + enteredString[29]
     + enteredString[3]
     + enteredString[13]
     + 2 * enteredString[11] == 1113 )
    ++iResult;
  if ( enteredString[20]
     + enteredString[24]
     + enteredString[10]
     + enteredString[15]
     + enteredString[6]
     + enteredString[13]
     + 2 * enteredString[22] == 589 )
    ++iResult;
  if ( enteredString[23]
     + enteredString[26]
     + enteredString[14]
     + enteredString[17]
     + enteredString[21]
     + enteredString[18]
     + enteredString[10]
     + enteredString[3]
     + enteredString[25]
     + 3 * (enteredString[4] + enteredString[19] + enteredString[27])
     + 2 * (enteredString[22] + enteredString[15] + enteredString[11]) == 2138 )
    ++iResult;
  if ( enteredString[15] + enteredString[2] + enteredString[8] == 287 )
    ++iResult;
  if ( enteredString[15]
     + enteredString[4]
     + enteredString[27]
     + enteredString[8]
     + enteredString[28]
     + 3 * enteredString[6]
     + 2 * enteredString[23] == 729 )
    ++iResult;
  if ( enteredString[0] + enteredString[2] == 199 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[23]
     + enteredString[17]
     + enteredString[10]
     + enteredString[6]
     + enteredString[19]
     + enteredString[3]
     + enteredString[25]
     + enteredString[8]
     + 3 * (enteredString[21] + enteredString[1] + enteredString[11])
     + 2 * (enteredString[22] + enteredString[14] + enteredString[12] + enteredString[29] + enteredString[5]) == 2787 )
    ++iResult;
  if ( enteredString[21]
     + enteredString[10]
     + enteredString[7]
     + enteredString[9]
     + enteredString[4]
     + enteredString[19]
     + enteredString[11]
     + enteredString[3]
     + enteredString[28]
     + 2 * enteredString[2] == 1071 )
    ++iResult;
  if ( enteredString[14]
     + enteredString[20]
     + enteredString[18]
     + enteredString[15]
     + enteredString[6]
     + enteredString[11]
     + enteredString[3]
     + enteredString[25]
     + enteredString[8]
     + enteredString[13] == 958 )
    ++iResult;
  if ( enteredString[10] + enteredString[4] + enteredString[16] + 2 * enteredString[8] == 477 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[17]
     + enteredString[9]
     + enteredString[15]
     + enteredString[4]
     + enteredString[3]
     + enteredString[27]
     + enteredString[8]
     + 3 * enteredString[28]
     + 2
     * (enteredString[14]
      + enteredString[20]
      + enteredString[7]
      + enteredString[2]
      + enteredString[6]
      + enteredString[16]) == 2015 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[26]
     + enteredString[14]
     + enteredString[20]
     + enteredString[10]
     + enteredString[7]
     + enteredString[15]
     + enteredString[1]
     + enteredString[27]
     + enteredString[28]
     + 2 * (enteredString[21] + enteredString[18] + enteredString[2] + enteredString[29]) == 1679 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[22]
     + enteredString[20]
     + enteredString[24]
     + enteredString[7]
     + enteredString[15]
     + enteredString[19]
     + enteredString[11]
     + enteredString[3]
     + enteredString[27]
     + enteredString[5]
     + enteredString[16]
     + enteredString[13]
     + 3 * enteredString[14]
     + 2 * (enteredString[12] + enteredString[10] + enteredString[4] + enteredString[25] + enteredString[28]) == 2404 )
    ++iResult;
  if ( enteredString[12]
     + enteredString[18]
     + enteredString[1]
     + enteredString[29]
     + enteredString[8]
     + enteredString[28]
     + 3 * enteredString[13]
     + 2
     * (enteredString[0]
      + enteredString[21]
      + enteredString[10]
      + enteredString[15]
      + enteredString[4]
      + enteredString[6]
      + enteredString[19]
      + enteredString[11]
      + enteredString[27]) == 2453 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[26]
     + enteredString[14]
     + enteredString[20]
     + enteredString[7]
     + enteredString[9]
     + enteredString[1]
     + enteredString[29]
     + enteredString[11]
     + enteredString[27]
     + enteredString[5]
     + enteredString[16]
     + enteredString[13]
     + 2 * (enteredString[17] + enteredString[3] + enteredString[28]) == 1722 )
    ++iResult;
  if ( enteredString[12]
     + enteredString[18]
     + enteredString[7]
     + enteredString[15]
     + enteredString[4]
     + enteredString[2]
     + enteredString[6]
     + enteredString[1]
     + 3 * (enteredString[5] + enteredString[13])
     + 2
     * (enteredString[22]
      + enteredString[26]
      + enteredString[10]
      + enteredString[3]
      + enteredString[27]
      + enteredString[28]) == 1971 )
    ++iResult;
  if ( enteredString[11] + enteredString[28] == 141 )
    ++iResult;
  if ( enteredString[22]
     + enteredString[14]
     + enteredString[17]
     + enteredString[10]
     + enteredString[1]
     + enteredString[27]
     + 3 * (enteredString[20] + enteredString[7])
     + 2 * (enteredString[18] + enteredString[4] + enteredString[19] + enteredString[25] + enteredString[13]) == 2184 )
    ++iResult;
  if ( enteredString[24]
     + enteredString[17]
     + enteredString[21]
     + enteredString[10]
     + enteredString[7]
     + enteredString[2]
     + enteredString[29]
     + enteredString[11]
     + enteredString[3]
     + enteredString[25]
     + enteredString[16]
     + 3 * (enteredString[22] + enteredString[14])
     + 2
     * (enteredString[0]
      + enteredString[18]
      + enteredString[15]
      + enteredString[1]
      + enteredString[19]
      + enteredString[8]) == 2825 )
    ++iResult;
  if ( enteredString[17] + enteredString[9] + enteredString[2] + enteredString[3] + enteredString[5] == 508 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[14]
     + enteredString[24]
     + enteredString[21]
     + enteredString[12]
     + enteredString[9]
     + enteredString[1]
     + enteredString[19]
     + enteredString[29]
     + enteredString[28]
     + 3 * (enteredString[18] + enteredString[13])
     + 2 * enteredString[16] == 1861 )
    ++iResult;
  if ( enteredString[20]
     + enteredString[15]
     + enteredString[29]
     + enteredString[11]
     + enteredString[27]
     + enteredString[16]
     + enteredString[13] == 673 )
    ++iResult;
  if ( enteredString[23]
     + enteredString[24]
     + enteredString[17]
     + enteredString[21]
     + enteredString[18]
     + enteredString[9]
     + enteredString[15]
     + enteredString[4]
     + enteredString[19]
     + enteredString[3]
     + enteredString[16]
     + 3 * enteredString[29]
     + 2 * (enteredString[11] + enteredString[27] + enteredString[28] + 2 * enteredString[5]) == 2269 )
    ++iResult;
  if ( enteredString[22] + enteredString[6] + enteredString[25] + enteredString[8] == 308 )
    ++iResult;
  if ( enteredString[24]
     + enteredString[12]
     + enteredString[7]
     + enteredString[15]
     + enteredString[6]
     + enteredString[5]
     + enteredString[8]
     + enteredString[16]
     + 2 * enteredString[9] == 1012 )
    ++iResult;
  if ( enteredString[14] + enteredString[16] == 216 )
    ++iResult;
  if ( enteredString[0]
     + enteredString[24]
     + enteredString[17]
     + enteredString[21]
     + enteredString[12]
     + enteredString[7]
     + enteredString[9]
     + enteredString[6]
     + enteredString[1]
     + enteredString[27]
     + enteredString[16]
     + enteredString[13]
     + 3 * enteredString[8]
     + 2 * (enteredString[26] + enteredString[14] + enteredString[4] + enteredString[2] + enteredString[28]) == 2237 )
    ++iResult;
  if ( enteredString[23]
     + enteredString[20]
     + enteredString[15]
     + enteredString[4]
     + enteredString[6]
     + enteredString[1]
     + enteredString[27]
     + 3 * enteredString[10]
     + 2 * enteredString[12] == 985 )
    ++iResult;
  str_Result_Msg[5] = ' ';
  if ( iResult == 0x64 )
  {
    str_Result_Msg[0] = 'T';
    str_Result_Msg[1] = 'h';
    str_Result_Msg[2] = 'a';
    str_Result_Msg[3] = 't';
    str_Result_Msg[4] = 's';
    str_Result_Msg[6] = 'i';
    str_Result_Msg[7] = 't';
    str_Result_Msg[8] = '!';
    str_Result_Msg[9] = '!';
    str_Result_Msg[10] = '\0';
    puts(str_Result_Msg);
  }
  else
  {
    str_Result_Msg[0] = 'N';
    str_Result_Msg[1] = 'o';
    str_Result_Msg[2] = 'p';
    str_Result_Msg[3] = 'e';
    str_Result_Msg[4] = ',';
    str_Result_Msg[6] = 'T';
    str_Result_Msg[7] = 'r';
    str_Result_Msg[8] = 'y';
    str_Result_Msg[9] = ' ';
    str_Result_Msg[10] = 'a';
    str_Result_Msg[11] = 'g';
    str_Result_Msg[12] = 'a';
    str_Result_Msg[13] = 'i';
    str_Result_Msg[14] = 'n';
    str_Result_Msg[15] = ' ';
    str_Result_Msg[16] = ';';
    str_Result_Msg[17] = ')';
    str_Result_Msg[18] = '\0';
    puts(str_Result_Msg);
  }
  ((void (__stdcall *)(signed int))addr_Sleep)(750);
  return 0;
}

Để trích xuất ra các vùng code tính toán mà không bị lẫn các đoạn code encrypt/decrypt, thực hiện script sau để loại bỏ:

/* IDC Script: this script used to finds both encryption and decryption loops and just nops them out*/
//declare local variables
auto ea;
auto addr;
auto size;
auto xorval;
auto x;
auto b;
 
ea = MinEA();
ea = FindBinary(ea, SEARCH_DOWN , "60 E8 00 00 00 00 5E 81");
while (ea != BADADDR)
{
   for (x=0;x<0x1C;x++)
   {
      PatchByte(ea+x, 0x90);
   }
   MakeUnknown(ea, 0x1C, 0);  //undefine the specified
   MakeCode(ea);  //convert to instruction
   ea = FindBinary(ea +1, SEARCH_DOWN , "60 E8 00 00 00 00 5E 81");
}   

Script này được chỉnh lại từ script trên, đơn giản là thực hiện tìm kiếm cả encryption và decryption loops và nop toàn bộ code liên quan. Sau đó, buộc IDA thực hiện phân tích lại vùng code đã patched.

2015-11-05_12-08-17Sau khi Script thực hiện xong, mã giả có được bằng HexRays sẽ như sau:

2015-11-05_12-16-22Sau khi có được thông tin toàn bộ mã giả tử HexRays, sử dụng trình Editor (ở đây tôi dùng Notepad++) để thực hiện lấy toàn bộ thông tin của các vùng code tính toán:

  • Loại bỏ toàn bộ dấu cách thừa bằng cách thay thế 2 dấu cách (”  “) bằng 1 dấu cách (” “). Lặp lại cho đến khi hết thì thôi. Ta có được kết quả tương tự như sau:

2015-11-05_15-48-02

  • Đặt mỗi phép tính trên một dòng bằng cách thay thế “\r\n +” (new line,space,plus) với ” +” (space,plus) và thay thế “\r\n *” (new line,space,star) bằng ” *” (space,star). Ta có được tương tự như sau:

2015-11-05_15-54-01

  • Loại bỏ các lệnh “if”. Loại bỏ các lệnh “++iResult;”. Thay thế “==” với “=”. Kết quả như sau:

2015-11-05_15-59-46

  • Cuối cùng, đổi tên biến “enteredString” thành biến “z” và loại bỏ các “[”“]”, “( ”“ )”. Cuối cùng có được code như sau:
 z0 + z22 + z26 + z20 + z10 + z7 + 3 * z14 + 2 * (z23 + z24 + z17 + z21 + z12 + z18) = 2024
 z0 + z22 + z23 + z26 + z14 + z24 + z17 + z12 + z10 + z15 + z4 + z2 + z6 + z1 + z29 + z11 + 2 * (z9 + z19) = 1927 
 z14 + z10 = 148 
 z0 + z14 + z10 + z11 + z3 + z25 + 2 * (z22 + z27) = 741 
 z24 + z29 = 229 
 z0 + z26 + z20 + z21 + z12 + z7 + z9 + z15 + z4 + z1 + z29 + z11 + z3 + z27 + z25 + z5 + z8 + z16 + 2 * (z6 + z19) = 2133 
 z14 + z17 + z7 + z15 + z11 + z8 = 619 
 z0 + z22 + z26 + z21 + z12 + z7 + z11 + z16 + z28 + 2 * z23 = 996 
 z24 + z17 + z6 + z1 + z29 + 2 * z21 = 717 
 z24 + z21 + z12 + z18 + z6 + z29 + z11 + z8 + z16 + 3 * z26 + 2 * (z0 + z17 + z2 + z19 + z5 + z28 + z13) = 2308 
 z20 + z17 + z21 + z7 + z9 + z1 + z19 + z11 + z27 + z8 + z28 + 3 * (z14 + z3) + 2 * (z0 + z23 + z18 + z6) = 2347 
 z0 + z26 + z20 + z24 + z10 + z9 + z6 + z11 + z25 + 2 * (z23 + z28) = 1041 
 z23 + z6 + z27 = 184 
 z23 + z18 + z9 + z19 + z11 + z25 + z5 + z16 + 2 * z1 = 1076 
 z0 + z22 + z26 + z20 + z24 + z10 + z7 + z4 + z2 + z6 + z19 + z11 = 1040 
 z0 + z22 + z23 + z14 + z18 + z10 + z4 + z6 + z1 + z19 + z11 + z8 + z16 + z28 + 5 * z21 + 3 * z13 + 2 * (z26 + z24) = 2393 
 z0 + z23 + z26 + z24 + z9 + z15 + z29 + z11 + z25 = 901 
 z0 + z22 + z18 + z10 + z7 + z9 + z29 + z11 + z28 + 3 * z26 = 893 
 z18 + z11 + z3 = 308 
 z22 + z14 + z20 + z12 + z10 + z4 + z2 + z19 + z29 + z5 + z8 + z13 + 2 * z18 = 1350 
 z0 + z26 + z14 + z20 + z17 + z7 + z9 + z1 + z19 + z5 + z8 + 2 * (z15 + z29 + z28) = 1580 
 z14 + z20 + z24 + z12 + z18 + z10 + z15 + z6 + z29 + z3 + z25 + 3 * z21 + 2 * (z0 + z7 + z4) = 2075 
 z23 + z26 + z24 + z17 + z21 + z18 + z10 + z6 + z11 + z5 + z8 + z13 + 3 * z22 + 2 * (z7 + z9 + z27) = 1721 
 z11 + z28 = 141 
 z26 + z24 + z17 + z12 + z7 + z4 + z29 + z5 + z16 + z13 + 2 * (z22 + z15 + z25) = 1550 
 z0 + z22 + z20 + z17 + z18 + z15 + z2 + z1 + z19 + z27 + z5 + z16 + z13 + 2 * (z14 + z24 + z10 + z7 + z6 + z11 + z25) = 2484 
 z22 + z17 + z10 + z5 + z16 + 2 * (z20 + z24) = 804 
 z7 + z4 + z2 + z27 + z25 + z5 + z8 + z16 + z13 + 3 * (z22 + z24 + z9) + 2 * (z0 + z1 + z28 + 2 * z10) = 2369 
 z23 + z17 + z12 + z27 + z16 + 2 * z9 = 704 
 z0 + z26 + z24 + z10 + z7 + z4 + z6 + z1 + z29 + z11 + z3 + z27 + z5 + z8 + z13 + 3 * z20 + 2 * (z22 + z14 + z15) = 2110 
 z26 + z14 + z21 + z18 + z9 + z15 + z2 + z29 + z3 + z5 = 968 
 z24 + z12 + z4 + z1 + z19 + z3 + z16 + 2 * (z26 + z21 + z6 + z5) = 1356 
 z0 + z22 + z21 + z9 + z15 + z29 + z3 + z5 + 3 * z23 + 2 * (z12 + z7 + z19 + z27) = 1869 
 z26 + z14 + z18 + z7 + z9 + z2 + z1 + z3 + z5 + z8 + z28 + 2 * (z0 + z20 + z17 + z21 + z15 + z6 + z19 + z25 + z13) = 2769 
 z22 + z23 + z10 + z15 + z19 + z11 + z8 + z28 + 2 * (z0 + z24 + z1 + z3 + z27 + 2 * (z26 + z7)) = 2147 
 z23 + z14 + z20 + z24 + z17 + z19 + z11 + z3 + z25 + z13 + 2 * (z0 + z21 + z9 + z2 + z6 + z1 + z16) = 2450 
 z0 + z26 + z24 + z17 + z21 + z18 + z10 + z9 + z19 + z27 + z25 + 3 * (z11 + z13) + 2 * (z12 + z28 + 2 * (z1 + z3)) = 2755 
 z0 + z23 + z26 + z20 + z21 + z12 + z7 + z9 + z2 + z1 + z11 + z5 + z16 + 3 * (z6 + z25) + 2 * (z24 + z18 + z10 + z15 + z28) = 2561 
 z21 + z4 + z19 + z5 + 2 * z14 = 642 
 z20 + z24 + z17 + z4 + z27 + z25 + z8 + z16 + 3 * z19 + 2 * (z26 + z14 + z7 + z13 + 2 * z29) = 2336 
 z20 + z24 + z12 + z18 + z10 + 3 * z3 + 2 * (z17 + z21 + z9 + z15 + z2 + z1 + z19 + z29 + z11 + z8) = 2925 
 z23 + z20 + z24 + z10 + z15 + z6 + z19 + z29 + z3 + z8 + z16 + z28 + 3 * z7 + 2 * (z14 + z17 + z12 + z9 + z1 + z25 + z13) = 2956 
 z22 + z26 + z24 + z7 + z15 + z2 + z11 + z3 + z25 + z5 + z13 + 2 * (z20 + z21 + z12 + z18 + z8) = 2050 
 z0 + z9 + z11 + z25 + z5 + 2 * z22 = 628 
 z0 + z26 + z20 + z12 + z2 + z6 + z29 + z11 + z3 + z25 + z5 + z16 + 2 * (z22 + z9 + z19 + 2 * z28) = 1842 
 z24 + z10 + z9 + z11 + z16 = 491 
 z22 + z23 + z24 + z17 + z12 + z6 + z29 + z27 + z5 + z16 + z28 + 3 * z4 + 2 * (z21 + z15 + z2 + z1 + z3 + z25) = 2557 
 z26 + z7 + z27 + 3 * z2 = 474 
 z23 + z14 + z21 + z12 + z7 + z15 + z2 + z11 + z3 + z8 + 2 * (z18 + z6 + z27 + z28) = 1472 
 z17 + z7 + z2 + z25 + z5 + z13 + 3 * z28 = 723 
 z0 + z22 + z23 + z14 + z24 + z21 + z12 + z10 + z7 + z15 + z19 + z29 + z3 + z16 + 3 * (z6 + z1) + 2 * (z18 + z25) = 2304 
 z20 + z24 + z17 + z21 + z10 + z15 + z2 + z6 + z1 + z19 + z3 + z25 + z8 + z28 + 3 * z12 + 2 * (z22 + z26 + z18 + z4) = 2234 
 z22 + z12 + z10 + z4 + z29 = 463 
 z21 + z18 = 211 
 z0 + z22 + z23 + z21 + z18 + z7 + z4 + z11 + z8 + z28 + z13 + 3 * z12 + 2 * (z26 + z9 + z29 + z27) = 2008 
 z20 + z12 + z18 + z15 + z29 + z5 + z28 + 3 * z10 + 2 * (z14 + z25) = 1228 
 z0 + z24 + z21 + z9 + z4 + z2 + z1 + z11 + z3 + z5 + z8 + z28 = 1191 
 z0 + z14 + z20 + z24 + z15 + z4 + z1 + z5 + z16 + 3 * z29 + 2 * (z2 + z8) = 1691 
 z0 + z23 + z14 + z17 + z12 + z10 + z9 + z15 + z19 + z3 + 2 * (z21 + z7 + z1 + z27 + z8 + 2 * z26) = 2070 
 z0 + z17 + z18 + z10 + z2 + z19 + z11 + z28 + 3 * (z14 + z20 + z1) + 2 * (z26 + z7 + z4 + z3 + z25 + z5) = 2776 
 z20 + z10 + z2 + z1 + z19 + z11 + z3 + z27 + z16 + z28 + 3 * (z14 + z12) + 2 * (z0 + z24 + z4) = 2169 
 z22 + z7 + z29 + z11 + z27 + z16 + z28 + z13 + 2 * (z23 + z6 + z25 + z5) = 1394 
 z0 + z22 + z14 + z24 + z21 + z18 + z4 + z6 + z29 + z3 + 3 * z2 + 2 * (z17 + 2 * z25) = 1928 
 z0 + z23 + z24 + z25 + z5 = 514 
 z26 + z17 + z21 + z19 + z25 + z16 + z13 = 700 
 z0 + z26 + z14 + z17 + z18 + z15 + z2 + z19 + z27 + z25 + z13 + 2 * z20 = 1184 
 z0 + z23 + z20 + z24 + z12 + z15 + z4 + z29 + z3 + z5 + 2 * z11 = 1273 
 z0 + z26 + z14 + z21 + z12 + z4 + z6 + z1 + z19 + z29 + z5 + z16 = 1192 
 z22 + z21 + z12 + z15 + z11 + z8 + z28 + z13 + 2 * z14 = 913 
 z24 + z18 + z10 + z25 + 2 * z20 = 555 
 z26 + z1 + z16 = 257 
 z12 + z18 + z7 + z9 + z15 + z4 + z3 + z13 + 2 * (z22 + z14 + z10 + z19 + z27) = 1561 
 z20 + z17 + z18 + z6 + z1 + z19 + z29 + z3 + z13 + 2 * z11 = 1113 
 z20 + z24 + z10 + z15 + z6 + z13 + 2 * z22 = 589 
 z23 + z26 + z14 + z17 + z21 + z18 + z10 + z3 + z25 + 3 * (z4 + z19 + z27) + 2 * (z22 + z15 + z11) = 2138 
 z15 + z2 + z8 = 287 
 z15 + z4 + z27 + z8 + z28 + 3 * z6 + 2 * z23 = 729 
 z0 + z2 = 199 
 z0 + z23 + z17 + z10 + z6 + z19 + z3 + z25 + z8 + 3 * (z21 + z1 + z11) + 2 * (z22 + z14 + z12 + z29 + z5) = 2787 
 z21 + z10 + z7 + z9 + z4 + z19 + z11 + z3 + z28 + 2 * z2 = 1071 
 z14 + z20 + z18 + z15 + z6 + z11 + z3 + z25 + z8 + z13 = 958 
 z10 + z4 + z16 + 2 * z8 = 477 
 z0 + z17 + z9 + z15 + z4 + z3 + z27 + z8 + 3 * z28 + 2 * (z14 + z20 + z7 + z2 + z6 + z16) = 2015 
 z0 + z22 + z26 + z14 + z20 + z10 + z7 + z15 + z1 + z27 + z28 + 2 * (z21 + z18 + z2 + z29) = 1679 
 z0 + z22 + z20 + z24 + z7 + z15 + z19 + z11 + z3 + z27 + z5 + z16 + z13 + 3 * z14 + 2 * (z12 + z10 + z4 + z25 + z28) = 2404 
 z12 + z18 + z1 + z29 + z8 + z28 + 3 * z13 + 2 * (z0 + z21 + z10 + z15 + z4 + z6 + z19 + z11 + z27) = 2453 
 z0 + z26 + z14 + z20 + z7 + z9 + z1 + z29 + z11 + z27 + z5 + z16 + z13 + 2 * (z17 + z3 + z28) = 1722 
 z12 + z18 + z7 + z15 + z4 + z2 + z6 + z1 + 3 * (z5 + z13) + 2 * (z22 + z26 + z10 + z3 + z27 + z28) = 1971 
 z11 + z28 = 141 
 z22 + z14 + z17 + z10 + z1 + z27 + 3 * (z20 + z7) + 2 * (z18 + z4 + z19 + z25 + z13) = 2184 
 z24 + z17 + z21 + z10 + z7 + z2 + z29 + z11 + z3 + z25 + z16 + 3 * (z22 + z14) + 2 * (z0 + z18 + z15 + z1 + z19 + z8) = 2825 
 z17 + z9 + z2 + z3 + z5 = 508 
 z0 + z14 + z24 + z21 + z12 + z9 + z1 + z19 + z29 + z28 + 3 * (z18 + z13) + 2 * z16 = 1861 
 z20 + z15 + z29 + z11 + z27 + z16 + z13 = 673 
 z23 + z24 + z17 + z21 + z18 + z9 + z15 + z4 + z19 + z3 + z16 + 3 * z29 + 2 * (z11 + z27 + z28 + 2 * z5) = 2269 
 z22 + z6 + z25 + z8 = 308 
 z24 + z12 + z7 + z15 + z6 + z5 + z8 + z16 + 2 * z9 = 1012 
 z14 + z16 = 216 
 z0 + z24 + z17 + z21 + z12 + z7 + z9 + z6 + z1 + z27 + z16 + z13 + 3 * z8 + 2 * (z26 + z14 + z4 + z2 + z28) = 2237 
 z23 + z20 + z15 + z4 + z6 + z1 + z27 + 3 * z10 + 2 * z12 = 985 

Với một hệ phương trình tuyến tính rối rắm có được như trên, nghĩ ngồi để giải được cái đồng phương trình này chắc chết sexy_girl. May mắn thay có một site hỗ trợ giải phương trình online tại: http://wims.unice.fr/wims/en_tool~linear~linsolver.en.html. Truy cập vào trang Web và paste toàn bộ đoạn code trên vào, sau đó nhấn “Solve the problem”:

2015-11-05_16-14-09Kết quả, có được như sautoo_sad:

2015-11-05_16-16-27Cụ thể:

{ z0 = 102, z1 = 108, z10 = 48, z11 = 108, z12 = 118, z13 = 101, z14 = 100, z15 = 95, z16 = 116, z17 = 104, z18 = 97, z19 = 116, z2 = 97, z20 = 95, z21 = 114, z22 = 49, z23 = 103, z24 = 104, z25 = 116, z26 = 33, z27 = 33, z28 = 33, z29 = 125, z3 = 103, z4 = 123, z5 = 89, z6 = 48, z7 = 117, z8 = 95, z9 = 115 }.

Chuyển đổi toàn bộ kết quả trên trên thành kí tự tương ứng sẽ có được flag cần tìm. Code bằng Python:

asscii=[102, 108, 97, 103, 123, 89, 48, 117, 95, 115, 48, 108, 118, 101, 100, 95, 116, 104, 97, 116, 95, 114, 49, 103, 104, 116, 33, 33, 33, 125]
flag = "".join([chr(c) for c in asscii])

print flag

Kết quả có được Flag: flag{Y0u_s0lved_that_r1ght!!!}.Kiểm tra kết quả:

2015-11-05_16-55-38End..