Skip to content

Latest commit

 

History

History
151 lines (130 loc) · 9.08 KB

File metadata and controls

151 lines (130 loc) · 9.08 KB

06 — The program: an unpacked Turbo C++ game that names its studio in two menus, loads from two floppies by a letter in a 2-byte file, asks the manual for a platform number, and has been told the answer is always right

Measure: python tools/strdump.py Bumpy/BUMPY.EXE --min 5 (notes/strings-bumpy.txt), python tools/mz.py Bumpy/BUMPY.EXE (notes/mz-bumpy.txt), python tools/dosdis.py Bumpy/BUMPY.EXE --at 0x856F --length 0xEF --org 0x736F (notes/dosdis-openresource.txt), --at 0x86D5 --length 0x5C --org 0x74D5 (notes/dosdis-queldisk.txt), --at 0xC05E --length 0x7D --org 0x2CE (notes/dosdis-videomenu.txt), --at 0x5215 --length 0x240 --org 0x4015 (notes/dosdis-protection.txt), --at 0x3F14 --length 0x30 --org 0x2D14 (notes/dosdis-protection-caller.txt); the byte-pattern scans quoted below were run by scratch scripts and are re-derivable from the offsets given.

In the clear, and nearly silent

BUMPY.EXE is 112,704 bytes: an MZ with 1,050 relocations in 598 distinct segments, a 4,608-byte header with the relocation table at 1Ch, no overlay, entry 0000:0000, the Turbo C++ 1990 c0 start-up decoded from its first byte. dospack.py finds no packer (0 of 30), lzexe.py, exepack.py and dataarea.py refuse it, and its entropy is 5.93 bits. After eight objects behind packers, the words are in the clear — and there are few: 413 runs of five printable bytes, nearly all code. The ones that are words:

  • Erreur dans TstJoy: utilisation d'un joystick logique non defini. and two more French joystick errors, $-terminated — a joystick library; in al,201h appears six times in the code;
  • Turbo C++ - Copyright 1990 Borland Intl., Divide error, Abnormal program termination, Stack overflow! — the runtime;
  • the file names, in the two record tables below;
  • INSERT THE OTHER DISK AND PRESS A KEY !, OVER FLOW !;
  • seven names of eight characters — BIG JIM., SUPER JO, STEVE..., WILIAM.., JOHNNY.., FRANK..., MIKE.... — and, at DS:08F0h, seven records of a far pointer to each and a u32: 5,000,000, 3,000,000, 1,000,000, 200,000, 30,000, 4,000, 500 — the default HALL OF FAME;
  • YOUR PASSWORD, ENTER YOUR PASSWORD, PASSWORD OK, PASSWORD ERROR, GAME OVER, ERROR, Enter the platform number;
  • nine six-letter words at a stride of seven — ACCESS BUTTON ISLAND PRETTY WINNER ZOMBIE LOVELY SYSTEM AAAAAA — of which eight are pointed to by a table of far pointers at DS:135Ch: the passwords of levels 2 to 9, if the order is the levels' (not verified against the password routine); AAAAAA follows in memory with no pointer;
  • QUELDISK at DS:4CE8h, one byte after a variable holding a disk letter;
  • BUMPY (C) LORICIEL 1992$< F1 >: CGA $< F2 >: EGA $< F3 >: VGA $< F4 >: TANDY $< F5 >: MCGA $< F6 >: VGA256$ at DS:529Ch and BUMPY (C) LORICIEL 1992$< F5 >: NO SOUND$< F6 >: PC BASE $< F7 >: ADLIB $< F8 >: MT32 $ at DS:684Ah.

The last two are the studio and the year, and the pre-briefing's own string dump had them on its lines 414 and 415. The routine at 0AB9h:02CEh prints the first with int 21h/09h at row 1, then the six mode lines at a stride of fifteen bytes, each only if a flag at DS:548Bh+i says the hardware was detected, and waits for F2 (scancode 3Ch → mode 1) or F3 (3Dh → mode 2) — the other keys are handled by a second routine. Mode 1 is the EGA and 2 the VGA, both BIOS mode 0Dh (04).

The loader: two record tables and two disks

DS:0090h  10 records          DS:0928h  19 records
 0 D1.PAV        b  30,726     0 BUMSPJEU.BIN  a  90,000
 1 D1.DEC        b  12,182     1 MASKBUMP.VEC  a  32,099
 2 SPRITE  .BIN  a   3,000     2 BUMPRESE.VEC  a  32,099
 3 GRILLE  .VEC  a  32,099     3 SCORE.VEC     a  32,099
 4 DDFNT2  .CAR  a   1,987     4 BUMPY.BNK     b   6,748
 5 BUMPYOBJ.VEC  a  32,099     5 BUMPY.MID     b  35,141
 6 BUMPYSPR.BIN  a  33,000     6 (empty)       z       0
 7 GRILLOBJ.VEC  a  32,099     7 (empty)       z       0
 8 D1.BUM        b   2,912     8..16 MONDE1..9.VEC  a  32,099 each
 9 FLECHE.BIN    a   2,200    17 DESSFIN.VEC   b  32,099
                              18 TITRE.VEC     b  32,099

A record is a far pointer to the name, a u16 holding a letter, and a u32. OpenResource (cs:736F) reads the letter: z means no file; otherwise it calls QuelDisque(letter) (cs:74D5), which opens QUELDISK, reads four bytes, and returns the first minus the letter — zero if the right disk is in the drive, and zero whenever either is z; if the result is not zero it calls the routine in [A1B8h], which prints INSERT THE OTHER DISK AND PRESS A KEY !, up to ten times. So the game shipped on two floppies, a with the sprites, the font, the worlds and the frame, b with the levels, the music, the title and the ending — and QUELDISK in the object is two bytes, 7A 1A: z and a Ctrl-Z. That is what the installer wrote next to C:\BUMPY so that a hard-disk copy is never asked for a disk. The u32 is the buffer the file is unpacked into (03); BUMPY.BNK and BUMPY.MID get their exact sizes, the sprite bank 90,000 for 89,116.

Five records — SPRITE.BIN, GRILLE.VEC, BUMPYOBJ.VEC, BUMPYSPR.BIN, GRILLOBJ.VEC — name files the folder does not have. Of the eighteen OpenResource sites, seventeen push a constant index, and none pushes 2, 3, 5, 6 or 7 of the first table; the eighteenth (cs:388E) is in the second table's scope. The pre-briefing read those names as an editor's; the listing says they are entries nothing opens, with buffer sizes that match a 32,099-byte screen and a 33,000-byte something. Whether an editor once used them the object does not say.

The write wrapper (cs:748A, int 21h/40h through the runtime) has no caller. The game never writes a file: no saved scores, no saved levels. SCORE.VEC is the HALL OF FAME frame, and the two plaintext .BUM were not written by this program.

The copy protection, and the two bytes

Enter the platform number is printed by the routine at cs:4015, and its caller (cs:2D14) says when:

if level > 1 and [119Ah] == 0:  call 4015h
if [119Ah] == FFh:  level := 1

— only when a game starts above level 1, that is, after a password. The routine copies two tables into locals (16 words from DS:11B6h: 0 0 46h 4Fh 51h 93h 9Fh A8h B4h B3h BEh C4h CAh CBh DAh E1h; 16 bytes from DS:11D6h: 0 0 4 6 7 5 0Fh 10h 18h 13h 1Ch 1Ah 19h 1Bh 11h 12h), sets the first table and reads 99 bytes of Dn.DEC as a palette block, draws a random number's worth of screen (rand & 15, retried while below 2), puts the sprite the first table names on the screen through the blit descriptor at [8884h], prints the question at (54h, 87h), and lets the player raise and lower a two-digit number with the joystick bits. The answer is the second table's entry: the platform numbers of the manual, 4, 6, 7, 5, 15, 16, 24, 19, 28, 26, 25, 27, 17, 18. Three tries; then ERROR at (8Ch, A8h), [119Ah] := FFh, and the caller restarts at level 1.

Except that the comparison is not there. At cs:4219, where the entered number should be compared with the expected one:

4219  8A 46 FE     mov  al,[bp-2]      ; the number entered
421C  88 46 FC     mov  [bp-4],al      ; written OVER the expected one
421F  EB 1D        jmp  423Eh          ; over the ERROR branch
4221  ...          the ERROR branch, which nothing jumps to
423E  C6 46 FB FF  mov  [bp-5],FFh     ; ends the three-try loop: success

A cmp al,[bp-4] ; je 423Eh is 3A 46 FC 74 1D; what is there is 88 46 FC EB 1D: two bytes, 3Ah → 88h and 74h → EBh, and the branch to ERROR is dead code. There is no original to diff, so the original bytes are a reading and the dead branch is the measurement. That is Sleepwalker's crack: not a disk check — there is no int 13h in the code, and QUELDISK is the installer's — but the manual lookup, defanged in place. CHKLIST.CPS holds Central Point Anti-Virus's record of this file, made on 1992-07-17, the day before the NFO (07).

Sound and input, by port

The code writes mov dx,388h twice (the OPL2's address port), 330h once and 331h five times (the MPU-401's data and status ports), 3C8h once (the VGA DAC), reads the joystick port 201h six times, calls int 33h (the mouse) eleven times, int 10h twelve, int 21h sixty-five, and int 13h never. The sound module is a far segment at 0CECh (file 0E0C0h), 13.5 KB, which also holds the sprite setup; the sound menu's choices (NO SOUND, PC BASE, ADLIB, MT32) are the four outputs those ports serve, BUMPY.BNK for the Ad Lib and BUMPY.MID for the MT-32 (07). Nothing in the sound module was read beyond the dispatcher.

What is not measured

  • the password routine itself — whether the eight words map to levels 2..9 in that order;
  • the game's rules: how the ball moves, what the tables' values do, what ends a platform;
  • the second key handler of the video menu (F1, F4, F5, F6), and the CGA, Tandy, MCGA and VGA256 paths of the graphics library;
  • the sound module past its dispatcher.