Trao đổi với tôi

http://www.buidao.com

12/21/16

[Anti Crack] My own tricks to detect OllyDbg

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:
  1. Sử dụng GetCurrentProcessId để lấy processId hiện tại của chương trình.
  2. 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.
  3. Khi đó, lấy giá trị th32ParentProcessID của cấu trúc PROCESSENTRY32 hiện tại.
  4. 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.
  5. 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"
  6. 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:
  1. Thiết lập bộ xử lí lỗi ngoại lệ bằng cách dùng hàm api SetUnhandledExceptionFilter
  2. 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
  3. Trong bộ xử lí, lấy cấu trúc EXCEPTION_POINTERS từ stack
  4. Từ cấu trúc EXCEPTION_POINTERS, lấy cấu trúc CONTEXT
  5. 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.
  6. Đặt cờ EXCEPTION_CONTINUE_EXECUTION vào EAX
  7. 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:

CODE
00401000 >PUSH ASD.00403033                    ; /FileName = "kernel32.dll"
00401005 CALL ; \LoadLibraryA
|
|
'--> 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:
  1. 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ớ)
  2. Gọi hàm Call GetProcAddress để lấy địa chỉ của hàm ExitProcess
  3. So sánh giá trị trả về từ hàm GetProcAddress với giá trị từ Image Base của kernel32.dll
  4. 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:
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