IOport : Access Input/Output ports in Mocka executables
As a true hacking geek, I needed to access the I/O ports of the PC. Under DOS that was no big deal, but under Linux it's different. I know I/O ports should be shielded most of the time by the operating system, but there are times that you simply need to flip some switches.
Switches on the LPT port (aka Geek Port) to be precise. To get all those projects working from our DOS era's.
So I decided it was time to bend some rules and extend Mocka with a small library that enables direct port
access from within Mocka executables.
This library goes down to the bare bones of the computing platform. I work on a PC running Slackware Linux. So that's the hardware I will support and since I know my way with assembly language, I made the actual interface code in AT&T style 80386 code.
The library consists of two parts:
In order to test what I just made, I needed to make a small program. This is what is included as 'prf'. It checks the workings of the module.
IOport.md, the FOREIGN MODULE.
FOREIGN MODULE IOport;
(* This module supports IO port access under Mocka / Linux
CopyLeft Jan 2004, Jan Verhoeven.
This module is published under de rules of the GNU GPL.
To change the working of this material do as follows:
$ jed IOport.asm
$ as IOport.asm -o IOport.o
and you get an improved version. Improvements and normal use
are entirely on your own risk.
Please feel free to improve this software, if needed. You may even
port this source to a lesser language like C.
This software comes without any warranty of any kind. *)
(* 01 : Get a working MODULE, no frills. Jan 02, 2004 *)
PROCEDURE InPort (port : CARDINAL) : CARDINAL;
(* Read a byte from I/O address 'port' and return it to the caller *)
PROCEDURE OutPort (port, value : CARDINAL);
(* Write a byte 'value' to I/O address 'port' *)
PROCEDURE IOperm (from, to : CARDINAL; value : BOOLEAN) : BOOLEAN;
(* Ask unix permission to access the specified I/O addresses:
from = first port to access,
to = last port to access,
value = TRUE or FALSE.
The values of the parameters 'from' and 'to' are inclusive.
So the snippet:
IF IOperm (0x378, 0x37A, TRUE) = FALSE THEN
WriteString ('Could not get access to I/O ports.');
WriteLn
END;
asks the operating system to access ports 0x378, 0x379 and
0x37A. If the caller has enough privileges, the access is
granted and the IF statement is not executed.
*)
END IOport.
This was the easy part. Now for something completely different.
IOport.asm, the assembly source.
What follows is the assembly language sourcecode for the interface routines. These are all rather
straighforward for all people who have ever tried to do something similar under DOS.
The main difference is the (at first) odd syntax of the AS assembler. But the syntax is not THAT odd. It's
just a lot like Modula-2.... All registernames must be prefixed by a percent sign (%). All data values must be
prefixed by a dollar sign ($). But that's all done to protect the programmer against his or her own speed.
The main problem is the reversed operand order. We all got used to the Intel syntax which is completely erratic. And now, if we go to the AT&T syntax (which is logical), we have problems since we got used to a bad predecessor system....
See for yourself what I mean. If you don't like this syntax, use NASM. Everything following a '#' symbol is a comment (until the end of the line).
# IMPLEMENTATION MODULE PortIO;
.globl InPort
.globl OutPort
.globl IOperm
.text
.align 4
# PROCEDURE InPort (port : CARDINAL) : CARDINAL;
InPort:
pushl %ebp # stack up EBP
movl %esp, %ebp # EBP := ESP
movl 8(%ebp), %edx # EDX := port
movl $0000, %eax # EAX := 0
inb %dx, %al # EAX := port value
movl %ebp, %esp # ESP := EBP
popl %ebp # restore old value of EBP
ret
# PROCEDURE OutPort (port, value : CARDINAL);
OutPort:
pushl %ebp
movl %esp, %ebp # set up EBP
movl 8(%ebp), %edx # EDX := port
movl 12(%ebp), %eax # EAX := value
outb %al, %dx # write to I/O port
movl %ebp, %esp
popl %ebp
ret
# PROCEDURE IOperm (from, to : CARDINAL; value : BOOLEAN) : BOOLEAN;
IOperm:
pushl %ebp
movl %esp, %ebp # save EBP and set up new EBP
movl 8(%ebp), %ebx # EBX := first port
movl 12(%ebp), %ecx # ECX := last port
subl %ebx, %ecx # ECX := ECX - EBX + 1
incl %ecx # ECX := nr of ports
jecxz IOpermError
movl 16(%ebp), %edx # EDX := flag
movl $101, %eax # EAX := code for IOperm call
int $0x80 # perform SYSTEM call
movl $0001, %eax # EAX := 0001
sbbl $0000, %eax # IF Error THEN EAX := 0000
jmp IOpermExit
IOpermError:
movl $0000, %eax
IOpermExit:
movl %ebp, %esp
popl %ebp
ret
You assemble the source with the command:
bash-2.05$ as IOport.asm -o IOport.oWith the 0608m compiler: copy the object file to the m2bin subdirectory. That's it. Commence now as usual:
At this moment I feel tempted to make more libraries in pure assembly language. With a list of some Linux Systemcalls (using INT 0x80), the full InOut library could be rewritten to snugly fit the PC hardware. And to come close to FST Modula-2.
Any volunteers?
prf.mi, the test software.
What follows is a typical testing program. It does nothing sensible and it is littered with WriteString
statements.
In first instance this program was called 'test.mi' and the executable was called 'test'. This caused a lot of
confusion since there is a Linux program called 'test' as well and it is in a directory which is early in the
searchpath. So every time I wanted to try my 'test' program, the other one was invoked.
So if you make a 'test' program under Linux do NOT call it 'test'!
MODULE prf;
FROM IOport IMPORT InPort, OutPort, IOperm;
FROM InOut IMPORT WriteString, WriteLn, WriteCard, WriteBf;
VAR ioPerm : BOOLEAN;
ioVal : CARDINAL;
BEGIN
ioPerm := FALSE;
WriteString ('Start....');
WriteLn;
WriteString ('ioPerm = ');
IF ioPerm = TRUE THEN
WriteString ('TRUE.')
ELSE
WriteString ('FALSE.')
END;
WriteLn;
WriteBf;
ioPerm := IOperm (378H, 37AH, TRUE);
IF ioPerm = FALSE THEN
WriteString ('No permission to access ports 378 - 37A.');
WriteLn;
HALT
ELSE
WriteString ('Permission received to access ports 378 - 37A.');
WriteLn
END;
WriteString ('ioPerm = ');
IF ioPerm = TRUE THEN
WriteString ('TRUE.')
ELSE
WriteString ('FALSE.')
END;
WriteLn;
WriteString ('We have executed the IOperm function and are out of it again.');
WriteLn;
WriteString ('Coming up now: OutPort and InPort and the WriteCard.');
WriteLn;
WriteBf;
OutPort (378H, 1);
ioVal := InPort (378H);
WriteCard (ioVal, 4);
WriteLn;
WriteString ('The WriteCard has executed. Time to redeem IOperm.');
WriteLn;
WriteString ('ioPerm = ');
IF ioPerm = TRUE THEN
WriteString ('TRUE.')
ELSE
WriteString ('FALSE.')
END;
WriteLn;
WriteBf;
IF ioPerm = TRUE THEN
WriteString ("ioPerm = TRUE and we're gonna give it back.");
WriteLn;
WriteBf;
ioPerm := IOperm (378H, 37AH, FALSE);
IF ioPerm = FALSE THEN
WriteString ('Could not return I/O privileges.');
WriteLn;
WriteString ('Aborting.');
WriteBf;
HALT
ELSE
WriteString ('I/O privileges successfully returned.');
WriteLn
END;
WriteBf
ELSE
WriteString ('If this message is printed, something fishy is going on.');
WriteLn
END;
WriteBf
END prf.
OK, lads, now download and use it.
Oh, I almost forgot: you need to give your program enough privileges, otherwise it will be denied to call the IOperm system call. After compiling your source, you must become 'root' and then enter the following magic spells:
bash-2.05# chown root.root prf bash-2.05# chmod 4755 prfA little swearing is allowed but a magic wand is an accessory and it will cost you extra. The wand is not covered by the GNU GPL, contrary to the sources you saw until now on this page.
If you forget to chown and chmod the executable you end up with this:
jan@beryllium:~/modula/lib$ ./prf Start.... ioPerm = FALSE. Permission received to access ports 378 - 37A. ioPerm = TRUE. We have executed the IOperm function and are out of it again. Coming up now: OutPort and InPort and the WriteCard. Segmentation faultForget either of the two and you get SegFaults by the dozen...
Page created 8 August 2004,
Page equipped with GoogleBuster technology