본문 바로가기

High Level Technique/Reversing

Anti Debugging - Static

Anti Debugging - Static


프로세스에서 자신이 디버깅 당하는지 여부를 파악하는 기법입니다. 만약 디버깅 중이라고 판단되면 일반 실행과 다른 코드를 실행하는 것입니다.

디버거를 탐지하거나 디버깅 환경을 탐지하거나 디버거를 강제 분리 시키는 방법이 있습니다.


대부분의 Static Anti Debugging은 OS에 의존하고 있어서 XP에서는 되는데 7에서는 안되고 7에서는 되는데 10에서는 안되는 경우가 있습니다.




Process Envritonment Block (PEB)


PEB에서 BeingDebugged, Ldr, ProcessHeap, NtGlobalFlag 멤버를 확인해야 합니다.


BeingDebugged 멤버는 디버깅 여부를 표시하는 Flag로 사용됩니다. Ldr, ProcessHeap, NtGlobalFlag 멤버들은 디버깅 프로세스의 독특한 Heap 메모리 특성과 관련되있습니다.


PEB의 주소를 구하는 방법은 FS:[30]으로 확인할 수 있습니다.



핵심원리의 실습예제인 StaAD_PEB.exe를 가지고 분석해보고, 우회도 해보도록 하겠습니다.



IsDebuggerPresent()


IsDebuggerPresent()는 PEB.BeingDebugged 값을 참조하여 디버깅 여부를 판별합니다. 





해당 위치의 값을 1에서 0으로 변경하면 IsDebuggerPresent()를 우회할 수 있습니다.



Not Debugging이라는 문자가 출력되는 것을 볼 수 있습니다.







Ldr(+0xC)


Heap 메모리 영역에 자신이 디버깅 당하는 프로세스라는 표시를 합니다. Heap 메모리의 사용되지 않는 영역을 0xFEEEEEEFE 값으로 채워버립니다.

PEB.Ldr 멤버는 _PEB_LDR_DATA 구조체를 가리키는 포인터 입니다. _PEB_LDR-data 구조체는 Heap 메모리 영역에 생성되기 때문에 이 영역을 보면 알 수 있습니다.



EBX의 값이 229000으로 되어있습니다. 이 주소가 PEB의 주소입니다.


좀 더 내려가서 분석을 해보면 PEB.Ldr의 주소가 나타납니다.




4010C7의 위치까지 분석을 해보면 0xEEFEEEFE 값을 넣는 것을 알 수 있습니다. 


4010C5에서 EDX의 값을 따라갑니다.


따라가게되면 EEFEEEFE로 값이 채워지게 되는데, 이 값을 00으로 채우면 우회할 수 있습니다. Windows 10에서는 값이 나타나지 않았습니다.


중간에 값을 확인하지 못해서 뻑나게 됩니다. 







PEB.ProcessHeap


PEB.ProcessHeap.Flags와 PEB.ProcessHeap.ForceFlags를 체크하는 안티 디버깅 기법입니다.



401112 주소에서 EDI에 ProcessHeap 구조체 주소가 저장됩니다.

401115 주소에서 EDI+C의 값이 PEB.ProcessHeap.Flags 값입니다. 이 값을 2로 변경합니다. (그런데 전 2로 되어있네요.. 강제로 EIP를 변경해서 그런가? OS 때문인가..)



40113F 주소에서 EDI+10이 PEB.ProcessHeap.ForceFlag 값입니다. 이 값을 0으로 변경합니다.





PEB.NtGlobalFlag


계속 진행하다 보면 401168 주소에서 PEB.NtGlobalFlag 안티 디버깅 트릭이 나타납니다.



EBX+68의 값이 PEB.NtGlobalFlag 값입니다. 이 값을 0으로 바꾸면 우회가 됩니다. 디버깅 중이면 0x70이 저장됩니다.




PEB.Ldr는 중간에 뻑나서 제대로 확인할 수 없었지만 나머지 값들을 우회해서 디버깅 탐지를 무력화 시켰습니다.






















NtQueryInformationProcess()


NtQueryInformationProcess()를 이용하면 프로세스의 디버깅 관련 정보 뿐만 아니라 다양한 정보를 얻을 수 있습니다.



