PE-sieve
Scans all running processes. Recognizes and dumps a variety of potentially malicious implants (replaced/implanted PEs, shellcodes, hooks, in-memory patches).
Loading...
Searching...
No Matches
threads_util.cpp
Go to the documentation of this file.
1#include "threads_util.h"
2
3#include <peconv.h>
4#include <tlhelp32.h>
5#include "../utils/ntddk.h"
6#include "custom_buffer.h"
7
8#ifdef _DEBUG
9#include <iostream>
10#endif
11
12
13namespace pesieve {
14 namespace util {
15
16 // Thread info structures:
22
23
25 {
26 static auto mod = GetModuleHandleA("ntdll.dll");
27 if (!mod) return false;
28
29 static auto pNtQueryInformationThread = reinterpret_cast<decltype(&NtQueryInformationThread)>(GetProcAddress(mod, "NtQueryInformationThread"));
30 if (!pNtQueryInformationThread) return false;
31
32 const DWORD thAccess = THREAD_QUERY_INFORMATION | THREAD_GET_CONTEXT;
33 HANDLE hThread = OpenThread(thAccess, 0, tid);
34 if (!hThread) {
35 hThread = OpenThread(THREAD_QUERY_INFORMATION, 0, tid);
36 if (!hThread) return false;
37 }
38 bool isOk = false;
39 ULONG returnedLen = 0;
40 LPVOID startAddr = 0;
41 NTSTATUS status = 0;
42 status = pNtQueryInformationThread(hThread, ThreadQuerySetWin32StartAddress, &startAddr, sizeof(LPVOID), &returnedLen);
43 if (status == 0 && returnedLen == sizeof(startAddr)) {
44 info.start_addr = (ULONGLONG)startAddr;
45 isOk = true;
46 }
47 returnedLen = 0;
48 THREAD_LAST_SYSCALL_INFORMATION syscallInfo = { 0 };
49 status = pNtQueryInformationThread(hThread, ThreadLastSystemCall, &syscallInfo, sizeof(syscallInfo), &returnedLen);
50 if (status == 0 && returnedLen >= sizeof(syscallInfo)) {
51 info.last_syscall = syscallInfo.SystemCallNumber;
52 isOk = true;
53 }
54 CloseHandle(hThread);
55 info.is_filled = isOk;
56 return isOk;
57 }
58
59 }; // namespace util
60}; // namespace pesieve
61
62
63size_t pesieve::util::query_threads_details(IN OUT std::map<DWORD, pesieve::util::thread_info>& threads_info)
64{
65 size_t queried_count = 0;
66 for (auto itr = threads_info.begin(); itr != threads_info.end(); ++itr) {
67 pesieve::util::thread_info& info = itr->second;
68 if (query_thread_details(info.tid, info)) queried_count++;
69 }
70 return queried_count;
71}
72
73bool pesieve::util::fetch_threads_info(IN DWORD pid, OUT std::map<DWORD, thread_info>& threads_info)
74{
75 AutoBuffer bBuf;
76 NTSTATUS status = STATUS_UNSUCCESSFUL;
77
78 while (status != STATUS_SUCCESS) {
79 ULONG ret_len = 0;
80 status = NtQuerySystemInformation(SystemProcessInformation, bBuf.buf, bBuf.buf_size, &ret_len);
81 if (status == STATUS_INFO_LENGTH_MISMATCH) {
82 if (!bBuf.alloc(ret_len)) {
83 return false;
84 }
85 continue; // try again
86 }
87 break; //other error, or success
88 };
89
90 if (status != STATUS_SUCCESS) {
91 return false;
92 }
93
94 bool pid_found = false;
95 SYSTEM_PROCESS_INFORMATION* info = (SYSTEM_PROCESS_INFORMATION*)bBuf.buf;
96 while (info) {
97 if (info->UniqueProcessId == pid) {
98 pid_found = true;
99 break;
100 }
101 if (!info->NextEntryOffset) {
102 break;
103 }
104 size_t record_size = info->NextEntryOffset;
105 if (record_size < sizeof(SYSTEM_PROCESS_INFORMATION)) {
106 // Record size smaller than expected, probably it is an old system that doesn not support the new version of this API
107#ifdef _DEBUG
108 std::cout << "The new version of SYSTEM_PROCESS_INFORMATION is not supported!\n";
109#endif
110 break;
111 }
112 info = (SYSTEM_PROCESS_INFORMATION*)((ULONG_PTR)info + info->NextEntryOffset);
113 if (!peconv::validate_ptr(bBuf.buf, bBuf.buf_size, info, sizeof(SYSTEM_PROCESS_INFORMATION))) {
114 break;
115 }
116 }
117 if (!pid_found) {
118 return false;
119 }
120 const size_t thread_count = info->NumberOfThreads;
121 if (!peconv::validate_ptr(bBuf.buf, bBuf.buf_size, info->Threads, thread_count * sizeof(info->Threads[0]))) {
122 return false;
123 }
124 bool fetched = false;
125 for (size_t i = 0; i < thread_count; i++) {
126 const DWORD tid = MASK_TO_DWORD((ULONGLONG)info->Threads[i].ClientId.UniqueThread);
127 auto itr = threads_info.find(tid);
128 if (itr == threads_info.end()) {
129 threads_info[tid] = thread_info(tid);
130 }
131 fetched = true;
132 thread_info &threadi = threads_info[tid];
133 threadi.is_extended = true;
134 threadi.ext.sys_start_addr = (ULONG_PTR)info->Threads[i].StartAddress;
135 threadi.ext.state = info->Threads[i].ThreadState;
136 threadi.ext.wait_reason = info->Threads[i].WaitReason;
137 threadi.ext.wait_time = info->Threads[i].WaitTime;
138 threadi.is_filled = true;
139 }
140 return fetched;
141}
142
143bool pesieve::util::fetch_threads_by_snapshot(IN DWORD pid, OUT std::map<DWORD, thread_info>& threads_info)
144{
145 HANDLE hThreadSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
146 if (hThreadSnapShot == INVALID_HANDLE_VALUE) {
147#ifdef _DEBUG
148 const DWORD err = GetLastError();
149 std::cerr << "[-] Could not create threads snapshot. Error: " << std::dec << err << std::endl;
150#endif
151 return false;
152 }
153 THREADENTRY32 th32 = { 0 };
154 th32.dwSize = sizeof(THREADENTRY32);
155
156 //check all threads in the process:
157 if (!Thread32First(hThreadSnapShot, &th32)) {
158 CloseHandle(hThreadSnapShot);
159#ifdef _DEBUG
160 std::cerr << "[-] Could not enumerate thread. Error: " << GetLastError() << std::endl;
161#endif
162 return false;
163 }
164
165 bool fetched = false;
166 do {
167 if (th32.th32OwnerProcessID != pid) {
168 continue;
169 }
170
171 fetched = true;
172
173 const DWORD tid = th32.th32ThreadID;
174 auto itr = threads_info.find(tid);
175 if (itr == threads_info.end()) {
176 threads_info[tid] = thread_info(tid);
177 }
178 } while (Thread32Next(hThreadSnapShot, &th32));
179
180 CloseHandle(hThreadSnapShot);
181 return fetched;
182}
#define MASK_TO_DWORD(val)
Definition iat_finder.h:9
bool fetch_threads_by_snapshot(IN DWORD pid, OUT std::map< DWORD, thread_info > &threads_info)
NTSTATUS(NTAPI *_RtlCreateProcessReflection)(HANDLE ProcessHandle
bool query_thread_details(IN DWORD tid, OUT pesieve::util::thread_info &info)
struct pesieve::util::_THREAD_LAST_SYSCALL_INFORMATION THREAD_LAST_SYSCALL_INFORMATION
size_t query_threads_details(IN OUT std::map< DWORD, thread_info > &threads_info)
struct pesieve::util::_THREAD_LAST_SYSCALL_INFORMATION * PTHREAD_LAST_SYSCALL_INFORMATION
DWORD(__stdcall *_PssCaptureSnapshot)(HANDLE ProcessHandle
struct pesieve::util::_thread_info thread_info
bool fetch_threads_info(IN DWORD pid, OUT std::map< DWORD, thread_info > &threads_info)
std::string info()
The string with the basic information about the scanner.
Definition pe_sieve.cpp:276
BYTE * alloc(size_t _buf_size)