18/09/2018, 15:18

LÝ THUYẾT DEBUGGING TRONG PHÂN TÍCH MÃ ĐỘC

Debug là gì, Debug cồm những loại nào, được dùng để làm gì và hơn thế nữa. Cùng Chuyên an ninh mạng Bùi Đình Cường của SecurityBox để hiểu rõ hơn Debugging trong phân tích mã độc nhé. 1.Debugger là gì? Debug được dùng để làm gì? Debugger (trình debug) là một chương trình ...

tim-hieu-debugging

Debug là gì, Debug cồm những loại nào, được dùng để làm gì và hơn thế nữa. Cùng Chuyên an ninh mạng Bùi Đình Cường của SecurityBox để hiểu rõ hơn Debugging trong phân tích mã độc nhé.

1.Debugger là gì? Debug được dùng để làm gì?

Debugger (trình debug) là một chương trình hoặc thiết bị phần cứng cho phép kiểm tra và thực thi một chương trình khác. Khi phát triển phần mềm, ta có thể nhập dữ liệu đầu vào cho chương trình và quan sát kết quả đầu ra, nhưng không thể theo dõi cách mà chương trình tính toán các kết quả đầu ra trong lúc nó đang chạy. Các debugger được thiết kế để developer có thể theo dõi và điều khiển trạng thái thực thi của chương trình.

tim-hieu-debugging

Các debugger có thể cung cấp các thông tin mà ta rất khó hoặc không thể lấy được từ các trình dịch ngược. Trình dịch ngược thường chỉ cho phép theo dõi trạng thái tĩnh của chương trình khi nó không được thực thi. Debugger cho phép ta quan sát giá trị của các thanh ghi khi chúng thay đổi trong quá trình thực thi chương trình.

Khả năng theo dõi và điều khiển một mã độc khi nó đang được thực thi cung cấp một góc nhìn sâu hơn về hành vi của nó. Debugger cho phép theo dõi giá trị của mọi ô nhớ, thanh ghi và tham số đầu vào của tất cả các hàm. Ta cũng có thể thay đổi bất cứ thứ gì trong quá trình thực thi một chương trình nhờ debugger, chẳng hạn giá trị của một biến bất kì tại bất cứ thời điểm nào.

2.Các loại Debug

2.1 Các Debugger Mức Mã Nguồn và Mức Hợp Ngữ (source-level và assembly-level)

Đa số lập trình viên đều quen thuộc với các debugger mức mã nguồn cho phép debug trong lúc lập trình. Loại debugger này thường được tích hợp sẵn trong các IDE, chúng cho phép đặt các breakpoint để theo dõi trạng thái các biến và thực thi chương trình từng dòng code một.

Các debugger mức hợp ngữ, hay debugger mức thấp, hoạt động trên mã hợp ngữ thay vì mã nguồn của chương trình. Ta cũng có thể thực thi chương trình từng dòng code một, đặt các breakpoint để dừng lại tại một dòng code cụ thể và theo dõi các vùng nhớ.

Một chuyên gia phân tích mã độc thường xuyên phải sử dụng các debugger mức hợp ngữ vì cô ta không có trong tay mã nguồn của mã độc.

2.2 Debug Mức Nhân và Debug Mức Người Dùng

(Kernel-Mode và User-Mode Debugging)

Debug mức nhân khó khăn hơn debug mức người dùng vì ta thường phải sử dụng hai hệ thống khác nhau khi thực hiện debug mức nhân. Trong user mode, debugger chạy được trên cùng một hệ thống với code được debug. Khi thực hiện debug mức người dùng, ta debug trên một trình thực thi đơn được phân tách rõ ràng với các trình thực thi khác.

Debug mức nhân chỉ có thể được thực hiện khi sử dụng hai hệ thống riêng biệt vì chỉ có một kernel trên mỗi hệ thống; nếu kernel được đặt breakpoint, không một ứng dụng nào trong hệ thống có thể tiếp tục được thực thi, ngay cả debugger. Ta phải sử dụng debugger trên một hệ thống và hệ thống còn lại sẽ thực thi chương trình cần debug. Ngoài ra, hệ điều hành cũng phải được cấu hình cho phép debug mức nhân và ta phải kết nối được hai hệ thống với nhau.

