summaryrefslogtreecommitdiffstats
path: root/res/ATFBlast_ALL/userport/UserPort/UserPort_SYS/UserPort.C
blob: f0e40df271e25aef3201f9f6e2781698f1be55d9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
/*********************************************************************

Author:     Tomas Franzon
Date:       11/09/2000
Program:    UserPort.SYS
Compile:    Use DDK BUILD facility

Purpose:    Give direct port I/O access to user mode programs.

This driver is influenced by an article written by Dale Roberts 8/30/95,
published in May 96 Dr Dobbs Journal, see www.ddj.com.
The driver gives user mode program access to selected ports by changing
the x86-processors IOPM (I/O Permission Map). The driver has one separate
map for all procsesses (AllProcessesIOPM) and one separate map 
(ThroughCreateFileIOPM) for processes that opens the file "\\.\UserPort"

The driver tries to read the registry keys:
HKEY_LOCAL_MACHINE\Software\UserPort\AllProcessesIOPM
HKEY_LOCAL_MACHINE\Software\UserPort\ThroughCreateFileIOPM
It will use the default values below if these doesn't exist.

*********************************************************************/
#include <ntddk.h>

/*
 *  Make sure our structure is packed properly, on byte boundary, not
 * on the default doubleword boundary.
*/
#pragma pack(push,1)

/*
 *  Structures for manipulating the GDT register and a GDT segment
 * descriptor entry.  Documented in Intel processor handbooks.
 */
typedef struct {
	unsigned limit : 16;
	unsigned baselo : 16;
	unsigned basemid : 8;
	unsigned type : 4;
	unsigned system : 1;
	unsigned dpl : 2;
	unsigned present : 1;
	unsigned limithi : 4;
	unsigned available : 1;
	unsigned zero : 1;
	unsigned size : 1;
	unsigned granularity : 1;
	unsigned basehi : 8;
} GDTENT;

typedef struct {
	unsigned short	limit;
	GDTENT	*base;
} GDTREG;

#pragma pack(pop)
/*
 *  The name of our device driver.
 */
#define DEVICE_NAME_STRING	L"UserPort"

/*
  OriginalMapCopy is used to restore the IOPM when the driver exists.
  CurrentMap points to NULL or to the place where the processors IOPM is located.
  Accessmap is the IOPM that is used by the driver.
  Every port address has one cooresponding bit in the IOPM. The driver supports
  addresses from 0x000 to 0x3FF and the IOPM size is then 0x3ff / 8 = 0x80.
 */

UCHAR OriginalAllProcIOPMCopy[0x80];
UCHAR OriginalThroughCreateFileIOPMCopy[0x80];
const UCHAR DefaultMap[0x80]=
/*0x000*/ {0xFF,0xFF,
/*0x010*/  0xFF,0xFF,
/*0x020*/  0xFF,0xFF,
/*0x030*/  0xFF,0xFF,
/*0x040*/  0xFF,0xFF,
/*0x050*/  0xFF,0xFF,
/*0x060*/  0xFF,0xFF,
/*0x070*/  0xFF,0xFF,
/*0x080*/  0xFF,0xFF,
/*0x090*/  0xFF,0xFF,
/*0x0A0*/  0xFF,0xFF,
/*0x0B0*/  0xFF,0xFF,
/*0x0C0*/  0xFF,0xFF,
/*0x0D0*/  0xFF,0xFF,
/*0x0E0*/  0xFF,0xFF,
/*0x0F0*/  0xFF,0xFF,
/*0x100*/  0xFF,0xFF,
/*0x110*/  0xFF,0xFF,
/*0x120*/  0xFF,0xFF,
/*0x130*/  0xFF,0xFF,
/*0x140*/  0xFF,0xFF,
/*0x150*/  0xFF,0xFF,
/*0x160*/  0xFF,0xFF,
/*0x170*/  0xFF,0xFF,
/*0x180*/  0xFF,0xFF,
/*0x190*/  0xFF,0xFF,
/*0x1A0*/  0xFF,0xFF,
/*0x1B0*/  0xFF,0xFF,
/*0x1C0*/  0xFF,0xFF,
/*0x1D0*/  0xFF,0xFF,
/*0x1E0*/  0xFF,0xFF,
/*0x1F0*/  0xFF,0xFF,
/*0x200*/  0x00,0x00,
/*0x210*/  0x00,0x00,
/*0x220*/  0x00,0x00,
/*0x230*/  0x00,0x00,
/*0x240*/  0x00,0x00,
/*0x250*/  0x00,0x00,
/*0x260*/  0x00,0x00,
/*0x270*/  0x00,0x00,
/*0x280*/  0x00,0x00,
/*0x290*/  0x00,0x00,
/*0x2A0*/  0x00,0x00,
/*0x2B0*/  0x00,0x00,
/*0x2C0*/  0x00,0x00,
/*0x2D0*/  0x00,0x00,
/*0x2E0*/  0x00,0x00,
/*0x2F0*/  0x00,0x00,
/*0x300*/  0x00,0x00,
/*0x310*/  0x00,0x00,
/*0x320*/  0x00,0x00,
/*0x330*/  0x00,0x00,
/*0x340*/  0x00,0x00,
/*0x350*/  0x00,0x00,
/*0x360*/  0x00,0x00,
/*0x370*/  0x00,0x00,
/*0x380*/  0xFF,0xFF,
/*0x390*/  0xFF,0xFF,
/*0x3A0*/  0xFF,0xFF,
/*0x3B0*/  0xFF,0x0F,
/*0x3C0*/  0xFF,0xFF,
/*0x3D0*/  0xFF,0xFF,
/*0x3E0*/  0xFF,0x00,
/*0x3F0*/  0x00,0x00};

