;;; -*-asm-*- ;;; ;;; Disassembly of the GCHQ `canyoucrackit.co.uk' program ;;; ;;; Commentary by Mark Wooding. section .text bits 32 global _start ;;;-------------------------------------------------------------------------- ;;; This is the code section, shown in hex digits in the PNG image. _start: ;; And so it begins. jmp setup ; skip past curious number dd 0xa3bfc2af ; curious number! setup: ;; In which an RC4 state is set up. The key is `efbeadde', which is ;; what you get if you start with `deadbeef' but read it starting ;; from the bottom. sub esp, 256 ; acquire space for the state table xor ecx, ecx ; clear a counter i .init mov [esp + ecx], cl ; set state[i] = i inc cl ; advance the counter jnz .init ; continue until all set up xor eax, eax ; clear counter j mov edx, 0xdeadbeef ; the key .swap add al, [esp + ecx] ; move j on by state[i] add al, dl ; and by the next key byte ror edx, 8 ; use the bytes cyclically mov bl, [esp + ecx] ; grab state[i] mov bh, [esp + eax] ; and state[j] mov [esp + eax], bl ; swap them around mov [esp + ecx], bh inc cl ; move i on to next entry jnz .swap ; continue until all done check: ;; In which we ensure that the ciphertext is actually present. This ;; uses the stack pointer as a magical autoincrementing pointer into ;; the ciphertext buffer -- or, at least, where the buffer is meant ;; to be. jmp dword findend ; find the end of this code .cont mov ebx, esp ; this is return address from call add ebx, strict dword 4 ; and this is the RC4 state again pop esp ; grab that return address pop eax ; read the first word cmp eax, 'AAAA' ; check that the marker is there jnz exit ; if not, bail pop eax ; read the next word cmp eax, 'BBBB' ; check the marker is there jnz exit ; if not, bail copy: ;; In which the ciphertext is copied to a writable buffer so that it ;; can be decrypted. pop edx ; next word: ciphertext length mov ecx, edx ; stash in count register mov esi, esp ; here's the actual ciphertext mov edi, ebx ; current bottom of the stack sub edi, ecx ; allow space for the ciphertext rep movsb ; copy it decrypt: ;; In which a ciphertext is decrypted, a bug is uncovered, and a clue ;; is revealed. mov esi, ebx ; pointer to the RC4 state table mov ecx, edx ; size of the ciphertext mov edi, ebx ; pointer to end of ciphertext sub edi, ecx ; pointer to start of ciphertext xor eax, eax ; clear counter i xor ebx, ebx ; clear counter j xor edx, edx ; clear temporary space .loop inc al ; step i by one add bl, [esi + eax] ; jump j by state[i] mov dl, [esi + eax] ; grab state[i] mov dh, [esi + ebx] ; and state[j] mov [esi + eax], dh ; swap the two over mov [esi + ebx], dl add dl, dh ; add the two together mod 256 xor dh, dh ; zero extend to 32-bits mov bl, [esi + edx] ; pick out state[i + j], clobber j! mov dl, [edi] ; load the ciphertext byte xor dl, bl ; unmask it mov [edi], dl ; and write it back as plaintext inc edi ; advance ciphertext pointer dec ecx ; decrement counter jnz .loop ; and continue until all done exit: ;; In which the tale ends. xor ebx, ebx ; return code: always zero mov eax, ebx ; copy here inc al ; advance: SYS_exit int 0x80 ; old-school Linux syscall nop nop findend: ;; In which the address of the ciphertext is found. call check.cont ; push the address of the next word dd "AAAA" ; code end marker ;;;-------------------------------------------------------------------------- ;;; This is the data section, base-64 encoded and stashed in the PNG header. dd "BBBB" ; data begin marker ciphertext: dd .end - .start ; length word .start db 0x91, 0xd8, 0xf1, 0x6d, 0x70, 0x20, 0x3a, 0xab db 0x67, 0x9a, 0x0b, 0xc4, 0x91, 0xfb, 0xc7, 0x66 db 0x0f, 0xfc, 0xcd, 0xcc, 0xb4, 0x02, 0xfa, 0xd7 db 0x77, 0xb4, 0x54, 0x38, 0xab, 0x1f, 0x0e, 0xe3 db 0x8e, 0xd3, 0x0d, 0xeb, 0x99, 0xc3, 0x93, 0xfe db 0xd1, 0x2b, 0x1b, 0x11, 0xc6, 0x11, 0xef, 0xc8 db 0xca, 0x2f .end: ;;;----- That's all, folks --------------------------------------------------