cseg segment assume cs:cseg org 100h first: jmp init olint label WORD old8 dd ? newint: push ax push bx push cx push dx push bp push si push di push ds push es push ss mov ah,1 xor cx,cx xor dx,dx int 1ah pushf call old8 pop ss pop es pop ds pop di pop si pop bp pop dx pop cx pop bx pop ax iret init: mov ax,3508h int 21h mov olint,bx mov olint[2],es mov ax,2508h lea dx,newint int 21h mov ax,ds:[2ch] mov es,ax mov ah,49h int 21h lea dx,init int 27h cseg ends end first
When time stood still
Once upon a time, when few were online, a common copy protection scheme for DOS computer games was to ask the player to enter some word from the printed manual, or a code from a table. The assumption was that disks were easy to copy, but printed materials were not, which was proved fairly accurate.
Sometimes the challenge was easy to bypass. Some games chose from a small set of multiple choice questions which obviously could be defeated with trial-and-error. Others would store their secret words in plaintext in the binary, which a simple utility would reveal. Yet others would always ask the same question if you held down a key as soon as the game loaded, presumably because they incremented a counter that determined the question while waiting for the first keypress.
I’m proud that to have found an easy but less trivial workaround in my early
teens. I noticed C tutorials often contained an example such as
srand(time(0))
, which seeded a pseudo-random number generator using the
system clock.
I suspected that game developers might have adopted this practice, so I wrote a Terminate-and-Stay-Resident (TSR) program that intercepted the timer interrupt (INT 8h) and set the time to midnight. In other words, it would always be midnight for the computer.
Sure enough, I’d set the date to 01-01-1981 and run my utility, and several games would ask the same copy protection question every time!
Here’s the source to W.ASM:
Hold still for a moment
Other games froze up if the clock failed to change, so I wrote a second TSR utility that also intercepted the keyboard interrupt (INT 9h). On detecting Alt-B, on the next 96 timer interrupts, it would reset the time to midnight. Since the timer interrupt went off 18.2 times a second, it would be midnight for about 5 seconds, then the clock would tick again.
A well-timed Alt-B was enough to force some of these games to issue the same challenge every time.
Here’s the source to TIMESTOP.ASM:
cseg segment assume cs:cseg org 100h first: jmp init hotkey equ 30h ;Scan code for "B" shftmsk equ 00001000b ;Shift mask old8 label word oldloc8 dd ? old9 label word oldloc9 dd ? count db 0 newint9 proc near push ax in al,60h cmp al,hotkey je keygood exit9: pop ax jmp oldloc9 keygood: mov ah,2 int 16h test al,shftmsk jz exit9 inc count jmp exit9 newint9 endp newint8 proc near cmp count,0 je exit8 push ax push bx push cx push dx xor cx,cx xor dx,dx mov ah,1 int 1ah inc count cmp count,60h jne all_done mov count,0 all_done:pop dx pop cx pop bx pop ax exit8: jmp oldloc8 newint8 endp init proc near mov ax,3508h int 21h mov old8,bx mov old8[2],es mov ax,3509h int 21h mov old9,bx mov old9[2],es mov ax,2508h lea dx,newint8 int 21h mov ax,2509h lea dx,newint9 int 21h mov ax,ds:[2ch] mov es,ax mov ah,49h int 21h lea dx,init int 27h init endp cseg ends end first
Downloads:
Tempus Fugit
A decade later, I was a PhD student studying cryptography. Although the tiny tools I built in my youth may have lost their relevance, I was obliged to continue thinking about exploits involving pseudo-randomness. For example:
-
Wikipedia lists prominent incidents involving pseudo-randomness, such as when an improperly seeded generator led to predictable OpenSSL keys in Debian.
-
The NSA chose the seemingly random numbers in the S-boxes used in the DES cipher. Years later, differential cryptanalysis was (publicly) discovered, and DES was surprisingly resistant thanks to these choices, though it remains unknown if DES is weakened in some other way. History echoed with Dual_EC_DRBG, but this time it was clear the intent was to insert a backdoor.
-
Random constants for things like hash algorithms should be nothing-up-my-sleeve numbers.