Bí thuật mượn xác: cách malware giấu mình trong chính những chương trình bạn tin tưởng nhất

DLL injection và process hollowing là hai kỹ thuật malware giấu mình trong process hợp pháp, né antivirus. Giải thích dễ hiểu qua analogy, ví dụ Stuxnet 2010 và lý do Windows không thể cấm.

Bí thuật mượn xác: cách malware giấu mình trong chính những chương trình bạn tin tưởng nhất

Có một câu mình bị bạn bè hỏi nhiều, mỗi khi họ cầm laptop tới nhờ "xem hộ máy mình có bị virus không":

"Thế cài antivirus rồi mà sao vẫn dính?"

Mình từng trả lời chung chung lắm: "thì virus nó lừa", "thì nó cập nhật mới hơn". Những câu đó đúng mà chẳng giải thích được gì. Cho đến một tối mình ngồi đọc lại cách một con malware cũ hoạt động, từng bước một, thì mới thấy câu trả lời thật thú vị hơn nhiều. Và hơi rùng mình.

Virus không giấu bằng cách chạy dưới cái tên của nó. Nó chạy dưới cái tên của những chương trình mà chính máy bạn, và chính antivirus của bạn, đã tin tưởng từ lâu.

Đó là trò mà dân an ninh mạng gọi là process injection. Mình muốn kể bạn nghe hai chiêu cổ điển và tinh vi nhất: DLL injectionprocess hollowing. Nghe kỹ thuật, nhưng thực ra cả hai dựa trên một ý tưởng mà ai cũng từng gặp ngoài đời.

Ván bài mở đầu: nhét người của mình vào nhà người khác

Hãy tưởng tượng mỗi chương trình đang chạy trên máy bạn là một nhân viên văn phòng. Mỗi người có một phòng làm việc riêng, một chiếc thẻ ra vào riêng, một bộ quyền truy cập riêng. Phòng kế toán thì được mở kho tiền, phòng nhân sự thì được mở hồ sơ nhân viên, bảo vệ thì đi đâu cũng được.

Antivirus giống như anh chàng bảo vệ ngồi ở cổng. Anh ta kiểm tra thẻ của bất cứ ai muốn bước vào toà nhà. Đâu vào đấy.

Vậy câu hỏi của attacker là: làm sao làm việc trong toà nhà này mà không bị anh bảo vệ cổng để ý?

Câu trả lời của DLL injection: đừng cố chui qua cổng. Hãy lén trộn một tờ giấy chỉ thị vào xấp tài liệu đang nằm trên bàn của một nhân viên đã ở bên trong rồi.

Khi nhân viên đó quay lại bàn, nhặt xấp giấy lên và đọc, họ bắt đầu làm theo những chỉ thị mới, tưởng rằng đó là việc của chính mình. Anh bảo vệ cổng chẳng thấy gì sai, vì người đang làm việc vẫn là nhân viên hợp pháp, vẫn đeo đúng thẻ. Chỉ là anh ta không biết nội dung công việc đã bị thay từ bên trong.

Đó chính xác là DLL injection. "Nhân viên" là một process (một chương trình đang chạy), ví dụ explorer.exe, cái quản lý thanh tác vụ và cửa sổ của Windows. "Tờ giấy chỉ thị" là một file nhỏ gọi là DLL, một gói code mà chương trình có thể nạp vào và chạy.

Attacker không cố gắng chạy malware dưới cái tên malware. Họ lén bảo một chương trình đáng tin cậy: "Ê, nạp thêm gói này vào và chạy theo nhé." Chương trình ngoan ngoãn làm theo. Và từ đó, code của attacker chạy với đầy đủ quyền của chương trình kia, quyền mà antivirus sẽ không dám đụng vào, vì đây là "nhân viên chính hãng".

Bạn có thể nghĩ: nghe khó thế, làm sao nhét được tờ giấy vào bàn người khác?

