This is the second part of article kport.dll for direct access i/o ports in windows NT/2K/XP.
I did write a new sample for use this DLL in VC++ 6.
Remember, for compiling kioport.sys you may have to install DDK.
This article is from Beyondlogic Portalk.
Accessing I/O Ports under NT/2000/XP
There are two solutions to solve the problem of I/O access under Windows NT/2000/XP. The first solution is to write a device driver which runs in ring 0 (I/O privilege level 0) to access your I/O ports on your behalf. Data can be passed to and from your usermode program to the device driver via IOCTL calls. The driver can then execute your I/O instructions. The problem with this is that, it assumes that you have the source code to make such a change.
Another possible alternative is to modify the I/O permission bitmap to allow a particular task, access to certain I/O ports. This grants your usermode program running in ring 3 to do unrestricted I/O operations on selected ports, as per the I/O permission bitmap. This method is not really recommended, but provides a means for allowing existing applications to run under windows NT/2000/XP. Writing a device driver to support your hardware is the preferred method. The device driver should check for any contentions before accessing the port.
However, using a driver such as Kioport can become quite inefficient. Each time an IOCTL call is made to read or write a byte or word to a port, the processor must switch from ring 3 to ring 0 to perform the operation and then switch back.
Compatibility - Using existing applications under Windows NT/2000/XP
As you already know, any 32bit program will cause a Privileged Instruction Exception. As most programming languages these days do not provide functions to read and write to ports, many hacks have been produced for Windows 9x and ME such as .DLL libraries. However if you need to run such a program under Windows NT, an exception will occur.
16 Bit Windows and DOS programs will run on virtual machines. In many cases existing applications should be transparent on Windows NT/2000/XP. However others just refuse to run. The virtual machines has support for communication ports, video, mouse, and keyboard. Therefore any program using these common I/O ports should run, however there is often a problem with timing. Other MS-DOS programs accessing specific hardware requires VDDs (Virtual Device Drivers) written to enable them to be used with Windows NT.
The Virtual Machine will intercept I/O operations and send them to an I/O handler for processing. The way the Virtual Machine does this, is by giving insufficient rights to I/O operations and creating an exception handler to dig back into the stack, find the last instruction and decode it. By giving the VDM full rights to I/O ports, it has no means of intercepting I/O operations, thus creating less problems with timing or the need to provide VDDs for obscurer hardware.
In order to change a process' IOPM, we must first have the process ID for the process we want to grant access too. This is accomplished by creating the process ourselves, so we can pass the
ProcessID to our device driver. A small application is used which accepts the program name as an argument. This application then creates the process (i.e. executes the program) which starts and continues as another process in the system.
Note: We can also register a callback with the operating system which notifies our driver of any processes started and what their ID is. We can then keep a directory of processes that we want to have access to certain ports. When this process is executed, the callback informs the driver it has started and what its
processID is. We could then automatically change the IOPM of this process.
When a Windows 32 bit program is started using
CreateProcess(), it will return the
ProcessID for the 32 Bit Program. This is passed to the Device Driver using an IOCTL Call.
DOS programs do not have their own
ProcessID's. They run under the Windows NT Virtual DOS Machine (NTVDM.EXE) which is a protected environment subsystem that emulates MS-DOS. When a DOS program is called using this program, it will get the
ProcessID for NTVDM.EXE and as a result changes NTVDM's IOPM. However, if NTVDM is already resident (if another DOS Program is running), it will return a process ID of zero. This doesn't cause a problem if the NT Virtual DOS Machine's IOPM is already set to allow any IO operation, however if the first DOS program was called from the command line without using "
AllowIo", the NTVDM will not have the modified IOPM. Windows 3.1 programs will run using WOW (Windows on Win32). This is another protected subsystem that runs within the NTVDM process. Running a Win3.1 program will return the
ProcessID of NTVDM in accordance with the problem set out above.
When the device driver has the
ProcessID, it finds the pointer to process for our newly created program and sets the IOPM to allow all I/O instructions. Once the
ProcessID has been given to our PortTalk device driver, the allowio programs finishes. Running a DOS/Win 3.1 program normally under NTVDM.EXE should not create any major problems. NTVDM will normally intercept most IO calls and check these resources against the registry to make sure they are not in use. If they are in use, a message box will pop up giving the user the option to terminate the program or ignore the error. If the user chooses to ignore the error, access will NOT be granted to the offending I/O Port.
Manipulating the IOPM (I/O Permission Bitmap)
Changing the IOPM within your Kernel Mode Drivers requires the knowledge of a couple of undocumented calls. These are
PsLookupProcessByProcessId(IN ULONG ulProcId,OUT struct _EPROCESS ** pEProcess);
The IOPM routines use a pointer to process and not the
ProcessID. Therefore our first task is to convert the
ProcessID to a pointer to process. There are documented calls such as
PsGetCurrentProcess(), however we don't want the current process but rather the pointer to process we wish to grant access to. This information is passed to the driver in the form of a
processID. We must then use the undocumented call
LookupProcessByProcessId to convert our
ProcessID to a Pointer to Process. Once we have a pointer to process, we can start manipulating the I/O permission bitmap using the following undocumented calls:
void Ke386SetIoAccessMap(int, IOPM *);
void Ke386QueryIoAccessMap(int, IOPM *);
void Ke386IoSetAccessProcess(PEPROCESS, int);
Ke386SetIoAccessMap will copy the IOPM specified to the TSS.
Ke386QueryIoAccessMap will read it from the TSS. The IOPM is a 8192 byte array specifying which ports are allowed access and which ones aren't. Each address is represented by one bit, thus the 8192 bytes will specify access up to 64K. Any zero bit will allow access, while a one will deny access. After the IOPM has been copied to the TSS, the IOPM offset pointer must be adjusted to point to our IOPM. This is done using the
int parameter must be set to
1 to enable it to be set. Calling the function with zero will remove the pointer.
Talking to the Device Driver - User Mode APIs
Kioport also has IOCTLs to allow reading and writing to I/O Ports. In this case, your usermode program would open the Kioport device driver and pass data to the driver through IOCTL calls. The driver then talks to the I/O port(s) in ring 0.
This is my DLL, and part of this code is from Portalk.
OpenPort() while loading our application, and
ClosePort() while unloading, so that we don't have to load the driver.
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
OpenPort() first verifies if it has installed the driver into system32\driver. If it cannot open it, try installing and starting it. The procedure is shown in the figure below:
These two functions read and write in the kioport.sys.
KPORT_API void APIENTRY Outportb(WORD PortNum, BYTE byte)
WORD * pBuffer;
pBuffer = (WORD *)&Buffer;
*pBuffer = PortNum;
Buffer = byte;
LPSTR szt = NULL;
error = DeviceIoControl(hKport, IOCTL_WRITE_PORT_UCHAR,
&Buffer, 3, NULL,
wsprintf(szt,"Error occured during outportb while"
" talking to KioPort driver %d\n",GetLastError());
KPORT_API BYTE APIENTRY Inportb(WORD PortNum)
pBuffer = (WORD*)&Buffer;
*pBuffer = PortNum;
LPSTR szt = NULL; error = DeviceIoControl(hKport,
wsprintf(szt,"Error occured during inportb while"
" talking to KioPort driver %d\n", GetLastError());
I now added IrpMan, Microsoft® Windows Server 2003 DDK, and has much information about making drivers.
Finally, sorry for my poor English. I am on my second year of studying English and I don't know much to write in this language.
- April 3,2005
- April 30,2005