Trước đây, thực hiện debug mức nhân trên cùng một hệ thống là điều khả thi. Chỉ có một công cụ hỗ trợ công việc này là SoftICE, nhưng nó không còn được phát triển và bảo trì từ 2007.

Có rất nhiều công cụ hỗ trợ debug mức nhân và mức người dùng nhưng WinDbg (debug mức nhân và mức người dùng) cùng OllyDbg (chỉ hỗ trợ tốt debug mức người dùng) đang là hai công cụ phổ biến nhất đối với các chuyên gia phân tích mã độc.

Xem thêm Case Study Phân tích mã độc của SecurityBox tại đây nhé!

3.Sử Dụng Một Debugger

Có hai cách để debug một chương trình. Cách thứ nhất là khởi chạy chương trình với một debugger. Khi chương trình được nạp vào bộ nhớ, nó sẽ dừng lại ngay trước khi thực thi entry point của mình. Ta có thể kiểm soát chương trình bắt đầu từ entry point của nó.

Cách thứ hai là gán một debugger vào một chương trình đang được thực thi. Tất cả các thread sẽ được tạm dừng và ta có thể debug chúng. Đây là một cách tốt để debug một chương trình khi nó đang được thực thi hoặc khi ta muốn thực hiện debug một process liên quan đến mã độc.

3.1 Single-Step

Thao tác đơn giản nhất khi debug là single-step trên một chương trình, ta chạy từng lệnh một và quan sát mọi thứ diễn ra trong một chương trình.

Ta có thể thực hiện single-step trên toàn bộ chương trình nhưng đối với những chương trình phức tạp, debug như vậy sẽ tiêu tốn rất nhiều thời gian. Single-step chỉ là cách tốt khi muốn hiểu chi tiết về một đoạn code ngắn. Tuy nhiên, ta phải lựa chọn đoạn code để thực hiện single-step một cách sáng suốt. Luôn luôn tập trung vào bức tranh tổng quát và tránh đi lan man trong từng chi tiết nếu không thực sự cần thiết.

Đoạn code dưới đây là một ví dụ mà ta có thể sử dụng single-step để thực hiện debug:

sử dụng single-step để thực hiện debug

Đoạn code trên minh họa một địa chỉ ô nhớ được truy cập và sửa đổi giá trị trong một vòng lặp. Giá trị dữ liệu tại (1) không phải dạng dữ liệu ASCII hoặc dạng quen thuộc, ta sử dụng debugger để single-step trong vòng lặp để theo dõi xem đoạn code này làm những gì. Giá trị 13 byte tính từ 0x406904 được thay đổi sau mỗi vòng lặp. Sau khi single-step qua 13 vòng lặp, ta kết luận được hàm trên sử dụng lệnh XOR để giải mã string LoadLibraryA. Nếu chỉ thực hiện phân tích tĩnh, khó có thể quan sát được điều này.

3.2 Step-Over và Step-Into

Khi thực hiện single-step, debugger dừng tại mọi dòng lệnh. Mục đích chính của phân tích mã độc là nắm được chương trình làm những gì chứ không phải là đi sâu vào chi tiết từng hàm chức năng của nó. Ví dụ, nếu chương trình gọi hàm LoadLibrary, ta không cần single-step qua từng lệnh một của hàm LoadLibrary.

Để kiểm soát các dòng lệnh thông qua debugger, ta có thể thực hiện step-over hoặc step-into trên các dòng lệnh. Khi thực hiện step-over đối với các lệnh gọi hàm, ta bỏ qua việc thực thi nội dung hàm đó. Ví dụ, nếu step-over một lời gọi hàm thì lệnh tiếp theo mà ta thấy trên debugger là lệnh ngay sau trả về hàm (function call return). Nếu step-into một lệnh gọi hàm, lệnh tiếp theo ta thấy trên debugger là lệnh đầu tiên trong hàm được gọi.

