Hacking & Security/Kernel2009. 6. 22. 13:11

1. UniqueProcessId 구하기.

* PEPROCESS+0x80(오프셋이 항상 이 값 보다는 크므로) 부터 +PAGE_SIZE까지

Traverse한다.

* PEPROCESS는 IoGetCurrentProcess()를 이용하고 PID는 PsGetCurrentProcessId()를 이용하여 구한다.

* for문으로 4의 배수에 맞추어 검색하여 PID와 같은 값이 발견되면 그 뒤에(뒤에 바로 ActiveProcessLinks가 나오므로) ActiveProcessLinks의 포인터가 Valid한 VA인지 확인하고, 다시 내 EPROCESS구조체로 link되는지 체크하여 정확하게 찾아낸다.

대략적인 C 코드는 아래와 같습니다.

VOID CalcPIDOffset(VOID)
{
 PEPROCESS Proc = IoGetCurrentProcess();
 HANDLE PID = PsGetCurrentProcessId();
 PVOID ThisEntry = NULL, NextEntry = NULL;
 int i = 0;
 for(i = 0x80; i < PAGE_SIZE - 8; i += 4)
 {
  if(*(PHANDLE)((PCHAR)Proc + i) == PID)
  {
   // ActiveProcessLinks Offset is PID Offset+4
   NextEntry = *(PVOID *)((PCHAR)Proc + i + 8);
   if(MmIsAddressValid(NextEntry))
   {
    ThisEntry = *(PVOID *)NextEntry;
    if((PCHAR)ThisEntry == (PCHAR)Proc + i + 4)
    {
     // Probably, this is PID offset
     PID_OFFSET = i;
     return;
    }
   }
  }
 }

 PID_OFFSET = 0x1ec; // Exception case, so, put Offset as WinNT/2003(generic).
}

* 모든 상황에 대처할 수 있도록, 예외 처리로는 NT/2003의 PID_OFFSET을 대입하게 하였습니다.

 

 2. Process Name Offset 구하기.

 PEPROCESS의 프로세스 이름 Offset을 따내는 방법입니다. 아래와 같이 딸 수 있습니다.

VOID CalcProcessNameOffset(VOID)
{
 PEPROCESS SysProc = PsInitialSystemProcess;
 int i = 0;
 for(i = 0; i < PAGE_SIZE; i++)
 {
  if(strncmp((PCHAR)SysProc + i, "System", 6) == 0)
  {
   IMAGE_NAME_OFFSET = i;
   return;
  }
 }
}

* PsInitialSystemProcess의 PEPROCESS를 트레버싱하여 System 문자열과 일치하는 offset을 찾습니다.

 

 3. Handle Table Offset 구하기.

 PEPROCESS의 Handle Table Offset을 구하는 법입니다. Handle Table이란 EPROCESS의

 아래 필드를 의미합니다.

lkd> dt !_EPROCESS 8a2407f8
ntdll!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   +0x06c ProcessLock      : _EX_PUSH_LOCK
   +0x070 CreateTime       : _LARGE_INTEGER 0x0
   +0x078 ExitTime         : _LARGE_INTEGER 0x0
   +0x080 RundownProtect   : _EX_RUNDOWN_REF
   +0x084 UniqueProcessId  : 0x00000004
   +0x088 ActiveProcessLinks : _LIST_ENTRY [ 0x8a10d2e8 - 0x8056a558 ]
   +0x090 QuotaUsage       : [3] 0
   +0x09c QuotaPeak        : [3] 0
   +0x0a8 CommitCharge     : 7
   +0x0ac PeakVirtualSize  : 0x759000
   +0x0b0 VirtualSize      : 0x1d0000
   +0x0b4 SessionProcessLinks : _LIST_ENTRY [ 0x0 - 0x0 ]
   +0x0bc DebugPort        : (null)
   +0x0c0 ExceptionPort    : (null)
   +0x0c4 ObjectTable      : 0xe1001e00 Ptr32 _HANDLE_TABLE

     ...

이 것을 구하는 방법은 크게 두가지를 생각해볼 수 있습니다.

