1 Kĩ thuật lập trình Virus - Copyright Spinx ( Phần 1) 1/7/2014, 20:45
Hồ Ngọc Hải
Các newbie xem cũng được nhưng bài này không dành cho newbie. Đây không phải khái niêm cơ bản, tôi chỉ muốn tổng kết một số kỹ thuật bảo vệ trong môn virus programming
Một chương trình diệt VR (AV) tốt là một AV tìm được nhiều VR. Một VR mạnh là một VR được bảo vệ. Được bảo vệ có nghĩa là VR có các tính năng chống phát hiện, chống emulate, chống disassemble, khó theo dõi hành vi. Ở đây tôi xin đề cập đến một vài kỹ thuật bảo vệ như vậy đã từng được các V-er áp dụng trong virus programming.
Có nhiều cách lắm và rất đa dạng như bất cứ kỹ thuật nào cần đến sức sáng tạo của con người. Trong phần này tôi sẽ đề cập tới các kỹ thuật chính:
- anti-emulator
- anti-heuristics
- anti-analysis (anti-disasm)
- anti-debug
- anti-monitor
- anti-antivirus (retro)
- anti-bait
Đây là giới thiệu chung nên tôi không đưa hết các code ví dụ. Có thể tôi sẽ phân tích và đưa ra code ví dụ sâu hơn ở từng kỹ thuật nếu thấy có nhiều bạn đọc quan tâm. Nếu bạn nào muốn thảo luận sâu hơn về kỹ thuật nào xin liên hệ trực tiếp.
Anti-Emulator bằng các tip-trick đơn giản:
Với phương pháp heuristic analysis các AV sẽ CÓ THỂ phát hiện ra mọi VR, kể cả các VR chưa từng gặp. Nó làm việc giống như một bộ giải mã, quét và kiểm tra các đoạn code khả nghi kiểu như các đoạn duyệt APIs, jump to ring-0, làm việc với *.exe mở và ghi các tệp khả thi (.exe, .dll...). Heuristic analysis là một í tưởng rất táo bạo tuy nhiên nghe có vẻ ... phi thực tế. Thế nhưng kỹ thuật này cho đến nay đã có rất nhiều cải tiến và phát triển trong hầu hết các AV nổi tiếng. Tuy vậy, nhiều AV có các bugs và nhiều khi không nhận các đoạn code nguy hiểm. Một số gặp khó khăn khi bắt gặp các mã lệnh "hiểm" (undocumented opcodes) và đa số chúng đều không thể quản lý stack chính các như khi VR chạy thực. Lợi dụng các đặc điểm này nảy sinh một số tricks để qua mặt các Emulator như:
- Kiểm tra stack:
mov edx, esp
push cs
pop eax
cmp esp, edx
jne emul_present ; Có AV emulator
Đoạn code trên hoàn toàn vô hại thậm chí hơi ngớ ngẩn nếu bạn xem qua nhưng trong thực tế chương trình của ban phát hiện được ngay có kẻ can thiệp vào stack của ban trong khi chạy, vậy chương trình của bạn không chạy thực mà chỉ đang trong chế độ emulator
- Tương tự thế bạn có thể lừa AV bằng vài lệnh RETF (rất nhiều VR nội có dùng)
- Sử dụng các opcode lạ (undocumented opcodes) của processor như SALC, BPICE. Vô hại với bạn nhưng khó hiểu với AV
......
Anti-Heuristics bằng kỹ thuật cao cấp:
Như vậy anti-emulator là đánh vào chỗ hổng của các giải thuật heuristic scanners. Các AV có thể khắc phục nhưng rất khó khăn đặc biệt nếu bạn dùng vài kỹ thuật cao hơn như SEH. Tạo ra lỗi giả rồi nhảy đến đoạn bẫy lỗi. Tôi viết VR cách đây lâu rồi nên các kỹ thuậu mới trên win có khi không cập nhật bằng các bác nữa. Tôi sẽ đi sâu vào DOS hơn một chút.
Trước kia trên DOS tôi thích sử dụng vài kỹ thuật bẫy ngắt với:
- Int 0 (divide by zero). Chỉ cần một phép chia cho 0, bạn sẽ nhảy ngay đến code mới mà emulator không theo vết được (ta có thể âm thầm descrypt ở đây)
- Dấu code gọi ngắt:
mov ax, 3D02h-key
add ax, key
int 21h ;Tránh để lộ thao tác đọc file
- Với 386/486 ngày xưa có thể cùng queue fetch (xin lỗi vì tôi hơi hoài cổ một chút):
mov word ptr cs:[offset piq], 20CDh
piq:
nop
nop
code này ngày nay không dùng được. Bạn sẽ nghĩ chương trình sẽ kết thúc vì lệnh int 20h (20CDh) ư? Không đâu! với 386/486 code chạy vẫn là nop
Thế nhưng ngày nay kỹ thuật này biến thể một chút vì các CPU pentium không có queue fetch nhưng các emulator phát triển từ xưa vẫn nghĩ là có. Kỹ thuật đảo lại là:
mov word ptr [offset prefetch], <mã lệnh jump>
prefetch:
int 20h
(sao bạn không cười? tôi có trình khó hiểu quá không nhỉ? )
- Tương tự ngắt int 0. Bạn có thể bẫy int 1, int 6
Thôi kết thúc DOS. Trở lại win ta có thể dùng:
- Structured Exception Handling (SEH). Kỹ thuật này phổ biến quá rồi ha
- Threads and fibers
- Pentium+, copro, MMX, 3DNow! opcodes (undocumented opcodes)
- Kỹ thuật đa hình metamorphism
- Nhảy bằng Callbacks
- .......
Chắc mọi người biết cả rồi vì tôi mới trở lại với "Nghệ thuật hắc ám" gần nên kiến thức trên win không mới lắm.
Anti-Analysis
Ở phần này tôi muốn nói về chống disassemblers. Ai cũng biết mấy thằng disassemblers thông dụng như IDA, Sourcer hay win32dasm. Nếu bạn là người xây dựng chương trình disassemblers bạn sẽ làm thể nào. Tất nhiên dễ nhất là bắt đầu từ đầu chương trình, dasm tuần tự. Nếu code tuần tự như tiến ta có kết quả đẹp nhất. Nếu không sau lần chạy thứ nhất ta sẽ hiệu chỉnh lại code theo các lệnh nhảy và call... Thử chạy sourcer, bạn sẽ thấy điểm yếu nhất của disassemblers là rất, rất khó xử lý lệnh call và jump. Vậy phương pháp của V-er là:
- Mã hoá càng nhiều càng tốt
- Sử dụng call với relative offset kiểu
call label
gdelta: db 0b8h ;MOV opcode
label: pop ebp
...
mov eax, [ebp + variable - gdelta]
(Cách này có ở đa số VR rồi)
- Nhảy vào ... giữa mã lệnh:
jmp opcd+1 ;jump into instruction
opcd: mov eax, 0fcebfa90h
Bạn có thể thấy ngay thực ra đây không phải lệnh mov mà lệnh ta là 0fcebfa90h cơ
- Chèn các mã kiểu (db 0b8h) vào nhiều nơi sau các lệnh ret, jmp, ...
- Patch code runtime (tương tự kiểu queue fetch ở trên
- ...
Anti-Debug
Các AVer đã tóm được một mẫu VR của bạn. Hix.. bây giờ thì quá khó. Tuy vậy ta cũng có thể chống đỡ trong ... tuyệt vọng. Với Anti-Debug, nếu AVer không cao thủ họ mới nghi ngờ thôi thì cũng có thể bỏ cuộc. Vả lại có chết cũng cho oanh liệt, gây khó khăn chứ. Nếu không disassemble được người diệt sẽ debug. Cách thông thường là tìm xem có phần mềm debugger thông dụng kiểu softice thì chuồn lẹ. Các cách nhận biết debugger có thể là:
- Gọi luôn API của debugger
- Kiểm tra debugger context
- Sử dụng SEH (xem trên)
- Gọi VxD service (Ring-0 only)
- Kiểm tra softice trên bộ nhớ bằng CreateFileA
- Chọc vào các thanh ghi debug (Ring-0 only)
Anti-Monitor
Cũng vậy thôi, ta tìm xem có thằng AV nào thông dụng đang chạy trên bộ nhớ thì chuồn. Có thể dùng hàm API FindWindowA mà tìm. Nếu thấy thịt nó luôn bằng cách sử PostMessageA đến window handle của nó
Anti-Antivirus
Chủ động tìm database của AV trên đĩa mà thịt (có thể AV sẽ không chạy được nữa). Hay nhất là patch được database (AV chạy bình thường nhưng không tìm được VR nữa. Một số db thông dụng:
*.AVC - AVP viral database
AVP.CRC - AVP crc file
*.VDB - DrWeb viral database
NOD32.000 - NODICE viral database
ANTI-VIR.DAT - TBAV crc file
CHKLIST.MS - MSAV crc file
Anti-Bait
Chọn file mà lây. Tránh các AV, tránh mấy chương trình thông dụng kiểu winword.exe. Như ngày xưa trên DOS ta hay tránh command.com í
Tóm lại, tôi tổng kết các kỹ thuật ở đây, đây là theo những gì tôi hiểu nên sai các bác bỏ qua cho. Nếu tôi trình bày khó hiểu quá thì hix... trình độ sư phạm tôi thế thôi. Đây toàn là í tưởng các Ver chuyên nghiệp đã dùng nếu bạn là Ver đã từng viết VR chắc sẽ hiểu ra và tự xây dựng đoạn code cho riêng mình được.
Một chương trình diệt VR (AV) tốt là một AV tìm được nhiều VR. Một VR mạnh là một VR được bảo vệ. Được bảo vệ có nghĩa là VR có các tính năng chống phát hiện, chống emulate, chống disassemble, khó theo dõi hành vi. Ở đây tôi xin đề cập đến một vài kỹ thuật bảo vệ như vậy đã từng được các V-er áp dụng trong virus programming.
Có nhiều cách lắm và rất đa dạng như bất cứ kỹ thuật nào cần đến sức sáng tạo của con người. Trong phần này tôi sẽ đề cập tới các kỹ thuật chính:
- anti-emulator
- anti-heuristics
- anti-analysis (anti-disasm)
- anti-debug
- anti-monitor
- anti-antivirus (retro)
- anti-bait
Đây là giới thiệu chung nên tôi không đưa hết các code ví dụ. Có thể tôi sẽ phân tích và đưa ra code ví dụ sâu hơn ở từng kỹ thuật nếu thấy có nhiều bạn đọc quan tâm. Nếu bạn nào muốn thảo luận sâu hơn về kỹ thuật nào xin liên hệ trực tiếp.
Anti-Emulator bằng các tip-trick đơn giản:
Với phương pháp heuristic analysis các AV sẽ CÓ THỂ phát hiện ra mọi VR, kể cả các VR chưa từng gặp. Nó làm việc giống như một bộ giải mã, quét và kiểm tra các đoạn code khả nghi kiểu như các đoạn duyệt APIs, jump to ring-0, làm việc với *.exe mở và ghi các tệp khả thi (.exe, .dll...). Heuristic analysis là một í tưởng rất táo bạo tuy nhiên nghe có vẻ ... phi thực tế. Thế nhưng kỹ thuật này cho đến nay đã có rất nhiều cải tiến và phát triển trong hầu hết các AV nổi tiếng. Tuy vậy, nhiều AV có các bugs và nhiều khi không nhận các đoạn code nguy hiểm. Một số gặp khó khăn khi bắt gặp các mã lệnh "hiểm" (undocumented opcodes) và đa số chúng đều không thể quản lý stack chính các như khi VR chạy thực. Lợi dụng các đặc điểm này nảy sinh một số tricks để qua mặt các Emulator như:
- Kiểm tra stack:
mov edx, esp
push cs
pop eax
cmp esp, edx
jne emul_present ; Có AV emulator
Đoạn code trên hoàn toàn vô hại thậm chí hơi ngớ ngẩn nếu bạn xem qua nhưng trong thực tế chương trình của ban phát hiện được ngay có kẻ can thiệp vào stack của ban trong khi chạy, vậy chương trình của bạn không chạy thực mà chỉ đang trong chế độ emulator
- Tương tự thế bạn có thể lừa AV bằng vài lệnh RETF (rất nhiều VR nội có dùng)
- Sử dụng các opcode lạ (undocumented opcodes) của processor như SALC, BPICE. Vô hại với bạn nhưng khó hiểu với AV
......
Anti-Heuristics bằng kỹ thuật cao cấp:
Như vậy anti-emulator là đánh vào chỗ hổng của các giải thuật heuristic scanners. Các AV có thể khắc phục nhưng rất khó khăn đặc biệt nếu bạn dùng vài kỹ thuật cao hơn như SEH. Tạo ra lỗi giả rồi nhảy đến đoạn bẫy lỗi. Tôi viết VR cách đây lâu rồi nên các kỹ thuậu mới trên win có khi không cập nhật bằng các bác nữa. Tôi sẽ đi sâu vào DOS hơn một chút.
Trước kia trên DOS tôi thích sử dụng vài kỹ thuật bẫy ngắt với:
- Int 0 (divide by zero). Chỉ cần một phép chia cho 0, bạn sẽ nhảy ngay đến code mới mà emulator không theo vết được (ta có thể âm thầm descrypt ở đây)
- Dấu code gọi ngắt:
mov ax, 3D02h-key
add ax, key
int 21h ;Tránh để lộ thao tác đọc file
- Với 386/486 ngày xưa có thể cùng queue fetch (xin lỗi vì tôi hơi hoài cổ một chút):
mov word ptr cs:[offset piq], 20CDh
piq:
nop
nop
code này ngày nay không dùng được. Bạn sẽ nghĩ chương trình sẽ kết thúc vì lệnh int 20h (20CDh) ư? Không đâu! với 386/486 code chạy vẫn là nop
Thế nhưng ngày nay kỹ thuật này biến thể một chút vì các CPU pentium không có queue fetch nhưng các emulator phát triển từ xưa vẫn nghĩ là có. Kỹ thuật đảo lại là:
mov word ptr [offset prefetch], <mã lệnh jump>
prefetch:
int 20h
(sao bạn không cười? tôi có trình khó hiểu quá không nhỉ? )
- Tương tự ngắt int 0. Bạn có thể bẫy int 1, int 6
Thôi kết thúc DOS. Trở lại win ta có thể dùng:
- Structured Exception Handling (SEH). Kỹ thuật này phổ biến quá rồi ha
- Threads and fibers
- Pentium+, copro, MMX, 3DNow! opcodes (undocumented opcodes)
- Kỹ thuật đa hình metamorphism
- Nhảy bằng Callbacks
- .......
Chắc mọi người biết cả rồi vì tôi mới trở lại với "Nghệ thuật hắc ám" gần nên kiến thức trên win không mới lắm.
Anti-Analysis
Ở phần này tôi muốn nói về chống disassemblers. Ai cũng biết mấy thằng disassemblers thông dụng như IDA, Sourcer hay win32dasm. Nếu bạn là người xây dựng chương trình disassemblers bạn sẽ làm thể nào. Tất nhiên dễ nhất là bắt đầu từ đầu chương trình, dasm tuần tự. Nếu code tuần tự như tiến ta có kết quả đẹp nhất. Nếu không sau lần chạy thứ nhất ta sẽ hiệu chỉnh lại code theo các lệnh nhảy và call... Thử chạy sourcer, bạn sẽ thấy điểm yếu nhất của disassemblers là rất, rất khó xử lý lệnh call và jump. Vậy phương pháp của V-er là:
- Mã hoá càng nhiều càng tốt
- Sử dụng call với relative offset kiểu
call label
gdelta: db 0b8h ;MOV opcode
label: pop ebp
...
mov eax, [ebp + variable - gdelta]
(Cách này có ở đa số VR rồi)
- Nhảy vào ... giữa mã lệnh:
jmp opcd+1 ;jump into instruction
opcd: mov eax, 0fcebfa90h
Bạn có thể thấy ngay thực ra đây không phải lệnh mov mà lệnh ta là 0fcebfa90h cơ
- Chèn các mã kiểu (db 0b8h) vào nhiều nơi sau các lệnh ret, jmp, ...
- Patch code runtime (tương tự kiểu queue fetch ở trên
- ...
Anti-Debug
Các AVer đã tóm được một mẫu VR của bạn. Hix.. bây giờ thì quá khó. Tuy vậy ta cũng có thể chống đỡ trong ... tuyệt vọng. Với Anti-Debug, nếu AVer không cao thủ họ mới nghi ngờ thôi thì cũng có thể bỏ cuộc. Vả lại có chết cũng cho oanh liệt, gây khó khăn chứ. Nếu không disassemble được người diệt sẽ debug. Cách thông thường là tìm xem có phần mềm debugger thông dụng kiểu softice thì chuồn lẹ. Các cách nhận biết debugger có thể là:
- Gọi luôn API của debugger
- Kiểm tra debugger context
- Sử dụng SEH (xem trên)
- Gọi VxD service (Ring-0 only)
- Kiểm tra softice trên bộ nhớ bằng CreateFileA
- Chọc vào các thanh ghi debug (Ring-0 only)
Anti-Monitor
Cũng vậy thôi, ta tìm xem có thằng AV nào thông dụng đang chạy trên bộ nhớ thì chuồn. Có thể dùng hàm API FindWindowA mà tìm. Nếu thấy thịt nó luôn bằng cách sử PostMessageA đến window handle của nó
Anti-Antivirus
Chủ động tìm database của AV trên đĩa mà thịt (có thể AV sẽ không chạy được nữa). Hay nhất là patch được database (AV chạy bình thường nhưng không tìm được VR nữa. Một số db thông dụng:
*.AVC - AVP viral database
AVP.CRC - AVP crc file
*.VDB - DrWeb viral database
NOD32.000 - NODICE viral database
ANTI-VIR.DAT - TBAV crc file
CHKLIST.MS - MSAV crc file
Anti-Bait
Chọn file mà lây. Tránh các AV, tránh mấy chương trình thông dụng kiểu winword.exe. Như ngày xưa trên DOS ta hay tránh command.com í
Tóm lại, tôi tổng kết các kỹ thuật ở đây, đây là theo những gì tôi hiểu nên sai các bác bỏ qua cho. Nếu tôi trình bày khó hiểu quá thì hix... trình độ sư phạm tôi thế thôi. Đây toàn là í tưởng các Ver chuyên nghiệp đã dùng nếu bạn là Ver đã từng viết VR chắc sẽ hiểu ra và tự xây dựng đoạn code cho riêng mình được.