Memory analysis

Searching for Processes and Threads

Searching for highly variable structures like processes and threads is a difficult task. The set of criteria must be carefully chosen. On one hand it should limit the amount of false-positives to a minimum while on the other hand it must not wrongly exclude valid objects from the result. This article documents the set of criteria I have implemented in PTfinder v0.2.00.

The first criterion is alignment. EPROCESS and ETHREAD structures reside in memory pool allocations. According to the documentation of ExAllocatePoolWithTag in the Microsoft Developer Network "memory allocations of less than PAGE_SIZE are not necessarily page-aligned but are aligned on an 8-byte boundary". So when working on a plain copy of the physical memory it'll suffice to check every 8th byte.

As a matter of fact it'll even work with VMware saved sessions, full memory dumps produced by Microsoft's debuggers and the Windows crashdump facility. Their file headers are a multiple of 8 bytes in size so they don't interfere with the search. In case of need PTfinder's --skip option allows to adjust to other file formats.

At the current position either a DISPATCHER_HEADER of the process or thread type must begin. As pointed out in my article about the dispatcher header, only two bytes, the structure's members Type and Size are evaluated. The type codes haven't been changed over all the versions from Windows 2000 up to and including Windows Vista. SIze is at least constant for a certain Type and version of Microsoft Windows. Two other members, Absolute and Inserted were observed to contain the value 0x00 only. As I couldn't find any documentation explaining this behavior, I decided to avoid the unnecessary risk and omitted both from the search pattern. I didn't notice any increase in false-postives, though.

Next there are some additional formal tests applied. These are also based on the DISPATCHER_HEADER structure, but on different types. Again only the Type and Size members will be evaluated.

For processes there must be a SYNCHRONIZATION_EVENT at offsets 0x70, 0x13c and 0x164. The first one is not required if UniqueProcessId == 0 (which is the Idle process). Please note that all offsets given in this article apply to Microsoft Windows 2000 Service Pack 4 only. I will post offsets for other versions soon. In the meanwhile please look up the proper offsets on your own from the documentation of EPROCESS and ETHREAD structures posted in this blog.

For threads a NOTIFICATION_TIMER is required at offset 0x0e8 along with two SEMAPHOREs at 0x190 and 0x1e8. The later is not required if Cid.UniqueProcess == 0 (which is again the Idle process).

Finaly some checks of the contents are applied.

PageDirectoryBase of the EPROCESS structure points to the process' page directory, a table which is used to turn virtual addresses into physical ones. If this member does not contain a valid value then most likely the whole EPROCESS structure is invalid. At first PageDirectoryBase must not be NULL. At second, it must point to the beginning of a memory page, which is 4096 bytes (or 0x1000 in hex) in size. Hence the criterion is PageDirectoryBase mod 0x1000 == 0.

ThreadListEntry contains two pointers, Flink and Blink, to ETHREAD structures. These pointers contain virtual addresses. As all the control structures reside in kernel memory, their virtual addresses must be above the border at 0x80000000 (0xc0000000 for systems booted with the /3GB switch).

In a similiar way a thread's control structure member ThreadsProcess will point back to an EPROCESS structure. The address must be above the "border" as well. Finally the thread's StartAddress must not be NULL. Both rules do not apply to the Idle thread (Cid.UniqueProcess == 0).

Deutsch

Deutschsprachige Ausgabe

Categories

Subscribe

Imprint

This blog is a project of
Andreas Schuster
Im Äuelchen 45
D-53177 Bonn
Germany
impressum@forensikblog.de

Copyright © 2005-2010 by
Andreas Schuster
All rights reserved.