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
--
To unsubscribe: http://lists.canonical.org/mailman/listinfo/kragen-discuss