PROCESSINFORCLASS ProcessInformationClass에 원하는 정보 형식을 저장한 후 NtQueryInformationProcess()를 호출하면 PVOID ProcessInformation에 해당 정보가 세팅됩니다.



핵심원리에 나와있는 PROCESSINFOCLASS와는 조금 다릅니다.

그래도 디버거 탐지에 사용되는 ProcessDebugPort(0x7), ProcessDebugObjectHandle(0x1E), ProcessDebugflags(0x1F)에 대해서 알아보도록 하겠습니다.



실습예제인 StaAD_NtQIP.exe를 가지고 해보도록 하겠습니다.




ProcessDebugPort(0x7)


프로세스가 디버깅 중일 때 Debug Prot가 할당됩니다. ProcessInformationClass 파라미터에 ProcessDeubgPort 값을 입력하면 Debug Prot를 얻을 수 있습니다.

디버깅 중이 아니면 dwDebugProt 에 0이 세팅되고 디버깅 중이면 0xFFFFFFFF이 세팅됩니다.


탐지하는 소스코드는 아래와 같습니다.




Windows 10 x64 환경에서 진행하니 핵심원리에 나와있는 어셈블리어와는 다르게 나타났습니다.



NtQueryInformationProcess()로 이동해서 확인을 해야하는데 40103B 주소에서 CALL ESI를 통해 ZwQueryInformationProcess로 이동하는 것을 볼 수 있습니다.



해당 위치로 이동해보면 위와 같은 어셈블리어가 나타납니다.


핵심원리에서 설명한 것과 비슷하게 진행해 보도록 하겠습니다.


핵심원리에서는 EDX에 7FFE0300 값을 저장하고 CALL 을 하지만 제 환경에서는 CALL 77D16D4E를 하고있습니다. 마찬가지로 407E00 주소에 후킹함수를 넣어보도록 하겠습니다.



이렇게 코드를 작성해서 실행시켜보았지만 Debugging 감지를 하게됩니다.


그래서 다시 분석을 해보았습니다.



CALL EDI 하는 부분이 있는데 해당 부분을 들어가보면 아래와 같습니다.



EAX의 값과 FFFFFFFF을 OR 연산을 하고 있습니다. 이 값을 0으로 변경하고 진행합니다.




실제 우회가 된 것인지는 사실 잘모르겠지만 실행 결과상 핵심원리에 나와있는 출력과 같게 나타났습니다.



ProcessDebugObjectHandle(0x1E)





ProcessDebugFlags(0x1F) (NoDebugInherit)














NtQuerySystemInformation()


디버깅 환경을 체크하는 안티 디버깅 기법입니다. 이 기법은 OS 가 Debug Mode로 부팅되었는지를 판단하는 기법입니다.


Windows xp 에서는 c:\boot.ini 를 변경합니다.


[boot loader]

timeout=30

deault=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS

[operation systems]

multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" /noexecute=option /fastdetect /debugport=com1 /baudrate=115200 /Debug



Windows 7 이상에서는 bcdedit.exe를 이용합니다.



debug mode로 설정이 되어있고, 재부팅 후 StaAD_NtQSI.exe를 실행시켜보았습니다.



그런데 Not debugging이라고 뜹니다. Windows 10에서는 안되나 봅니다.


간단히 정리만 하고 넘어가겠습니다.


SYSTEM_INFORMATION_CLASS SystemInformationClass 파라미터에 원하는 시스템 정보를 입력하고  PVOID SystemInformation 파라미터에 관련 구조체 주소를 넘겨주면 API가 리턴하면서 그 구조체 정보를 채워줍니다.


SYSTEM_INFORMATION_CLASS는 열거형으로 아래와 같습니다.



SystemInformationClass 파라미터에 SystemKernelDebuggerInformation(0x23) 값을 입력하면 현재 OS 시스템이 디버그 모드로 부팅되었는지 알 수 있습니다.


SystemKernelDebuggerInformation(0x23)


NtQuerySystemInformation() 를 호출할 때 첫 번째 파라미터에 SystemKernelDebuggerInformation(0x23) 값을 입력하고 두 번째 파라미터에 SYSTEM_KERNEL_DEBUGGER_INFORMATION이라고 알려진 구조체 주소를 입력합니다. 리턴되면 디버그 모드인 경우 SYSTEM_KERNEL_DEBUGGER_INFORMATION.DebuggerEnabled에 1이 세팅됩니다.  SYSTEM_KERNEL_DEBUGGER_INFORMATION.DebuggerNotPresent 항목은 항상 1로 세팅됩니다.



