PDA

View Full Version : Get IDT address in multiprocessors


Alorent
August 17th, 2005, 16:26
Hello all!

I was looking how I can get the address of the different IDTs (Interrupt Descriptor Table) in a multiprocessor/HyperThreading machine.

If I execute the command "sidt" I get the address for the current processor...but how I can get that address for other processors?

I can see that SoftICE knows the IDTs of all processors....but how?

Thanks.

Opcode
August 17th, 2005, 16:52
Hi,

I don't know how exactly the SoftICE does to get the 2 IDTR values, but
you can use KeSetTargetProcessorDPC to queue a DPC to run at an specific processor and use the SIDT instruction.

Maybe there are some unknown structure or variable that may tell us the
address of the two KPRC structures of the kernel when running in a HT system.

Please, let us know if you find something... I'm very interested in this
kind of questions.

Regards,
Opc0de

Extremist
August 17th, 2005, 18:27
SetProcessAffinityMask()
SetThreadAffinityMask()

Kayaker
August 17th, 2005, 22:00
Hi

Softice doesn't really do anything special, it just reads the current value of the IDTR register with the SIDT instruction. There is only 1 IDTR register, though you can have a separate IDT table for each processor. The new values are set by the OS sometime magically during the processor switch.

Opcode's got some trickeries going on there with DPC's, gotta check that out sometime ;-)
(I wonder if somewhere in SetProcessAffinityMask -> KeSetAffinityThread -> HalSystemVectorDispatchEntry might yield a clue to where those KPRC structures could be located from? Perhaps the "current" IDTR is saved somewhere before the switch?)

Unless you're trying to do a processor switch from kernel mode, the following code will switch the running thread to each logical processor of a single physical CPU with Hyperthreading enabled, and retrieve the base address of the relevant IDT.
...As Extremist says, you could use either SetProcessAffinityMask() or SetThreadAffinityMask()

Code:

///////////////////////////////////////////////////////
// SwitchIDT
//
// Switch to each logical processor with Hyperthreading
// and retrieve the base address of the relevant IDT
///////////////////////////////////////////////////////


BOOL SwitchIDT () {

///////////////////////////////////////////////////////

struct IDT_INFO
{
unsigned short wReserved;
unsigned short wLimit; // size of table
IDT_GATE* pIdt; // base address of table
};

IDT_INFO IdtInfo = {0};

ULONG lpProcessAffinityMask = 0;
ULONG lpSystemAffinityMask = 0;

///////////////////////////////////////////////////////

// Affinity Info

GetProcessAffinityMask(
GetCurrentProcess(), // HANDLE hProcess,
&lpProcessAffinityMask, // OUT PDWORD_PTR lpProcessAffinityMask,
&lpSystemAffinityMask); // OUT PDWORD_PTR lpSystemAffinityMask

// function obtains lpProcessAffinityMask value from EPROCESS.Affinity
// function obtains lpSystemAffinityMask value from BaseStaticServerData
// both are generally same value of 0x03 (binary mask 11)
// with Hyperthreading enabled

fprintf (stderr, "ProcessAffinityMask %x \n", lpProcessAffinityMask);
fprintf (stderr, "SystemAffinityMask %x \n", lpSystemAffinityMask);


///////////////////////////////////////////////////////

// Switch current thread to processor 1
SetProcessAffinityMask(
GetCurrentProcess(), // HANDLE hProcess,
1); // DWORD_PTR dwProcessAffinityMask

Sleep(0); // Give OS a chance to switch to the desired CPU


// Read IDTR register
__asm sidt IdtInfo.wLimit

// Output current IDT base address
fprintf (stderr, "CPU1 IDT Base %x \n", IdtInfo.pIdt);


// Switch current thread to processor 2 if HT is enabled
if (lpProcessAffinityMask != 1)
{
SetProcessAffinityMask(
GetCurrentProcess(), // HANDLE hProcess,
2); // DWORD_PTR dwProcessAffinityMask

Sleep(0); // Give OS a chance to switch to the desired CPU


// Read IDTR register
__asm sidt IdtInfo.wLimit

// Output current IDT base address
fprintf (stderr, "CPU2 IDT Base %x \n", IdtInfo.pIdt);
}

///////////////////////////////////////////////////////

return TRUE;

} //

///////////////////////////////////////////////////////


Cheers,
Kayaker

Opcode
August 18th, 2005, 00:33
Hi Kayaker!

Quote:
[Originally Posted by Kayaker]
There is only 1 IDTR register, though you can have a separate IDT table for each processor.


I think that there are 2 IDTR registers.
Maybe I'm wrong.