Step-over cho phép ta giảm được số lệnh cần phân tích một cách đáng kể nhưng cũng đem lại rủi ro nếu ta step-over qua các hàm quan trọng. Ngoài ra, một số hàm không bao giờ trả về (Ví dụ: Nội dung hàm thực hiện vòng lặp vô hạn) và nếu ta step-over một hàm như thế, debugger sẽ không thể đạt được sự kiểm soát chương trình. Trong trường hợp này, ta cần chạy lại chương trình và thực hiện step-into hàm không trả về đó.

Khi step-into một hàm, rất dễ để lại bị cuốn vào những single-step xuyên suốt các lệnh trong hàm đó; bản thân hàm đó cũng rất dễ gọi tới các hàm khác và hàm khác nữa khiến ta rất dễ vướng vào những đoạn code vốn không liên quan tới những gì mà mình muốn tìm kiếm. May mắn là nhiều debugger cho phép ta quay lại hàm được gọi và một số debugger cung cấp chức năng step-out cho phép chạy cho đến khi hàm thực hiện trả về.

Tóm lại:

Step-over: Chạy step by step, không thực thi từng lệnh trong nội dung hàm, thực thi toàn hàm và nhận giá trị trả về.

Step-into: Chạy step by step, đi vào thực hiện chi tiết từng lệnh bên trong hàm.

Step-out: Thực thi các lệnh và thoát khỏi hàm hiện tại (Sau lệnh return gần nhất).

Phân tích mã độc là một trong những công việc cần thiết của một Pentester. Trong quá trình đào tạo và làm về dịch vụ pentest, SecurityBox nhận thấy một số bạn mới vào nghề chưa nắm rõ quy trình pentest. Nếu bạn đang tìm hiểu về pentest thì nên xem tại bài viết Checklist trong pentest.

3.3 Dừng thực thi với các Breakpoint

Các breakpoint được dùng để làm điểm dừng thực thi và cho phép ta quan sát trạng thái của chương trình. Khi một chương trình dừng tại breakpoint, nó được sẽ ở trạng thái broken. Ta cần các breakpoint vì không thể truy cập các thanh ghi hoặc địa chỉ ô nhớ khi chương trình đang chạy và các giá trị này thay đổi liên tục.

Ví dụ dưới đây minh họa sự hữu ích của breakpoint. Có một lời gọi tới EAX và trình dịch ngược không thể cho ta biết hàm nào được gọi, ta chỉ cần đặt breakpoint tại lệnh đó để tìm ra chính xác là hàm nào được gọi. Khi chương trình thực thi tới breakpoint, nó sẽ dừng lại và debugger hiển thị giá trị của EAX, chính là vị trí hàm sẽ được gọi.

Một ví dụ khác bên dưới thể hiện một hàm với một lời gọi tới hàm CreateFile để mở handle tới một file. Trong trình dịch ngược, rất khó để xác định tên của file mặc dù tên file là một tham số đầu vào khi gọi hàm CreateFile vì giá trị tham số đầu vào cùa hàm này thường xuyên là giá trị trả về của các hàm khác và rất dễ gây rối khi ta phân tích tĩnh. Trong trường hợp này, sử dụng một debugger để xác định tên file sẽ đơn giản hơn.

Ta đặt một breakpoint tại lời gọi hàm CreateFileW tại 0x401055 và quan sát các giá trị trên stack khi chương trình dừng tại breakpoint. Hình bên dưới là ảnh chụp màn hình WinDbg tại thời điểm chương trình dừng tại breakpoint ta vừa đặt. Ta hiển thị tham số đầu tiên của hàm dưới dạng ASCII string được tên file là “LogFile.txt”.

Mặc dù có thể xác định tên file này trong IDA nhưng sử dụng một debugger là cách nhanh và đơn giản hơn.

