Intercept 15H/4FH to use a hotkey

67 Views Asked by At

I have a flag variable which is supposed to be 1 upon pressing ctr+alt+s and 0 upon pressing ctrl+alt+h . Int 09 is a hardware interrupt invoked by a keypress and it will further call a software interrupt 15H/4FH as I was told in class. I need to check whether the service 4F was called for interrupt 15, if so I need to intercept the interrupt to create a 'keyboard hook' - I've lost my notes from the keyboard hook lecture and cannot recall it very well. All I can remember is that if the service number that was used when INT 15H was called was 4FH then I need to intercept and invoke keyboard hook else proceed as normal.

I know that I can use the keyboard byte at 40H:17H to check for alt and ctrl. I remember that int 09 will be invoked automatically upon a keypress and I need to check the keyboard at port 60H to check for scan code of s or h.

This my code that checks for service 4F:

 ; Input Handling
    
    
     ; get-vector safe keep orignal int 15
    
    mov ax, es:[15 * 4] 
    
    mov word ptr oldInt15, ax 
    
    mov ax, es:[15 * 4 + 2]
    mov word ptr oldInt15[2], ax
      
    ; Decide wether to call keyboard hook  
      
    cmp ah, 4Fh
    je _KeyboardHook

All I know is that the service number would be inside ah so thats what I'm checking. Will it really happen by itself upon a keypress?

This is my attempt at the keyboard hook:

_KeyboardHook:
     
   
    
    in al, 60h
    cmp al, 16h
    je _set
    cmp al, 0bh
    je _clear
    
    
    _set:
    mov ax, 40h
    mov es, ax
    
    mov ax, es:[0017]
    
    and ax, 00110000B
    jz _dontSet
    
    mov boolDisplay, 01h
    
    _dontSet:
    jmp _Further
    
    _clear:
    
    mov ax, 40h
    mov es, ax
    
    mov ax, es:[0017]
    
    and ax, 00110000B
    jz _dontClear
    
    mov boolDisplay, 00h
    
    _dontClear:
    jmp _Further 

I was unsure about intercepting 15H at this point because if it is called with service 4F and then its changed wouldn't we need to wait for INT 15H to be invoked again so that the changed vector would be invoked instead? I'm starting to think I was only supposed to intercept and change the service 4F of this vector. I've done vector intercepting but I'm not sure about how to intercept a service. In any case does this make sense? Would you be able to use hot keys like this?

The code thats after _Further: is controlled by the boolDisplay flag but nothing happens when I press the intended hot keys

1

There are 1 best solutions below

2
Sep Roland On

Your code is a bit all over the place!

Not only is it hard (even impossible) to follow the execution of your code, it contains several errors as written:

mov ax, es:[15 * 4 + 2]
mov word ptr oldInt15[2], ax

; Decide wether to call keyboard hook  
cmp ah, 4Fh

The interrupt is 15h, that's an hexadecimal number. You erroneously wrote a decimal number 15. And once you used the AX register, don't expect to find the function number in AH. Remember that AL and AH are part of AX.

in al, 60h
cmp al, 16h
je _set
cmp al, 0bh
je _clear

Where did you get these scancodes from? 1Fh is for 's' and 23h is for 'h'.

and ax, 00110000B
jz _dontSet

and ax, 00110000B
jz _dontClear

Where did you get these bit settings from? Bit 2 is for CTRL(s) and bit 3 is for ALT(s). Numbering starts from the right, so the correct mask is 00001100b. Also testing for zero is not enough to make sure both CTRL and ALT are pressed together.

First hook the interrupt 15h.

Pay attention to the fact that it is hexadecimal 15h (so not decimal 15 that you wrote).
You hook the vector at program start and release the hook before program termination:

; Create .COM program. We'll have CS=DS=ES=SS.
    org     256
; Hook the 15h interrupt.
    xor     ax, ax
    mov     es, ax
    cli
    mov     ax, offset MyInt15
    xchg    ax, es:[0015h*4]
    mov     [Int15+1], ax                ; Patch the 'jmpf' instruction (offset)
    mov     ax, cs
    xchg    ax, es:[0015h*4+2]
    mov     [Int15+3], ax                ; Patch the 'jmpf' instruction (segment)
    sti

    ...

; Restore the 15h interrupt.
    cli
    mov     ax, [Int15+1]
    mov     es:[0015h*4], ax
    mov     ax, [Int15+3]
    mov     es:[0015h*4+2], ax
    sti
; Terminate.
    mov     ax, 4C00h   
    int     21h
; -----------------------------------
boolDisplay db 0
; -----------------------------------

Then write the replacement handler.

You watch out for a function number of 4Fh in the AH register. You chain to the original int 15h handler for anything else:

MyInt15:
    cmp     ah, 4Fh                     ; Is it Keyboard Intercept ?
    jne     Int15

    ...

Int15:
    ; jmpf    0:0                         ; Chain to the original handler
    db  0EAh, 0, 0, 0, 0
; -----------------------------------

Then check for your hotkeys.

The scancode is in the AL register, and the status for CTRL and ALT is in the BIOS data area at address 0417h (but not in the bits that you selected in your question!):

MyInt15:
    cmp     ah, 4Fh
    jne     Int15
    push    ax
    push    ds
    xor     ax, ax
    mov     ds, ax
    test    byte ptr [0417h], 00001100b ; Bit 2 is 'Either CTRL'
    pop     ds                          ; Bit 3 is 'Either ALT'
    pop     ax
    jz      Int15                       ; Neither bit set
    jpo     Int15                       ; Only one bit set

    cmp     al, 1Fh                     ; Scancode of 's'
    je      SetBool
    cmp     al, 23h                     ; Scancode of 'h'
    je      ClearBool

Int15:
    ; jmpf    0:0                         ; Chain to the original handler
    db  0EAh, 0, 0, 0, 0

SetBool:
    mov     cs:[boolDisplay], 1
    iret
ClearBool:
    mov     cs:[boolDisplay], 0
    iret
; -----------------------------------

[EDIT]

From a comment:

I'm checking for 4F in the NewInt15 proc after hooking it to the vector table. If its not 4f then I'm restoring the original INT 15 in the vector table.

That's totally wrong and not what I wrote in my answer above! Restoring the original int 15h happens only once and just before terminating the program. Most certainly not from within the new handler.

So if the service number is 4F then the scan code of s or h is supposed to be in al?

Correct, and it is like that for many other scancodes too. It is the usual int 09h handler that retrieves the scancode from the keyboard, moves it to AL, and invokes int 15h, AH=4Fh.

What follows is a demo program that was fully tested to work in DOSBox.

Just press any keys that you like, and observe that once you press CTRLALTs the number on screen becomes '1'. As soon as you press CTRLALTh the number on screen becomes '0'.
Use Esc to exit the program. Try some other keys too...

; Create .COM program. We'll have CS=DS=ES=SS.
    ORG     256
; Hook the 15h interrupt.
    xor     ax, ax
    mov     es, ax                       ; CONST ES=0
    cli
    mov     ax, offset MyInt15
    xchg    ax, es:[0015h*4]
    mov     [Int15+1], ax                ; Patch the 'jmpf' instruction (offset)
    mov     ax, cs
    xchg    ax, es:[0015h*4+2]
    mov     [Int15+3], ax                ; Patch the 'jmpf' instruction (segment)
    sti

MainLoop:
    mov     al, boolDisplay              ; [0,1]
    add     al, '0'
    mov     ah, 0Eh                      ; BIOS.Teletype
    int     10h
    mov     ah, 00h                      ; BIOS.WaitForKeystroke
    int     16h                          ; -> AX
    cmp     al, 27
    jne     MainLoop                     ; Continue while not ESC

; Restore the 15h interrupt.
    cli
    mov     ax, [Int15+1]
    mov     es:[0015h*4], ax
    mov     ax, [Int15+3]
    mov     es:[0015h*4+2], ax
    sti
; Terminate.
    mov     ax, 4C00h                    ; DOS.Terminate
    int     21h
; -----------------------------------
boolDisplay db 0
; -----------------------------------
; IN (al,CF) OUT (al,CF)
MyInt15:
    cmp     ah, 4Fh
    jne     Int15
    push    ax
    push    ds
    xor     ax, ax
    mov     ds, ax
    test    byte ptr [0417h], 00001100b ; Bit 2 is 'Either CTRL'
    pop     ds                          ; Bit 3 is 'Either ALT'
    pop     ax
    jz      Int15                       ; Neither bit set
    jpo     Int15                       ; Only one bit set

    cmp     al, 1Fh                     ; Scancode of 's'
    je      SetBool
    cmp     al, 23h                     ; Scancode of 'h'
    je      ClearBool

Int15:
    ; jmpf    0:0                         ; Chain to the original handler
    db  0EAh, 0, 0, 0, 0

SetBool:
    mov     cs:[boolDisplay], 1
    iret
ClearBool:
    mov     cs:[boolDisplay], 0
    iret
; -----------------------------------