1. 다른 필드로부터 델타(Delta; 오프셋 차이값)을 계산하여 그 값을 가산함으로써 오프셋을 얻는 방법.

  -> 이 방법을 사용하려면 많은 테스트가 이루어져야 하며, 불안정하기 때문에 여기서는 사용하지 않겠습니다. 이 방법을 사용하려면 모든 버젼에서 해당 차이값이 동일하다는 확실한 근거가 있어야합니다.

2. 해당 필드의 구조를 이용하는 방법.
 -> _HANDLE_TABLE은 아래와 같은 구조로 되어있습니다.

lkd> dt !_HANDLE_TABLE 0xe1001e00
ntdll!_HANDLE_TABLE
   +0x000 TableCode        : 0xe1890001
   +0x004 QuotaProcess     : (null)
   +0x008 UniqueProcessId  : 0x00000004
   +0x00c HandleTableLock  : [4] _EX_PUSH_LOCK
   +0x01c HandleTableList  : _LIST_ENTRY [ 0xe18640d4 - 0x8056b848 ]
   +0x024 HandleContentionEvent : _EX_PUSH_LOCK
   +0x028 DebugInfo        : (null)
   +0x02c ExtraInfoPages   : 0
   +0x030 FirstFree        : 0x570
   +0x034 LastFree         : 0xc58
   +0x038 NextHandleNeedingPool : 0x1000
   +0x03c HandleCount      : 387
   +0x040 Flags            : 0
   +0x040 StrictFIFO       : 0y0

 보이십니까? UniqueProcessId를 0x08번지에 가지고 있는걸 볼 수 있습니다.

 이를 이용하여 핸들 테이블 Offset을 구할 수 있고, 그 오프셋으로부터의 Delta값을 이용하여 다른 필드도 구할 수 있을겁니다.(아까도 당부했지만, Delta값을 이용한 방법은 여러 OS에서 테스트를 해봐야합니다.)

이를 이용한 C 코드는 아래와 같습니다.

VOID CalcHandleTableOffset(VOID)
{
 PEPROCESS Proc = IoGetCurrentProcess();
 HANDLE PID = PsGetCurrentProcessId();
 PVOID GuessHandleTable = NULL;
 int i = 0;
 for(i = 0; i < PAGE_SIZE; i+=4)
 {
  GuessHandleTable = *(PVOID *)((PCHAR)Proc + i);
  if(MmIsAddressValid(GuessHandleTable))
  {
   if(*(PHANDLE)((PCHAR)GuessHandleTable + 0x08) == PID)
   {
    HANDLE_TABLE_OFFSET = i;
    return;
   }
  }
 }
}

 

 4. ActiveProcessLinks

 UniqueProcessId Offset + 4를 하면 ActiveProcessLinks Offset(LIST_ENTRY)가 나옵니다.

 이는 NT 계열에서는 모두 동일(NT4~Vista에서 필자가 확인)하기 때문에, 사용해도 됩니다.

 

 ActiveProcessLinks_Offset = PID_OFFSET + 4;

 

 5. DebugPort

 주로 프로세스가 디버깅중인지 커널단에서 강력하게 확인할 때 사용되어집니다.

 디버거가 활성화 되어있다면 이 값은 NULL이 아닌 값이 되므로 체크할 때 사용됩니다.

 오프셋은 핸들 테이블의 오프셋 - 0x08으로 아래와 같이 쓸 수 있습니다.

 

 DEBUG_PORT_OFFSET = HANDLE_TABLE_OFFSET - 0x08;

 

 **********************************************************************

 

 이 부분은 논외이지만, 심심하므로(?) 내가 원하는 Process의 PEPROCESS를 얻어오는 방법에 대해서 간단하게 생각해봅시다.

 우선 Native API를 이용하는 방법으로 PsLookupProcessByProcessId()를 이용해볼 수 있습니다. 이 함수는 PspCidTable에서 Process를 찾아내어 반환하는 역할을 합니다. 따라서 가장 저 수준의 역할을 하는 Native API로 이를 후킹하면 프로세스 접근을 강력하게 막을수도 있을것입니다. 또한 기존 핸들로부터 얻을수도 있을겁니다. 이는 NtDuplicateObject와 NtQuerySystemInformation을 이용하면 됩니다. 그 후, ObReferenceObjectByHandle을 하면 얻을 수 있을겁니다.