우회 방법


Windows XP의 경우 boot.ini의 /debugprot=com1 /baudrate=115200 /Debug 값을 제거하고, Windows 7이상의 OS에서는 bcdedit /debug off를 하면 됩니다.

















NtQueryObject()


시스템에서 디버거가 프로세스를 디버깅하고 있다면 DebugObject 타입의 커널 객체가 생성되는데 이 DebugOjbect의 존재를 확인하는 것입니다.



두 번째 파리미터 OjbectInformationClass에 원하는 값을 넣고 호출하면 세 번재 파라미터 ObjectInformation에 관련 정보의 구조체 포인터를 리턴합니다.


OjbectAllTypesInformation 항목을 이용해서 시스템의 모든 객체 정보를 구한 다음 DebugOjbect가 있는지 확인합니다. VS2013에서는 ObjectAllTypesInformation 항목이 존재하지 않습니다.



StaAD_NtQO.exe를 가지고 분석해 보도록 하겠습니다.



디버거를 이용해서 실행해본 결과 Debugging!!!이 출력되었습니다. 디버깅이 되고 있다는 말이죠.



401059 주소에서 ZwQueryObject가 실행됩니다. 스택을 보면 19FF08에 3이라는 값이 들어있습니다. 이 값이 ObjectAllTypesInformation 입니다.


이 값을 0으로 변경하고 진행하면 디버거를 탐지하지 못합니다.
















ZwSetIformationThread()


프로세스에서 강제로 디버거를 떼버리는 기법입니다. ZwSetInformationThread()를 이용하면 디버깅 하고 있는 디버거를 떼어낼 수 있습니다.





첫 번째 파라미터 ThreadHandle에 현재 Thread의 Handle을 넘겨주고 두 번째 파라미터 ThreadInformationClass에 ThreadHide From Debugger(0x11) 값을 입력하면 디버거 프로세스가 분리됩니다.


실습예제인 StaAD_ZwSIT.exe를 디버거로 실행시켜 보겠습니다.



위 그림처럼 Debugger가 탐지가 되었다고 출력됩니다.



401027 주소에서 CALL ESI를 실행하는데 이 부분이 ZwSetInformationThread 입니다. 



해당 부분에서 실행을 시킨 결과 작동이 중지되면서 실행할 수 없게 됩니다.



스택의 값을 살펴보면 ThreadHideFromDebugger(0x11)의 값이 저장되어 있습니다. 이 값을 0으로 변경하면 됩니다.


그러면 정상적으로 프로그램이 진행됩니다.

















TSL 콜백 함수


TSL 콜백 함수는 유용하게 사용되는 안티 디버깅 기법이빈다. 이 부분은 이전 포스팅에서 설명했으니 넘어가겠습니다.















ETC


프로그램의 이름을 검색하여 디버깅이 되고 있는지 아닌지 판단합니다.

FindWindows(), CreatToolhelp32Snapshot(), GetComputerName(), GetCommandLine(), VMWareService.exe, VMWareTray.exe, VMWareUser.exe 등등



실습예제 StaAD_FindWindows.exe를 디버거로 실행시켜 보겠습니다.



디버거를 이용하여 실행시킨 결과 Debugger를 찾아다고 출력합니다.




FindWindow 부분이 나타나는데 여기서 OllyDBG라는 문자를 볼 수 있습니다. 해당 부분을 찾아가 NULL로 변경합니다.



해당 부분을 NULL로 변경하면 탐지하지 못합니다.



FindWindow 부분은 우회를 했습니다.


다음으로는 GetWindowText()를 우회해야 합니다.



4010AD에서 JE 명령어를 통해서 조건 분기가 이루어지는데 해당 부분에서 JE 명령어를 바꾸거나 GetWindow() 리턴 값을 변조하여 할 수 있습니다.



'High Level Technique > Reversing' 카테고리의 다른 글

Advanced Anti Debugging  (0) 2016.08.09
Anti Debugging - Dynamic  (1) 2016.08.08
Anti Debugging  (0) 2016.08.05
IA32 Instruction  (1) 2016.08.04
Structured Exception Handler (SEH)  (0) 2016.08.04