Пытаюсь вот уже который день получить прямой доступ к портам из UserSpace, что не так? Код (Text): typedef struct { DWORD Offset; WORD Selector; }FAR_CALL, *PFAR_CALL; FAR_CALL FarCall; DWORD Param; // какой должен быть параметр? DWORD Ring0Proc; // как взять адрес процесса ? DWORD Ring0ProcAdr; // отправка на порт __declspec(naked) void SendToPort( WORD port, CHAR value ) { __asm { mov al, value mov dx, port out dx, al } } __declspec(naked) void GateToRing0( void ) { //{ перезагрузка регистра FS и вызов Ring0 кода } _asm { lea esi, lab lea eax, FarCall mov dword ptr [esi], eax mov eax, Ring0Proc ; вылетает тут с Unhandled exception at 0x012c17ba : 0xC0000005: ; Access violation writing location 0x012c17cd. mov Ring0ProcAdr, eax mov eax, Param _emit 0xFF _emit 0x1D lab: _emit 0x00 _emit 0x00 _emit 0x00 _emit 0x00 } } // Тестовая программка int main( int argc, char **argv ) { GateToRing0(); SendToPort( 0x03f2, 0 ); // try write something to floppy port return 0; } Вобщем чего я не понимаю, как можно пробиться в Ring0 Ибо дельфовый асм не совсем понимаю, да и вообще знаю его поверхностно... Помогите разрешить обращение к портам
Можно открыть битовую карту в TSS, либо установить флажёк IOPL в 3(для любых портом будет открыт доступ, планировщик сохраняет это поле). Из юзермода делалось вторым способом, сервис NtSetInformatiuonProcess(ProcessUserModeIOPL) с соответствующими привилегиями. В ядре просто установить два бита в флажках(в вашем случае да, нормально в регистровом фрейме, который будет загружен в процессор при возврате). 0xFF, 0x1D это вызов калгейта инструкцией Call far ptr ds:[XXXX]. Предварительно шлюз вызова должен быть создан в GDT на всех процессорах. По этому адресу должн быть записан описатель шлюза как смещение:селектор, у вас описывается структурой FAR_CALL, собственно адрес в инструкцию динамически записывается(тут у вас и исключение), тоесть этот код должен быть в памяти доступной на запись. Код вызываемый через шлюз для установки IOPL должен выглядеть следующим образом: Код (Text): push 3000h popfd retf 1. Если код вызываемый через шлюз вызова находится в юзермодном диапазоне адресов, необходимо создать в этой странице тред захватывающий её, просто в цикле обращающийся к этой странице, иначе при выгруженной в своп странице вы вспыхнет голубой огонет со IRQL_NOT_LESS_OR_EQUAL. 2. Загрузить ядерные селекторы, сформировать регистровый фрейм(KTRAP_FRAME), если предполагается вызывать чтолибо при разрешённых прерываниях.
Clerk т.е ? Код (Text): void set_iop3() { __asm { push 3202h popfd retf; <- получаю Access violation reading location } } Этот код, должен выставить IOPL в 3? или для выполения out, in нужно ещё что-то? NtSetInformatiuonProcess() - случаем не из DDK ?
Сервис из ядра, нтос, про ддк ничего не знаю.) Если калгейт вызван, то после отработки кода при возврате восстанавливаются валидные Cs, Ss и Esp. Исключения не может быть. Переписать код на нормальный асм, писать драйвера на дельфи это извращение.
Вот набросал кодес для формирования регистрового фрейма: Код (Text): ; Формирование регистрового фрейма для вызова через калгейт. ; При переходе через шлюз вызова стек переключается на ядерный из TSS. ; +12 Eip <- Current Esp ; +8 Cs ; +4 Esp ; +0 Ss ; (TSS.Esp0): ... TsSegGs equ 00030H CALLGATE_ENTRY macro ; o При входе прерывания разрешены. ; o Вызов из юзермода(при возврате прерывания размаскируются). ; o Шлюз не имеет параметров(иначе необходимо корректировать стек, восстанавливая его из TSS). cli pushfd sub esp,4 push NULL ; Error code. push ebp push ebx push esi push edi push fs sub esp,2*4 push eax push ecx push edx push ds push es push gs mov eax,KGDT_R0_PCR sub esp,TsSegGs mov ecx,KGDT_R3_DATA or RPL_MASK mov ebp,esp mov fs,ax push 2 mov ds,cx mov es,cx popfd ; Очищаем DF и пр. assume ebp:PKTRAP_FRAME mov eax,[ebp].rSegCs ; Eip mov ecx,[ebp].rEip ; EFlags xor edx,edx and ecx,NOT(EFLAGS_VM or EFLAGS_RF) ; Корректируем флажки. mov [ebp].rSegCs,KGDT_R3_CODE or RPL_MASK ; Перед возвратом в юзермод прерывания необходимо размаскировать. or ecx,EFLAGS_IF mov ebx,dword ptr fs:[PcPrcbData + PbCurrentThread] mov [ebp].rEip,eax mov [ebp].rEFlags,ecx push dword ptr fs:[PcExceptionList] ; Отладочные регистры не сохраняем, сбрасываем регистр управления на случай. mov Dr7,edx ; Сохраняем сех-фрейм. mov dword ptr fs:[PcExceptionList],EXCEPTION_CHAIN_END pop [ebp].ExceptionList ; - Сохраняем указатель на регистровый фрейм. $LOAD Ecx, ThTrapFrame $LOAD Edx, ThPreviousMode mov dword ptr [ebx + ecx],ebp ; ETHREAD.TrapFrame ; - Устанавливаем предыдущий режим. mov byte ptr [ebx + edx],UserMode ; Разрешаем прерывания. sti ;... ; Маскируем прерывания перед возвратом. cli lea esp,[ebp + TsSegGs] pop gs pop es pop ds pop edx pop ecx pop eax ; Удаляем PreviousPreviousMode и ExceptionList. lea esp,[esp + 2*4] pop fs pop edi pop esi pop ebx pop ebp lea esp,[esp + 4] ; Удаляем код ошибки. iretd endm Не тестил, но вроде правильно должно быть. Смещения ThTrapFrame и ThPreviousMode вычислять динамически, ибо разные в версиях. Макро $LOAD для пикода: Код (Text): $LOAD macro Reg32, Variable Local dt_ Call dt_ dt_: pop Reg32 mov Reg32,dword ptr [Reg32 + (offset Variable - offset dt_)] endm Инклуды и код для поиска смещений есть на вирустеке у меня в папке.
Clerk Еще нужно сохранять DRx и V86, иначе это не будет работать при отлаживаемых процессах и при запущенных дос сеансах. И, кстати говоря, даже полноценный вариант у меня не работал. И вообще, зачем это нужно? Есть же IoConnectInterrupt, она возволяет полноценно устанавливать обработчики с такими впрапперами. Можно поставить левое прерывание, дернуть оттуда DispatchCode и потом удалить его. А код себе скопировать. Я так делаю у себя в отладчике - универсально.
Great Почемуже не будет это работать, это не обработчик прерывания, это вызывается шерез шлюз вызова и не из VDM. Для прерываний да, сохранять остальную чать фрейма необходимо.
Clerk мне нужно просто набросать две функи: byte inp( short port ) void outp( short port, byte data ) и дать им привилегии работать из ring3 такие работали из под DOS, были в поставке Turbo C 3.0 и умерли в Win32 и VS.NET Собственно вопрос, как это организовать, если наше приложение не имеет функциональности сервиса.
Дурацкий вопрос, inpout32.dll уже не рулит? Clerk Я запутался уже, я давно последний раз писал ручное создание треп фрейма. И вот не помню, в каких случаях не работало. Вообщем это надо посмотреть снова, а это сейчас мне не в тему. В любом случае, это незачем делать - IoConnectInterrupt и KINTERRUPT:ispatchCode позволяют это все сделать автоматически
Lolmen Был простой способ – NtSetInformationProcess(ProcessUserModeIOPL) – позволяющий при наличии привилегии SeTcbPrivilege ("Act as part of the operating system") включить возможность писать в порты из третьего ринга. Но сейчас посмотрели – в vista sp1 функция после проверки привилегии просто возвращает STATUS_NOT_IMPLEMENTED. Таким образом, необходимость писать свой \ использовать стороний драйвер (inpout32.dll) держит правду.
Имхо лучше будет всю обработку в ядре исполнить, чем юзать дров который прямой ввод-вывод откроет, где был топик там додумались в юзермоде прерывания от оборудования обрабатывать :-!