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