Một case-study khác: Ta có một mã độc và một bản ghi gói tin mạng. Trong bản ghi packet, phát hiện dữ liệu đã được mã hóa. Ta có thể tìm lời gọi hàm send để phân tích đoạn code thực hiện chức năng mã hóa nhưng vẫn rất khó để giải mã bản ghi vì ta không biết key mã hóa/giải mã. Việc đọc dữ liệu sẽ đơn giản hơn khi sử dụng một debugger. Ta cần đặt một breakpoint tại hàm mã hóa trước khi dữ liệu được mã hóa tại dòng 0x4010EF, như minh họa bên dưới.

0x4010EF

Hình bên dưới là cửa sổ debug trong OllDbg hiển thị bộ nhớ đệm trước khi được sử dụng bởi hàm mã hóa. Tại dòng 4010EF, dữ liệu ASCII chuẩn bị được đưa vào hàm mã hóa (SimpleSe.GetData) là “Secret Message”.

Ta có thể đặt nhiều loại breakpoint khác nhau bao gồm các breakpoint mềm, cứng và breakpoint có điều kiện (software breakpoint, hardware breakpoint, conditional breakpoint). Dù mọi breakpoint đều phục vụ chung một mục đích nhưng tùy vào từng trường hợp, breakpoint này có hiệu quả mà breakpoint khác có thể không hiệu quả.

3.4 Breakpoint mềm

Các breakpoint ta từng đặt từ đầu đến giờ đều là breakpoint mềm, chương trình tạm dừng khi một lệnh cụ thể được thực thi. Khi ta đặt một breakpoint mà không có tùy chọn đặc biệt nào, hầu hết debugger đều mặc định đó là một breakpoint thực thi phần mềm.

Debugger triển khai breakpoint phần mềm bằng cách ghi đè byte đầu tiên của một câu lệnh với 0xCC, lệnh ngắt INT3, ngắt này được thiết kế dành riêng cho các debugger. Khi lệnh 0xCC được thực thi, OS sinh một ngoại lệ và chuyển quyền điều khiển cho debugger.

 debugger

Hình bên trên minh họa một hàm được đặt một breakpoint cùng với memory dump của nó. Hàm trên bắt đầu với lệnh push ebp với opcode tương ứng là 0x55 nhưng trong memory dump, nó bắt đầu bằng byte 0xCC, đánh dấu breakpoint. Nếu các byte đánh dấu breakpoint này bị thay đổi trong quá trình thực thi chương trình, breakpoint bị vô hiệu hóa. Ví dụ nếu ta đặt breakpoint cho một đoạn code nhưng đoạn code đó lại tự sửa đổi hoặc bị sửa đổi bởi một đoạn code khác thì breakpoint vừa đặt sẽ bị xóa. Bất kì đoạn code nào khác khi đọc vùng nhớ của một hàm có đặt breakpoint, chúng sẽ đọc byte đầu tiên là 0xCC thay vì giá trị ban đầu của byte đó. Ngoài ra, các đoạn code có chức năng kiểm tra tính toàn vẹn của hàm này sẽ ghi nhận sự thay đổi.

Trong user mode, ta có thể đặt bao nhiêu breakpoint tùy thích nhưng trong kernel mode, số breakpoint được đặt có thể bị giới hạn. Thông tin breakpoint không yêu cầu nhiều không gian nhớ và ta có thể đặt nhiều breakpoint tùy ý.

Nghiên cứu các case study hay của chuyên gia Bùi Đình Cường Tại Đây

3.5 Breakpoint cứng

Kiến trúc x86 hỗ trợ đặt breakpoint cứng thông qua một số thanh ghi. Mỗi khi vi xử lý thực thi một lệnh, có một phần cứng để kiểm tra xem con trỏ lệnh có bằng với địa chỉ breakpoint không. Không như breakpoint phần mềm, với breakpoint phần cứng, ta không cần quan tâm byte nào được lưu tại địa chỉ nào. Nếu ta đặt một breakpoint tại địa chỉ 0x401234, vi xử lý sẽ ngắt tại địa chỉ đó mà không cần quan tâm dữ liệu nào đang được lưu trên đó; đây là giải pháp tốt khi muốn debug code dễ bị thay đổi.