unsigned OrgGDTSize;     // The original sise of the TSS

/*
 Ke386IoSetAccessProcess() adjusts the IOPM offset pointer to the IOPM at 0x88
 Ke386IoSetAccessProcess() is located in NTOSKRNL.EXE but is not included in any
 header file or documented anywhere...
*/

void Ke386IoSetAccessProcess(PEPROCESS, int);

/*********************************************************************
  Service handler for a CreateFile() user mode call.

  This routine is entered in the driver object function call table by
the DriverEntry() routine.  When the user mode application calls
CreateFile(), this routine gets called while still in the context of
the user mode application, but with the CPL (the processor's Current
Privelege Level) set to 0.  This allows us to do kernel mode
operations.  UserPort() is called to give the calling process I/O
access.  All the user mode application needs do to obtain I/O access
is open this device with CreateFile().  No other operations are
required.
*********************************************************************/
NTSTATUS CreateFileDispatch(
    IN  PDEVICE_OBJECT  DeviceObject,
    IN  PIRP            Irp
    )
{
  // Give the current process IO access
	Ke386IoSetAccessProcess(PsGetCurrentProcess(), 1);

	Irp->IoStatus.Information = 0;
	Irp->IoStatus.Status = STATUS_SUCCESS;
	IoCompleteRequest(Irp, IO_NO_INCREMENT);
	return STATUS_SUCCESS;
}

// remove the link \\.\UserPort and restore the IOPM 
void UserPortUnload(IN  PDRIVER_OBJECT  DriverObject)
{
	WCHAR DOSNameBuffer[] = L"\\DosDevices\\" DEVICE_NAME_STRING;
	UNICODE_STRING uniDOSString;
	GDTREG gdtreg;
	GDTENT *g;
	short TaskSeg;
	UCHAR *TSSAllProcessesIOPM;
	UCHAR *TSSThroughCreateFileIOPM;
	unsigned i;
	UCHAR *TSSbase; 

	_asm cli;					
	_asm sgdt gdtreg;	
	_asm str TaskSeg;	
	g = gdtreg.base + (TaskSeg >> 3);	
	g->limit = OrgGDTSize;
	g->type = 9;					
	_asm ltr TaskSeg;			
	_asm sti;						

	TSSbase = (UCHAR *) (g->baselo | (g->basemid << 16) | (g->basehi << 24));                
  TSSAllProcessesIOPM = *((USHORT *)(TSSbase + 0x66)) + TSSbase;
 	TSSThroughCreateFileIOPM = TSSbase + 0x88;

	// Restore to the original map
	for (i=0;i<sizeof(DefaultMap);i++)
	{
		TSSAllProcessesIOPM[i] = OriginalAllProcIOPMCopy[i];
		TSSThroughCreateFileIOPM[i] = OriginalThroughCreateFileIOPMCopy[i];
	}

	RtlInitUnicodeString(&uniDOSString, DOSNameBuffer);
	IoDeleteSymbolicLink (&uniDOSString);
	IoDeleteDevice(DriverObject->DeviceObject);
}

