debuggingassemblyreverse-engineeringx86-16corewars

Assembly safes and keys- why it won't work?


So we have like this safes challenge in assembly, you need to create safes and keys that will break them and end the infinite loop. Here's an example for a safe:

loopy:
mov ax, [1900]
cmp ax,1234
jne loopy

and a key:

loopy2:
mov ax, 1234
mov [1900],ax
jmp loopy2

So I have a safe and a key, and I don't understand why it doesn't work:

here's my safe:

org 100h
mySafe:  
mov dx,5
mov ax, [5768h] 
mov bx,7 
mov word [180h],2
mul word [180h]
mov [180h],bx
push ax
dec bx
mov cx,dx
mov ax,dx
loopy1:
add bx,ax
loop loopy1
dec bx
pop ax
add ax,bx
mul word [180h]
cmp ax,350
jne mySafe

And here's my key:


org 100h
loopy:
mov word [5768h],10
jmp loopy


ret

The right answer to break the loop should be 10 and it works when I put in on the safe, somehow with the key it doesn't work and I can't figure out why.. (the "word" is needed for nasm)


Solution

  • The value in dx used as the counter for the loop instruction comes from the first mul instruction.

    This multiplication is just doubling the key, so dx is either 0 or 1 (an easy way to see this is to think of the multiplication as a left shift by one or by remembering that the sum of two n-bit numbers has at most n+1 bits)


    If dx is zero, the whole loopy1 block does nothing (as dx also sets ax) and the value in ax at the end of the safe is 7*(5 +2k) where k is the key (see the commented code below).

    It is then easy to see that 350 = 7*(5+2k) => 2k = 45 has no solution. Therefore no key for which dx is zero can unlock the safe.
    A key has dx 0 iif its value is less than 32768 (again, this is easy to see when thinking of the multiplication as a left shift by one).

    Corollary: 10 cannot be a solution.

    safe:
      mov dx,5
      mov ax, [k]               ;ax = k (key)
      mov bx,7 
      mov word [aux],2    
      mul word [aux]            ;dx = 0 ax = 2k
      mov [aux],bx              ;aux = 7
      push ax                   ;ax = 2k
      dec bx                    ;bx = 6 
      dec bx                    ;bx =    5
      pop ax                    ;ax = 2k
      add ax,bx                 ;ax = 5 + 2k
      mul word [aux]            ;ax = 7*(5 +2k)
    
      cmp ax,350
      ret 
    

    If there is a key that unlocks the safe then it must be greater or equal to 32768 so that dx is 1 after the first multiplication. With this condition, the value in ax at the end of the safe can be written as 7*(6 + (2k & 0xffff)) => k & 0x7fff = 22.
    Adding the condition stated at the very beginning of this section, the final value for k is 32768 + 22 = 32790 or 0x8016 in hex. I've leaped quite a few logical steps in manipulating the equation and forming the result but, again, thinking of 2k as a shift may help visualize them.

    Corollary: Due to the algebraic structure involved, this is the only solution.

    safe:
      mov dx,5
      mov ax, [k]           ;ax = k
      mov bx,7 
      mov word [aux],2    
      mul word [aux]            ;dx:ax = 2k
      mov [aux],bx              ;[aux] = 7
      push ax                   ;dx = 1 ax = 2k & 0xffff
      dec bx                    ;bx = 6 
      mov cx,dx                 ;cx = 1
      mov ax,dx                 ;ax = 1
    loopy1:
      add bx,ax                 ;bx = 6 + 1
      dec cx
    jnz loopy1
      dec bx                    ;bx = 6 
      pop ax                    ;ax = 2k & 0xffff
      add ax,bx                 ;ax = 6 + (2k & 0xffff)
      mul word [aux]            ;ax = 7*(6 + (2k & 0xffff))
    
      cmp ax,350
      ret 
    

    Considering that you have a mov dx, 5 before the first multiplication, did you (or the author of the safe) forget that mul affects dx?
    If you wrap the first mul in push dx / pop dx (or just move mov dx, 5 after it), you would get, at the end of the safe, a value in ax equals to 7*(30 +2k) which implies k = 10 indeed.