How to use READ_REGISTER_XXX 32 bit on 64 bit systems

Hi, I have an issue where I’m not sure what is wrong with the following code. Now I admit that I still don’t fully understand how to use the IOREGSEL and IOWIN registers for IOAPIC access. Not properly documented (IMO).

Now I get a hold of the APIC base address. Then I do a MmMapIoSpace. So far so good. And I would expect it to be correct because of it (although I don’t rule anything out). Now I have to write to a 32-bit IOREGSEL and read from a 32-bit IOWIN register. The way I do it is to use WRITE_REGISTER_ULONG64 and READ_REGISTER_ULONG64. Now I don’t think they are correct because the registers are 32-bit wide only, but the 32 bit version of the macros only takes a 32-bit address. So I’m lost here as to how to do it on a 64-bit OS like Windows 10. MmMapIoSpace returns a 64 bit address of course.

If there is any other mistakes you see please feel free to shout out. The code in its simplest form is as follows. (error checking left out as well). I loop through and return as soon as I find something.

#define APIC_CHUNKSIZE 0x20

// I/O APIC
pOutputData->dwApicBaseAddress = GetApicBaseAddress();
PHYSICAL_ADDRESS physAddress = { 0 };
physAddress.LowPart = pOutputData->dwApicBaseAddress & APIC_MASK;
hIoMemMap = MmMapIoSpace(physAddress, APIC_CHUNK_SIZE, MmNonCached);

volatile PULONGLONG pApicIoRegSel = (PULONGLONG)hIoMemMap;
pApicIoRegSel += 0x00;

volatile PULONGLONG pApicIoWin = (PULONGLONG)hIoMemMap;
pApicIoWin += 0x10;

int irq = 0x10;
ULONGLONG qwApicData(0);
do
{
WRITE_REGISTER_ULONG64(pApicIoRegSel, irq);//IRQ_APIC_KEYBOARD);
qwApicData = READ_REGISTER_ULONG64(pApicIoWin);

if (qwApicData != 0) {
break;
}

if (qwApicData == 0) {
WRITE_REGISTER_ULONG64(pApicIoRegSel, irq+1);//IRQ_APIC_KEYBOARD);
qwApicData = READ_REGISTER_ULONG64(pApicIoWin);

if (qwApicData != 0) {
break;
}
}

irq += 2;
} while (irq <= 0x3E);

pOutputData->qwIOAPIC_IRQ1 = qwApicData;

MmUnmapIoSpace(hIoMemMap, APIC_CHUNK_SIZE);

The code executes in kernel mode driver and returns 0, never finding anything.

regards
Kjetil

The calls take a pointer for the address, on 64-bit READ_REGISTER_ULONG
takes a 64-bit address see
https://msdn.microsoft.com/en-us/library/windows/hardware/ff566394(v=vs.85
%29.aspx?f=255&MSPPError=-2147217396

Don Burn
Windows Driver Consulting
Website: http://www.windrvr.com

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@ikjetil.no
Sent: Saturday, November 21, 2015 9:47 AM
To: Windows System Software Devs Interest List
Subject: [ntdev] How to use READ_REGISTER_XXX 32 bit on 64 bit systems

Hi, I have an issue where I’m not sure what is wrong with the following
code. Now I admit that I still don’t fully understand how to use the
IOREGSEL and IOWIN registers for IOAPIC access. Not properly documented
(IMO).

Now I get a hold of the APIC base address. Then I do a MmMapIoSpace. So far
so good. And I would expect it to be correct because of it (although I don’t
rule anything out). Now I have to write to a 32-bit IOREGSEL and read from a
32-bit IOWIN register. The way I do it is to use WRITE_REGISTER_ULONG64 and
READ_REGISTER_ULONG64. Now I don’t think they are correct because the
registers are 32-bit wide only, but the 32 bit version of the macros only
takes a 32-bit address. So I’m lost here as to how to do it on a 64-bit OS
like Windows 10. MmMapIoSpace returns a 64 bit address of course.

If there is any other mistakes you see please feel free to shout out. The
code in its simplest form is as follows. (error checking left out as well).
I loop through and return as soon as I find something.

#define APIC_CHUNKSIZE 0x20

// I/O APIC
pOutputData->dwApicBaseAddress = GetApicBaseAddress();
PHYSICAL_ADDRESS physAddress = { 0 };
physAddress.LowPart = pOutputData->dwApicBaseAddress & APIC_MASK; hIoMemMap
= MmMapIoSpace(physAddress, APIC_CHUNK_SIZE, MmNonCached);

