//------------------------------------------------------------------- // iokludge.c // // This module installs a 'workaround' for the x86_64 kernel's // prohibition against access to x86 I/O ports by unprivileged // applications. It achieves this by installing a new handler // for the 'obsolete' (and otherwise unused) exception 9 which // an application may invoke without privilege restrictions if // the software interrupt instruction 'int $0x09' is executed. // (This is easy to accomplish with inline assembly language.) // The new exception-handler merely sets the IOPL-field in the // RFLAGS register to 3, permitting 'in' and 'out' to execute. // The original handler is restored as this module is removed. // // to compile: $ mmake iokludge.c // to install: $ /sbin/insmod iokludge.ko // and remove: $ /sbin/rmmod iokludge // // NOTE: This code was written and tested for kernel 2.6.26.6. // // programmer: ALLAN CRUSE // written on: 17 APR 2009 //------------------------------------------------------------------- #include // for module_init(), module_exit() #define INT_ID 9 // this IDT-entry is no longer used typedef struct { long lo, hi; } GATE_DESCRIPTOR; char modname[] = "iokludge"; unsigned short oldidtr[5], newidtr[5]; GATE_DESCRIPTOR *oldidt, *newidt; unsigned long kpage; void load_IDTR( void *regimage ) { asm(" lidtq %0 " : : "m" (*(unsigned short*)regimage) ); } //-------- INTERRUPT SERVICE ROUTINE ------------ void isr_entry( void ); asm(" .text "); asm(" .type isr_entry, @function "); asm("isr_entry: "); asm(" orq $0x3000, 16(%rsp) "); asm(" iretq "); //------------------------------------------------- static int __init iokludge_init( void ) { GATE_DESCRIPTOR oldgate, newgate; long long isrlocn; printk( "<1>\nInstalling \'%s\' module\n", modname ); // allocate kernel memory for a new Interrupt Descriptor Table kpage = get_zeroed_page( GFP_KERNEL ); if ( !kpage ) return -ENOMEM; // initialize our module's global variables asm(" sidtq oldidtr \n sidtq newidtr "); memcpy( newidtr+1, &kpage, sizeof( unsigned long ) ); oldidt = (GATE_DESCRIPTOR*)(*(unsigned long*)(oldidtr+1)); newidt = (GATE_DESCRIPTOR*)(*(unsigned long*)(newidtr+1)); memcpy( newidt, oldidt, 256 * sizeof( GATE_DESCRIPTOR ) ); // setup our Interrupt Gate isrlocn = (long long)isr_entry; oldgate = oldidt[ INT_ID ]; oldgate.lo &= 0x0000FFFFFFFF0000L; newgate.hi = (isrlocn >> 32); newgate.hi &= 0x00000000FFFFFFFFL; newgate.lo = isrlocn; newgate.lo &= 0x00000000FFFFFFFFL; newgate.lo |= (newgate.lo << 32); newgate.lo &= 0xFFFF00000000FFFFL; newgate.lo |= oldgate.lo; newgate.lo |= (0x6000L << 32); newidt[ INT_ID ] = newgate; // activate our new Interrupt Descriptor Table load_IDTR( newidtr ); smp_call_function( load_IDTR, newidtr, 1, 1 ); return 0; //SUCCESS } static void __exit iokludge_exit(void ) { // reactivate the original Interrupt Descriptor Table smp_call_function( load_IDTR, oldidtr, 1, 1 ); load_IDTR( oldidtr ); // release allocated kernel memory if ( kpage ) free_page( kpage ); printk( "<1>Removing \'%s\' module\n", modname ); } module_init( iokludge_init ); module_exit( iokludge_exit ); MODULE_LICENSE("GPL");