Thực ra Windows cung cấp sẵn những công cụ cho việc này. Không phải lỗi, mà là tính năng (mình sẽ nói kỹ hơn ở phần sau). Về mặt kỹ thuật, attacker xin máy mở một "khe" vào bộ nhớ của process mục tiêu, viết đường dẫn tới file DLL độc hại vào một khoảng trống trong bộ nhớ đó, rồi bảo hệ điều hành: "Tạo cho tôi một luồng công việc mới bên trong process này, và việc đầu tiên của luồng đó là nạp gói DLL ở đường dẫn kia." Hệ điều hành làm đúng theo, vì từ góc nhìn nó, đây là một yêu cầu hợp lệ.

Khi DLL được nạp, nó chạy ngay lập tức. Từ khoảnh khắc đó, attacker đã ở trong toà nhà.

DLL injection, lén trộn chỉ thị vào process hợp pháp

Và đây là điều khiến mình khá bất ngờ khi mới hiểu hết: chính phần mềm hợp pháp cũng làm việc này mỗi ngày.

Khi bạn mở Discord rồi bật game, cái bảng nhỏ hiện tên bạn bè đang nói chuyện phủ lên góc màn hình game, đó là Discord inject một DLL vào trong game của bạn để vẽ giao diện lên trên. Antivirus inject một DLL giám sát vào gần như mọi chương trình để theo dõi hành vi đáng ngờ. Các công cụ debug, các overlay Steam hay Xbox, thậm chí phần mềm tự động hoá, tất cả đều dùng đúng cơ chế đó.

Cùng một con dao. Mặt sáng giúp Discord hiện bạn bè lên game, mặt tối giúp virus đọc mật khẩu ngân hàng của bạn. Máy tính không phân biệt được ai tốt ai xấu. Nó chỉ thấy một yêu cầu nạp DLL, và thực hiện.

Ván khó hơn: mượn luôn cái xác

DLL injection giỏi, nhưng vẫn có điểm yếu: tờ giấy chỉ thị nằm lơ lửng trong bộ nhớ, và nếu bảo vệ cẩn thận kiểm tra từng phòng, anh ta có thể phát hiện ra có một gói lạ được nạp vào. Nó để lại dấu vết.

Process hollowing thì đi xa hơn một bước. Nó không nhét thứ gì vào. Nó thay thế toàn bộ.

Quay lại analogy toà nhà: tưởng tượng một kẻ gian muốn mở một cơ sở bất hợp pháp, nhưng biết cảnh sát sẽ kiểm tra. Anh ta làm thế này. Đầu tiên, anh ta đăng ký kinh doanh hợp pháp một tiệm bánh. Xin giấy phép y tế, giấy phép vệ sinh an toàn thực phẩm, treo biển "Tiệm Bánh Bình" to đùng. Cho thanh tra đến kiểm tra lò nướng, bột mì, nhân viên đội mũ, tất cả chuẩn. Qua cửa ải, được duyệt.

Rồi, trong đêm, anh ta tháo sạch mọi thứ ra. Lò nướng đi, bột mì đi, tủ kính đi. Thay vào đó là một dàn máy in tiền giả, y hệt ở vị trí cũ. Biển "Tiệm Bánh Bình" vẫn treo ngoài cửa. Giấy phép vẫn dán tường.

Khi cảnh sát đi ngang, họ thấy một tiệm bánh đang hoạt động. Có mùi bánh, có nhân viên, có biển hiệu. Họ không vào soi từng lò. Nhưng bên trong, không có cái bánh nào được làm ra cả.

Đó là process hollowing, từng bước:

Attacker bảo Windows khởi động một chương trình hoàn toàn hợp pháp, notepad.exe chẳng hạn, cái phần mềm ghi chú có sẵn trên mọi máy Windows, nhưng khởi động ở chế độ "đứng im", chưa cho chạy. Cửa sổ chưa hiện, code chưa kịp chạy dòng nào.

Rồi, trong khi notepad đang đứng im đó, attacker tiến vào bộ nhớ của nó và móc ruột ra, xoá sạch code gốc của notepad, thay bằng code độc hại của chính mình. Bây giờ bộ nhớ của "notepad" không còn chứa notepad nữa. Nó chứa virus.