volatile PULONGLONG pApicIoRegSel = (PULONGLONG)hIoMemMap;
pApicIoRegSel += 0x00;

volatile PULONGLONG pApicIoWin = (PULONGLONG)hIoMemMap; pApicIoWin += 0x10;

int irq = 0x10;
ULONGLONG qwApicData(0);
do
{
WRITE_REGISTER_ULONG64(pApicIoRegSel, irq);//IRQ_APIC_KEYBOARD);
qwApicData = READ_REGISTER_ULONG64(pApicIoWin);

if (qwApicData != 0) {
break;
}

if (qwApicData == 0) {
WRITE_REGISTER_ULONG64(pApicIoRegSel,
irq+1);//IRQ_APIC_KEYBOARD);
qwApicData = READ_REGISTER_ULONG64(pApicIoWin);

if (qwApicData != 0) {
break;
}
}

irq += 2;
} while (irq <= 0x3E);

pOutputData->qwIOAPIC_IRQ1 = qwApicData;

MmUnmapIoSpace(hIoMemMap, APIC_CHUNK_SIZE);

The code executes in kernel mode driver and returns 0, never finding
anything.

regards
Kjetil


NTDEV is sponsored by OSR

Visit the list at: http://www.osronline.com/showlists.cfm?list=ntdev

OSR is HIRING!! See http://www.osr.com/careers

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer

I’m sorry but that is a PULONG. Isn’t that a 32-bit address?

Kjetil

No it is a 64-bit address to a 32-bit value.

Don Burn
Windows Driver Consulting
Website: http://www.windrvr.com

-----Original Message-----
From: xxxxx@lists.osr.com
[mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@ikjetil.no
Sent: Saturday, November 21, 2015 9:58 AM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] How to use READ_REGISTER_XXX 32 bit on 64 bit systems

I’m sorry but that is a PULONG. Isn’t that a 32-bit address?

Kjetil


NTDEV is sponsored by OSR

Visit the list at: http://www.osronline.com/showlists.cfm?list=ntdev

OSR is HIRING!! See http://www.osr.com/careers

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer

Ok, Thanks, I got it working now :slight_smile:

Kjetil

I suppose I’d be wasting my time asking why in the world you want to access the IOAPIC, right?

Because… this isn’t something you should be doing.

Peter
OSR
@OSRDrivers

I do not think Windows supports any driver touching IO APIC, which is exclusively owned by the HAL.


Maxim S. Shatskih
Microsoft MVP on File System And Storage
xxxxx@storagecraft.com
http://www.storagecraft.com

wrote in message news:xxxxx@ntdev…
> Hi, I have an issue where I’m not sure what is wrong with the following code. Now I admit that I still don’t fully understand how to use the IOREGSEL and IOWIN registers for IOAPIC access. Not properly documented (IMO).
>
> Now I get a hold of the APIC base address. Then I do a MmMapIoSpace. So far so good. And I would expect it to be correct because of it (although I don’t rule anything out). Now I have to write to a 32-bit IOREGSEL and read from a 32-bit IOWIN register. The way I do it is to use WRITE_REGISTER_ULONG64 and READ_REGISTER_ULONG64. Now I don’t think they are correct because the registers are 32-bit wide only, but the 32 bit version of the macros only takes a 32-bit address. So I’m lost here as to how to do it on a 64-bit OS like Windows 10. MmMapIoSpace returns a 64 bit address of course.
>
> If there is any other mistakes you see please feel free to shout out. The code in its simplest form is as follows. (error checking left out as well). I loop through and return as soon as I find something.
>
> #define APIC_CHUNKSIZE 0x20
>
> // I/O APIC
> pOutputData->dwApicBaseAddress = GetApicBaseAddress();
> PHYSICAL_ADDRESS physAddress = { 0 };
> physAddress.LowPart = pOutputData->dwApicBaseAddress & APIC_MASK;
> hIoMemMap = MmMapIoSpace(physAddress, APIC_CHUNK_SIZE, MmNonCached);
>
> volatile PULONGLONG pApicIoRegSel = (PULONGLONG)hIoMemMap;
> pApicIoRegSel += 0x00;
>
> volatile PULONGLONG pApicIoWin = (PULONGLONG)hIoMemMap;
> pApicIoWin += 0x10;
>
> int irq = 0x10;
> ULONGLONG qwApicData(0);
> do
> {
> WRITE_REGISTER_ULONG64(pApicIoRegSel, irq);//IRQ_APIC_KEYBOARD);
> qwApicData = READ_REGISTER_ULONG64(pApicIoWin);
>
> if (qwApicData != 0) {
> break;
> }
>
> if (qwApicData == 0) {
> WRITE_REGISTER_ULONG64(pApicIoRegSel, irq+1);//IRQ_APIC_KEYBOARD);
> qwApicData = READ_REGISTER_ULONG64(pApicIoWin);
>
> if (qwApicData != 0) {
> break;
> }
> }
>
> irq += 2;
> } while (irq <= 0x3E);
>
> pOutputData->qwIOAPIC_IRQ1 = qwApicData;
>
> MmUnmapIoSpace(hIoMemMap, APIC_CHUNK_SIZE);
>
> The code executes in kernel mode driver and returns 0, never finding anything.
>
> regards
> Kjetil
>