Các breakpoint phần cứng còn một ưu điểm khác so với breakpoint mềm là chúng có thể được đặt để ngắt cả truy cập lẫn thực thi. Ta có thể đặt một breakpoint cứng để ngắt bất cứ khi nào một địa chỉ nhớ được đọc hoặc ghi. Khi cần xác định dữ liệu tại một vùng nhớ có giá trị gì, ta đặt một breakpoint cứng trên đó và khi có một thao tác ghi lên vùng nhớ ấy, debugger sẽ ngắt mà không quan tâm địa chỉ lệnh đang được thực thi (không quan tâm breakpoint thực thi). Ta có thể đặt breakpoint được kích hoạt khi có thao tác đọc, khi có thao tác ghi hoặc khi có một trong hai thao tác đọc và ghi.

Breakpoint cứng cũng tồn tại nhiều hạn chế: Chỉ có 4 thanh ghi có thể lưu địa chỉ breakpoint.

Một hạn chế khác là breakpoint cứng dễ dàng bị sửa đổi bởi chương trình đang chạy. Chipset có sẵn 8 thanh ghi debug nhưng chỉ có 6 thanh ghi là có thể dùng được. 4 thanh ghi đầu tiên từ DR0 tới DR3 lưu địa chỉ breakpoint. Thanh ghi điều khiển debug, DR7, lưu thông tin khi nào địa chỉ breakpoint trên DR0-DR3 có hiệu lực và chúng là breakpoint đọc, ghi thay thực thi. Chương trình độc hại có thể sửa đổi giá trị các thanh ghi này để gây trở ngại với debugger. Tuy nhiên, các chip x86 có sẵn cơ chế bảo vệ các thanh ghi trên. Nếu set cờ General Detect trong thanh ghi DR7, breakpoint sẽ được kích hoạt trước khi thực hiện bất cứ một lệnh mov nào truy cập tới thanh ghi debug.Tuy nhiên, cơ chế này không phải là hoàn hảo vì nó chỉ phát hiện các lệnh mov.

3.6 Breakpoint có điều kiện

Breakpoint có điều kiện là các breakpoint mềm và chỉ ngắt khi thỏa mãn một điều kiện logic. Ví dụ, giả sử ta đặt một breakpoint tại hàm GetProcAddress. Breakpoint này sẽ ngắt bất cứ khi nào hàm GetProcAddress được gọi. Nhưng giả sử ta chỉ muốn ngắt khi tham số truyền vào là RegSetValue. Ta có thể đặt một breakpoint có điều kiện với điều kiện là giá trị trên stack tương ứng với tham số đầu tiên.

Breakpoint có điều kiện được triển khai như một breakpoint mềm. Debugger đánh giá điều kiện và nếu điều kiện không được thỏa mãn, nó sẽ tiếp tục thực thi mà không cần đưa ra cảnh báo tới người dùng. Các debugger hỗ trợ một số điều kiện khác nhau.

Các breakpoint được thực thi lâu hơn so với thực thi lệnh bình thường và chương trình cần debug sẽ chạy chậm hơn đặc biệt là khi ta đặt breakpoint có điều kiện đối với một lệnh chạy thường xuyên. Vì lý do này, breakpoint có điều kiện chỉ hiệu quả và sẽ rất hiệu quả khi ta cần phân tích kỹ một đoạn code ngắn.

4.Các ngoại lệ (Exception)

Sử dụng các ngoại lệ là cách chủ yếu để debugger đạt được quyền điều khiển một chương trình đang chạy.

Các ngoại lệ không phải là mối quan tâm của chỉ riêng mã độc, chuyên gia phân tích mã độc hay hoạt động debugging. Chúng thường sinh ra bởi các lỗi phần mềm. Ngoại lệ cũng thường được sử dụng để quản lý luồng (flow) thực thi của một chương trình mà không cần đến một debugger.