Bước cuối: attacker thả tay ra, cho chương trình chạy tiếp. Và cái chạy ra không phải là notepad. Nó là malware, nhưng mặc nguyên bộ đồ của notepad, đội nguyên chiếc mũ, đeo nguyên chiếc thẻ tên. Task Manager, cái bảng mà bạn bấm Ctrl+Shift+Esc để xem "chương trình nào đang chạy", vẫn báo đúng: notepad.exe. Đường dẫn file vẫn chỉ về thư mục hệ thống chính hãng. Người bình thường nhìn vào, thấy mình đang mở notepad. Không có gì để nghi.

Process hollowing, bốn bước móc ruột và thay thế

Mình muốn bạn dừng lại một giây với hình ảnh này. Vì nó giải thích rất nhiều thứ.

Stuxnet: khi bí thuật này thay đổi lịch sử

Có một câu chuyện mà mình nghĩ bạn nên nghe, vì nó cho thấy hai chiêu này không phải chuyện lý thuyết suông.

Năm 2010, thế giới phát hiện ra một con malware mà đến nay vẫn được coi là một trong những phần mềm tinh vi nhất từng được viết. Tên nó: Stuxnet.

Mục tiêu của Stuxnet là nhà máy làm giàu uranium Natanz, ở Iran. Để phá được nhà máy đó, nó cần thao tác những chiếc máy ly tâm, thiết bị quay tốc độ cực cao để tách đồng vị uranium, mà không bị các kỹ sư vận hành phát hiện. Vấn đề là nhà máy bị cô lập hoàn toàn khỏi internet. Không có đường mạng trực tiếp để xâm nhập.

Stuxnet giải bằng cách nhảy từ USB này qua USB khác, từ máy này qua máy kia, cho đến khi lọt được vào máy tính bên trong nhà máy. Và khi đã ở trong, nó không chạy loạn. Nó lẩn trốn bên trong những chương trình điều khiển công nghiệp mà chính các kỹ sư đó tin tưởng tuyệt đối, phần mềm SCADA của Siemens, thứ mà mọi người trong nhà máy dùng mỗi ngày để vận hành máy móc.

Mình muốn bạn tưởng tượng góc nhìn của một kỹ sư Iran trong phòng điều khiển. Anh ta nhìn màn hình. Phần mềm báo mọi máy ly tâm đang quay đúng tốc độ. Nhiệt độ ổn. Áp suất ổn. Anh ta tin phần mềm, vì phần mềm đó do hãng danh tiếng làm, được cài chính chủ, không ai động vào.

Nhưng bên dưới, Stuxnet đã thay đổi thực tế: nó làm máy ly tâm quay quá tốc độ rồi dừng đột ngột, quay rồi dừng, xoắn vặn cho đến khi hàng trăm chiếc tự hủy. Ước tính khoảng 1.000 máy ly tâm, gần một phần năm số máy nhà máy, bị phá hỏng. Trong khi màn hình điều khiển vẫn bình thản hiện "mọi thứ ổn".

Đó là sức mạnh của việc giấu mình trong chương trình đáng tin cậy. Người vận hành không nghi ngờ, vì làm sao chương trình chính hãng lại nói dối được.

Stuxnet là malware đầu tiên trong lịch sử gây ra thiệt hại vật lý ở quy mô lớn. Nó không đánh cắp file, không mã hoá ổ đĩa đòi tiền chuộc. Nó làm vỡ máy móc thật, ở ngoài đời thật. Và nền tảng của nó, một trong những nền tảng, chính là khả năng tiêm code vào các process hợp pháp và ẩn nấp ở đó.

Tại sao Windows không "sửa" luôn chuyện này?

Đây là câu hỏi mình tự hỏi rất lâu. Nghe xong hai kỹ thuật trên, phản ứng đầu tiên của mình là: thế thì Microsoft sửa Windows đi, cấm luôn việc tiêm code vào process khác là xong chứ?

Nhó rồi mình đào thêm, mới hiểu vì sao câu trả lời không đơn giản.

Khả năng "tiêm" code vào process khác không phải một lỗ hổng bị quên. Nó là một thiết kế có chủ đích, và phần mềm hợp pháp phụ thuộc vào nó nặng hơn mình tưởng rất nhiều.