(윈도우에서는 CSRSS.exe가 모든 프로세스에 대한 핸들을 하나씩 가지고 있으므로 이를 이용하면 NtOpenProcess를 거치지 않고도 열 수 있습니다.)

 이번엔 커널 구조체를 이용하는 방법들로, 우선 ActiveProcessLinks를 Traversing 하는 방법이 있겠습니다. 가장 보편적인 방법이며, 윈도우가 이 방법을 사용하고 있습니다. 또한 보통 프로세스를 숨길때는 이를 끊어서 목록에서 안보이게 합니다.

이 것 외에도 Handle Table의 Link를 이용하거나, 직접 PspCidTable의 포인터를 구해내어(PsLookupProcessByProcessId를 traverse) 직접 찾아내거나, 그 테이블을 조작해서 핸들을 얻어낸 후 ObReferenceObjectByHandle()을 한다면 PEPROCESS를 얻을 수 있을겁니다.

Posted by skensita
Programming/Kernel / Driver2008. 12. 3. 11:38

//
// Executive Process (EPROCESS)
//
typedef struct _EPROCESS
{
    KPROCESS Pcb;
    EX_PUSH_LOCK ProcessLock;
    LARGE_INTEGER CreateTime;
    LARGE_INTEGER ExitTime;
    EX_RUNDOWN_REF RundownProtect;
    HANDLE UniqueProcessId;
    LIST_ENTRY ActiveProcessLinks;
    ULONG QuotaUsage[3];
    ULONG QuotaPeak[3];
    ULONG CommitCharge;
    ULONG PeakVirtualSize;
    ULONG VirtualSize;
    LIST_ENTRY SessionProcessLinks;
    PVOID DebugPort;
#if (NTDDI_VERSION >= NTDDI_LONGHORN)
    union
    {
        PVOID ExceptionPortData;
        ULONG ExceptionPortValue;
        UCHAR ExceptionPortState:3;
    };
#else
    PVOID ExceptionPort;
#endif
    PHANDLE_TABLE ObjectTable;
    EX_FAST_REF Token;
    ULONG WorkingSetPage;
#if (NTDDI_VERSION >= NTDDI_LONGHORN)
    EX_PUSH_LOCK AddressCreationLock;
    PETHREAD RotateInProgress;
#else
    FAST_MUTEX AddressCreationLock;

// FIXME: FAST_MUTEX for XP, KGUARDED_MUTEX for 2K3
    KSPIN_LOCK HyperSpaceLock;
#endif
    PETHREAD ForkInProgress;
    ULONG HardwareTrigger;
    MM_AVL_TABLE PhysicalVadroot;
    PVOID CloneRoot;
    ULONG NumberOfPrivatePages;
    ULONG NumberOfLockedPages;
    PVOID *Win32Process;
    struct _EJOB *Job;
    PVOID SectionObject;
    PVOID SectionBaseAddress;
    PEPROCESS_QUOTA_BLOCK QuotaBlock;
    PPAGEFAULT_HISTORY WorkingSetWatch;
    PVOID Win32WindowStation;
    HANDLE InheritedFromUniqueProcessId;
    PVOID LdtInformation;
    PVOID VadFreeHint;
    PVOID VdmObjects;
    PVOID DeviceMap;
#if (NTDDI_VERSION >= NTDDI_LONGHORN)
    ULONG AlpcPagedPoolQuotaCache;
    PVOID EtwDataSource;
    PVOID FreeTebHint;
#else
    PVOID Spare0[3];
#endif
    union
    {
        HARDWARE_PTE_X86 PagedirectoryPte;
        ULONGLONG Filler;
    };
    ULONG Session;
    CHAR ImageFileName[16];
    LIST_ENTRY JobLinks;
    PVOID LockedPagesList;
    LIST_ENTRY ThreadListHead;
    PVOID SecurityPort;
    PVOID PaeTop;
    ULONG ActiveThreads;
#if (NTDDI_VERSION >= NTDDI_LONGHORN)
    ULONG ImagePathHash;
#else
    ACCESS_MASK GrantedAccess;
#endif
    ULONG DefaultHardErrorProcessing;
    NTSTATUS LastThreadExitStatus;
    struct _PEB* Peb;
    EX_FAST_REF PrefetchTrace;
    LARGE_INTEGER ReadOperationCount;
    LARGE_INTEGER WriteOperationCount;
    LARGE_INTEGER OtherOperationCount;
    LARGE_INTEGER ReadTransferCount;
    LARGE_INTEGER WriteTransferCount;
    LARGE_INTEGER OtherTransferCount;
    ULONG CommitChargeLimit;
    ULONG CommitChargePeak;
    PVOID AweInfo;
    SE_AUDIT_PROCESS_CREATION_INFO SeAuditProcessCreationInfo;
    MMSUPPORT Vm;
    LIST_ENTRY MmProcessLinks;
    ULONG ModifiedPageCount;
#if (NTDDI_VERSION >= NTDDI_LONGHORN)
    union
    {
        struct
        {
            ULONG JobNotReallyActive:1;
            ULONG AccountingFolded:1;
            ULONG NewProcessReported:1;
            ULONG ExitProcessReported:1;
            ULONG ReportCommitChanges:1;
            ULONG LastReportMemory:1;
            ULONG ReportPhysicalPageChanges:1;
            ULONG HandleTableRundown:1;
            ULONG NeedsHandleRundown:1;
            ULONG RefTraceEnabled:1;
            ULONG NumaAware:1;
            ULONG ProtectedProcess:1;
            ULONG DefaultPagePriority:3;
            ULONG ProcessDeleteSelf:1;
            ULONG ProcessVerifierTarget:1;
        };
        ULONG Flags2;
    };
#else
    ULONG JobStatus;
#endif
    union
    {
        struct
        {
            ULONG CreateReported:1;
            ULONG NoDebugInherit:1;
            ULONG ProcessExiting:1;
            ULONG ProcessDelete:1;
            ULONG Wow64SplitPages:1;
            ULONG VmDeleted:1;
            ULONG OutswapEnabled:1;
            ULONG Outswapped:1;
            ULONG ForkFailed:1;
            ULONG Wow64VaSpace4Gb:1;
            ULONG AddressSpaceInitialized:2;
            ULONG SetTimerResolution:1;
            ULONG BreakOnTermination:1;
#if (NTDDI_VERSION >= NTDDI_LONGHORN)
            ULONG DeprioritizeViews:1;
#else
            ULONG SessionCreationUnderway:1;
#endif
            ULONG WriteWatch:1;
            ULONG ProcessInSession:1;
            ULONG OverrideAddressSpace:1;
            ULONG HasAddressSpace:1;
            ULONG LaunchPrefetched:1;
            ULONG InjectInpageErrors:1;
            ULONG VmTopDown:1;
            ULONG ImageNotifyDone:1;
            ULONG PdeUpdateNeeded:1;
            ULONG VdmAllowed:1;
            ULONG SmapAllowed:1;
#if (NTDDI_VERSION >= NTDDI_LONGHORN)
            ULONG ProcessInserted:1;
#else
            ULONG CreateFailed:1;
#endif
            ULONG DefaultIoPriority:3;
#if (NTDDI_VERSION >= NTDDI_LONGHORN)
            ULONG SparePsFlags1:2;
#else
            ULONG Spare1:1;
            ULONG Spare2:1;
#endif
        };
        ULONG Flags;
    };
    NTSTATUS ExitStatus;
#if (NTDDI_VERSION >= NTDDI_LONGHORN)
    USHORT Spare7;
#else
    USHORT NextPageColor;
#endif
    union
    {
        struct
        {
            UCHAR SubSystemMinorVersion;
            UCHAR SubSystemMajorVersion;
        };
        USHORT SubSystemVersion;
    };
    UCHAR PriorityClass;
    MM_AVL_TABLE VadRoot;
    ULONG Cookie;
} EPROCESS, *PEPROCESS;