Các ngoại lệ First-chance và Second-chance

Các debugger thường được cấp hai  cơ hội để xử lý đối với cùng một ngoại lệ: first-chance exception và second-chance exception. (FCE – SCE, quy ước viết tắt không chính thức, vanganhchibi).

Nếu ngoại lệ xảy ra khi một debugger đang được gán kèm, chương trình sẽ dừng thực thi và debugger được trao cơ hội đầu tiên để có quyền điều kiển. Debugger có thể xử lý ngoại lệ này hoặc chuyển nó cho chương trình. Nếu chương trình có một exception handler, exception handler đó sẽ được trao cơ hội xử lý ngoại lệ nếu debugger không tận dụng cơ hội đầu tiên đó. Ví dụ, một chương trình máy tính số học có thể được gán một exception handler cho ngoại lệ division-by-0. Nếu chương trình thực thi một phép toán division-by-0, nếu chương trình không được gán một debugger nào, exception handler sẽ thông báo lỗi tới người dùng và tiếp tục thực thi chương trình.

Nếu chương trình không xử lý ngoại lệ, debugger sẽ được trao một cơ hội khác để xử lý ngoại lệ đó. Khi debugger nhận được một SCE tức là chương trình có thể sẽ bị crash nếu không có debugger và debugger phải xử lý ngoại lệ để chương trình có thể tiếp tục thực thi.

Khi phân tích mã độc, ta thường không quan tâm đến bugs và FCE có thể được bỏ qua. Nhưng SCE thì không thể bỏ qua được vì chương trình có thể sẽ không thể tiếp tục thực thi. Nếu gặp SCE khi đang phân tích mã độc, đó có thể là một bug trong mã độc và khiến mã độc crash, nhưng đó cũng có thể là do mã độc không tương thích với môi trường thực thi chứ bản thân nó không có bug.

Các ngoại lệ phổ biến

Có một vài ngoại lệ phổ biến và phổ biến nhất là ngoại lệ xảy ra khi ngắt INT 3 được thực thi. Debugger có một mã riêng để xử lý ngoại lệ INT 3 nhưng OS thường coi chúng như các ngoại lệ khác.

Chương trình có thể có lệnh riêng cho nó để xử lý ngoại lệ INT 3 nhưng nếu có một debugger được gán thì cơ hội đầu tiên sẽ được trao cho debugger. Nếu debugger chuyền ngoại lệ này cho chương trình, exception handler của chương trình sẽ xử lý ngoại lệ.

Single-step cũng được coi là một ngoại lệ trong OS. Cờ trap trong thanh ghi cờ được dùng trong single-stepping. Khi cờ trap được set, vi xử lý thực thi một lệnh và sinh một ngoại lệ.

Ngoại lệ memory-access violation được sinh khi một đoạn code cố gắng truy cập tới một vùng nhớ mà nó không được phép. Ngoại lệ này thường xảy ra khi địa chỉ nhớ không hợp lệ hoặc địa chỉ nhớ không được phép truy cập vì cơ chế điều khiển truy cập.

Một vài lệnh cụ thể có thể được thực thi khi vi xử lý đang ở chế độ đặc quyền. Khi một chương trình cố gắng thực thi các lệnh đó ngoài chế độ đặc quyền, vi xử lý cũng sinh một ngoại lệ.

Đặc quyền-không đặc quyền đối với vi xử lý tương tự kernel mode-user mode đối với không gian hệ điều hành. VD đặc quyền: ghi lên phần cứng (firmware, driver), sửa đổi bảng phân trang bộ nhớ.

5.Sửa đổi một chương trình thực thi bằng Debugger

Debugger có thể được dùng để sửa đổi một chương trình đang thực thi. Ta có thể sửa đổi các cờ điều khiển, con trỏ lệnh hoặc sửa các đoạn code để thay đổi cách chương trình thực thi.