Mình đã kể Discord dùng nó để vẽ overlay lên game. Còn antivirus? Để bắt được virus, phần mềm diệt virus cần theo dõi từng chương trình một đang làm gì. Cách duy nhất thực tế để làm điều đó là inject một thành phần giám sát vào bên trong mỗi process, để từ bên trong quan sát. Nếu cấm DLL injection, antivirus gần như tê liệt.

Còn debugger, công cụ mà lập trình viên dùng mỗi ngày để tìm lỗi trong code? Nó cũng dựa vào khả năng đọc và sửa bộ nhớ của một chương trình đang chạy, đúng cơ chế mà process hollowing lợi dụng. Cấm luôn thì cả ngành phát triển phần mềm khựng lại.

Đó là tension thật, và mình thấy nó đáng nghĩ hơn là đáng trách. Cùng một sức mạnh: mặt sáng cho phép công cụ bảo vệ bạn, mặt tối cho phép malware trốn bạn. Windows, hay bất kỳ hệ điều hành nào, phải cho phép sức mạnh đó tồn tại, rồi cố gắng phân biệt ai đang dùng nó với mục đích gì. Mà phân biệt ý định từ bên trong một cái máy là bài toán khó nhất trong toàn bộ ngành bảo mật.

Hệ quả là ngành công nghiệp antivirus không cố gắng cấm injection nữa. Họ chuyển sang kiểm nó: mỗi khi có ai cố gắng tiêm code vào process khác, hệ thống ghi lại và phân tích xem hành vi đó có hợp lý không. Phần mềm diệt virus hiện đại (mà giới kỹ thuật gọi là EDR) treo hàng nghìn "camera" ẩn trong hệ điều hành, cố bắt đúng khoảnh khắc một tờ giấy chỉ thị lọt qua cửa sổ.

Nhưng đây là cuộc chạy đua không có đích. Attacker nghĩ ra cách lách camera mới mỗi tuần. Người bảo vệ lắp camera mới mỗi tháng. Bóng và ánh sáng luân phiên đuổi nhau.

Điều này ảnh hưởng tới bạn như thế nào?

Có thể bạn nghĩ Stuxnet là chuyện của quốc gia, của điện hạt nhân, xa lạ lắm. Nhưng cùng hai kỹ thuật đó, ở quy mô nhỏ hơn, đang được dùng mỗi ngày để lấy cắp thứ mà bạn giữ trong máy tính.

Bạn có bao giờ nghe nói tới "trojan ăn cắp tài khoản ngân hàng"? Cách nó làm việc thường rất tinh tế: nó inject code vào trong chính trình duyệt web của bạn, Chrome, Firefox, cái thứ mà bạn dùng để đăng nhập ngân hàng mỗi ngày. Khi bạn gõ mật khẩu vào trang ngân hàng, trojan đọc được ngay tại chỗ, vì nó đang chạy bên trong trình duyệt, ở đúng cái process mà bạn tin tưởng để gửi dữ liệu lên mạng. Antivirus khó bắt, vì từ góc nhìn nó, trình duyệt vẫn đang hoạt động bình thường. Nó có quyền tiếp xúc với mật khẩu mà, đó là việc của nó.

Hay những con malware đánh cắp cookie phiên đăng nhập, cái thứ mà bẻ được là vào luôn tài khoản mà không cần mật khẩu. Cùng cách: ẩn trong trình duyệt, lấy cookie ra, gửi đi.

Bọn mã hoá tống tiền, ransomware, thì dùng process hollowing để giấu loader của chúng, cái giai đoạn "chuẩn bị" trước khi bắt đầu mã hoá toàn bộ ổ đĩa. Chúng ẩn trong một process trông vô hại đủ lâu để né camera của EDR, rồi khi đã sẵn sàng, bùng nổ. Theo dõi của nhiều nhóm nghiên cứu cho thấy các họ ransomware lớn như LockBit hay Conti từng dùng đúng kỹ thuật này.