xxxxx@ikjetil.no wrote:

Now I get a hold of the APIC base address. Then I do a MmMapIoSpace. So far so good. And I would expect it to be correct because of it (although I don’t rule anything out). Now I have to write to a 32-bit IOREGSEL and read from a 32-bit IOWIN register. The way I do it is to use WRITE_REGISTER_ULONG64 and READ_REGISTER_ULONG64. Now I don’t think they are correct because the registers are 32-bit wide only, but the 32 bit version of the macros only takes a 32-bit address. So I’m lost here as to how to do it on a 64-bit OS like Windows 10. MmMapIoSpace returns a 64 bit address of course.

You are overthinking this. The WRITE_REGISTER_xxx APIs are semantically
identical to dereferencing a pointer. The address values in all of
those APIs match whatever the operating system is. You use the “xxx”
variation that matches the register you need to access.

In your case, you need WRITE_REGISTER_ULONG.

If there is any other mistakes you see please feel free to shout out. The code in its simplest form is as follows. (error checking left out as well). I loop through and return as soon as I find something.

volatile PULONGLONG pApicIoRegSel = (PULONGLONG)hIoMemMap;
pApicIoRegSel += 0x00;

volatile PULONGLONG pApicIoWin = (PULONGLONG)hIoMemMap;
pApicIoWin += 0x10;

The “volatile” is unnecessary here, assuming you use the
READ/WRITE_REGISTER macros. They will add it.

Are you trying to get to an address that is 0x10 bytes into the memory
map? Because that’s not what you’ve done. Remember that, in C, when
you add to a pointer, it multiplies by the size of thing pointed to.
When you add 1 to a PULONGLONG, you are actually adding 8 to the
address. It can sometimes be easier to have your base register pointer
be a PUCHAR, just so arithmetic is easier.

PUCHAR pIoMemMap = MmMapIoSpace( … );
PULONG pApicIoRegSel = (PULONG)(pIoMemMap + 0x00);
PULONG pApcIoWin = (PULONG)(pIoMemMap + 0x10);


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Thank you for response.

Tim: Yes my pointer arithmetic was wrong. Is working now. Thank you for pointing this out. I should have seen it as I am familiar with this.

Peter/Maxim: I am trying to prove/disprove whether or not a computer has a SMI Interrupt handler for the keyboard. Indicating thus whether or not a boot/root-kit is installed. I have had this problem and don’t want to see it happen again. So I want to create a little KMDF driver and little application that tests this. I have never written a driver before and it seems like a completely different world to be in. Also how to communicate with hardware and memory etc. is a learning experience. Have not done this since 90’s Real Mode.

Kjetil

> Peter/Maxim: I am trying to prove/disprove whether or not a computer has a SMI Interrupt handler for

the keyboard. Indicating thus whether or not a boot/root-kit is installed.

Wow… this stuff is too deep and unsupported in Windows. The drivers are only supported to see their own devices, APIC belongs to HAL, and the low-level interrupt handlers belong to the kernel.

Hacking this stuff is unreliable (it is playing Core Wars in memory with the possible malware), and can be broken by a Service Pack (or Windows 10 TH2).

Also ensure to test your stuff with the malware present, so it will not crash in this case :slight_smile:


Maxim S. Shatskih
Microsoft MVP on File System And Storage
xxxxx@storagecraft.com
http://www.storagecraft.com

Maxim: I see and thank you. But since I am only reading data I think the possibility of something going wrong is low. The question I have now is if the data I read are real. The information is poorly documented and all though it seems to work, I am not sure until I get it confirmed by someone else.

Kjetil

The only way to prove the box is clean is to program it from scratch. All these schemes that try to detect infection at the same (or higher, like SMM) execution level are not bulletproof.