My own tricks to
detect OllyDbg
Author :
Pumqara
Tranz by: Computer_Angle
Target: OllyDbg
Công cụ: tài liệu tham khảo Api, OllyDbg, mASM
Giới thiệu:
Các công cụ Debugger sử dụng ring3 được sử dụng bởi phần lớn các cracker một phần là do nó có giao diện thân thiện, và dễ sử dụng hơn rất nhiều so với công cụ debugger sử dụng ring0 ( như softice). Trong phần bài này, tôi xin nói về cách thức nhận dạng một công cụ debugger ring3 thuộc lại "xịn" nhất đó là OllyDbg.
Bên cạnh một vài cách nhận dạng đã được biết như IsDebuggerPresent và fs:[20], tôi đã tìm ra được một số cách khác khá hữu hiệu, và hi vọng với bài viết này các bạn sẽ có thể sử dụng, cũng như phát triển kĩ thuật này nhuần nhuyễn hơn.
Cách I: FindWindow:
Phương pháp này dựa trên hàm api FindWindow của hệ thống. Như ta biết, các cửa sổ của Ollydbg đều có caption và classname. Vì thế ta sẽ cố tìm xem tại thời điểm hiện tại chương trình OllyDbg có được chạy hay không.
Hàm FindWindow : trả về handle của cửa sổ chứa class name và window name phù hợp với chuỗi nhập vào, lưu ý là hàm này không tìm trong cửa sổ con.
Cấu trúc hàm:
QUOTE
|
HWND FindWindow(
LPCTSTR lpClassName, // address of class name LPCTSTR lpWindowName // address of window name ); Tham số: lpClassName: chứa tên của class name. lpWindowName: chứa tên của cửa sổ, nếu tham số này là NULL thì có nghĩa là chấp nhận mọi window names (caption). Giá trị trả về: là EAX=0 nếu không tìm ra |
Đoạn code:
CODE
|
.data
strOllyClsName db "OLLYDBG",0 .code invoke FindWindow, ADDR strOllyClsName, NULL cmp eax, 00000000h jne Olly_Detected |
Cách II: CreateToolhelp32Snapshot, Process32First/Next:
Cách thức sử dụng:
- Sử dụng GetCurrentProcessId để lấy processId hiện tại của chương trình.
- Tạo một vòng lặp của hàm CreateToolhelp32Snapshot và Process32First/Next , so sánh mọi giá trị th32ProcessID trong cấu trúc PROCESSENTRY32 với giá trị processid mà ta đã lấy từ bước 1 cho tới khi nào tìm ra.
- Khi đó, lấy giá trị th32ParentProcessID của cấu trúc PROCESSENTRY32 hiện tại.
- Lại tạo một vòng lặp của hàm CreateToolhelp32Snapshot và Process32First/Next , so sánh mọi giá trị th32ProcessID trong cấu trúc PROCESSENTRY32 với giá trị th32ParentProcessID mà ta đã lấy từ bước 3 cho tới khi nào tìm ra.
- Lấy giá trị szExeFile của cấu trúc PROCESSENTRY32 hiện tại và kiểm tra xem nó có phải là "Ollydbg.exe"
- Nếu có thì có nghĩa là chương trình hiện tại đang bị
debug bởi Olly
Đoạn code:
CODE
|
.586
.model flat, stdcall option casemap:none include d:\masm32\INCLUDE\Windows.inc include d:\masm32\INCLUDE\user32.inc include d:\masm32\INCLUDE\kernel32.inc includelib d:\masm32\lib\user32.lib includelib d:\masm32\lib\kernel32.lib .data strCaption db "OllyDbg Detector!",0 strFound db "OllyDbg found!",0 strNotFound db "OllyDbg NOT found!",0 strOllyDbg db "OLLYDBG.EXE",0h valCurrentPiD dd 0 valParentPiD dd 0 hSnapShot dd 0 .data? proces PROCESSENTRY32 <> .code start: ; Create the snapshot invoke CreateToolhelp32Snapshot, TH32CS_SNAPPROCESS,NULL mov hSnapShot,eax ;Get the ProcessId of the Current Process invoke GetCurrentProcessId mov valCurrentPiD,eax lea esi,offset proces assume esi:ptr PROCESSENTRY32 mov [esi].dwSize,sizeof PROCESSENTRY32 ;Begin the first Loop and find the current process ;using the valCurrentPiD invoke Process32First,hSnapShot,addr proces lea esi,offset proces assume esi:ptr PROCESSENTRY32 mov ebx,valCurrentPiD cmp ebx,[esi].th32ProcessID jne nope1 nope1: invoke Process32Next,hSnapShot,addr proces lea esi,offset proces assume esi:ptr PROCESSENTRY32 mov ebx,valCurrentPiD cmp ebx,[esi].th32ProcessID jne nope1 push [esi].th32ParentProcessID pop valParentPiD invoke CloseHandle,hSnapShot ; Create the snapshot again invoke CreateToolhelp32Snapshot, TH32CS_SNAPPROCESS,NULL mov hSnapShot,eax mov [esi].dwSize,sizeof PROCESSENTRY32 ; Begin the second Loop and find the parent ; process using the valParentPiD invoke Process32First,hSnapShot,addr proces lea esi,offset proces assume esi:ptr PROCESSENTRY32 mov ebx,valParentPiD cmp ebx,[esi].th32ProcessID jne nope2 nope2: invoke Process32Next,hSnapShot,addr proces lea esi,offset proces assume esi:ptr PROCESSENTRY32 mov ebx,valParentPiD cmp ebx,[esi].th32ProcessID jne nope2 ; Extract filename of the Parent Process from the whole string lea eax, [esi].szExeFile push eax invoke lstrlen,eax sub eax,11 pop ebx add ebx,eax ; Case Upper the string and compare it with "OLLYDBG.EXE" invoke CharUpper,ebx invoke lstrcmp,ebx,addr strOllyDbg .IF eax==0 invoke MessageBox,0,addr strFound,addr strCaption,0 .ELSE invoke MessageBox,0,addr strNotFound,addr strCaption,0 .ENDIF invoke CloseHandle,hSnapShot invoke ExitProcess,0 end start |
Cách III: SetUnhandledExceptionFilter
Chức năng của hàm SetUnhandledExceptionFilter:
Cho phép chương trình ngăn chặn việc xử lí lỗi ngoại lệ mặc định của hệ thống. Sau khi gọi hàm này, nếu có lỗi ngoại lệ phát sinh trong chương trình không bị debug , thì việc xử lí lỗi sẽ được chuyển giao cho bộ quản lí lỗi đã được chỉ định ở trên bởi thông số lpTopLevelExceptionFilter của hàm.
Ý tưởng của phương pháp này là ta sẽ tạo một bộ quản lý lỗi ngoại lệ bằng hàm SetUnhandledExceptionFilter. Sau đó ta sẽ cố tình phát sinh một lỗi bất thường trong chương trình, khi đó, nếu chương trình đang bị debug thì lỗi này sẽ o được chuyển giao cho bộ quản lí lỗi đã được chỉ định ở trên. Khi ấy, debugger sẽ không biết phải làm gì và dẫn tới crash chương trình. Chương trình o bị debug thì mọi thứ diễn ra bình thường.Điều này có nghĩa là, khi ấy, chương trình sẽ gọi bộ quản lí lỗi, xử lí lỗi phát sinh và chuyển tới đoạn lệnh kế tiếp.
Trình tự thực hiện:
- Thiết lập bộ xử lí lỗi ngoại lệ bằng cách dùng hàm api SetUnhandledExceptionFilter
- Tạo lỗi ngoại lệ và nhảy tới bộ quản lý lỗi khi và chỉ khi chương trình không bị debug
- Trong bộ xử lí, lấy cấu trúc EXCEPTION_POINTERS từ stack
- Từ cấu trúc EXCEPTION_POINTERS, lấy cấu trúc CONTEXT
- Thay đổi regEIP của cấu trúc CONTEXT sang vị trí lệnh mà chương trình sẽ thực thi tiếp.
- Đặt cờ EXCEPTION_CONTINUE_EXECUTION vào EAX
- Phục hồi stack và tiếp tục thực thi chương trình.
Code minh họa:
CODE
|
.586
.model flat, stdcall option casemap:none include d:\masm32\INCLUDE\Windows.inc include d:\masm32\INCLUDE\user32.inc include d:\masm32\INCLUDE\kernel32.inc includelib d:\masm32\lib\user32.lib includelib d:\masm32\lib\kernel32.lib .data strCaption db "OllyDbg Detector",0 strNotFound db "OllyDbg NOT found!",0 .code ExcpHandler proc mov eax, dword ptr [esp+4]; eax = EXCEPTION_POINTERS mov eax, [eax+4] ; eax = CONTEXT assume eax:ptr CONTEXT mov [eax].regEip, offset safe_address; Change regEip pushad invoke MessageBox,0,addr strNotFound,addr strCaption,0 popad xor eax, eax ;\ ; ) Set EXCEPTION_CONTINUE_EXECUTION dec eax ;/ retn 4; Normalize stack and return ExcpHandler endp start: invoke SetUnhandledExceptionFilter,offset ExcpHandler mov ebx,dword ptr [0FFFFFFFFh];Exception is here! safe_address: invoke ExitProcess,0 end start |
Cách 4: APi
Redirection
Một cách khác mà tôi nghĩ ra đó là dựa vào cơ chế quản lý các hàm api được gọi trong chương trình debug của Ollydbg. Oleh Yuschuk (tác giả của OllyDbg) đã sử dụng api redirection khi chương trình bị debug gọi hàm api. Có thể xem vd dưới đây:
Một cách khác mà tôi nghĩ ra đó là dựa vào cơ chế quản lý các hàm api được gọi trong chương trình debug của Ollydbg. Oleh Yuschuk (tác giả của OllyDbg) đã sử dụng api redirection khi chương trình bị debug gọi hàm api. Có thể xem vd dưới đây:
CODE
|
00401000 >PUSH
ASD.00403033 ; /FileName = "kernel32.dll"
00401005 CALL | | '--> 0040105C JMP DWORD PTR DS:[402004]; JMP DWORD PTR DS:[<&KERNEL32.LoadLibraryA>] | | '--> 87FF4120 PUSH BFF776D0 ; PUSH KERNEL32.LoadLibraryA 87FF4125 JMP KERNEL32.BFF957CA |
Điều này có nghĩa là, tác giả đã load bảng Import Table từ chương trình bị debug và sau đó điền các giá trị mới vào IAT, redirect các hàm API gốc qua một buffer chứa hàm gọi tới địa chỉ API thật. Quan trọng nhất là tác giả OllyDbg đã mô phỏng hàm GetProcAddress , khiến cho nó luôn trả về giá trị đã bị redirect thay vì là giá trị gốc ban đầu.Vd như giá trị địa chỉ thật của IsDebuggerPresent trong vùng nhớ là BFF946F6, nhưng hàm luôn trả về 87FF4110, đây là redirected address. Như vậy, ta sẽ dùng cách thức này để phát hiện Olly.
Cách thức thực hiện:
- Nạp thư viện kernel32.dll, ta sẽ có giá trị trả về là Image Base (vị trí mà thư viện này nạp trong vùng nhớ)
- Gọi hàm Call GetProcAddress để lấy địa chỉ của hàm ExitProcess
- So sánh giá trị trả về từ hàm GetProcAddress với giá trị từ Image Base của kernel32.dll
- Nếu giá trị trả về là lớn hơn, đều này có nghĩa là hàm này được gọi trực tiếp, o bị redirected và ngược lại.
Code minh họa:
CODE
|
.586
.model flat, stdcall option casemap:none include d:\masm32\INCLUDE\Windows.inc include d:\masm32\INCLUDE\user32.inc include d:\masm32\INCLUDE\kernel32.inc includelib d:\masm32\lib\user32.lib includelib d:\masm32\lib\kernel32.lib .data strCaption db "OllyDbg Detector",0 strFound db "OllyDbg found!",0 strNotFound db "OllyDbg NOT found!",0 strLibrary db "kernel32.dll",0 strFunction db "ExitProcess",0 .code start: invoke LoadLibrary,addr strLibrary push eax; eax = kernel32.dll's Image Base invoke GetProcAddress,eax,addr strFunction ; eax = ExitProcess's address or redirector ? ; ebx = kernel32.dll's Image Base pop ebx cmp eax,ebx; Address of ExitProcess < Kernel32.dll's ; Image Base ? jl Olly_Detected invoke MessageBox,0,addr strNotFound,addr strCaption,NULL invoke ExitProcess,0 Olly_Detected: invoke MessageBox,0,addr strFound,addr strCaption,NULL invoke ExitProcess,0 end start |
Author: ThangCuAnh [TQN] :
Cách
5:
Nếu có gì không được vừa lòng các cậu thì sorry. Tôi rất lười
viết, chỉ post code được thôi.
Dưới đây là code anti-debug ring-3 debuggers (Olly, WinDbg...) mà tôi tìm ra và sưu tầm. Mạn phép post lên đây để các bạn tham khảo, và dùng cho các crackme của các cracker VN ta. Có 1 số cái đã bị bypass, 1 số thì chưa:
Dưới đây là code anti-debug ring-3 debuggers (Olly, WinDbg...) mà tôi tìm ra và sưu tầm. Mạn phép post lên đây để các bạn tham khảo, và dùng cho các crackme của các cracker VN ta. Có 1 số cái đã bị bypass, 1 số thì chưa:
CODE
; File: AntiDbg.asm
; Anti ring3 Debuggers, code by ThangCuAnh
.386
.model flat, stdcall
option casemap:none
include windows.inc
include kernel32.inc
include user32.inc
includelib kernel32.lib
includelib user32.lib
CTEXT MACRO text:VARARG
LOCAL TxtName
.data
TxtName BYTE text, 0
.code
EXITM
ENDM
.data
bDebuggerPresent dd 0
pCheckRemoteDebuggerPresent dd 0
hProcess dd 0
dwRetLen dd 0
.code
start:
; Get process handle of our process
invoke GetCurrentProcessId
invoke OpenProcess, PROCESS_ALL_ACCESS, FALSE, eax
mov hProcess, eax
; Check debugger by CheckRemoteDebuggerPreset (not bypassed)
invoke GetModuleHandle, CTEXT("kernel32.dll")
invoke GetProcAddress, eax, CTEXT("CheckRemoteDebuggerPresent")
mov pCheckRemoteDebuggerPresent, eax
.if eax != 0
push offset bDebuggerPresent
push hProcess
call pCheckRemoteDebuggerPresent
.if bDebuggerPresent != 0
invoke MessageBox, 0, CTEXT("Debugger found"), CTEXT("Aa..."), MB_OK
.endif
.endif
; Check by NtQueryInformationProcess function,
; can be bypassed by hooking NtQueryInformationProcess (IceExt in SoftIce)
invoke GetModuleHandle, CTEXT("ntdll.dll")
invoke GetProcAddress, eax, CTEXT("NtQueryInformationProcess")
.if eax != 0
push offset dwRetLen
push sizeof DWORD
mov bDebuggerPresent, 0
push offset bDebuggerPresent
push 7
push hProcess
call eax
.if bDebuggerPresent != 0
invoke MessageBox, 0, CTEXT("Debugger found"), CTEXT("Aa..."), MB_OK
.endif
.endif
; Cause OllyDbg crash, can by bypassed by a paching version of OllyDbg
.if bDebuggerPresent != 0
invoke MessageBox, 0, CTEXT("OllyDbg is your favorite tool. Are you using OllyDbg ???"),
CTEXT("Question"), MB_YESNO
.if eax == IDYES
invoke MessageBox, 0, CTEXT("You will die here..."), CTEXT("Bum..."), MB_OK
invoke OutputDebugString, CTEXT("%s%s%s")
.endif
.else
invoke MessageBox, 0, CTEXT("Debugger not found"), CTEXT("Great"), MB_OK
.endif
invoke ExitProcess, eax
end start
Source
from
HVA
Benina
moded