Mình không kể để làm bạn sợ. Mình kể để bạn hiểu một điều: ranh giới giữa "máy tôi sạch" và "máy tôi bị theo dõi" mỏng hơn bạn nghĩ. Máy bạn không cần phải có một con virus chạy loạn ngoài cổng. Nó chỉ cần một chương trình bạn tin tưởng bị thêm vào vài dòng chỉ thị mà bạn không biết.

Mấy câu mình đoán bạn đang thắc mắc

Viết tới đây, mình đoán có mấy câu bạn sẽ muốn hỏi ngay. Mình từng hỏi y hệt.

Nếu malware đã chạy được rồi, sao không chạy thẳng payload mà phải xoáy vào `victim.exe` làm gì?

Vì chạy thẳng thì lộ ngay cả người lẫn dao.

Một file malware.exe nằm chình ình trên đĩa rồi mở kết nối ra server lạ, là bữa tối của antivirus: bị quét, bị flag, bị chặn mạng trước khi kịp làm gì. Nhưng nếu payload chỉ tồn tại trong RAM, đội bộ đồ của notepad.exe, thì cảnh đổi khác. Antivirus nhìn vào thấy một chương trình hệ thống chính hãng đang chạy. Firewall thấy svchost.exe ra internet, chuyện bình thường. Payload cần sống đủ lâu để gọi về server chỉ huy, quét mạng nội bộ, mã hoá ổ đĩa, mà không bị nghi. Nói nôm na, nó không cần "chạy được", nó cần "sống sót được". Inject chính là cách đổi thân phận để sống sót.

Có một lý do nữa tinh tế hơn: tách rời. Loader, cái mở đầu chuỗi, chỉ việc inject xong rồi có thể tự huỷ. Nếu sau này bị phát hiện, người điều tra chỉ thấy một loader nhỏ đã chạy xong nhiệm vụ, còn payload thật vẫn nấp trong một process hợp pháp nào đó, không chết theo.

Thế ai là người trực tiếp "móc ruột" notepad.exe? Chính nó tự xoá mình à?

Không. Lúc đó notepad đang bị "đóng băng", chưa chạy được dòng code nào. Nó là cái xác, không phải người cầm dao.

Người cầm dao là một process khác của chính attacker, dân kỹ thuật gọi là loader hay injector. Loader dùng các API mà Windows cấp sẵn cho debugger để thao tác từ bên ngoài: mở một "khe" vào bộ nhớ của notepad, xoá ảnh gốc đi, ghi code độc hại vào, sửa vài con trỏ để khi notepad "thức giấc" thì nhảy vào code mới. Toàn bộ diễn ra trước khi notepad kịp chạy dòng nào. Trong analogy tiệm bánh, chính kẻ gian tự tay tháo lò và thay máy in, chứ không phải tiệm bánh tự phá mình.

Và đây là chi tiết đáng nghĩ: những API đó, kiểu WriteProcessMemory hay NtUnmapViewOfSection, không phải công cụ lậu. Chúng là tính năng chính thức, có tài liệu, được debugger và phần mềm giám sát dùng mỗi ngày. Malware chỉ mượn đúng bộ công cụ hợp pháp đó.

Nhưng cái loader đó từ đâu mà có? Nó cũng phải qua cổng bảo vệ chứ, sao không bị bắt ngay từ đầu?

Đúng, loader cũng là một process, và lẽ ra cổng bảo vệ phải thấy nó. Chỉ là nó không bao giờ tự xưng tên thật. Thường thì chính bạn là người mở cửa: bấm vào file đính kèm email giả mạo, cài một phần mềm "crack", mở tài liệu Word chứa macro, hoặc ghé phải một trang web bị gài. Loader nằm gọn trong những cái đó, đội bộ đồ của phần mềm vô hại.

Còn cổng bảo vệ, loader né bằng vài cách. Một là nó bị "đóng gói" (pack), kiểu chữ ký quét hôm trước qua nhưng hôm sau đã biến dạng. Hai là nó mượn tên và cả chữ ký số của phần mềm thật, trông hợp pháp đến mức cổng bật đèn xanh. Trò xảo quyệt nhất gọi là "living off the land": loader không phải file malware nào cả, mà là một công cụ có sẵn của chính Windows, PowerShell hay rundll32 chẳng hạn, bị ép chạy lệnh của attacker. Không có file độc hại để quét, vì thứ duy nhất đang chạy là công cụ do chính Microsoft làm. Lọt qua cổng xong, nó mới đứng bên trong thực hiện việc hollowing victim.exe.

