Карта ввода-вывода находится в TSS и описывает всё адресное пространство портов ввода/вывода, которое составляет 65536 портов, т.е. для описания всего адресного пространства понадобится 8Кб.
TSS представлен дескриптором в GDT, с пределом равным 000020AB (8363 байта)
=============================================
Sel. Base Limit DPL P G Description
=============================================
0028 F7717D70 000020AB 0 P 1b 32-Bit TSS (Busy)
=============================================
#define INT_DIRECTION_MAP_SIZE 32
typedef UCHAR KINT_DIRECTION_MAP[INT_DIRECTION_MAP_SIZE];
#define IOPM_COUNT 1 // Number of i/o access maps that exist
#define IOPM_SIZE 8192 // Size of map callers can set.
#define PIOPM_SIZE 8196 // Size of structure we must allocate to hold it.
typedef struct _KiIoAccessMap
{
KINT_DIRECTION_MAP DirectionMap;
UCHAR IoMap[PIOPM_SIZE];
} KIIO_ACCESS_MAP;
В TSS лежит сама карта и смещение:
typedef struct _KTSS
{
...
USHORT IoMapBase; // Смещение карты ввода/вывода от начала TSS.
KIIO_ACCESS_MAP IoMaps[IOPM_COUNT];
...
Установить карту ввода-вывода для процесса можно с помощью ф-ции Ke386SetIoAccessMap, которая скопирует в TSS->IoMaps.IoMap пользовательскую карту и установит IoMapBase для процесса на нее.
Связь процесса с картой ввода-вывода:
typedef struct _KPROCESS
{
...
USHORT IopmOffset; <== определено смещение на нее
UCHAR Iopl;
...
} KPROCESS, *PKPROCESS, *PRKPROCESS;
При создании процесса вызывается ф-ция KeInitializeProcess:
VOID KeInitializeProcess( __out PRKPROCESS Process, __in KPRIORITY BasePriority, __in KAFFINITY Affinity, __in ULONG_PTR DirectoryTableBase[2], __in BOOLEAN Enable )
{
...
//
// Initialize IopmBase and Iopl flag for this process (i386 only)
//
Process->IopmOffset = KiComputeIopmOffset(IO_ACCESS_MAP_NONE); <== IoMapBase будет указывать за пределы сегмента TSS
...
}
Для всех процессов(если для них не установлена карта ввода-вывода):
+0x000 Pcb : _KPROCESS
...
+0x030 IopmOffset : 0x20ac <== смещение за пределы сегмента
+0x032 Iopl : 0 ''
А если учесть, что TSS представлен дескриптором в GDT, с пределом равным 000020AB (8363 байта), то получается, что любое обращение процесса к TSS будет генерить #GF, если не установлено 2 бита в eflags.
Итог:
IOPM для всех процессов может быть только один, причем по умолчанию смещение на карту находится на пределами сегмента TSS, однако ф-ция Ke386IoSetAccessProcess к примеру для конкретного процесса и для общей TSS устанавливает IopmOffset на общую карту доступа портов ввода-вывода.
А само смещение обновляется при переключении процессов(аттач/детач):
cPublicProc _KiSwapProcess ,2
...
;
; Change IOPM
;
mov ax,[edx]+PrIopmOffset
mov [ecx]+TssIoMapBase,ax
...
То есть в Windows просто не возможно иметь две и более разных карт ввода-вывода в TSS, места там хватает только для одной( limit == 20ABh ).
TSS представлен дескриптором в GDT, с пределом равным 000020AB (8363 байта)
=============================================
Sel. Base Limit DPL P G Description
=============================================
0028 F7717D70 000020AB 0 P 1b 32-Bit TSS (Busy)
=============================================
#define INT_DIRECTION_MAP_SIZE 32
typedef UCHAR KINT_DIRECTION_MAP[INT_DIRECTION_MAP_SIZE];
#define IOPM_COUNT 1 // Number of i/o access maps that exist
#define IOPM_SIZE 8192 // Size of map callers can set.
#define PIOPM_SIZE 8196 // Size of structure we must allocate to hold it.
typedef struct _KiIoAccessMap
{
KINT_DIRECTION_MAP DirectionMap;
UCHAR IoMap[PIOPM_SIZE];
} KIIO_ACCESS_MAP;
В TSS лежит сама карта и смещение:
typedef struct _KTSS
{
...
USHORT IoMapBase; // Смещение карты ввода/вывода от начала TSS.
KIIO_ACCESS_MAP IoMaps[IOPM_COUNT];
...
Установить карту ввода-вывода для процесса можно с помощью ф-ции Ke386SetIoAccessMap, которая скопирует в TSS->IoMaps.IoMap пользовательскую карту и установит IoMapBase для процесса на нее.
Связь процесса с картой ввода-вывода:
typedef struct _KPROCESS
{
...
USHORT IopmOffset; <== определено смещение на нее
UCHAR Iopl;
...
} KPROCESS, *PKPROCESS, *PRKPROCESS;
При создании процесса вызывается ф-ция KeInitializeProcess:
VOID KeInitializeProcess( __out PRKPROCESS Process, __in KPRIORITY BasePriority, __in KAFFINITY Affinity, __in ULONG_PTR DirectoryTableBase[2], __in BOOLEAN Enable )
{
...
//
// Initialize IopmBase and Iopl flag for this process (i386 only)
//
Process->IopmOffset = KiComputeIopmOffset(IO_ACCESS_MAP_NONE); <== IoMapBase будет указывать за пределы сегмента TSS
...
}
Для всех процессов(если для них не установлена карта ввода-вывода):
+0x000 Pcb : _KPROCESS
...
+0x030 IopmOffset : 0x20ac <== смещение за пределы сегмента
+0x032 Iopl : 0 ''
А если учесть, что TSS представлен дескриптором в GDT, с пределом равным 000020AB (8363 байта), то получается, что любое обращение процесса к TSS будет генерить #GF, если не установлено 2 бита в eflags.
Итог:
IOPM для всех процессов может быть только один, причем по умолчанию смещение на карту находится на пределами сегмента TSS, однако ф-ция Ke386IoSetAccessProcess к примеру для конкретного процесса и для общей TSS устанавливает IopmOffset на общую карту доступа портов ввода-вывода.
А само смещение обновляется при переключении процессов(аттач/детач):
cPublicProc _KiSwapProcess ,2
...
;
; Change IOPM
;
mov ax,[edx]+PrIopmOffset
mov [ecx]+TssIoMapBase,ax
...
То есть в Windows просто не возможно иметь две и более разных карт ввода-вывода в TSS, места там хватает только для одной( limit == 20ABh ).
Комментариев нет:
Отправить комментарий