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.
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,201happears 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);AAAAAAfollows in memory with no pointer; QUELDISKat 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 andBUMPY (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).
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.
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).
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.
- 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.