// This routine is the entrypoint of the driver.
// This routine reads the AllProcessesIOPM and ThroughCreateFileIOPM from registry and start the driver
NTSTATUS DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
    )
{
	PDEVICE_OBJECT deviceObject;
	NTSTATUS status;
	WCHAR NameBuffer[] = L"\\Device\\" DEVICE_NAME_STRING;
	WCHAR DOSNameBuffer[] = L"\\DosDevices\\" DEVICE_NAME_STRING;
	UNICODE_STRING uniNameString, uniDOSString;
	GDTREG gdtreg;
	GDTENT *g;
	short TaskSeg;
	UCHAR *TSSAllProcessesIOPM;
	UCHAR *TSSThroughCreateFileIOPM;
	const UCHAR *AllProcessesIOPM = DefaultMap;
	const UCHAR *ThroughCreateFileIOPM = DefaultMap;
	unsigned i;
	UCHAR *TSSbase; 
  UCHAR InformationBuffer1[sizeof(DefaultMap)+sizeof(KEY_VALUE_PARTIAL_INFORMATION)];
  PKEY_VALUE_PARTIAL_INFORMATION Information1 = (PKEY_VALUE_PARTIAL_INFORMATION)InformationBuffer1;
  UCHAR InformationBuffer2[sizeof(DefaultMap)+sizeof(KEY_VALUE_PARTIAL_INFORMATION)];
  PKEY_VALUE_PARTIAL_INFORMATION Information2 = (PKEY_VALUE_PARTIAL_INFORMATION)InformationBuffer2;
	ULONG ResultLength;
	HANDLE KeyHandle;
	OBJECT_ATTRIBUTES ObjectAttributes;
	UNICODE_STRING ThroughCreateFileIOPMString,AllProcessesIOPMString,RegPathString;

	RtlInitUnicodeString(&AllProcessesIOPMString, L"AllProcessesIOPM");
	RtlInitUnicodeString(&ThroughCreateFileIOPMString, L"ThroughCreateFileIOPM");
	RtlInitUnicodeString(&RegPathString,L"\\Registry\\Machine\\Software\\UserPort");

	InitializeObjectAttributes(&ObjectAttributes,&RegPathString,OBJ_CASE_INSENSITIVE,NULL,NULL);

	if (STATUS_SUCCESS == ZwOpenKey(&KeyHandle, KEY_QUERY_VALUE,&ObjectAttributes))
	{
		if (STATUS_SUCCESS == ZwQueryValueKey(KeyHandle,&ThroughCreateFileIOPMString,KeyValuePartialInformation,Information1,sizeof(InformationBuffer1),&ResultLength))
			ThroughCreateFileIOPM = Information1->Data;
		if (STATUS_SUCCESS == ZwQueryValueKey(KeyHandle,&AllProcessesIOPMString,KeyValuePartialInformation,Information2,sizeof(InformationBuffer2),&ResultLength))
			AllProcessesIOPM = Information2->Data;
		ZwClose(KeyHandle);
	}
	
	_asm cli;							              // don't get interrupted!
	_asm str TaskSeg;                   // get the TSS selector      
	_asm sgdt gdtreg;                   // get the GDT address       
	g = gdtreg.base + (TaskSeg >> 3);   // get the TSS descript
                                        // get the TSS address       
	OrgGDTSize = g->limit;
	g->limit += 0x082;					        // modify TSS segment limit
	g->type = 9;						            // mark TSS as "not busy"
	_asm ltr TaskSeg;					          // reload task register (TR)
	_asm sti;							              // let interrupts continue*/

	TSSbase = (UCHAR *) (g->baselo | (g->basemid << 16) | (g->basehi << 24));                
  TSSAllProcessesIOPM = *((USHORT *)(TSSbase + 0x66)) + TSSbase;
 	TSSThroughCreateFileIOPM = TSSbase + 0x88;

	// Copy the AccessMap to TSSbase + 0x20ad and save the original map
	for (i=0;i<sizeof(DefaultMap);i++)
	{
		OriginalAllProcIOPMCopy[i] = TSSAllProcessesIOPM[i];
		TSSAllProcessesIOPM[i] = AllProcessesIOPM[i];
		OriginalThroughCreateFileIOPMCopy[i] = TSSThroughCreateFileIOPM[i];
		TSSThroughCreateFileIOPM[i] = TSSThroughCreateFileIOPM[i] & ThroughCreateFileIOPM[i];
	}

	//
	//  Set up device driver name and device object.
	//  Make the driver accessable though the file \\.\UserPort
	
	RtlInitUnicodeString(&uniNameString, NameBuffer);
	RtlInitUnicodeString(&uniDOSString, DOSNameBuffer);

	status = IoCreateDevice(DriverObject, 0,
					&uniNameString,
					FILE_DEVICE_UNKNOWN,
					0, FALSE, &deviceObject);

	if(!NT_SUCCESS(status))
		return status;

	status = IoCreateSymbolicLink (&uniDOSString, &uniNameString);

	if (!NT_SUCCESS(status))
		return status;

	//
	//  Initialize the Driver Object with driver's entry points.
	// All we require are the Create and Unload operations.
	//
	DriverObject->MajorFunction[IRP_MJ_CREATE] = CreateFileDispatch;

	DriverObject->DriverUnload = UserPortUnload;

	return STATUS_SUCCESS;
}