Nội dung gồm nhiều kiến thức nâng cao, khuyến nghị đọc trước bài viết về shellcode x86 masm Kỹ thuật xây dựng Shellcode x86 trên nền MASM – Security Blog
Giới thiệu virus lây file
Virus lây file bao gồm 2 phần:
- Lây nhiễm qua file mới
- Thực thi chức năng độc hại
Chức năng độc hại có thể thiết kế tùy chỉnh, nên bài viết sẽ tập trung vào thiết kế chức năng lây nhiễm.

PE format có 1 trường quan trọng là AddressOfEntryPoint, trường này trỏ tới địa chỉ code đầu tiên được gọi. Tức là khi chương trình bắt đầu, eip sẽ nhảy tới đây để thực thi code.

Sau khi lây nhiễm, AOEP được sử đổi để trỏ tới phần mã độc hại, thực thi xong và trỏ về AOEP ban đầu.

PE Format và thiết kế virus lây file
Tham khảo cấu trúc PE tại: PE Format – Win32 apps | Microsoft Learn
1 file PE khi được load lên mem, PE header sẽ được đọc. Máy tính xác định các sections của PE, dựa vào thông tin trên disk, thông tin khi load lên mem để load các section này lên mem và thực thi.
Có 1 số kĩ thuật đặc trưng để chèn mã độc hại vào file sạch:
- Ghi đè vào section đã có (yêu cầu section có quyền excute)
- Ghi mã độc hại các stub rỗng của file
- Ghi vào cuối file, và tạo section mới.
Bài viết sẽ thực hiện kĩ thuật ghi vào cuối file và tạo section mới.
Vậy tại sao không ghi vào cuối file, sửa AOEP tới phần mã này mà còn phải tạo thêm section mới? Như đã nói ở trước, khi load PE lên mem, Windows chỉ quan tâm tới header, đọc các header để load các phần mã còn lại lên mem. Nếu không có header riêng cho phần mã độc cuối file thì sẽ không load lên mem và thực thi được.
Trước khi bắt đầu vào thiết kế code, cần xác định các trường trong 1 file PE cần sửa đổi khi thêm mã độc vào cuối file.
File Header cần sửa đổi trường NumberOfSections, trường này cần +1 khi thêm section mới.

- AddressOfEntryPoint trỏ tới phần mã độc hại. Cần lưu Original AOEP để jump tới sau khi thực thi xong mã độc.
- SectionAlignment và FileAlignment không cần sửa đổi, nhưng cần lưu lại vào stack để sử dụng cho việc căn chỉnh sau này.
- SizeOfImage cần tính toán lại vì size của file đã tăng lên, giá trị mới bằng cũ cộng thêm size code độc hại (đã căn chỉnh bằng FileAlignment)

Section Headers là nơi lưu trữ thông tin các section, cần thêm 1 section info mới tại đây, đồng thời trỏ thông tin tới phần mã độc được ghi vào file.
- Name: Tạo tùy chỉnh, nên để lại dấu hiệu nhận biết là section của bản thân.
- Virtual Size: Size phần section trên mem.
- Virtual Address: RVA section khi được load lên mem, được tính toán bằng RVA (sections đứng trước) + Virtual Size (sections đứng trước) sau đó làm tròn theo SectionAlignment.
- Raw Size: Size section trên disk, căn chỉnh theo FileAlignment.
- Raw Address: RVA section trên đĩa, tính toán bằng Raw Address của sections trước đó + Raw Size section trước đó, căn chỉnh với FileAlignment.
- Characteristics: Gồm các flag thể hiện quyền và thuộc tính của section.

Phần sau mô tả sự khác biệt giữa PE trước và sau khi được load lên mem, từ đó đưa ra phương pháp tính New AOEP.

Để đơn giản, mã độc sẽ bắt đầu ngay từ section mới, nên chỉ cần set AOEP bằng với RVA của new section là được.
Sơ đồ WinAPI và các bước cần để viết mã lây file.

Triển khai mã virus với x86 masm
Phần triển khai mã asm sẽ sử dụng 2 hàm GetModuleBase và GetFuncAddr ở bài viết Kỹ thuật xây dựng Shellcode Windows x86 trên nền MASM – Security Blog.
Danh sách các WinAPI cần lấy địa chỉ là:
- FindFirstFile/FindNextFile: Tìm kiếm file để lây nhiễm
- CreateFileA/GetFileSize
- CreateFileMappingA/MapViewOfFile/UnmapViewOfFile
- SetFilePointer/ReadFile/WriteFile
- CloseHandle
Ngoài ra cần MessageBox hiển thị để biết rằng file đó đã lây nhiễm và có thể thực thi bình thường. Trên thực tế MessageBox sẽ được thay thế bằng mã độc hại của virus.
- LoadLibraryA
- MessageBoxA
Phần đầu cần phải triển khai delta để truy cập các biến.
.386
.model flat
option casemap:none
.code
_main proc
start_label:
call start_code
start_code:
pop ebx
sub ebx, start_code
Sử dụng ebp và stack được cấp phát để lưu trữ các biến local.
xor eax, eax
mov [ebp - 4], eax ;Base Kernel32.dll
mov [ebp - 8], eax ;Base User32.dll
mov [ebp - 12], eax ;VA MessageBoxA
mov [ebp - 16], eax ;VA LoadLibraryA
mov [ebp - 20], eax ;VA GetProcAddress
mov [ebp - 24], eax ;VA FindFirstFileA
mov [ebp - 28], eax ;VA FindNextFileA
Thông qua PEB lấy địa chỉ GetProcAddress, dựa vào đó để lấy hết địa chỉ các API cần thiết.
lea eax, [ebx + offset sFindFirstFileA]
push eax
push [ebp - 4] ;Base Kernel32.dll
mov eax, [ebp - 20] ;VA GetProcAddress
call eax
add esp, 8
test eax, eax
jz Exit
mov [ebp - 24], eax ;VA FindFirstFileA