Ví dụ, để tránh một lời gọi hàm, ta có thể đặt một breakpoint tại vị trí hàm được gọi. Khi chương trình thực thi đến breakpoint, ta set giá trị con trỏ lệnh là địa chỉ lệnh ngay sau lời gọi hàm. Nếu hàm là đặc biệt quan trọng, chương trình có thể không chạy đúng hoặc bị crash. Nếu hàm không quá quan trọng và ảnh hưởng nhiều tới các đoạn code khác, nếu may mắn, chương trình có thể thực thi một cách bình thường.

Ta cũng có thể sử dụng debugger để thay đổi con trỏ lệnh để phân tích một hàm cụ thể. Ví dụ, ta có một hàm quản lý string tên là encodeString nhưng không thể xác định nó được gọi tại vị trí nào. Ta có thể dùng một debugger để chạy tới hàm mà không cần biết nó được gọi từ đâu. Để debug hàm encodeString và quan sát xem nó làm gì với dữ liệu đầu vào là “VanganhChibi”, ta có thể đặt giá trị tại esp+4 là con trỏ trỏ tới string “VanganhChibi”, sau đó set con trỏ lệnh tới lệnh đầu tiên của hàm encodeString và thực hiện single-step trên hàm đó để theo dõi nó làm những gì. Dĩ nhiên, khi làm như trên, ta sửa đổi stack của chương trình và khiến chương trình không thể chạy đúng nữa sau khi hàm encodeString hoàn tất thực thi, nhưng kỹ thuật này lại đặc biệt hiệu quả khi ta chỉ quan tâm tới hành vi của một đoạn code cụ thể.

Ví dụ minh họa dưới đây là một mẫu virus trong thực tế, có những hành vi khác nhau tùy vào cài đặt ngôn ngữ hệ thống của máy tính bị lây nhiễm. Nếu ngôn ngữ hệ thống là Tiếng Trung Giản thể, virus tự gỡ bỏ nó và không gây thiệt hại nào. Nếu ngôn ngữ hệ thống là tiếng Anh, nó hiển thị một pop-up với tin nhắn “You luck’s so good.“. Nếu ngôn ngữ hệ thống là tiếng Nhật hoặc Indonesia, virus thực hiện ghi đè dữ liệu rác lên ổ đĩa cứng nhằm hủy hoại máy tính.

so-sanh-cai-dat-ngon-ngu-trong-debug

Đoạn code trên thực hiện so sánh cài đặt ngôn ngữ. Đầu tiên, mã độc gọi hàm GetSystemDefaultLCID. Tùy vào kết quả trả về, mã độc gọi tới một trong ba hàm khác nhau. Mã Locale ID cho tiếng Anh, Nhật, Indonesia và Trung Quốc lần lượt là 409h, 411h, 421h, C04h.

Mã độc gọi đến hàm sub_411037 nếu ngôn ngữ hệ thống là tiếng Anh, gọi hàm sub_41100F nếu ngôn ngữ là tiếng Nhật hoặc Indonesia, và gọi hàm sub_41100A nếu ngôn ngữ là tiếng Trung.

Để phân tích được các hàm chức năng độc hại, ta phải thực thi được mã độc như trên hệ thống sử dụng tiếng Nhật hoặc Indonesia. Ta dùng một debugger để ép mã độc thực hiện các đoạn code độc hại mà không cần thay đổi cài đặt ngôn ngữ hệ thống bằng cách đặt một breakpoint tại 0x41134F để thay đổi giá trị trả về từ hàm GetSystemDefaultLCID. Nếu hệ thống ta sử dụng đang dùng tiếng Anh US làm ngôn ngữ hệ thống, hàm này sẽ trả về giá trị 409h. Trong debugger, ta có thể sửa đổi giá trị của thanh ghi EAX thành 411h và tiếp tục thực thi mã độc, mã độc sẽ thực hiện những chức năng đối với hệ thống sử dụng tiếng Nhật.

0