The Intel Manual Vol III - 7.6.1.1:

Code:
"The following features are duplicated for each logical processor:
• General purpose registers (EAX, EBX, ECX, EDX, ESI, EDI, ESP, and EBP)
.......
• Control registers (CR0, CR2, CR3, CR4) and system table pointer registers (GDTR,
LDTR, IDTR, task register)
..."


I have been researching the HT/MP systems for a while and I have found
that the key is the IPI messages of the APIC.
I expect to have something to publish in the next months.

Thanks for the source!

Regards,
Opcode

Kayaker
August 18th, 2005, 03:29
Thanks for the clarification Opcode, I hadn't realized that. In fact, it looks like I'll need an updated set of Intel manuals, the version I have doesn't seem to have that passage about duplicated registers, either in section 7.6.11 or anywhere else it seems.

What does "duplicated for each logical processor" really mean though? In hardware terms, is there 1 or 2 sets of all these registers per *physical* processor? I'm not well acquainted with the details, but I thought Hyperthreading was more of a virtual sharing of a single set of physical registers that allowed threads to execute in an "interleaved" fashion. Physically speaking then, would there still not only be 1 set of control registers, general purpose registers, etc. per CPU?

Looking forward to seeing an explanation of the mysteries of the APIC!

Regards,
Kayaker

Alorent
August 19th, 2005, 05:17
Hello,

Thanks a lot for all the info. You are really gurus!

Kayaker, thanks a lot for the code, it's quite clear.

I think that the APIC solution must be the most ellegant one in this case. Intel says that it's stored at address 0xFFDFF120...though I looked into WinXP with SOFTICE and nothing seems to be there...I'm a bit lost in this issue.

Thanks.

Opcode
August 19th, 2005, 07:38
Hi,

First, you need to get the physical address of the APIC by
using the RDMSR instruction to read the IA32_APIC_BASE_MSR value.

After this, you can use the MmMapIoSpace function
to map from this physical address to a virtual one.

The standard physical address for the apic is 0xFEC00000.
You can use "PHYS 0xFEC00000" in SoftICE to get the virtual address.

The address 0xFFDFF120 is the _KPRCB structure.
You can use WinDBG to see the whole structure.
Or use the "types _KPRCB" in SoftICE.

When you are using a MP/HT system, the two _KPRCB addresses
are inside the KiProcessorBlock kernel variable.

Code:

lkd> dd KiProcessorBlock
8054b080 ffdff120 f7acf120 00000000 00000000
8054b090 00000000 00000000 00000000 00000000
8054b0a0 00000000 00000000 00000000 00000000


To get the PCR address of each processor is just simple:
FIRST KPCR address = 0xFFDFF120 - 0x120
SECOND KPCR address = 0xF7ACF120 - 0x120

We can easily find the IDT address inside the KPCR structure.
The problem is that the KiProcessorBlock is not exported

Regards,
Opc0de

Opcode
August 19th, 2005, 07:50
Quote:
[Originally Posted by Opcode]We can easily find the IDT address inside the KPCR structure.
The problem is that the KiProcessorBlock is not exported

Oops, I'm wrong.

You can use my little trick at
http://www.rootkit.com/newsread.php?newsid=101
to get the KiProcessorBlock address.

The KiProcessorBlock is exported by the KdVersionBlock structure
as you can see here:
http://www.rootkit.com/newsread.php?newsid=153

Regards,
Opc0de

Alorent
August 19th, 2005, 09:51
Thanks Opc0de!

Very nice article you wrote in there I only have WinXP here for testing and it worked

So, that way only works on WinXP? isnt there a general solution for all NT systems?

Thanks.

Opcode
August 19th, 2005, 09:57
Hi Alorent,

Yes, unfortunately it works only in XP/2k3.
I have not tested it yet in the Vista beta 1.

Opc0de

90210
August 26th, 2005, 03:56
Quote:
[Originally Posted by Opcode]
We can easily find the IDT address inside the KPCR structure.
The problem is that the KiProcessorBlock is not exported


The other way to find PCRs is to find _HalpProcessorPCR array in the HAL (by looking to the exported HalInitializeProcessor). This is an array of *KPCRs, and the idt of each processor can be found in the PKPCR->IDT (KIDTENTRY), or KPCR+0x38.

This method is implemented in the IceExt (http://wasm.ru/baixado.php?mode=tool&id=140) and also (thx, Sten ) in my sw_remove (http://dzena.net/sw_remove.zip).

Check their mp_AnalyzeHalInitProcessor() and mp_HookInterrupt().

wbr, 90210//HI-TECH