2

Problem

I'm trying to reverse engineer some Windows kernel routines on a Windows 7 x86 (Home Basic) VM with kd. I have already reversed KeInitializeDpc by disassambling it. The problem with KeReadyThread is that I can't tell where the third and the fourth mov are pointing to,as the function doesn't seem to have any argument or anything that can help me.

This is the code

lkd> uf KeReadyThread  
nt!KeReadyThread:
829080f6 8bff            mov     edi,edi  
829080f8 56              push    esi  
829080f9 8bf0            mov     esi,eax  
829080fb 8b4650          mov     eax,dword ptr [esi+50h]  
829080fe 8b4874          mov     ecx,dword ptr [eax+74h]  
82908101 f6c107          test    cl,7  
82908104 7409            je      nt!KeReadyThread+0x19 (8290810f)  

nt!KeReadyThread+0x10:  
82908106 e8e7cef6ff      call    nt!KiInSwapSingleProcess (82874ff2)  
8290810b 84c0            test    al,al  
8290810d 7505            jne     nt!KeReadyThread+0x1e (82908114)  

nt!KeReadyThread+0x19:  
8290810f e87c70feff      call    nt!KiFastReadyThread (828ef190)  

nt!KeReadyThread+0x1e:  
82908114 5e              pop     esi  
82908115 c3              ret

_KTHREAD structure

