Windows API: Getting the thread id of another process

DON'T post new tutorials here! Please use the "Pending Submissions" board so the staff can review them first.
Post Reply
User avatar
Gogeta70
^_^
^_^
Posts: 3275
Joined: 25 Jun 2005, 16:00
18

Windows API: Getting the thread id of another process

Post by Gogeta70 »

Hey everyone ^_^

You'd be surprised how difficult this task actually is. After trying a few different things and doing some research, i've found a fairly simple way of getting the thread id of each thread in a process. This can be used for a few things, one being that you can hijack the execution of a thread and inject your own code into that thread. With that said, let's get to the interesting stuff :mrgreen:

One method i've seen in use a lot is using some inline assembly to get the address of your processes thread information block.

Code: Select all

// This code will compile on G++/Mingw compilers. It won't compile in visual c++
// This requires the following compiler switch: -masm=intel
void GetTibAddress(DWORD *TibAddr)
{
	__asm("mov eax, fs:[0x18]\n"
		"mov dword ptr [TibAddr], eax\n"
		 );
}
The "fs:" prefix is used to reference the thread information block of the current thread. For example, "fs:[0x00]" points to the first byte of the TIB. At "fs:[0x18]" is the memory address of "fs:[0x00]".

The problem with this method is that it only can retrieve the TIB address of your program. In another process, the TIB address may be different. However, if we can find the location of the TIB of each thread in an external process, we can then get a thread id for each thread.

Luckily for us, it appears that the address of the TIB is determined by a set of rules.

-The address always appears in the highest range of memory in the program. Basically, it's always above 0x7A000000.
-It's always stored at an address that follows this pattern: 0x7zzzx000, where z is always the same every time you run the program, and x is the only value that's changed.
-The block of memory it resides in is always private
-The block of memory it resides in always has the PAGE_READWRITE permission.

I've come up with these rules based on my observations of program behavior on my 2 computers, so these rules could be incorrect on a different computer. But it's worked pretty well for me so far.

Anyway, with the rules above, we can begin to write our code.
The program needs to:
-Query each page of memory with an address above 0x7A000000
-Filter out private memory and pages with ReadWrite access
-And finally, it needs to scan each of the resulting memory pages for the presence of a TIB

Code: Select all

#include <cstdio>
#include <windows.h>

int main(int argc, char* argv[])
{
	if(argc != 2)
		return 1;
   
	//Let's assume the user passes the process id as an argument
	DWORD Pid = atol(argv[1]);
	//DWORD Pid = GetCurrentProcessId();
	DWORD ThreadId = 0, MemPid = 0;
	void *tibAddr = 0;
	unsigned char *Addr = (unsigned char*) 0x00000000;;
   
	//Let's open our target process...
	HANDLE pHandle = OpenProcess(PROCESS_ALL_ACCESS, false, Pid);
   
	if(pHandle == NULL)
		return 1;
   
	MEMORY_BASIC_INFORMATION mem;
	DWORD Offset = 0;
   
   //Here, we're going to query the process memory 1 page
   //at a time until VirtualQueryEx returns 0, which means we've
   //queries all the processes memory.
   while(VirtualQueryEx(pHandle, Addr, &mem, sizeof(mem)) != 0)
   {
		
		Addr += mem.RegionSize; //Increase the address by the size of the current page
		  
		//Filter out the pages we need...
		if(mem.Type != MEM_PRIVATE)
			continue;
		
		if(mem.Protect != PAGE_READWRITE)
			continue;
		  
		//Going by the rules i outlined above, the thread information block
		//always starts at an address with a multiple of 0x1000.
		//So, we'll search each multiple of 0x1000 in each page
		//for the start of a TIB ^_^
		DWORD MaxLoop = (0x0000FFFF & mem.RegionSize) / 0x1000;
		
		for(DWORD i = 0; i < MaxLoop; i++)
		{
			Offset = 0x1000 * i;
			ReadProcessMemory(pHandle, Addr-mem.RegionSize+0x18+Offset, &tibAddr, 4, 0); //If this page holds a TIB, this should read a memory value that is equal to the address it's reading from
			ReadProcessMemory(pHandle, Addr-mem.RegionSize+0x20+Offset, &MemPid, 4, 0); //This is reading the "Process Id" field of the TIB. Used for comparison.
			
			if(tibAddr == Addr-mem.RegionSize+Offset && Pid == MemPid)
			{
				ReadProcessMemory(pHandle, Addr-mem.RegionSize+0x24+Offset, &ThreadId, 4, 0);
				printf("Thread Information Block found: 0x%.8x\n", reinterpret_cast<unsigned long>(tibAddr));
				printf("Thread id: %lu\n", ThreadId);
				
				break;
			}
		}
	}
	
	CloseHandle(pHandle);
   
	return 0;
}
Well, that's the general idea anyway. I haven't compiled this code so there may be some errors, if you spot any, let me know. Also, i want to point out that this code lacks a lot of error checking, i just didn't want to clutter the code too much with it.

If you have any questions on anything in this guide, feel free to ask ^_^
¯\_(ツ)_/¯ It works on my machine...

User avatar
maboroshi
Dr. Mab
Dr. Mab
Posts: 1624
Joined: 28 Aug 2005, 16:00
18

Re: Windows API: Getting the thread id of another process

Post by maboroshi »

Well I think the idea is very intriguing and I will keep this in mind whenever I need to do this

*cheers man

Edit Works Now

User avatar
Gogeta70
^_^
^_^
Posts: 3275
Joined: 25 Jun 2005, 16:00
18

Re: Windows API: Getting the thread id of another process

Post by Gogeta70 »

Okay, i've fixed the code. I have no issues compiling it and the program is working correctly too.
¯\_(ツ)_/¯ It works on my machine...

User avatar
maboroshi
Dr. Mab
Dr. Mab
Posts: 1624
Joined: 28 Aug 2005, 16:00
18

Re: Windows API: Getting the thread id of another process

Post by maboroshi »

Very cool man this will come in Handy in the future!

Image

is a result of my test :-)

Post Reply