08048054 <_start>: 8048054: e8 00 00 00 00 call 8048059 <_start+0x5> 8048059: 59 pop %ecx 804805a: 8b 59 04 mov 0x4(%ecx),%ebx 804805d: 53 push %ebx 804805e: 53 push %ebx 804805f: 53 push %ebx 8048060: 53 push %ebx 8048061: c3 ret上は,x86で動作するゴミプログラムの逆アセンブリです.
push EBX(0x53:ゴミ)でゴミデータをpushするだけのゴミ命令が埋め込まれており, その機械語コードをEBXにロードすることでEBXに0x53535353(ゴミゴミゴミゴミ) というゴミデータを入れ,ゴミ命令によってゴミデータでスタックを埋め, 最後にretすると0x53535353というゴミアドレスに戻ろうとすることで セグメンテーションフォルトとなりゴミのようなコアダンプを吐くという, まさにゴミプログラムです.
出力されたコアダンプを逆アセンブルして見てみると,スタックがゴミで埋まって おり,まさにゴミプログラムという点が, 駄洒落にふさわしいと言えるでしょう.
... bfbfe650: 53 push %ebx bfbfe651: 53 push %ebx bfbfe652: 53 push %ebx bfbfe653: 53 push %ebx bfbfe654: 53 push %ebx bfbfe655: 53 push %ebx bfbfe656: 53 push %ebx bfbfe657: 53 push %ebx bfbfe658: 53 push %ebx bfbfe659: 53 push %ebx bfbfe65a: 53 push %ebx bfbfe65b: 53 push %ebx bfbfe65c: 53 push %ebx bfbfe65d: 53 push %ebx bfbfe65e: 53 push %ebx bfbfe65f: 53 push %ebx ...
/* for Linux/x86 (not amd64) */ #include <stdio.h> #include <stdlib.h> #include <signal.h> int halt(void) { asm volatile ("hlt"); } struct sigframe { int pretcode; int sig; struct sigcontext sc; }; void sighandler(int val) { long *sp; unsigned char *pc; volatile struct sigframe *frame; sp = __builtin_frame_address(0); frame = (struct sigframe *)(sp + 1); pc = (unsigned char *)frame->sc.eip; if (*pc == 0xf4) { frame->sc.eip = (long)(pc + 1); return; } exit(0); } int main(void) { signal(SIGSEGV, sighandler); halt(); write(1, "OK\n", 3); return 0; }上は,Linux/x86で動作する不死身のプログラムです.
hlt命令を呼び出しており,通常はここで Segmentation fault で実行停止しますが SIGSEGVをハンドリングしてhlt命令の場合にはプログラムカウンタを進めて復帰し hlt命令を読み飛ばすことで,不死のプログラムになっています.
実行結果は以下です.
$ ./virtual-halt OK以下はhalt()の逆アセンブル結果です.
08048424 <halt>: 8048424: 55 push %ebp 8048425: 89 e5 mov %esp,%ebp 8048427: f4 hlt 8048428: 5d pop %ebp 8048429: c3 rethlt命令の機械語コードが0xf4(不死)であり,プログラムの不死の動作に掛け合わせて いるところが,この駄洒落の面白いところです.
.section .text .globl _start .type _start, @function _start: mov $yorosiku, %eax lea 8(%eax), %ecx mov $0, %esi mov $4, %eax mov $1, %ebx mov $4, %edx 1: inc %esi /* 0x46 */ dec %ecx /* 0x49 */ inc %esi /* 0x46 */ dec %ecx /* 0x49 */ dec %ecx /* 0x49 */ inc %esi /* 0x46 */ dec %ecx /* 0x49 */ inc %esi /* 0x46 */ int $0x80 cmp $8, %esi jne 1b mov $1, %eax mov $0, %ebx int $0x80 yorosiku: .string "sikuyoro"上は,Linux/x86で動作するプログラムです.
08048098 <_start>: 8048098: b8 cf 80 04 08 mov $0x80480cf,%eax 804809d: 8d 48 08 lea 0x8(%eax),%ecx 80480a0: be 00 00 00 00 mov $0x0,%esi 80480a5: b8 04 00 00 00 mov $0x4,%eax 80480aa: bb 01 00 00 00 mov $0x1,%ebx 80480af: ba 04 00 00 00 mov $0x4,%edx 80480b4: 46 inc %esi 80480b5: 49 dec %ecx 80480b6: 46 inc %esi 80480b7: 49 dec %ecx 80480b8: 49 dec %ecx 80480b9: 46 inc %esi 80480ba: 49 dec %ecx 80480bb: 46 inc %esi 80480bc: cd 80 int $0x80 80480be: 83 fe 08 cmp $0x8,%esi 80480c1: 75 f1 jne 80480b4 <_start+0x1c> 80480c3: b8 01 00 00 00 mov $0x1,%eax 80480c8: bb 00 00 00 00 mov $0x0,%ebx 80480cd: cd 80 int $0x80 080480cf <yorosiku>: 80480cf: 73 69 jae 804813a <yorosiku+0x6b> 80480d1: 6b 75 79 6f imul $0x6f,0x79(%ebp),%esi 80480d5: 72 6f jb 8048146 <yorosiku+0x77>以下が実行結果です.
$ ./yorosiku yorosikuプログラム中では inc ESI, dec ECX の機械語コードがそれぞれ 0x46, 0x49 であり, これらの連続が「よろしくよろしく」となっています.
このため「よろしく」という命令を「よろしくよろしく...」と繰り返し実行することで 「yorosiku」という文字列を実際に表示している点が掛け合わされており, そこがこの駄洒落の面白さです.
さらに表示文字列のデータは「しくよろ」となっており,また inc / dec による 機械語コードも途中から「しくよろしくよろ」という若者言葉になっていて,駄洒落を 中高年のものとして敬遠しがちな 若者にも馴染みやすい駄洒落となっています.
00 11 22 33 44 55 00 66 77 88 99 AA 08 00 45 00 00 54 D4 9D 00 00 40 01 22 B8 C0 A8 01 02 C0 A8 01 01*00*00* 2A 18 A7 04 00 00 53 9B 9F 77 00 06 50 C7 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37上は,ICMP Echo Reply のパケットである.
ICMPヘッダの type と code の部分(上記*の部分)が 00 00 となっており
「俺!俺!」と言っている.
ICMP Echo による存在問い合わせに対して,自分はいるよと応答している様が
type と code に掛け合っており,
そこがこの駄洒落の面白い点です.