lkd> dt _KTHREAD
nt!_KTHREAD
   +0x000 Header           : _DISPATCHER_HEADER
   +0x010 CycleTime        : Uint8B
   +0x018 HighCycleTime    : Uint4B
   +0x020 QuantumTarget    : Uint8B
   +0x028 InitialStack     : Ptr32 Void
   +0x02c StackLimit       : Ptr32 Void
   +0x030 KernelStack      : Ptr32 Void
   +0x034 ThreadLock       : Uint4B
   +0x038 WaitRegister     : _KWAIT_STATUS_REGISTER
   +0x039 Running          : UChar
   +0x03a Alerted          : [2] UChar
   +0x03c KernelStackResident : Pos 0, 1 Bit
   +0x03c ReadyTransition  : Pos 1, 1 Bit
   +0x03c ProcessReadyQueue : Pos 2, 1 Bit
   +0x03c WaitNext         : Pos 3, 1 Bit
   +0x03c SystemAffinityActive : Pos 4, 1 Bit
   +0x03c Alertable        : Pos 5, 1 Bit
   +0x03c GdiFlushActive   : Pos 6, 1 Bit
   +0x03c UserStackWalkActive : Pos 7, 1 Bit
   +0x03c ApcInterruptRequest : Pos 8, 1 Bit
   +0x03c ForceDeferSchedule : Pos 9, 1 Bit
   +0x03c QuantumEndMigrate : Pos 10, 1 Bit
   +0x03c UmsDirectedSwitchEnable : Pos 11, 1 Bit
   +0x03c TimerActive      : Pos 12, 1 Bit
   +0x03c Reserved         : Pos 13, 19 Bits
   +0x03c MiscFlags        : Int4B
   +0x040 ApcState         : _KAPC_STATE
   +0x040 ApcStateFill     : [23] UChar
   +0x057 Priority         : Char
   +0x058 NextProcessor    : Uint4B
   +0x05c DeferredProcessor : Uint4B
   +0x060 ApcQueueLock     : Uint4B
   +0x064 ContextSwitches  : Uint4B
   +0x068 State            : UChar
   +0x069 NpxState         : Char
   +0x06a WaitIrql         : UChar
   +0x06b WaitMode         : Char
   +0x06c WaitStatus       : Int4B
   +0x070 WaitBlockList    : Ptr32 _KWAIT_BLOCK
   +0x074 WaitListEntry    : _LIST_ENTRY
   +0x074 SwapListEntry    : _SINGLE_LIST_ENTRY
   +0x07c Queue            : Ptr32 _KQUEUE
   +0x080 WaitTime         : Uint4B
   +0x084 KernelApcDisable : Int2B
   +0x086 SpecialApcDisable : Int2B
   +0x084 CombinedApcDisable : Uint4B
   +0x088 Teb              : Ptr32 Void
   +0x090 Timer            : _KTIMER
   +0x0b8 AutoAlignment    : Pos 0, 1 Bit
   +0x0b8 DisableBoost     : Pos 1, 1 Bit
   +0x0b8 EtwStackTraceApc1Inserted : Pos 2, 1 Bit
   +0x0b8 EtwStackTraceApc2Inserted : Pos 3, 1 Bit
   +0x0b8 CalloutActive    : Pos 4, 1 Bit
   +0x0b8 ApcQueueable     : Pos 5, 1 Bit
   +0x0b8 EnableStackSwap  : Pos 6, 1 Bit
   +0x0b8 GuiThread        : Pos 7, 1 Bit
   +0x0b8 UmsPerformingSyscall : Pos 8, 1 Bit
   +0x0b8 ReservedFlags    : Pos 9, 23 Bits
   +0x0b8 ThreadFlags      : Int4B
   +0x0bc ServiceTable     : Ptr32 Void
   +0x0c0 WaitBlock        : [4] _KWAIT_BLOCK
   +0x120 QueueListEntry   : _LIST_ENTRY
   +0x128 TrapFrame        : Ptr32 _KTRAP_FRAME
   +0x12c FirstArgument    : Ptr32 Void
   +0x130 CallbackStack    : Ptr32 Void
   +0x130 CallbackDepth    : Uint4B
   +0x134 ApcStateIndex    : UChar
   +0x135 BasePriority     : Char
   +0x136 PriorityDecrement : Char
   +0x136 ForegroundBoost  : Pos 0, 4 Bits
   +0x136 UnusualBoost     : Pos 4, 4 Bits
   +0x137 Preempted        : UChar
   +0x138 AdjustReason     : UChar
   +0x139 AdjustIncrement  : Char
   +0x13a PreviousMode     : Char
   +0x13b Saturation       : Char
   +0x13c SystemCallNumber : Uint4B
   +0x140 FreezeCount      : Uint4B
   +0x144 UserAffinity     : _GROUP_AFFINITY
   +0x150 Process          : Ptr32 _KPROCESS
   +0x154 Affinity         : _GROUP_AFFINITY
   +0x160 IdealProcessor   : Uint4B
   +0x164 UserIdealProcessor : Uint4B
   +0x168 ApcStatePointer  : [2] Ptr32 _KAPC_STATE
   +0x170 SavedApcState    : _KAPC_STATE
   +0x170 SavedApcStateFill : [23] UChar
   +0x187 WaitReason       : UChar
   +0x188 SuspendCount     : Char
   +0x189 Spare1           : Char
   +0x18a OtherPlatformFill : UChar
   +0x18c Win32Thread      : Ptr32 Void
   +0x190 StackBase        : Ptr32 Void
   +0x194 SuspendApc       : _KAPC
   +0x194 SuspendApcFill0  : [1] UChar
   +0x195 ResourceIndex    : UChar
   +0x194 SuspendApcFill1  : [3] UChar
   +0x197 QuantumReset     : UChar
   +0x194 SuspendApcFill2  : [4] UChar
   +0x198 KernelTime       : Uint4B
   +0x194 SuspendApcFill3  : [36] UChar
   +0x1b8 WaitPrcb         : Ptr32 _KPRCB
   +0x194 SuspendApcFill4  : [40] UChar
   +0x1bc LegoData         : Ptr32 Void
   +0x194 SuspendApcFill5  : [47] UChar
   +0x1c3 LargeStack       : UChar
   +0x1c4 UserTime         : Uint4B
   +0x1c8 SuspendSemaphore : _KSEMAPHORE
   +0x1c8 SuspendSemaphorefill : [20] UChar
   +0x1dc SListFaultCount  : Uint4B
   +0x1e0 ThreadListEntry  : _LIST_ENTRY
   +0x1e8 MutantListHead   : _LIST_ENTRY
   +0x1f0 SListFaultAddress : Ptr32 Void
   +0x1f4 ThreadCounters   : Ptr32 _KTHREAD_COUNTERS
   +0x1f8 XStateSave       : Ptr32 _XSTATE_SAVE

Solution

The only thing that came to my mind as a possible solution is to run the code and see what the registers point to,but on top of not knowing whether it is correct or not, I don't know how to do it. As you may have guessed I'm pretty inexperienced.

3 Answers 3

1

Obviously, KeReadyThread has a single argument passed to it in the eax register.

