Programming/Kernel / Driver2008. 12. 5. 16:37

어떻게 하면 프로세스 리스트에서 숨겨진 프로세스만
골라낼 수 있을까?
이건 많은 이들이 궁금해 하는 주제인듯 하여,
아주 심플한 소스와 함께 한번 소개해 보겠다.
뭐, 별건 아니고 그냥 말그대로 숨겨진 프로세스를
효과적으로 잡아 내는것에 대한 소스이다.

1. 전역 변수 PEPROCESS SE_Proc; 를 만든다.

2. DriveEntry에 다음과 같은줄을 추가했다.
------------------------------------------------------
NTSTATUS DriverEntry (
IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING pRegistryPath ) {
...
...
SE_Proc = PsGetCurrentProcess();
...
...
}

3. ZwQuerySystemInformation()을 SSDT Hook하여
다음 함수에 연결하였다.
-----------------------------------------------------------
NTSTATUS NTAPI NewZwQuerySystemInformation(
IN ULONG SystemInformationClass,
IN PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength
)
{
NTSTATUS rc;
CHAR Attack_Process_Name[PROCNAMELEN];
PEPROCESS eproc,currproc;
PLIST_ENTRY start_plist,plist_hTable = NULL;
DWORD *d_pid;
extern PsOffset_HandleTable,PsOffset_HandleList,PsOffset_hPID;
extern SE_Proc;
char *nameptr;
CHAR Hided_Process_Name[PROCNAMELEN];

InterlockedIncrement( &G_nLockUseCounter );
GetProcessName( Attack_Process_Name );

eproc = SE_Proc;
plist_hTable = (PLIST_ENTRY)((*(DWORD*)((DWORD)eproc +
PsOffset_HandleTable)) + PsOffset_HandleList);
start_plist = plist_hTable;

rc = ((ZWQUERYSYSTEMINFORMATION)(OldZwQuerySystemInformation)) (
SystemInformationClass,
SystemInformation,
SystemInformationLength,
ReturnLength );

if( NT_SUCCESS( rc ) )
{

if(5 == SystemInformationClass)
{
struct _SYSTEM_PROCESSES *curr = (struct _SYSTEM_PROCESSES *)SystemInformation;
struct _SYSTEM_PROCESSES *prev = NULL;

while(curr)
{
ANSI_STRING process_name;
RtlUnicodeStringToAnsiString( &process_name, &(curr->ProcessName), TRUE);
if( (255 > process_name.Length) && (0 < process_name.Length) )
{
d_pid = (DWORD*)(((DWORD)plist_hTable + PsOffset_hPID)
- PsOffset_HandleList);
if(curr->ProcessId != *d_pid)
{
DbgPrint("---Hided process---");
nameptr = (PCHAR)eproc + gProcessNameOffset;
strncpy(Hided_Process_Name, nameptr, NT_PROCNAMELEN);
Hided_Process_Name[NT_PROCNAMELEN] = 0;
DbgPrint("ProcessName:%s",Hided_Process_Name);
DbgPrint("ProcessId:0x%X",*d_pid);
DbgPrint("EPROCESS:0x%X",eproc);
DbgPrint("-------------------");
}

if(0 == strncmp( process_name.Buffer, MYPROCESS, NT_PROCNAMELEN))
{
DbgPrint("[Alarm] ProcessScan Detected\n");
DbgPrint("Called by %s\n",Attack_Process_Name);

if(prev)
{
if(curr->NextEntryDelta)
{
prev->NextEntryDelta += curr->NextEntryDelta;
}
else
{
prev->NextEntryDelta = 0;
}
}
else
{
if(curr->NextEntryDelta)
{
(char *)SystemInformation += curr->NextEntryDelta;
}
else
{
SystemInformation = NULL;
}
}
}
}
RtlFreeAnsiString(&process_name);
prev = curr;
if(curr->NextEntryDelta) ((char *)curr += curr->NextEntryDelta);
else
{
if(start_plist != plist_hTable)
{
do
{
d_pid = (DWORD*)(((DWORD)plist_hTable + PsOffset_hPID)
- PsOffset_HandleList);
if(*d_pid == 0)
{
plist_hTable = plist_hTable->Flink;
break;
}
PsLookupProcessByProcessId(*d_pid,&eproc);
nameptr = (PCHAR)eproc + gProcessNameOffset;
strncpy(Hided_Process_Name, nameptr, NT_PROCNAMELEN);
Hided_Process_Name[NT_PROCNAMELEN] = 0;
DbgPrint("---Hided process---");
DbgPrint("ProcessName:%s",Hided_Process_Name);
DbgPrint("ProcessId:0x%X",*d_pid);
DbgPrint("EPROCESS:0x%X",eproc);
DbgPrint("-------------------");
plist_hTable = plist_hTable->Flink;

}while(start_plist != plist_hTable);
}
curr = NULL;
}
}
}
}
InterlockedDecrement( &G_nLockUseCounter );
return rc;
}
----------------------------------------------------------

뭐, 이게 끝이다.
사용한 방법을 간단히 설명하자면,
ZwQuerySystemInformation()으로
프로세스 리스트를 구해오고자 할떄,
해당 프로세스 번째의 Pid와
HandleTable을 Tracing하면서
해당 번쨰의 Pid가 같은지 비교한다.
같지 않으면 거기엔 Process가 Hide가 된것이다.
ZwQuerySystemInformation()으로 구해진
프로세스 리스트가 끝났을떄,
아직 HandleTable의 Tracing이 아직 끝나지
않은 상태라면, 그 뒤로 숨겨진 프로세스들이 있다고 볼 수 있다.
그럼으로 그들을 출력해준다.
그런데, 마지막 프로세스는 Process Scan을 행한 프로세스
자신인데, PID는 0이다. 그럼으로 PID가 0일때는
출력하지 않고 멈춘다.

출력양식은 다음과 같다.
-------------------------------------------
---Hided process---
ProcessName:SomethingHided.exe
ProcessId:0xB47
EPROCESS:0x80485064
-------------------
-------------------------------------------

이 코드에서는 ZwQuerySystemInformation()내에서
처리하는 방식으로 했지만,
단순히 숨겨져 있는 프로세스 목록만 알고 싶다면,
HandleTable과 EPROCESS의 pListEntry를
이용하는 것이 좋을 것이다.

이 방법에서 조금 더 나아가면,
숨겨진 프로세스를 골라낼 수 있을 뿐만 아니라,
curr->NextEntry에 struct _SYSTEM_PROCESSES
형태로 데이터를 집어넣고 연결하여 프로세스 리스트에
반영되게 할 수도 있다. :)

-본자료는 듀얼님이 작성하신것으로 스크랩글임을 밝혀둡니다
Posted by skensita