# bug in Process Explorer (a gift for malware)

years ago I found a bug in Process Explorer tool, written by Mark Russinovich. well, not a bug, just misfeature :) Process Explorer tries to determine the start address of a thread, but does this wrong and under certain conditions gives us an incorrect result. I sent a report to Mark, but had got no answer and the bug is still unfixed.

to demonstrate this bug (well, not a bug, just misfeature) I wrote a simple program http://nezumi.org.ru/souriz/va_thread.zip), creating two threads:
the first one – is a normal thread, created by CreateThread API call, the second one – is malware-like thread: program allocates memory on the heap, copies malicious code to the allocated block, and calls Create[Remote]Thread. to simplificate things va_thread.c uses CreateThread, creating a malware-like thread inside its own address space.
* * * va_thread.c doesn’t not inject code into any other process! * * *

thus, we have three threads:
1) main process-thread;
2) “fair” thread, created by CreateThread;
3) “malware-like” thread, created by Create[Remote]Thread on the heap;

Process Explorer correctly determines the start addresses of the two first threads, but stumbles over third, claims that the address is: KERNEL32.DLL+B700h, what is definitely wrong! actual start address should look like 003F0000h (the exact address depends on the heap
allocator behavior).

I don’t know how Process Explorer determines the start address of a thread, but I found my own simply and reliable way to do that. I discovered that the start address of any thread is stored at the bottom of the user stack in the second or third dword, followed by lpParameter. this trick works under Windows 2000 and Server 2003, I didn’t check it out under XP yet, but I hope, it’ll be the same (is there someone who wants to check it under XP? plz, tell me what
you’ll get).

I wrote quick-n-dirty utility proc_list.c correctly determines
the start addresses in the most cases and discovers malware, injecting shell-code into process by allocating memory via VirtualAllocEx, or loading malicious dll via creating remote thread and passing address of the LoadLibraryA/W with name of malicious dll in lpParameter.

* if malicious code is on the heap, the start address of the malware-thread belongs to MEM_PRIVATE block (instead of MEM_IMAGE like normal exe/dll does);

* if malicious code is injected via dll, the start address of the malware-thread matches the address of LoadLibraryA/W.

I checked this method on a quite big malware collection and got a good result.

$proc_list.exe > out.txt
* * * PROCESS INFO * * *
—————————————————–
szExeFile : va_thread.exe
cntUsage : 0h
th32ProcessID : 58Ch ; <- we need to know ProcessID
th32DefaultHeapID : 0h
th32ModuleID : 0h
cntThreads : 3h
th32ParentProcessID : 668h
pcPriClassBase : 8h
dwFlags : 0h
–thr————————————————
cntUsage : 0h
th32ThreadID : 6B4h
th32OwnerProcessID : 58Ch ; <- va_thread.exe
tpBasePri : 8h
tpDeltaPri : 0h
dwFlags : 0h
handle : 3D8h
ESP : 0012FF84h
EIP : 77F883A3h
start address : 00401010h ; <- main thread
point to args : 00000000h
type : MEM_IMAGE ; <- normal thread
[0012FFF0h: 00000000 00000000 00401010 00000000]

–thr————————————————
cntUsage : 0h
th32ThreadID : 668h
th32OwnerProcessID : 58Ch ; <- va_thread.exe
tpBasePri : 8h
tpDeltaPri : 0h
dwFlags : 0h
handle : 3D8h
ESP : 003EFF80h
EIP : 77F883A3h
start address : 00401000h ; <- “fair” thread
point to args : 0012FFC8h
type : MEM_IMAGE ; <- normal thread
[003EFFF0h: 00000000 00401000 0012FFC8 00000000]

–thr————————————————
cntUsage : 0h
th32ThreadID : 764h
th32OwnerProcessID : 58Ch ; <- va_thread.exe
tpBasePri : 8h
tpDeltaPri : 0h
dwFlags : 0h
handle : 3D8h
ESP : 0050FF80h
EIP : 77F883A3h
start address : 003F0000h ; <- heap block
point to args : 0012FFC8h
type : MEM_PRIVATE ; <- malware thread
[0050FFF0h: 00000000 003F0000 0012FFC8 00000000]

Advertisements

One Response to “# bug in Process Explorer (a gift for malware)”

  1. Seems like it works on XP as well. I’ve written a simple python script for Immunity Debugger which utilizes the idea. Tested it on XP SP2 and all the results it was showing were pretty much correct. Nice observation. Here is the script.

    #!/usr/bin/env python

    __VERSION__ = ‘1.0’

    import immlib
    import array
    import struct

    def main():
    imm=immlib.Debugger()
    allthreads=imm.getAllThreads()

    for thread in allthreads:
    stackTop = thread.getStackTop()

    possibleEntry = imm.readLong(stackTop-8)
    if possibleEntry == 0:
    possibleEntry = imm.readLong(stackTop-0xC)

    imm.Log(“Thread %.08X | Stack top: %.08X | Entry point: %.08X” % (thread.getId(), stackTop, possibleEntry))

    if __name__==”__main__”:
    print “This module is for use within Immunity Debugger only”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: