Analysis model: gpt-5.5 xhigh

The Firm Loader (1) by The FiRM - Technical Dissection

The Firm Loader (1) is an August 1989 MS-DOS cracktro / loader by The FiRM. Pouet lists it as a cracktro for MS-DOS, and the preserved Scene.org package notes that this loader was spread with Ancient Land of Ys.

Release year: 1989

This is a much more interesting specimen than a plain TheDraw screen saver. The visible page is text mode, but the file is a custom .COM loader shell: it saves the machine state, installs keyboard/timer/Ctrl-Break hooks, detects the display target, draws a table-driven CP437 logo, blinks one outline for a short wait period, decodes an obfuscated child filename, runs that child through DOS EXEC, then restores the interrupt table and video state before exiting.

The inner loops are compact but dense. The drawing stream is not a bitmap. Each logo cell is stored as a packed text-memory byte offset plus a 4-bit character index. The filename is not stored plainly either: a symmetric overlapping word-XOR pass turns the buffer at 05B0h into gitum.000 immediately before the INT 21h AX=4B00h handoff, then runs the same pass again to hide it.

Sources

Pouet records:

Title:        the firm loader (1)
Group:        The Firm
Type:         Cracktro
Platform:     MS-Dos
Released:     August 1989

The Scene.org package contains a short info.txt which says the loader was spread with Ancient Land of Ys.

Visual References

The cracktro has one main visual state plus a short blink/wait phase. The 00:00.000 timestamp below means "after the table render has produced the page", not a captured DOSBox timeline.

Time Still What is happening
00:00.000 The Firm Loader (1) public Pouet screenshot Public Pouet screenshot of the text-mode loader page.
00:00.000 The Firm Loader (1) decoded text-mode reconstruction Direct reconstruction from the two coordinate streams, the 16-byte CP437 character table, the texture stream, and the bottom text stream.

Generated runtime map:

The Firm Loader (1) YS.COM runtime map

Visual/Archive-To-Code Concordance

This page does not have a timed DOSBox video sequence. The useful visual evidence is the public Pouet GIF screenshot, the decoded text-mode reconstruction, and the generated runtime map. Those are enough to tie the visible loader page to the recovered code without inventing a motion capture.

The public screenshot and decoded reconstruction both correspond to the page builder at 01B5h. The startup path normalizes the segment registers, saves machine state, installs interrupt wrappers, detects the display target, and then calls the visible-page builder:

0140  call 056Eh        ; normalize DS/ES/SS and SP
014F  call 0339h        ; save video, cursor, ports, Ctrl-Break, full IVT
0152  call 03D3h        ; install INT 23h, INT 09h, INT 08h wrappers
0155  call 04DAh        ; detect display, set mode, choose text segment
0158  call 01B5h        ; draw visible page and wait/blink

Inside 01B5h, the top-left THE logo and the large FiRM outline are not a stored 4000-byte text page. They are two coordinate streams rendered through 0235h, using different base delay/attribute values:

01C6  mov bp,3F00h
01C9  mov si,079Eh
01CC  mov dl,[05E3h]
01D0  call 0235h        ; draw first coordinate stream

01D3  mov bp,3400h
01D6  mov si,085Ch
01D9  mov dl,[05E4h]
01DD  call 0235h        ; draw second coordinate stream

The decoded screen proves that interpretation: the renderer's packed words become text-memory byte offsets plus CP437 character indexes. The inner loop extracts DI = AX >> 4, uses the low nibble as an index into the 16-byte table at 0792h, and writes an attribute/character word:

0245  lodsw             ; AX = packed coordinate/char-index word
024B  mov di,ax
024D  shr di,1
024F  shr di,1
0251  shr di,1
0253  shr di,1          ; DI = AX >> 4
0255  and al,0Fh        ; character-table index
0257  xlat
0258  mov cl,al
026E  call 0322h        ; final cell write

The small visual difference between the static screenshot and the runtime logic is the wait/blink phase. After the initial draw, the loader performs up to 64 passes where it redraws only the second outline stream with alternating attributes:

01EC  mov bx,0040h      ; 64 passes
01EF  mov bp,0001h      ; fast redraw mode
01F2  mov dl,[05E4h]
01F6  mov dh,[05E5h]
020B  xchg dh,dl
020D  call 0235h        ; redraw second outline stream

Because BP=0001h, the renderer skips the temporary flash cell and writes the final CP437 cell directly. That is the loader's only intended animation-like visual state: an attribute blink of the large FiRM outline while waiting for a key or timeout.

The generated runtime map belongs to the non-visual loader shell around the page. After the wait, 01B5h clears the text screen, the filename mutator at 0590h decodes 05B0h into gitum.000, and the standard DOS EXEC parameter block at 05D0h is patched to point into the current CS:

015B  call 0590h        ; decode child filename buffer
015E  mov  ax,cs
0160  mov  [05D4h],ax
0163  mov  [05D8h],ax
0166  mov  [05DCh],ax
0169  mov  bx,05D0h
016C  mov  dx,05B0h
016F  mov  ax,4B00h
0172  int  21h          ; EXEC child

The same map also explains why the loader saves and restores so much state for a simple-looking text page. The visible page is only the front end; the program also installs Ctrl-Break, keyboard, and timer wrappers, supervises the keyboard hook while the child runs, restores the IVT and hardware ports, re-obfuscates the child filename, and only then prints a success or DOS error message.

So the visual evidence is organized like this: the public GIF is the preserved screen state, the decoded screen proves the packed coordinate streams and CP437 table, and the runtime map ties that page to the surrounding loader lifecycle: state save, interrupt hooks, CGA-safe cell writer, blink wait, hidden child EXEC, restore, and status output.

Archive

The examined ZIP:

8ffff321b587272febbc42ffa3cc685b6dac21cab26c9b0b5e650ab04113f21a  the_firm_loader.zip

ZIP central directory:

info.txt   48 bytes    stored      2020-01-12 14:59
YS.COM   2502 bytes    deflated    1989-08-15 23:52

Extracted files:

d988a5703b5680b1a1859235f327ffb04eb2cb467ebd312cbad2ec371eaeaf66  info.txt
8832bcf7bce8022b92a481c225c25ebb470f746c958876288c7ff7105db4e747  YS.COM

Public screenshot:

934969b72f4f89d00b1d6b5af207951671e34be3b0e5dacfa6a5b95ec423cccd  the-firm-loader-1-pouet.gif

The archive does not contain the eventual child program. The loader decodes and tries to EXEC gitum.000, but the package only includes YS.COM and info.txt.

COM Layout

For a DOS .COM file:

file offset 0000h -> runtime CS:0100h
runtime offset    = file offset + 0100h
file offset       = runtime offset - 0100h

The top-level runtime layout:

0100-013F  jump plus ASCII author tag
0140-01B4  startup, save/install/setup, EXEC handoff, status messages
01B5-0234  visible-page builder and 64-pass wait/blink loop
0235-0338  coordinate renderer, text writer, CGA-safe cell writer
0339-03D2  save and restore video/ports/IVT/control-break state
03D3-04D9  INT 23h, INT 09h, and INT 08h hook installation and handlers
04DA-056D  display detector and B800h/B000h target selection
056E-05AE  segment/stack normalizer and filename mutator
05AF-06A0  obfuscated filename, EXEC parameter block, messages, text streams
06A1-0791  texture/twinkle seed table
0792-079D  16-byte CP437 drawing-character table
079E-085B  first logo coordinate stream
085C-0AC5  second logo / outline coordinate stream
0AC6-0AEF  saved flags, ports, vectors, return thunk
0AF0-0EEF  1024-byte saved interrupt-vector-table copy

The file starts with a jump over a plain author banner:

0100  EB 3E             jmp 0140h
0102                    " FIRMLOADER <  was written by       The       Software Surgeon"

That banner is data only; execution begins at 0140h.

Startup Flow

The main routine is compact:

0140  call 056Eh        ; normalize DS/ES/SS and SP
0143  mov  bx,010Fh
0147  mov  ah,4Ah
0149  int  21h          ; shrink DOS memory block
014B  mov  al,00h       ; error code for resize failure
014D  jc   0174h
014F  call 0339h        ; save video, cursor, ports, Ctrl-Break, full IVT
0152  call 03D3h        ; install INT 23h, INT 09h, INT 08h wrappers
0155  call 04DAh        ; detect display, set mode, choose text segment
0158  call 01B5h        ; draw visible page and wait/blink
015B  call 0590h        ; decode child filename buffer
015E  mov  ax,cs
0160  mov  [05D4h],ax
0163  mov  [05D8h],ax
0166  mov  [05DCh],ax   ; patch EXEC parameter block segments to CS
0169  mov  bx,05D0h     ; EXEC parameter block
016C  mov  dx,05B0h     ; ASCIIZ child filename
016F  mov  ax,4B00h
0172  int  21h          ; EXEC child
0174  lahf              ; save EXEC/resize flags across cleanup
0175  call 056Eh        ; normalize again after child/interrupt path
0178  call 0381h        ; restore IVT, ports, mode, cursor, Ctrl-Break
017B  call 0590h        ; re-obfuscate filename buffer
017E  sahf              ; recover original carry/error state
017F  jae  01A3h        ; success path

The LAHF / SAHF pair is important. The loader wants to restore machine state and re-hide the filename before it decides which message to print, but it also wants the carry flag and AL error code from the earlier DOS call.

Error dispatch:

0181  mov dx,05E8h      ; memory allocation error
0184  cmp al,00h
0186  je  01ACh
0188  mov dx,0605h      ; file not found
018B  cmp al,02h
018D  je  01ACh
018F  mov dx,0619h      ; insufficient memory
0192  cmp al,08h
0194  je  01ACh
0196  mov dx,0631h      ; DOS error-code (00) detected
0199  aam 0Ah
019B  xchg ah,al
019D  add [0643h],ax    ; patch two ASCII digit slots
01A1  jmp 01ACh

AL=00h is not a normal DOS EXEC error. It is preloaded before the memory resize branch, so a failed INT 21h AH=4Ah prints the memory-allocation message through the same cleanup path.

On success:

01A3  mov si,067Ch
01A6  call 02FBh        ; draw "Thanks for supporting THE FiRM!"
01A9  mov dx,0653h      ; CR/LF/LF "$"
01AC  mov ah,09h
01AE  int 21h
01B0  mov ax,4C00h
01B3  int 21h

So the loader never returns to the caller. It exits through DOS after the child program returns or after an error.

Segment Normalizer

Routine 056Eh is called at entry, after EXEC, and from interrupt-exit paths. It behaves like a local far/near thunk:

056E  pop  cs:[0ACBh]   ; save return IP
0573  mov  cs:[0AC9h],cs
0578  mov  ds,cs:[0AC9h]
057D  mov  es,[0AC9h]
0581  cli
0582  mov  ss,[0AC9h]
0586  mov  sp,10F0h
0589  sti
058A  jmp  [0ACBh]      ; continue after the original call

This makes the whole program independent of whatever segment registers the child or interrupt path left behind. It also discards the caller's stack and uses a fixed stack at CS:10F0h, safely above the 2502-byte file body and its state buffers.

Saved State

Routine 0339h snapshots more than the visible text page needs:

033F  mov ah,0Fh
0341  int 10h
0343  mov [0ACDh],al    ; current video mode

0346  mov ah,03h
0348  int 10h
034A  mov [0ACEh],cx    ; cursor shape

034E  mov ax,3300h
0351  int 21h
0353  mov [0AD0h],dl    ; Ctrl-Break flag

0357  in  al,61h
0359  mov [0AD1h],al
035C  in  al,21h
035E  mov [0AD2h],al
0361  in  al,0A1h
0363  mov [0AD3h],al    ; speaker/PIC masks

0368  xor si,si
036A  mov ds,si
0370  mov di,0AF0h
0373  mov cx,0200h
0376  rep movsw         ; copy 1024-byte IVT from 0000:0000

The IVT copy is full table state, not just the vectors this program touches. The restore routine 0381h copies those 1024 bytes back under CLI/STI, then restores ports 61h, 21h, A1h, reprograms PIT channel 0 to mode 3 with a zero count, restores the DOS Ctrl-Break flag, resets the old video mode, and restores the cursor shape.

The PIT reset is:

03A6  mov al,36h
03A8  out 43h,al
03AA  mov al,00h
03AC  out 40h,al
03AE  mov al,00h
03B0  out 40h,al

That puts channel 0 back into the standard square-wave timer mode with count 0000h, meaning 65536.

Display Setup

Routine 04DAh calls the detector at 0515h and stores its return value in 0AC6h. The rest of the renderer uses 0AC6h == 1 as the "wait on port 03DAh before touching text memory" case.

Display setup:

04DE  call 0515h
04E1  mov [0AC6h],al
04E4  cmp al,00h
04E6  je  04F3h

04E8  mov al,03h
04EA  mov ah,00h
04EC  int 10h           ; colour 80x25 text
04EE  mov ax,0B800h
04F1  jmp 050Dh

04F3  mov al,02h
04F5  mov ah,00h
04F7  int 10h           ; loader's monochrome branch
04F9  mov word [05E3h],0707h
04FF  mov word [05E5h],0F0Fh
0505  mov byte [05E7h],00h
050A  mov ax,0B000h

050D  mov [0AC7h],ax    ; text memory segment for all writers

The nonzero path sets BIOS mode 3 and writes to B800h. The zero path is the loader's monochrome branch: it changes the attributes and writes to B000h.

The detector itself first tries INT 10h AX=1A00h display-combination state, then INT 10h AX=1200h BL=10h, then a BIOS data-area check at 0040:0063h. The returned value is not used as a rich adapter enum. In this file it only selects colour versus monochrome attributes and whether text writes should wait for the CGA status bit.

Page Builder

Routine 01B5h builds the visible page:

01BB  mov cx,[0ACEh]
01BF  or  ch,20h
01C2  mov ah,01h
01C4  int 10h           ; hide cursor

01C6  mov bp,3F00h
01C9  mov si,079Eh
01CC  mov dl,[05E3h]    ; colour path: 0Fh
01D0  call 0235h        ; draw first coordinate stream

01D3  mov bp,3400h
01D6  mov si,085Ch
01D9  mov dl,[05E4h]    ; colour path: 09h
01DD  call 0235h        ; draw second coordinate stream

01E0  mov si,06A1h
01E3  call 0290h        ; XOR/rotate texture patch

01E6  mov si,0657h
01E9  call 02FBh        ; bottom "* * * PRESENTS * * *"

The first coordinate stream draws the THE logo at the top-left. The second stream draws the large FiRM outline. The text stream at 0657h writes the bottom centered presentation line.

The reconstruction generated from those tables produced these nonblank rows:

02:    THE                                      Software Surgeon    Bilbo Baggins
03:                                             Maestro             Barimor
04:                                             Mr. Camaro          The Guch
05:                                             Lord Blix           Wild Patrick
06:                                             Chancelor Hogmeat   Chancelor Hogmeat
07:                                             Electric Rat        Da Viper
09-21: large FiRM outline
23:                         * * *   P R E S E N T S   * * *

The image also contains line-drawing characters; the simplified text above is only a row index, not a replacement for the decoded screenshot.

Coordinate Renderer

Routine 0235h is the core logo renderer. Inputs:

SI = coordinate stream
DL = attribute byte
BP = delay / special mode

It begins by loading the destination text segment and the 16-byte character table:

023C  mov es,[0AC7h]
0240  mov bx,0792h      ; 16-byte CP437 character table
0243  mov ch,dl         ; attribute in high byte of screen word

Inner loop:

0245  lodsw             ; AX = packed coordinate/char-index word
0246  cmp ax,0FFFFh
0249  je  0288h
024B  mov di,ax
024D  shr di,1
024F  shr di,1
0251  shr di,1
0253  shr di,1          ; DI = AX >> 4 = byte offset in text memory
0255  and al,0Fh        ; low nibble = character table index
0257  xlat              ; AL = [0792h + AL]
0258  mov cl,al         ; screen word = CH:CL = attribute:character

Each stream word is therefore:

bits 15..4  text-memory byte offset
bits  3..0  CP437 character-table index

This is why the stream can draw boxes and diagonal outline pieces without storing full character/attribute words for every point.

The optional delayed flash write:

025A  cmp bp,0001h
025D  je  026Eh
025F  push cx
0260  mov ch,[05E6h]    ; colour path: 07h
0264  mov cl,04h        ; temporary diamond/control glyph
0266  call 0322h
0269  mov cx,bp
026B  loop 026Bh        ; busy delay
026D  pop cx
026E  call 0322h        ; final cell

The first two calls use BP=3F00h and BP=3400h, so they draw a temporary character, wait, then draw the real CP437 line piece. The animation loop later uses BP=0001h, which skips the temporary flash and just redraws the second outline quickly.

The key-skip path:

0271  cmp bp,0B00h
0275  jb  0245h
0277  je  0282h
0279  mov ah,01h
027B  int 16h
027D  je  0282h
027F  mov bp,0B00h
0282  mov ah,0Bh
0284  int 21h
0286  jmp 0245h

For the initial slow draw, a pending key shortens later delays by forcing BP=0B00h. The DOS AH=0Bh call is a standard input-status poll whose return value is ignored here; it just gives DOS/BIOS a chance to observe console status while the renderer continues.

Cell Writer

Every renderer path eventually calls 0322h:

0324  cmp byte [0AC6h],01h
0329  jne 0333h
032B  mov dx,03DAh
032E  in  al,dx
032F  shr al,1
0331  jae 032Eh         ; wait until status bit 0 was set
0333  mov es:[di],cx
0336  pop dx
0337  pop ax
0338  ret

When the display detector returned 1, the write waits on CGA status port 03DAh before storing the character/attribute word. On the other paths it writes directly to B800h or B000h.

Texture / Twinkle Patch

Routine 0290h draws a 12-row by 20-column patch beginning at text-memory byte offset 0184h. It is not a plain bitmap. It picks one of twelve 20-byte windows from the table at 06A1h using the BIOS tick count, then XORs and rotates characters already present on the screen.

Selection:

029B  mov ah,00h
029D  int 1Ah           ; CX:DX = BIOS ticks since midnight
029F  xor ax,ax
02A1  xchg ax,dx
02A2  shr ax,1
02A4  mov cx,000Ch
02A8  div cx            ; remainder in DX selects 0..11
02AA  mov al,14h
02AC  mul dl            ; AL = selected row * 20
02AE  mov bx,si         ; base = 06A1h
02B0  add si,ax
02B2  mov di,0184h

Inner 20-column loop:

02B9  mov cx,0014h
02BC  mov ah,[05E7h]    ; colour path: 0Ch
02C0  cmp byte [si],0FFh
02C3  jne 02C7h
02C5  mov si,bx         ; FF wraps back to base table
02C7  lodsb
02C8  mov dx,si
02CA  xor al,dl         ; character byte depends on current stream address
02DD  xor es:[di],ax    ; XOR char/attr into existing screen word
02E0  rol byte es:[di],1
02E3  inc di
02E4  inc di
02E5  loop 02C7h

At the row boundary:

02E7  pop cx
02E8  test cx,0001h
02EC  je  02F1h
02EE  add di,0050h      ; skip one text row on alternating passes
02F1  loop 02B8h

The patch therefore adds a deterministic but tick-selected disturbance to part of the page. It uses only 240 byte reads per call and reuses the text page as its own destination buffer.

Text Writer

Routine 02FBh handles the bottom line and the success message. Stream format:

word  destination byte offset in text memory
byte  initial attribute
byte  character data...
00h   attribute switch marker; next byte becomes the new attribute
FFh   end of stream

The loop:

0304  lodsw
0305  mov di,ax
0307  lodsb
0308  mov ch,al         ; attribute
030A  lodsb
030B  mov cl,al         ; character
030D  cmp al,00h
030F  je  0307h         ; zero means read a new attribute
0311  cmp al,0FFh
0313  je  031Ch
0315  call 0322h
0318  inc di
0319  inc di
031A  jmp 030Ah

The stream at 0657h decodes as:

destination: 0E8Eh
attribute:   F0h
text:        * * *   P R E S E N T S   * * *

The success stream at 067Ch starts at 00A0h, writes Thanks for supporting in attribute 07h, sees a zero marker, switches to attribute 0Fh, and then writes THE FiRM!.

Wait / Blink Loop

After the first render, the loader waits up to 64 redraw passes:

01EC  mov bx,0040h      ; 64 passes
01EF  mov bp,0001h      ; fast redraw mode for renderer
01F2  mov dl,[05E4h]    ; colour path: 09h
01F6  mov dh,[05E5h]    ; colour path: 0Ch

01FA  xor cx,cx
01FC  loop 01FCh        ; 65536-loop busy delay

01FE  mov ah,01h
0200  int 16h
0202  jne 0215h         ; key ready -> consume and exit wait

0204  mov ah,0Bh
0206  int 21h           ; DOS stdin status, return unused

0208  mov si,085Ch
020B  xchg dh,dl        ; alternate 09h and 0Ch
020D  call 0235h        ; redraw second outline stream
0210  dec bx
0211  je  0219h
0213  jmp 01FAh

Because BP=1, the renderer skips the temporary flash cell and writes the second coordinate stream directly. The only visual change during this phase is the big FiRM outline alternating between the two colour attributes.

The wait exits either when a key is pending or after all 64 passes:

0215  mov ah,00h
0217  int 16h           ; consume key
0219  mov cx,0000h
021C  mov dx,184Fh
021F  mov bh,07h
0221  mov ax,0600h
0224  int 10h           ; clear full 80x25 page
0226  mov cx,[0ACEh]
022A  mov ah,01h
022C  int 10h           ; restore saved cursor shape

The page clear happens before the child EXEC, so the loaded game or payload starts with a clean text page.

Filename Mutator

Routine 0590h is symmetric: one call decodes, a second call restores the original obfuscated bytes. It operates on overlapping words, so each iteration can read one byte that the previous iteration just wrote.

The actual loop:

0590  push ax
0591  push bx
0592  push cx
0593  mov  cx,001Fh
0596  mov  bx,0619h

0599  mov  ax,[bx-0069h] ; starts at 05B0h
059C  add  bx,bx
059E  inc  bx
059F  xor  al,bh
05A1  inc  bx
05A2  xor  ah,bl
05A4  shr  bx,1
05A6  mov  [bx-006Ah],ax ; starts at 05B0h
05A9  loop 0599h

Emulating that exact byte order on the preserved file gives:

before 05B0h: 6B 51 4E 41 5B 1E 02 7C 7E 48 B5 BB ...
after  05B0h: 67 69 74 75 6D 2E 30 30 30 00 FF FF ...
ASCII:        g  i  t  u  m  .  0  0  0 \0

So the decoded child program name is:

gitum.000

A second call produces the original obfuscated bytes again. That is why the loader can hide the child name in the file, reveal it for EXEC, then hide it again before printing status and exiting.

DOS EXEC Parameter Block

Immediately before EXEC, the code patches three segment fields in the block at 05D0h:

015E  mov ax,cs
0160  mov [05D4h],ax
0163  mov [05D8h],ax
0166  mov [05DCh],ax
0169  mov bx,05D0h
016C  mov dx,05B0h
016F  mov ax,4B00h
0172  int 21h

The parameter block bytes before segment patching:

05D0: 00 00    environment segment = inherit parent environment
05D2: 80 00    command tail offset = 0080h
05D4: 00 00    command tail segment, patched to CS
05D6: 5C 00    first FCB offset = 005Ch
05D8: 00 00    first FCB segment, patched to CS
05DA: 6C 00    second FCB offset = 006Ch
05DC: 00 00    second FCB segment, patched to CS

That is the standard DOS EXEC parameter block for AH=4Bh: inherit the environment, pass the PSP command tail, and pass the two default FCBs from the loader's PSP area.

Interrupt Hooks

The loader installs three interrupt paths.

INT 23h is the Ctrl-C / Ctrl-Break handler:

041D  clc
041E  jmp 0174h

That lands in the normal cleanup path. It deliberately clears carry, so a Ctrl-Break exit is treated like a non-error cleanup rather than a DOS failure.

INT 09h first points to a far jump through 0AD5h:

0421  jmp far cs:[0AD5h]

At install time, 0AD5h:0AD7h points to the local keyboard handler at 042Eh. The old BIOS keyboard vector is saved at 0AD9h:0ADBh.

The local handler:

042E  push ax
042F  in   al,60h
0431  mov  ah,al
0433  xchg al,cs:[05E2h] ; AL = previous scan byte, [05E2] = current
0438  cmp  ax,cs:[05E0h] ; bytes 1Dh,4Eh
043D  pop  ax
043E  jne  0454h
0440  in   al,61h
0442  mov  ah,al
0444  or   al,80h
0446  out  61h,al
0448  mov  al,ah
044A  out  61h,al
044C  mov  al,20h
044E  out  20h,al
0450  clc
0451  jmp  0174h
0454  jmp far cs:[0AD9h] ; chain old INT 09h

The hotkey comparison is a two-scan-byte sequence, not an ASCII key. The stored word is 4E1Dh, meaning previous scan byte 1Dh followed by current scan byte 4Eh. On an IBM PC keyboard that is the Ctrl make code followed by keypad plus. If the sequence is not seen, the handler chains to the old keyboard ISR.

INT 08h is a timer wrapper:

0426  call 0459h
0429  jmp far cs:[0ADDh] ; old INT 08h

Routine 0459h watches the real IVT entry at 0000:0024h, the keyboard interrupt vector. Its job is to keep the loader's keyboard wrapper and the saved old keyboard vector coherent while the child program is running:

045D  xor ax,ax
045F  mov es,ax
0461  mov ax,cs
0465  mov bx,0024h      ; IVT slot for INT 09h
0468  cmp byte cs:[0AD4h],00h
046E  jne 04A1h
...
0476  mov byte cs:[0AD4h],0FFh
047C  mov ax,[0AD9h]    ; rotate old vector into wrapper slot
047F  mov [0AD5h],ax
...
0495  mov ax,042Eh
0498  mov es:[bx],ax
049B  mov es:[bx+2],cs  ; install local handler directly

The reverse branch notices when the IVT again points at the wrapper and swaps the saved pointers back:

04AF  mov byte cs:[0AD4h],00h
04B5  mov ax,[0AD5h]
04B8  mov [0AD9h],ax
...
04C1  mov ax,042Eh
04C4  mov [0AD5h],ax
04C7  mov [0AD7h],cs
04CB  mov ax,0421h
04CE  mov es:[bx],ax
04D1  mov es:[bx+2],cs

This is more elaborate than the visible page requires. It is loader-shell infrastructure: keep a bailout key alive while the child runs, then restore the machine exactly.

What Makes It Distinctive

For an August 1989 PC cracktro, this is not visually huge, but it is technically busy:

The practical runtime sequence is:

normalize segments and stack
shrink DOS memory block
save video, cursor, Ctrl-Break, PIC/speaker ports, and full IVT
install Ctrl-Break, keyboard, and timer wrappers
detect text display target
draw THE + FiRM page from coordinate streams
blink the FiRM outline for up to 64 passes or until keypress
clear text page
decode 05B0h -> "gitum.000"
EXEC gitum.000 with inherited environment and PSP command tail/FCBs
restore IVT, ports, timer, video mode, cursor, and Ctrl-Break state
re-obfuscate filename buffer
print success or DOS error message
exit via INT 21h AX=4C00h

That combination of tiny visual data, defensive state restoration, and a hidden child handoff is exactly the profile of a reusable cracktro loader rather than a standalone demo effect.