Documented By :

ReactOS


P.S : 이 EPROCESS 구조체는 Windows 버전에 따라서 호환이 될수도 있고, 안될수도 있습니다

Posted by skensita
Programming/Kernel / Driver2008. 12. 3. 11:37

제가 ActiveProcessLinks의 오프셋을 어떻게하면 구할수 있을까 생각하다가

아주 좋은 방법이 떠올랐습니다 +_+


EPROCESS 구조체의 정보를 보면서 하나하나 알아보죠 ㅇ_ㅇ

다음은 각 Windows별로 EPROCESS 구조체 정보를 생략한 것입니다.

(※이 구조체 정보는 타사이트서 퍼왔습니다)


이 방법은 최소한(?) 다음 Windows에서 성공적으로 동작합니다:

Windows 2000 SP4

Windows XP SP2

Windows Server 2003 No SP

Windows Server 2003 SP1

우선 Windows 2000부터 봅시다.

Windows 2000 SP4

   +0x000 Pcb              : _KPROCESS
   +0x06c ExitStatus       : Int4B
   +0x070 LockEvent        : _KEVENT
   +0x080 LockCount        : Uint4B
   +0x088 CreateTime       : _LARGE_INTEGER
   +0x090 ExitTime         : _LARGE_INTEGER
   +0x098 LockOwner        : Ptr32 _KTHREAD
   +0x09c UniqueProcessId  : Ptr32 Void
   +0x0a0 ActiveProcessLinks : _LIST_ENTRY ---->ActiveProcessLinks
   +0x0a8 QuotaPeakPoolUsage : [2] Uint4B
