Как получить контекст ядерных потоков?

Тема в разделе "WASM.NT.KERNEL", создана пользователем 0x56, 2 фев 2008.

  1. 0x56

    0x56 New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2008
    Сообщения:
    63
    1. PsGetContextThread выдает нули для EIP, EBP, ESP.

    2. Просмотрел KeTrapFrameToContext, KiEspFromTrapFrame в РеактОС, вроде все понятно, имеют значение следующие значения:

    ЕrapFrame->Eip
    TrapFrame->Ebp
    TrapFrame->TempEsp
    TrapFrame->HardwareEsp

    Хорошо, пробую перебирать потоки, получаю указатель на трэп, как там.
    Код (Text):
    1. TrapFrame = (PKTRAP_FRAME)((ULONG_PTR)(CurrentEthread + ulOffset_KTHREAD_InitialStack) - sizeof(FX_SAVE_AREA) - sizeof(KTRAP_FRAME));
    Только вот адреса непонятно куда показывают, не в ntoskrnl это точно и не в загруженные модули, сами адреса валидные.
    Делаю все на DISPATH_LEVEL с бряком, тут же смотрю в отладчике, все как полагается, все треды в свопе,
    EIP для всех отладчик показывает в nt!KiSwapContext+0x2f, далее stack trace показывает на прерванные функции из ntoskrnl.

    Так откуда же выдирает отладчик эту информацию?
     
  2. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    РеактОС не смотри - там, бывает, по-другому реализовано.
    Пробовал PsGetContextThread( .., .., KernelMode ) ? У меня вроде выдавало.
     
  3. 0x56

    0x56 New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2008
    Сообщения:
    63
    Great
    у меня нет
    кстати пробовал твой эксперементальный метод с DbgEip и APC, тоже не ненормальные адреса и нули чаще, но ты же сам там поток создавал
    пробегаюсь по потокам ручками, в этом нет подвоха?
    Код (Text):
    1.    
    2.             // KPROCESS.ThreadListHead
    3.             ulOffset_KPROCESS_ThreadListHead = 0x50;
    4.  
    5.             // ETHREAD.ThreadListEntry
    6.             ulOffset_ETHREAD_ThreadListEntry = 0x22c;
    7.  
    8.             // ETHREAD.UniqueThread (4 bytes)
    9.             ulOffset_ETHREAD_UniqueThread    = 0x1f0;
    10.  
    11.             // KTHREAD.ThreadListEntry
    12.             ulOffset_KTHREAD_ThreadListEntry = 0x1b0;
    13.  
    14.             // KTHREAD.InitialStack (4 bytes)
    15.             ulOffset_KTHREAD_InitialStack    = 0x018;
    16.  
    17.             // KTHREAD.KernelStack (4 bytes)
    18.             ulOffset_KTHREAD_KernelStack     = 0x028;
    19.  
    20.             // KTHREAD.State (1 bytes)
    21.             ulOffset_KTHREAD_State           = 0x02d;
    22.  
    23.             // KTHREAD.TrapFrame (4 bytes)
    24.             ulOffset_KTHREAD_TrapFrame       = 0x134;
    25.  
    26.  
    27.     ULONG Count=0;
    28.     ULONG Tid = 0;
    29.     ULONG Eip = 0; 
    30.     ULONG xEsp[4];
    31.     ULONG x0Esp[4];
    32.  
    33.     NTSTATUS ns;
    34.     PLIST_ENTRY ThreadLink = NULL;
    35.     PEPROCESS PsSystemProcess = NULL;
    36.     ULONG CurrentEthread = 0, CurrentTable = 0, StartEthread = 0;
    37.     ULONG StackTrapFrameHardwareEsp = 0, TcbTrapFrameHardwareEsp = 0;
    38.  
    39.     KIRQL OldIrql;
    40.     KAPC_STATE ApcState;
    41.     PKTRAP_FRAME TrapFrame;
    42.     CONTEXT ctx = {CONTEXT_FULL};
    43.     BOOLEAN bCanCheckThread = TRUE;
    44.  
    45. PsSystemProcess = PsGetCurrentProcess();
    46.     if(PsSystemProcess)
    47.     {
    48.         //KeStackAttachProcess(PsSystemProcess, &ApcState);
    49.  
    50.         CurrentEthread = *((ULONG *)((ULONG)PsSystemProcess + ulOffset_KPROCESS_ThreadListHead));
    51.         CurrentEthread = CurrentEthread - ulOffset_KTHREAD_ThreadListEntry;
    52.         StartEthread = CurrentEthread;
    53.  
    54.         while(1)
    55.         {
    56.             if(Count>=1 && CurrentEthread == StartEthread) break;
    57.  
    58.             if((PETHREAD)CurrentEthread != PsGetCurrentThread())
    59.             {
    60.                 Tid = *(PULONG)(CurrentEthread + ulOffset_ETHREAD_UniqueThread);
    61.  
    62.                 DbgPrint("\n\t\t\tThread  ::  %d [0x%x], 0x%08x\n\n", Tid, Tid, CurrentEthread);
    63.  
    64.                 Eip = 0;
    65.                 bCanCheckThread = TRUE;
    66.                 memset(&ctx, 0, sizeof(ctx));
    67.  
    68.                 if(*NtBuildNumber == WINXP_BUILD_NUMBER) bCanCheckThread = ((KTHREAD_STATE)(CurrentEthread + ulOffset_KTHREAD_State) != Terminated);
    69.                
    70.                 if(bCanCheckThread)
    71.                 {      
    72.                     /*
    73.                     TrapFrame = (PKTRAP_FRAME)((ULONG)(CurrentEthread + ulOffset_KTHREAD_InitialStack) - sizeof(FX_SAVE_AREA) - sizeof(KTRAP_FRAME));
    74.  
    75.                     if(MmIsAddressValid(TrapFrame))
    76.                     //if(1)
    77.                     {                      
    78.                         //DbgPrint("TrapFrame->Eip  ::  0x%08x\n", TrapFrame->Eip);
    79.                         //DbgPrint("TrapFrame->DbgEip  ::  0x%08x\n", TrapFrame->DbgEip);
    80.  
    81.                         //if(((TrapFrame->SegCs & MODE_MASK) != KernelMode) || (TrapFrame->EFlags & EFLAGS_V86_MASK))
    82.                         //{
    83.                     //      DPRINT(("FIGOVyi trap\n", TrapFrame));
    84.                     //  }
    85.  
    86.                         //TrapFrame = (PKTRAP_FRAME)(CurrentEthread + ulOffset_KTHREAD_TrapFrame);
    87.  
    88.                         //DumpTrapFrame(TrapFrame);
    89.  
    90.                        
    91.  
    92.                         DumpTrapFrame(TrapFrame);
    93.  
    94.                         //TrapFrame = KiGetThreadTrapFrame((PUCHAR)(CurrentEthread + ulOffset_KTHREAD_InitialStack));
    95.  
    96.                         //DumpTrapFrame(TrapFrame);
    97.  
    98.                         //DbgBreakPoint();
    99.                     }
    100.                     else
    101.                     {
    102.                         DPRINT(("TrapFrame address [0x%08x] is not a valid virtual address\n", TrapFrame));
    103.                     }*/
    104.                    
    105.                    
    106.                     ns = PsGetContextThread((PETHREAD)CurrentEthread, &ctx, KernelMode);
    107.  
    108.                     if(NT_SUCCESS(ns))
    109.                     {
    110.                         //ctx.Esp = KiEspFromTrapFrame(TrapFrame);
    111.                         //ctx.Eip = TrapFrame->Eip;
    112.  
    113.                         DbgPrint("ctx.Eip = 0x%08x\n",ctx.Eip);
    114.                         DbgPrint("ctx.Ebp = 0x%08x\n",ctx.Ebp);
    115.                         DbgPrint("ctx.Esp = 0x%08x\n",ctx.Esp);
    116.                         DbgPrint("\n");
    117.  
    118.                         /*
    119.                         memset(xEsp, 0, sizeof(xEsp));
    120.                         memset(x0Esp, 0, sizeof(x0Esp));                   
    121.  
    122.                         //if(MmIsAddressValid((void*)ctx.Esp))
    123.                         if(1)
    124.                         {
    125.                             if(MmIsAddressValid((void*)ctx.Esp)) memcpy(xEsp, (void*)ctx.Esp, sizeof(xEsp));
    126.                            
    127.                             if(MmIsAddressValid((void*)(CurrentEthread + ulOffset_KTHREAD_KernelStack)))
    128.                             {
    129.                                 memcpy(x0Esp, (void*)(CurrentEthread + ulOffset_KTHREAD_KernelStack), sizeof(x0Esp));
    130.  
    131.                                 TrapFrame = KiGetThreadTrapFrame((PUCHAR)(CurrentEthread + ulOffset_KTHREAD_InitialStack));
    132.  
    133.                                 if(MmIsAddressValid(TrapFrame))
    134.                                 {
    135.                                     __try
    136.                                     {
    137.                                         StackTrapFrameHardwareEsp = TrapFrame->HardwareEsp;
    138.                                     }
    139.                                     __except(EXCEPTION_EXECUTE_HANDLER)
    140.                                     {
    141.                                         StackTrapFrameHardwareEsp = -2;
    142.                                     }
    143.                                 }
    144.                                 else
    145.                                 {
    146.                                     StackTrapFrameHardwareEsp = -3;
    147.                                 }
    148.  
    149.                                 if(MmIsAddressValid(TrapFrame))
    150.                                 {
    151.                                     __try
    152.                                     {
    153.                                         TcbTrapFrameHardwareEsp = TrapFrame->HardwareEsp;
    154.                                     }
    155.                                     __except(EXCEPTION_EXECUTE_HANDLER)
    156.                                     {
    157.                                         TcbTrapFrameHardwareEsp = -2;
    158.                                     }
    159.                                 }
    160.                                 else
    161.                                 {
    162.                                     TcbTrapFrameHardwareEsp = -3;
    163.                                 }                          
    164.                                
    165.                                 // Print ETHREAD*, EPROCESS*, KTRAP_FRAME* and EIP (if we have got it)
    166.  
    167.                                 DPRINT(
    168.                                             (
    169.                                                 "%d[%x], %08x, cs:eip %04x:%08x, "
    170.                                                 "StackTrapFrameHardwareEsp %08x, TcbTrapFrameHardwareEsp %08x, "
    171.                                                 "r0esp %08x, r3esp %08x, "
    172.                                                 "[r0esp]: %08x %08x %08x %08x, "
    173.                                                 "[r3esp]: %08x %08x %08x %08x\n",
    174.  
    175.                                                 Tid,Tid,
    176.                                                 (PETHREAD)CurrentEthread,
    177.                                                 ctx.SegCs, ctx.Eip,
    178.  
    179.                                                 StackTrapFrameHardwareEsp,TcbTrapFrameHardwareEsp,
    180.  
    181.                                                 (CurrentEthread + ulOffset_KTHREAD_KernelStack),
    182.                                                 ctx.Esp,
    183.  
    184.                                                 x0Esp[0],
    185.                                                 x0Esp[1],
    186.                                                 x0Esp[2],
    187.                                                 x0Esp[3],
    188.  
    189.                                                 xEsp[0],
    190.                                                 xEsp[1],
    191.                                                 xEsp[2],
    192.                                                 xEsp[3]
    193.                                             )
    194.                                         );
    195.                             }
    196.                             else
    197.                             {
    198.                                 DPRINT(("KThread->KernelStack [0x%08x] is not a valid address\n", (CurrentEthread + ulOffset_KTHREAD_KernelStack)));
    199.                             }
    200.                         }
    201.                         else
    202.                         {
    203.                             DPRINT(("ctx.Esp [0x%08x] is not a valid address in address space\n", ctx.Esp));
    204.                         }
    205.                         */
    206.                     }
    207.                     else
    208.                     {
    209.                         //DPRINT(("TrapFrame address [0x%08x] is not a valid virtual address\n", TrapFrame));
    210.                         DPRINT(("PsGetContextThread failed for [0x%08x] with status 0x%08x\n", (PETHREAD)CurrentEthread, ns));
    211.                     }
    212.                    
    213.                 }
    214.             }
    215.  
    216.             ThreadLink = (PLIST_ENTRY)(CurrentEthread + ulOffset_ETHREAD_ThreadListEntry);
    217.             CurrentEthread = (ULONG)ThreadLink->Flink;
    218.             CurrentEthread = CurrentEthread - ulOffset_ETHREAD_ThreadListEntry;
    219.  
    220.             Count++;
    221.         }
     
  4. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    а что возвращает PsGetContextThread?
     
  5. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    А если KPCR.TSS ?
     
  6. 0x56

    0x56 New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2008
    Сообщения:
    63
    почти для всех STATUS_SUCCESS
    для некоторых STATUS_UNSUCCESSFUL
     
  7. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Clerk
    я смотрел там нет ничего полезного.
     
  8. 0x56

    0x56 New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2008
    Сообщения:
    63
    с этими оффсетами то не разъименовал пару указателей
    и структуре CONTEXT оказывается надо скармливать флаги перед вызовом
    почему никто не заметил :)

    так или иначе PsGetContextThread выдает EIP==0xffffffff
    и представляет интерес именно получение контекстов на DISPATH_LEVEL
    PsGetContextThread не подходит

    вижу что где-то в стеке валяется этот адрес nt!KiSwapContext+0x2f
    скорее всего надо указатель на трэп-фрейм неправильный
     
  9. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    щас посмотрю на эту тему.
     
  10. TheDeath

    TheDeath New Member

    Публикаций:
    0
    Регистрация:
    20 июл 2003
    Сообщения:
    66
    Адрес:
    Russia,Новосибирск
    Поставь бряк на kthread.KernelStack и сразу увидишь,кто заполняет ktrapframe и что будет в стеке.Концы уходят в hal.dll
    Обычно это делает прерывание таймера на котором висит планировщик.
     
  11. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    да, когда поток теряет квант времени или когда входит в ожидание, заполняется KTRAP_FRAME, который можно вынуть из KernelStack.
     
  12. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    PspGetSetContextSpecialApc()
     
  13. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    Посмотрел, вроде при выполнении KiSwapContext состояние регистров процессора сохраняется в структуре KTRAP_FRAME в стеке, на который указывает ETHREAD.Tcb.KernelStack
     
  14. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Clerk

    собственно это и выражалось вот этим кодом в первом посте.
    аналогичный код можно найти в обаботчиков APC установки и снятия контекста.
     
  15. 0x56

    0x56 New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2008
    Сообщения:
    63
    Great
    это из РЕАКТОС, неправильный получается указатель
    а вот из твоего какого-то примера
    Код (Text):
    1. PKTRAP_FRAME KiGetThreadTrapFrame(PUCHAR pEThreadInitialStack)
    2. {
    3.     return (PKTRAP_FRAME)(pEThreadInitialStack - PSPALIGN_UP(sizeof(KTRAP_FRAME), KTRAP_FRAME_ALIGN) - SIZEOF_FX_SAVE_AREA);
    4. }
    - нормальный указатель на трэп в стеке, судя потому,что там для всех TrapFrame->Eip = 0xffffffff, туда и лезет PsGetContextThread
    видимо он действительно используется для юзермодных, для ядерных не обновляется
     
  16. 0x56

    0x56 New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2008
    Сообщения:
    63
    Код (Text):
    1. VOID
    2. KiInitializeContextThread (
    3.     IN PKTHREAD Thread,
    4.     IN PKSYSTEM_ROUTINE SystemRoutine,
    5.     IN PKSTART_ROUTINE StartRoutine OPTIONAL,
    6.     IN PVOID StartContext OPTIONAL,
    7.     IN PCONTEXT ContextRecord OPTIONAL
    8.     )
    9.  
    10. /*++
    11.  
    12. Routine Description:
    13.  
    14.     This function initializes the machine dependent context of a thread object.
    15.  
    16.     N.B. This function does not check the accessibility of the context record.
    17.          It is assumed the the caller of this routine is either prepared to
    18.          handle access violations or has probed and copied the context record
    19.          as appropriate.
    20.  
    21. Arguments:
    22.  
    23.     Thread - Supplies a pointer to a dispatcher object of type thread.
    24.  
    25.     SystemRoutine - Supplies a pointer to the system function that is to be
    26.         called when the thread is first scheduled for execution.
    27.  
    28.     StartRoutine - Supplies an optional pointer to a function that is to be
    29.         called after the system has finished initializing the thread. This
    30.         parameter is specified if the thread is a system thread and will
    31.         execute totally in kernel mode.
    32.  
    33.     StartContext - Supplies an optional pointer to an arbitrary data structure
    34.         which will be passed to the StartRoutine as a parameter. This
    35.         parameter is specified if the thread is a system thread and will
    36.         execute totally in kernel mode.
    37.  
    38.     ContextRecord - Supplies an optional pointer a context frame which contains
    39.         the initial user mode state of the thread. This parameter is specified
    40.         if the thread is a user thread and will execute in user mode. If this
    41.         parameter is not specified, then the Teb parameter is ignored.
    42.  
    43. Return Value:
    44.  
    45.     None.
    46.  
    47. --*/
    48.  
    49. {
    50.  
    51.     PKEXCEPTION_FRAME CxFrame;
    52.     PKEXCEPTION_FRAME ExFrame;
    53.     ULONG_PTR InitialStack;
    54.     PKTRAP_FRAME TrFrame;
    55.  
    56.     //
    57.     // If a context frame is specified, then initialize a trap frame and
    58.     // and an exception frame with the specified user mode context.
    59.     //
    60.  
    61.     InitialStack = (ULONG_PTR)Thread->InitialStack;
    62.     if (ARGUMENT_PRESENT(ContextRecord)) {
    63.         TrFrame = (PKTRAP_FRAME)(((InitialStack) -
    64.                   sizeof(KTRAP_FRAME)) & ~((ULONG_PTR)15));
    65.         ExFrame = (PKEXCEPTION_FRAME)(((ULONG_PTR)TrFrame -
    66.                   sizeof(KEXCEPTION_FRAME)) & ~((ULONG_PTR)15));
    67.         CxFrame = (PKEXCEPTION_FRAME)(((ULONG_PTR)ExFrame -
    68.                   sizeof(KEXCEPTION_FRAME)) & ~((ULONG_PTR)15));
    69.  
    70.         //
    71.         // Zero the exception and trap frames and copy information from the
    72.         // specified context frame to the trap and exception frames.
    73.         //
    74.  
    75.         RtlZeroMemory((PVOID)ExFrame, sizeof(KEXCEPTION_FRAME));
    76.         RtlZeroMemory((PVOID)TrFrame, sizeof(KTRAP_FRAME));
    77.         KeContextToKframes(TrFrame, ExFrame,
    78.                            ContextRecord,
    79.                            ContextRecord->ContextFlags | CONTEXT_CONTROL,
    80.                            UserMode);
    81.  
    82.         //
    83.         // If the FPCR quadword in the specified context record is zero,
    84.         // then assume it is a default value and force floating point round
    85.         // to nearest mode (the hardware default mode is chopped rounding
    86.         // which is not desirable for NT). It would be nice to initialize
    87.         // the SoftFpcr here also but not all threads have a Teb.
    88.         //
    89.  
    90.         if (TrFrame->Fpcr == 0) {
    91.             ((PFPCR)(&TrFrame->Fpcr))->DynamicRoundingMode = ROUND_TO_NEAREST;
    92.         }
    93.  
    94.         //
    95.         // Set the saved previous processor mode in the trap frame and the
    96.         // previous processor mode in the thread object to user mode.
    97.         //
    98.  
    99.         TrFrame->PreviousMode = UserMode;
    100.         Thread->PreviousMode = UserMode;
    101.  
    102.         //
    103.         // Initialize the return address in the exception frame.
    104.         //
    105.  
    106.         ExFrame->IntRa = 0;
    107.  
    108.     } else {
    109.         ExFrame = NULL;
    110.         CxFrame = (PKEXCEPTION_FRAME)(((InitialStack) -
    111.                   sizeof(KEXCEPTION_FRAME)) & ~((ULONG_PTR)15));
    112.  
    113.         //
    114.         // Set the previous mode in thread object to kernel.
    115.         //
    116.  
    117.         Thread->PreviousMode = KernelMode;
    118.     }
    119.  
    120.     //
    121.     // Initialize context switch frame and set thread start up parameters.
    122.     //
    123.     // N.B. ULONG becomes canonical longword with (ULONGLONG)(LONG) cast.
    124.     //
    125.  
    126.     CxFrame->SwapReturn = (ULONGLONG)(LONG_PTR)KiThreadStartup;
    127.     if (ExFrame == NULL) {
    128.         CxFrame->IntFp = (ULONGLONG)(LONG_PTR)ExFrame;
    129.  
    130.     } else {
    131.         CxFrame->IntFp = (ULONGLONG)(LONG_PTR)TrFrame;
    132.     }
    133.  
    134.     CxFrame->IntS0 = (ULONGLONG)(LONG_PTR)ContextRecord;
    135.     CxFrame->IntS1 = (ULONGLONG)(LONG_PTR)StartContext;
    136.     CxFrame->IntS2 = (ULONGLONG)(LONG_PTR)StartRoutine;
    137.     CxFrame->IntS3 = (ULONGLONG)(LONG_PTR)SystemRoutine;
    138.  
    139.     CxFrame->Psr = 0;           // clear everything
    140.     ((PSR *)(&CxFrame->Psr))->INTERRUPT_ENABLE = 1;
    141.     ((PSR *)(&CxFrame->Psr))->IRQL = DISPATCH_LEVEL;
    142.     ((PSR *)(&CxFrame->Psr))->MODE = 0;
    143.  
    144.     //
    145.     // Set the initial kernel stack pointer.
    146.     //
    147.  
    148.     Thread->KernelStack = (PVOID)(ULONGLONG)(LONG_PTR)CxFrame;
    149.     return;
    150. }
    threadini.c из исходников win2k
    у когонибудь есть из этой серии файлики?
     
  17. 0x56

    0x56 New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2008
    Сообщения:
    63
    да не, как он может быть около вершины стека
    контекст по логике и должен быть на дне стеке где-то, рядом с InitialStack
    а KernelStack показывает рядом с ESP из отладчика
     
  18. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    в коде APC в WRK (windows 2003 server)

    Код (Text):
    1.     if (Ctx->Mode == KernelMode) {
    2.         TrapFrame = Thread->Tcb.TrapFrame;
    3.     }
    4.  
    5.     if (TrapFrame == NULL) {
    6.         TrapFrame = PspGetBaseTrapFrame (Thread);
    7.     }
    Сам макрос:

    Код (Text):
    1. #elif defined(_X86_)
    2.  
    3. #define PspGetBaseTrapFrame(Thread) (PKTRAP_FRAME)((ULONG_PTR)Thread->Tcb.InitialStack - \
    4.                                                    PSPALIGN_UP(sizeof(KTRAP_FRAME),KTRAP_FRAME_ALIGN) - \
    5.                                                    sizeof(FX_SAVE_AREA))
    0x56
    да и правда из реактоса. зачем ты вообще его смотришь то?
    я недоверяю его сорсам в плане совместимости с NT
     
  19. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
  20. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Стоит отметить еще, что для твоей цели совершенно незачем перебирать потоки с State == Wait - они заведомо не выполняют код нужной тебе функции. Перебирать надо только потоки с State == Ready - потоки, готовые к выполнению, которым будет передан квант времени.