Nếu notepad.exe bị thay ruột, thì file notepad.exe nằm trên ổ đĩa của mình cũng bị sửa luôn chứ?

Không. Đây là điểm nhiều người hiểu nhầm, và cũng là lý do chuyện này khó bắt.

Toàn bộ việc móc ruột chỉ diễn ra trong RAM, bộ nhớ sống của process lúc đang chạy. File notepad.exe gốc trong C:\Windows, do Microsoft ký số, vẫn nguyên vẹn, vẫn chính hãng. Quét bằng bất kỳ công cụ nào cũng ra sạch. Vì malware không sửa file. Nó chỉ mượn "cái xác đang chạy" của file đó trong bộ nhớ.

Đó là lý do máy bạn có thể bị theo dõi mà ổ đĩa không có lấy một file độc hại. Mọi thứ chỉ nằm trong RAM, biến mất khi tắt máy, rồi được loader nạp lại khi cần. Phát hiện kiểu này đòi hỏi soi bộ nhớ đang chạy, chứ không phải quét tệp tĩnh.

Linux hay macOS có bị kiểu này không, hay chỉ mỗi Windows?

Có, nhưng cơ chế khác và ít bị nhắm hơn.

Linux có LD_PRELOAD, ép chương trình nạp một thư viện lạ trước tiên, hoặc ptrace, công cụ debug cho phép một process gắn vào và sửa process khác. macOS có cơ chế inject dylib tương tự. Bất kỳ nền tảng nào có debugger và thư viện chia sẻ đều có một biến thể của trò này.

Nhưng Windows bị nhắm nhiều hơn, không phải vì kém an toàn hơn, mà vì nó phổ biến hơn trên máy người dùng, và vì thiết kế DLL của nó linh hoạt theo cách rất thuận lợi cho injection. Rốt cuộc mọi hệ điều hành đều đứng trước cùng một nghịch lý: muốn cho phần mềm hợp pháp can thiệp vào nhau để debug và bảo vệ, thì cũng phải để malware dùng đúng lối đó. Không ai né được bài toán phân biệt ý định.

Một suy nghĩ để ngỏ

Mình ngồi nghĩ về hai kỹ thuật này mấy hôm rồi, và điều làm mình ấn tượng nhất không phải sự tinh vi của chúng.

Mình ấn tượng với việc cả hai đều dựa vào niềm tin. Tin tưởng của hệ điều hành vào những chương trình đã được duyệt. Tin tưởng của người dùng vào những phần mềm quen thuộc. Tin tưởng của anh kỹ sư Iran vào cái màn hình đang báo "mọi thứ ổn". Rồi tin tưởng đó bị lợi dụng, từ bên trong, ở đúng chỗ mà camera cổng không soi tới.

Và mình nhận ra: đó cũng là cách lừa đảo tinh vi nhất ngoài đời thật. Kẻ gian giỏi không bao giờ phá cửa. Anh ta giả làm người nhà, mượn chiếc thẻ, đi thẳng qua cổng, và làm việc trong nhà bạn khi bạn đang ngồi đó mà không hề hay biết.

Điểm khác biệt duy nhất là ở máy tính, "người nhà" đó có thể là notepad.exe, thứ trông vô tội nhất trên đời.

Mình vẫn chưa trả lời hết câu hỏi "cài antivirus rồi sao vẫn dính". Nhưng ít nhất giờ mình hiểu rõ hơn hình dáng của câu trả lời: không phải vì antivirus kém, mà vì trò chơi này nghiêng về phía kẻ chủ động giấu mình. Mỗi khi người bảo vệ đóng một cánh cửa, attacker đã tìm được cửa sổ khác. Và đôi khi cửa sổ đó chính là chương trình mà bạn mở mỗi sáng.

Bình