bootstrapping from ECHO (SPOILERS)
Kragen Javier Sitaker
kragen at canonical.org
Sat Apr 23 04:37:43 EDT 2011
On Fri, Apr 22, 2011 at 12:55:32PM -0400, Kragen Javier Sitaker wrote:
> I haven't successfully run it yet.
Okay, now I have run it. It works perfectly. Brilliantly done.
Here is my analysis of how it works, and in particular why certain
things are done the way they are. This would be spoilery to anyone who
wants the fun of reverse-engineering the code themselves, so those
people shouldn't read the rest of this message.
-- Spoilers below --
$ cat E.COM
jUX4UPYhUUX5UTP_h@@Z0u1QZJ0u=0uCh//Z!UHhOOZ!UHkUHPG2UHhURX5UP2!hULX2!A
$ xxd < E.COM
0000000: 6a55 5834 5550 5968 5555 5835 5554 505f jUX4UPYhUUX5UTP_
0000010: 6840 405a 3075 3151 5a4a 3075 3d30 7543 h@@Z0u1QZJ0u=0uC
0000020: 682f 2f5a 2155 4868 4f4f 5a21 5548 6b55 h//Z!UHhOOZ!UHkU
0000030: 4850 4732 5548 6855 5258 3555 5032 2168 HPG2UHhURX5UP2!h
0000040: 554c 5832 2141 0d0a ULX2!A..
$ objdump -D -m i8086 -b binary E.COM
E.COM: file format binary
Disassembly of section .data:
00000000 <.data>:
(Keep in mind this is actually address 0x0100, not 0000. Objdump has an option --adjust-vma=0x0100
that avoids this problem, but I didn't know about it until now.)
0: 6a 55 push $0x55
2: 58 pop %ax ax ← 0x0055
3: 34 55 xor $0x55,%al ax ← 0 (so jUX4U does ax ← 0)
5: 50 push %ax
6: 59 pop %cx cx ← 0 (so PY copies ax to cx. We can't xor
directly into cx because the instruction
encoding for that would be 83 f1 55, not 34 55,
and 83 and f1 are both non-ASCII bytes.)
7: 68 55 55 push $0x5555 (h rather than j to push a full 16-bit word)
a: 58 pop %ax ax ← 0x5555
b: 35 55 54 xor $0x5455,%ax ax ← 0x0100 (start address of the program;
so hUUX5UT does ax ← 0x0100; presumably
hoaX5o` and haxX5ay, among other combinations,
would do the same thing)
e: 50 push %ax
f: 5f pop %di di ← 0x0100 (so P_ copies ax to di. Similar
ASCII considerations apply to xoring directly
into di: 81 f7 55 54.)
10: 68 40 40 push $0x4040
13: 5a pop %dx dx ← 0x4040
14: 30 75 31 xor %dh,0x31(%di) change multiplier at address 0x131 from 0x50 to 0x10
17: 51 push %cx pushing 0
18: 5a pop %dx dx ← 0 (so QZ copies cx to dx)
19: 4a dec %dx dx ← 0xffff
1a: 30 75 3d xor %dh,0x3d(%di) invert byte at 0x13d from 0x32 (xor) to 0xcd
(interrupt, specifically interrupt 21h)
1d: 30 75 43 xor %dh,0x43(%di) inverting byte at 0x43 (another 0x32 that
becomes interrupt 21h)
20: 68 2f 2f push $0x2f2f
23: 5a pop %dx dx ← 0x2f2f
24: 21 55 48 and %dx,0x48(%di) clear top the bits xx.x.... on the two bytes
after the end of the program
27: 68 4f 4f push $0x4f4f
2a: 5a pop %dx
2b: 21 55 48 and %dx,0x48(%di) clear the bits x.xx.... on the same bytes; now
only their low-order nibbles remain set.
2e: 6b 55 48 50 imul $0x50,0x48(%di),%dx
remember that the instruction at 0x14 changed
the multiplier to 0x10, so this is putting the
byte past the end of the program into dl, but
shifted left by four bits. (Also, the byte
after it goes into dh.)
32: 47 inc %di
33: 32 55 48 xor 0x48(%di),%dl This snarfs the byte at 0x49, whose low-order
nibble might have set bits in it, and combines
it with the byte from 0x48 that is already in
%dl.
36: 68 55 52 push $0x5255
39: 58 pop %ax ax ← 0x5255
3a: 35 55 50 xor $0x5055,%ax ax ← 0x0200, and in particular ah ← 0x02, the
interrupt 21h code to output a character to
standard output. So hURX5UP is ax ← 0x0200.
3d: 32 21 xor (%bx,%di),%ah Really interrupt 21h.
3f: 68 55 4c push $0x4c55
42: 58 pop %ax ax ← 0x4c55, and in particular ah ← 0x4c,
terminate program.
43: 32 21 xor (%bx,%di),%ah Really interrupt 21h.
45: 41 inc %cx Unnecessary padding?
46: 0d .byte 0xd Involuntarily appended by ECHO.
47: 0a .byte 0xa
More information about the Kragen-discuss
mailing list