Доброго времени суток! У меня есть необходимость копаться в потоках нескольких запущенных процессов, иногда переключать их контекст. Для перечисления потоков написал такую функцию: Код (Text): // В хедере typedef LONG (WINAPI* PZWQSI)(UINT, PVOID, ULONG, PULONG); // Указатель на ZwQuerySystemInformation // Совместимость ILP32 и LLP64 #ifdef _WIN64 #define MEMADR LONGLONG #else #define MEMADR DWORD #endif // Информация о клиенте typedef struct _CLIENT_ID { MEMADR UniqueProcess; MEMADR UniqueThread; } CLIENT_ID, *PCLIENT_ID; // Информация о потоках typedef struct _SYSTEM_THREADS { LARGE_INTEGER KernelTime; LARGE_INTEGER UserTime; LARGE_INTEGER CreateTime; ULONG WaitTime; PVOID StartAddress; CLIENT_ID ClientId; LONG Priority; LONG BasePriority; ULONG ContextSwitchCount; LONG State; LONG WaitReason; } SYSTEM_THREADS, *PSYSTEM_THREAD; // Информация о процессах typedef struct _SYSTEM_PROCESSES { ULONG NextEntryDelta; ULONG ThreadCount; ULONG Reserved1[6]; LARGE_INTEGER CreateTime; LARGE_INTEGER UserTime; LARGE_INTEGER KernelTime; USHORT ProcessNameLength; USHORT ProcessNameMaxLength; PWSTR ProcessNameBuffer; LONG BasePriority; #ifdef _WIN64 ULONG hz1; ULONG ProcessId; ULONG hz22; ULONG InheritedFromProcessId; ULONG hz3, hz4, hz5; #else ULONG ProcessId; ULONG InheritedFromProcessId; #endif ULONG HandleCount; ULONG Reserved2[2]; VM_COUNTERS VmCounters; IO_COUNTERS IoCounters; SYSTEM_THREADS Threads[1]; } SYSTEM_PROCESSES, *PSYSTEM_PROCESSES; // В цпп'шнике // Перечислить все тиды потоков процесса по пиду процесса // pid - пид процесса, тиды которого нужно перечислить // tids - указатель на память (буффер), куда вернуть список тидов // maxtids - максимальное количество тидов, на которое расчитан буффер ULONG GetProcessTidsArray(DWORD pid, DWORD* tids, ULONG maxtids) { // Получаем адрес функции ZwQuerySystemInformation из ntdll.dll if(m_LibNtdll == NULL) { m_LibNtdll = LoadLibrary(L"ntdll.dll"); } PZWQSI pZwQuerySystemInformation = (PZWQSI)GetProcAddress(m_LibNtdll, "ZwQuerySystemInformation"); if(pZwQuerySystemInformation == NULL) { return 0; } // Подбираем размер буффера PVOID buffer = NULL; ULONG buflen = 0x8000; ULONG retlen = 0; LONG ret = 0; do { buffer = VirtualAlloc(NULL, buflen, MEM_COMMIT, PAGE_READWRITE); if(buffer == 0) { return 0; } ret = pZwQuerySystemInformation(5, buffer, buflen, &retlen); if(ret == (LONG)0xC0000004L) { VirtualFree(buffer, buflen, MEM_DECOMMIT); buflen = buflen * 2; } else if(ret != (LONG)0x00000000L) { VirtualFree(buffer, buflen, MEM_DECOMMIT); return 0; } } while(ret == (LONG)0xC0000004L); // Копаемся в результатах, ищем нужный процесс PSYSTEM_PROCESSES pProc = (PSYSTEM_PROCESSES)buffer; bool pidfound = false; while(1) { if(pProc->ProcessId == pid) { pidfound = true; break; } if(pProc->NextEntryDelta == 0) { break; } else { pProc = PSYSTEM_PROCESSES((BYTE*)pProc + pProc->NextEntryDelta); } } // Если нужный пид найден - возвращаем тиды массивом if(pidfound) { if(pProc->ThreadCount < maxtids) // Если все влезает - возвращаем все { for(unsigned int t = 0; t < pProc->ThreadCount; t++) { tids[t] = pProc->Threads[t].ClientId.UniqueThread; } return pProc->ThreadCount; } else // Если не влезает, возвращаем то, что влезло { for(unsigned int t = 0; t < maxtids; t++) { tids[t] = pProc->Threads[t].ClientId.UniqueThread; } return maxtids; } } else { return 0; } } Как бы это алгоритм широко известен и работает, ну да ладно, проблема в другом. Отладчики умеют отличать потоки, разделяя их на: Main Thread, Worker Thread и RPC Thread. Мне было бы полезно уметь делать так же)) Думал, что это происходит по полю State или Priority, однако документации по этому поводу крайне мало, а отладка показала, что мои предположения были ошибочны. Может кто-нить что-нить знает по данному вопросу?))
Rel По адресу функции потока должны опеределять его принадлежность, а так состояние и приоритет самому потоку изменить можно
вы имели ввиду, что по главной функции потока можно определить его принадлежность? тобишь фактический если главная функция - main(), то поток будет являться главным потоком приложения? я об этом не подумал))) но тут возникают еще несколько вопросов))): 1) как мне определить адрес главной функции (main()) другого процесса? 2) в структуре system_threads поле startaddress означает именно адрес главной функции потока, или текущее значение регистра eip потока (регистра rip для x64)? просто разные "документации недокументированных структур" говорят по-разному))))
конечно можно найти смещение относительно базового адреса, покопавшись в pe-заголовке, но мне хочется найти решение без доступа к исполняемому файлу...
Из собственного опыта....... Делаем снимок потоков с отбором по PID процесса ПЕРВЫЙ в списке будет MainThread, наверное так устроено у мелкомягких, логично ?! Пример из собственного инструмента C:\Windows\System32\drivers\QcomWlanSrvx64.exe QcomWlanSrvx64.exe Процесс: 64 бит pid 3984 0: 00000F94 ( 3988) Thread ID 8 base priority 1: 0000115C ( 4444) Thread ID 8 base priority 2: 00001CC8 ( 7368) Thread ID 8 base priority Всего потоков: 3 Параметры потоков: 0: 3988 1243550000 StackBase 124354C000 StackLimit 7FF637866E78 StartSAddress 1: 4444 1243D00000 StackBase 1243CFD000 StackLimit 7FFD191D63D0 StartSAddress 2: 7368 1243900000 StackBase 12438FE000 StackLimit 7FFD267B6320 StartSAddress Здесь MainThread будет 3988 1243550000 StackBase 124354C000 StackLimit 7FF637866E78 StartSAddress 7FF637866E78 StartSAddress => OEP программы и еще ................................................................ C:\Windows\explorer.exe explorer.exe Процесс: 64 бит pid 6256 0: 00001874 ( 6260) Thread ID 8 base priority 1: 0000192C ( 6444) Thread ID 8 base priority 2: 00001934 ( 6452) Thread ID 9 base priority 3: 00001960 ( 6496) Thread ID 8 base priority 4: 0000196C ( 6508) Thread ID 8 base priority 5: 00001A1C ( 6684) Thread ID 8 base priority .............................. ............................... 110: 00001E60 ( 7776) Thread ID 8 base priority 111: 000016B8 ( 5816) Thread ID 8 base priority Всего потоков: 112 Параметры потоков: 0: 6260 00A80000 StackBase 00A6C000 StackLimit 7FF7566ED3F0 StartSAddress 1: 6444 02F10000 StackBase 02F02000 StackLimit 7FFD267B6320 StartSAddress 2: 6452 03090000 StackBase 03082000 StackLimit 7FFD25E2C900 StartSAddress 3: 6496 03390000 StackBase 03382000 StackLimit 7FFD267B6320 StartSAddress 4: 6508 04850000 StackBase 04842000 StackLimit 7FFD267B6320 StartSAddress 5: 6684 05080000 StackBase 05072000 StackLimit 7FFD267B6320 StartSAddress ''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 110: 7776 56A70000 StackBase 56A62000 StackLimit 7FFD267B6320 StartSAddress 111: 5816 56AF0000 StackBase 56AE2000 StackLimit 7FFD267B6320 StartSAddress MainThread: 0: 6260 00A80000 StackBase 00A6C000 StackLimit 7FF7566ED3F0 StartSAddres 7FF7566ED3F0 StartSAddres => OEP программы
vitokop, вы привнесли свежее дуновение постенговой некрофилии, что, впрочем, никого тут давно не удивляет. Продолжайте отвечать на ответы
Rel, зато этот- без трех дней четырнадцатилетняя давность и некроответ был дан и поныне присутствующему среди нас Вам. Что Вы и можете отметить послезавтра, и с чем и поздравляем!
Наконец-то я завершу свой проект 14-летней давности, буду всей своей программерской тимой отмечать, всех джунов набухаю в честь этого.
Давным давно, когда ко-фаундер был прыщавым школьником, он очень хотел научиться работать с компьютером. Вопреки ожиданиям, ничего сложного в этом не оказалось. Освоив монитор и клавиатуру, он возжелал научиться писать программы. И, как ни странно, ему это снова удалось. Затем ко-фаундер скомпилил свой первый сплоет. Случилось чудо - сплоет скомпилился и заработал. Так прошло 15 лет. А затем началось самое интересное: ко-фаундер оглянулся вокруг. Оказалось, что все кто хотели научиться работать с компьютером - научились. Из них, те кто хотели писать программы - пишут их. А те, кто раз попробовали скомпилить сплоет - компелируют его по сей день. (За исключением, конечно, обитателей хэкерных форумов и прочих). Таким образом был развеян миф об элитарности хэка и основано БХЦ. А главный хэкерный секрет заключается в том, что его не существует. То есть (фактически) научиться компелировать сплоеты может каждый.
Application, всё лишь потому, бро, что скамерсант от жидохеккера недалеко падает! lol --- Сообщение объединено, 14 окт 2023 --- Потоки переопределены. Ибо ваистену!:
sl0n, потому, что те, кто - уже совсем не те, а Баггерс по-прежнему Ненаказуемый, а вот и нотариально заверенный пруф: --- Сообщение объединено, 18 окт 2023 --- Да и как можно завязать с тем, что ононимус всё равно не забывает