ActiveProcessLinks의 오프셋은 0x0a0.

UniqueProcessId과 QuotaPeakPoolUsage 사이에 위치해 있군요.


다음은 Windows XP SP2......

Windows XP SP2

   +0x000 Pcb              : _KPROCESS
   +0x06c ProcessLock      : _EX_PUSH_LOCK
   +0x070 CreateTime       : _LARGE_INTEGER
   +0x078 ExitTime         : _LARGE_INTEGER
   +0x080 RundownProtect   : _EX_RUNDOWN_REF
   +0x084 UniqueProcessId  : Ptr32 Void
   +0x088 ActiveProcessLinks : _LIST_ENTRY ---->ActiveProcessLinks
   +0x090 QuotaUsage       : [3] Uint4B
ActiveProcessLinks의 오프셋은 0x088.

UniqueProcessId과 QuotaUsage 사이에 위치해 있군요.


이번에는 Windows Server 2003 No SP

Windows Server 2003 No SP

   +0x000 Pcb              : _KPROCESS
   +0x06c ProcessLock      : _EX_PUSH_LOCK
   +0x070 CreateTime       : _LARGE_INTEGER
   +0x078 ExitTime         : _LARGE_INTEGER
   +0x080 RundownProtect   : _EX_RUNDOWN_REF
   +0x084 UniqueProcessId  : Ptr32 Void
   +0x088 ActiveProcessLinks : _LIST_ENTRY ---->ActiveProcessLinks
   +0x090 QuotaUsage       : [3] Uint4B
ActiveProcessLinks의 오프셋은 0x088.

UniqueProcessId과 QuotaUsage 사이에 위치해 있군요.


마지막으로, Windows Server 2003 SP1을 알아봅시다.

Windows Server 2003 SP1

   +0x000 Pcb              : _KPROCESS
   +0x078 ProcessLock      : _EX_PUSH_LOCK
   +0x080 CreateTime       : _LARGE_INTEGER
   +0x088 ExitTime         : _LARGE_INTEGER
   +0x090 RundownProtect   : _EX_RUNDOWN_REF
   +0x094 UniqueProcessId  : Ptr32 Void
   +0x098 ActiveProcessLinks : _LIST_ENTRY ---->ActiveProcessLinks
   +0x0a0 QuotaUsage       : [3] Uint4B
ActiveProcessLinks의 오프셋은 0x098.

UniqueProcessId과 QuotaUsage 사이에 위치해 있군요.


그런데, 위에서 자세히 보면 ActiveProcessLinks의 오프셋은, UniqueProcessId+4 라는것을 알수가 있군요 +_+

실제로 구해 볼까요?

Windows 2000 SP4 : 0x09c+0x004=0x0a0

Windows XP SP2 : 0x084+0x004=0x088

Windows Server 2003 No SP : 0x084+0x004=0x088(Windows XP SP2와 동일)

Windows Server 2003 SP1 : 0x094+0x004=0x098


결과적으로,


ActiveProcessLinks Offset = UniqueProcessId Offset + 4

ActiveProcessLinks=(PLIST_ENTRY)((char *)EPROCESS 구조체 포인터+UniqueProcessId Offset+4)


음......

과연 다른 운영체제에서는 잘 동작하는지 궁금하네요 +_+

Posted by skensita