解決策1:C(Mac OS X x86_64)、109バイト
golf_sol1.cのソース
main[]={142510920,2336753547,3505849471,284148040,2370322315,2314740852,1351437506,1208291319,914962059,195};
上記のプログラムは、__ DATAセグメントの実行アクセスでコンパイルする必要があります。
clang golf_sol1.c -o golf_sol1 -Xlinker -segprot -Xlinker __DATA -Xlinker rwx -Xlinker rwx
次に、プログラムを実行するには、次を実行します。
./golf_sol1 $(ruby -e 'puts "\xf5\xff\xff\xfe\xff\xff\x44\x82\x57\x7d\xff\x7f"')
結果:
残念ながら、Valgrindはシステムコールから割り当てられたメモリを監視しないため、検出されたリークを表示できません。
ただし、vmmapを見て、割り当てられたメモリの大きなチャンク(MALLOCメタデータ)を確認できます。
VIRTUAL REGION
REGION TYPE SIZE COUNT (non-coalesced)
=========== ======= =======
Kernel Alloc Once 4K 2
MALLOC guard page 16K 4
MALLOC metadata 16.2M 7
MALLOC_SMALL 8192K 2 see MALLOC ZONE table below
MALLOC_TINY 1024K 2 see MALLOC ZONE table below
STACK GUARD 56.0M 2
Stack 8192K 3
VM_ALLOCATE (reserved) 520K 3 reserved VM address space (unallocated)
__DATA 684K 42
__LINKEDIT 70.8M 4
__TEXT 5960K 44
shared memory 8K 3
=========== ======= =======
TOTAL 167.0M 106
TOTAL, minus reserved VM space 166.5M 106
説明
したがって、改善されたソリューションに進む前に、ここで実際に何が起こっているのかを説明する必要があると思います。
この主な機能は、Cの欠落している型宣言(したがって、文字を書くことを無駄にすることなくデフォルトでintになります)およびシンボルの動作を乱用しています。リンカはmain
、呼び出すために呼び出されるシンボルを見つけることができるかどうかだけを考慮します。したがって、ここではmainをintの配列にして、実行するシェルコードで初期化します。このため、mainは__TEXTセグメントではなく__DATAセグメントに追加されるため、実行可能な__DATAセグメントを使用してプログラムをコンパイルする必要があります。
mainにあるシェルコードは次のとおりです。
movq 8(%rsi), %rdi
movl (%rdi), %eax
movq 4(%rdi), %rdi
notl %eax
shrq $16, %rdi
movl (%rdi), %edi
leaq -0x8(%rsp), %rsi
movl %eax, %edx
leaq -9(%rax), %r10
syscall
movq (%rsi), %rsi
movl %esi, (%rsi)
ret
これは、メモリのページを割り当てるためにsyscall関数を呼び出すことです(syscall mach_vm_allocateは内部的に使用します)。RAXは0x100000a(必要な関数をsyscallに伝えます)である必要がありますが、RDIは割り当てのターゲットを保持します(この場合、mach_task_self()になります)、RSIはアドレスを保持して、新しく作成されたメモリへのポインターを書き込む必要があります(つまり、スタック上のセクションを指しているだけです)、RDXは割り当てのサイズを保持しています(バイトを節約するためにRAXまたは0x100000aを渡しているだけです)、R10はフラグを保持しています(できることを示しています)どこにでも割り当てられます)。
今では、RAXとRDIがどこから値を取得しているかは明白ではありません。RAXは0x100000aである必要があり、RDIはmach_task_self()が返す値である必要があることを知っています。幸いなことに、mach_task_self()は実際には変数(mach_task_self_)のマクロであり、毎回同じメモリアドレスにあります(ただし、再起動時に変更する必要があります)。私の特定のインスタンスでは、mach_task_self_がたまたま0x00007fff7d578244にあります。そのため、指示を減らすために、代わりにこのデータをargvから渡します。これが、この式でプログラムを実行する理由です$(ruby -e 'puts "\xf5\xff\xff\xfe\xff\xff\x44\x82\x57\x7d\xff\x7f"')
最初の引数。文字列は2つの値を組み合わせたものです。RAX値(0x100000a)は32ビットのみで、1の補数が適用されています(したがって、nullバイトはありません。元の値を取得するための値ではありません)、次の値はRDI(0x00007fff7d578244)が左にシフトされ、最後に2つの余分なジャンクバイトが追加されています(再びnullバイトを除外するために、元に戻すには右にシフトします)。
syscallの後、新しく割り当てられたメモリに書き込みます。これは、mach_vm_allocate(またはこのsyscall)を使用して割り当てられたメモリが実際にはVMページであり、自動的にメモリにページングされないためです。むしろ、データが書き込まれるまで予約され、それらのページはメモリにマップされます。予約されているだけで要件を満たしているかどうかはわかりませんでした。
次のソリューションでは、シェルコードにヌルバイトがないため、プログラムのコードの外にシェルコードを移動してサイズを小さくできるという事実を活用します。
解決策2:C(Mac OS X x86_64)、44バイト
golf_sol2.cのソース
main[]={141986632,10937,1032669184,2,42227};
上記のプログラムは、__ DATAセグメントの実行アクセスでコンパイルする必要があります。
clang golf_sol2.c -o golf_sol2 -Xlinker -segprot -Xlinker __DATA -Xlinker rwx -Xlinker rwx
次に、プログラムを実行するには、次を実行します。
./golf_sol2 $(ruby -e 'puts "\xb8\xf5\xff\xff\xfe\xf7\xd0\x48\xbf\xff\xff\x44\x82\x57\x7d\xff\x7f\x48\xc1\xef\x10\x8b\x3f\x48\x8d\x74\x24\xf8\x89\xc2\x4c\x8d\x50\xf7\x0f\x05\x48\x8b\x36\x89\x36\xc3"')
同じサイズの割り当てを行っているため、結果は以前と同じになります。
説明
ソリューション1とほぼ同じ概念に従いますが、例外は、コードの一部をプログラムの外部に移動したことです。
mainにあるシェルコードは次のとおりです。
movq 8(%rsi), %rsi
movl $42, %ecx
leaq 2(%rip), %rdi
rep movsb (%rsi), (%rdi)
これは基本的に、argvで渡したシェルコードをコピーしてこのコードの後に配置します(したがって、コピーした後、挿入されたシェルコードを実行します)。私たちにとって有利なのは、__ DATAセグメントが少なくともページサイズになるため、コードがそれほど大きくなくても、「安全に」さらに書き込むことができるということです。欠点はここでは理想的なソリューションであり、コピーさえ必要とせず、代わりにargvのシェルコードを直接呼び出して実行するだけです。しかし、残念ながら、このメモリには実行権がありません。このメモリの権限を変更できますが、単にコピーするよりも多くのコードが必要になります。別の戦略は、外部プログラムから権利を変更することです(詳細は後で説明します)。
argvに渡すシェルコードは次のとおりです。
movl $0xfefffff5, %eax
notl %eax
movq $0x7fff7d578244ffff, %rdi
shrq $16, %rdi
movl (%rdi), %edi
leaq -0x8(%rsp), %rsi
movl %eax, %edx
leaq -9(%rax), %r10
syscall
movq (%rsi), %rsi
movl %esi, (%rsi)
ret
これは以前のコードとほとんど同じですが、唯一の違いは、EAXとRDIの値を直接含めていることです。
考えられる解決策1:C(Mac OS X x86_64)、11バイト
プログラムを外部で変更するという考え方は、外部プログラムにリークを移動する可能性のある解決策を提供します。実際のプログラム(サブミッション)は単なるダミープログラムであり、リークプログラムはターゲットプログラムにメモリを割り当てます。今、私はこれがこのチャレンジのルールに当てはまるかどうかはわかりませんでしたが、それでもそれを共有しました。
したがって、チャレンジプログラムに設定されたターゲットを使用して外部プログラムでmach_vm_allocateを使用する場合、チャレンジプログラムは次のようなものである必要があるだけです。
main=65259;
そのシェルコードはそれ自体への短いジャンプ(無限ジャンプ/ループ)であるため、プログラムは開いたままで、外部プログラムから参照できます。
考えられる解決策2:C(Mac OS X x86_64)、8バイト
おかしなことに、valgrindの出力を見ていると、少なくともvalgrindによると、dyldがメモリをリークしていることがわかりました。したがって、事実上、すべてのプログラムがメモリをリークしています。この場合、実際には何もしない(単に終了する)プログラムを作成することができ、実際にはメモリリークが発生します。
ソース:
main(){}
==55263== LEAK SUMMARY:
==55263== definitely lost: 696 bytes in 17 blocks
==55263== indirectly lost: 17,722 bytes in 128 blocks
==55263== possibly lost: 0 bytes in 0 blocks
==55263== still reachable: 0 bytes in 0 blocks
==55263== suppressed: 16,316 bytes in 272 blocks