To actually reverse your way to that argument's structure/type you'll need to do quite a bit of reverse engineering.

Luckily for you, the guys at ReactOS already did that for you. If you take a look at that link, you'll see ReactOS's implementation of KeReadyThread accepting a single parameter of the structure PKTREAD.

This is KeReadyThread's code from ReactOS:

113 VOID
114 NTAPI
115 KeReadyThread(IN PKTHREAD Thread)
116 {
117     KIRQL OldIrql;
118     ASSERT_THREAD(Thread);
119     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
120 
121     /* Lock the Dispatcher Database */
122     OldIrql = KiAcquireDispatcherLock();
123 
124     /* Make the thread ready */
125     KiReadyThread(Thread);
126 
127     /* Unlock dispatcher database */
128     KiReleaseDispatcherLock(OldIrql);
129 }

And specifically, that dereference you're seeing at the top of the assembly you shared is the implementation of ASSERT_THREAD:

#define ASSERT_THREAD (object) ASSERT((((object)->Header.Type & KOBJECT_TYPE_MASK) == ThreadObject))
6
  • This sort of confused me even further: why does the command i used { lkd> uf KeInitializeDpc } disassable ASSERT_THREAD ? . I thought I disassembled the function you posted. I'm taking these kernel routines from a book and they are meant to introduce people to re, am I supposed to reverse engineer the arguments too or is it ok to look online ?
    – shxdow
    Commented Oct 14, 2016 at 14:05
  • ASSERT_THREAD is a macro, not a function. Macros are a C preprocessor constructs. the C preprocessor does (for the sake of this discussion) a simple search&replace before the C compiler starts working on the code. Thus for all intents and purposes, that code is part of that function.
    – NirIzr
    Commented Oct 14, 2016 at 17:58
  • @Nirlzr I understand what a macro is, but this doesn't answer my question: why is the macro even brought in. What I don't understand is, why by trying to disassemble the whole function by using it's name, I get a part of it that has a different name too. If my logic isn't flawed,in order to have as output ASSERT_THREAD I should type lkd> uf ASSERT_THREAD not KeReadyThread
    – shxdow
    Commented Oct 14, 2016 at 20:01
  • Alright. So there c compiler doesn't see any macros. Macros are translated before the compilation part so there's no ASSERT_THREAD object as far as the compiler/debbuger is aware.
    – NirIzr
    Commented Oct 14, 2016 at 20:22
  • Thank you for pointing out that I'm not disassembling KeReadyThread,but what I really can't figure out is: why is it even happening. Why in the snippet I posted, I can only see ASSERT_THREAD and not the rest of the code
    – shxdow
    Commented Oct 14, 2016 at 20:37
1

This Thread Popped up Due To Recent Activity

Quoting from The Accepted Answer An Answer By Nirlzr

And specifically, that dereference you're seeing at the top of the assembly you shared is the implementation of ASSERT_THREAD:

the Disassembly and Derefernces does not belong to ASSERT_THREAD

the derefernces check the StackCount memeber of _KAPC_STATE.Process.StackCount

kd> dt nt!_ETHREAD Tcb.ApcState.Process->StackCount.StackCount
   +0x000 Tcb                                         : 
      +0x040 ApcState                                    : 
         +0x010 Process                                     : 
            +0x074 StackCount                                  : 
               +0x000 StackCount                                  : Pos 3, 29 Bits  

googling geoffrey chappel _KTHREAD will provide more information about the overlay and the Priortity member a char which is fitted inside KAPC_STATE for x86

0

The third move is referring to a member inside the Thread argument (shown in the ReactOS source linked by Nirlzr) which is at offset +0x50. As seen in the _KTHREAD struct there's nothing at offset +0x50 and the 2 closest members we have at that offset are ApcState and ApcStateFill. Looking further into ApcStateFill is pointless as it is just a string and would render the fourth move pointless, however ApcState is interesting as it is a _KAPC_STATE struct. Interestingly enough, ApcState has a member at +0x10 which is called Process which works accordingly with the third move where we move a member value at offset +[0x40 + 0x10]. The fourth move refers to a pointer to another struct of _KPROCESS at the Process member earlier discussed in the _KAPC_STATE struct. This pointer to a _KPROCESS + 0x70 gives us the member StackCount. And in the end, cl ends up with the value at the member StackCount which will be compared with 7.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.