7

私の友人は、x86 で動作する小さな概念実証アセンブラーを作成しました。x86_64 にも移植することにしましたが、すぐに問題が発生しました。

私は C で小さなプログラムを書き、そのコードをコンパイルして objdump しました。その後、それを Python スクリプトに挿入したため、x86_64 コードは正しいです。

from ctypes import cast, CFUNCTYPE, c_char_p, c_long

buffer = ''.join(map(chr, [ #0000000000000000 <add>:
  0x55,                     # push   %rbp
  0x48, 0x89, 0xe5,         # mov    %rsp,%rbp
  0x48, 0x89, 0x7d, 0xf8,   # mov    %rdi,-0x8(%rbp)
  0x48, 0x8b, 0x45, 0xf8,   # mov    -0x8(%rbp),%rax
  0x48, 0x83, 0xc0, 0x0a,   # add    $0xa,%rax
  0xc9,                     # leaveq 
  0xc3,                     # retq
]))

fptr = cast(c_char_p(buffer), CFUNCTYPE(c_long, c_long))
print fptr(1234)

では、このスクリプトを実行するたびにセグメンテーション違反が発生し続けるのはなぜですか?

mprotect と実行フラグがないことについてまだ質問があります。バッファ オーバーランなどのほとんどの基本的なセキュリティ エクスプロイトから保護すると言われています。しかし、それが使用されている本当の理由は何ですか? .text に到達するまで書き続けてから、素敵な PROT_EXEC エリアに命令を挿入することができます。もちろん、.text で書き込み保護を使用しない限り

しかし、なぜその PROT_EXEC がいたるところにあるのでしょうか。.text セクションが書き込み保護されていると、非常に役立ちませんか?

4

5 に答える 5

8

ヴィンセントが述べたように、これは割り当てられたページが実行不可としてマークされているためです。新しいプロセッサはこの機能をサポートしており、それをサポートする OS によってセキュリティの追加レイヤーとして使用されます。アイデアは、特定のバッファ オーバーフロー攻撃から保護することです。例えば。一般的な攻撃は、挿入したコードを指すように戻りアドレスを書き換えて、スタック変数をオーバーフローさせることです。非実行スタックでは、プロセスの制御ではなく、segfault のみが生成されるようになりました。同様の攻撃は、ヒープ メモリにも存在します。

これを回避するには、保護を変更する必要があります。これは、ページ アラインされたメモリでのみ実行できるため、おそらくコードを次のように変更する必要があります。

libc = CDLL('libc.so')

# Some constants
PROT_READ = 1
PROT_WRITE = 2
PROT_EXEC = 4

def executable_code(buffer):
    """Return a pointer to a page-aligned executable buffer filled in with the data of the string provided.
    The pointer should be freed with libc.free() when finished"""

    buf = c_char_p(buffer)
    size = len(buffer)
    # Need to align to a page boundary, so use valloc
    addr = libc.valloc(size)
    addr = c_void_p(addr)

    if 0 == addr:  
        raise Exception("Failed to allocate memory")

    memmove(addr, buf, size)
    if 0 != libc.mprotect(addr, len(buffer), PROT_READ | PROT_WRITE | PROT_EXEC):
        raise Exception("Failed to set protection on buffer")
    return addr

code_ptr = executable_code(buffer)
fptr = cast(code_ptr, CFUNCTYPE(c_long, c_long))
print fptr(1234)
libc.free(code_ptr)

注: ページを解放する前に、実行可能フラグの設定を解除することをお勧めします。ほとんどの C ライブラリは、完了時に実際にメモリを OS に返さず、独自のプールに保持します。これは、EXEC ビットをクリアせずに別の場所でページを再利用し、セキュリティ上の利点を回避することを意味する可能性があります。

また、これはかなり移植性がないことに注意してください。Linuxでテストしましたが、他のOSではテストしていません。Windows では動作しませんが、他の UNIX (BSD、OsX?) では動作する可能性があります。

于 2008-11-09T01:06:14.363 に答える
7

友人と調査を行ったところ、これはプラットフォーム固有の問題であることがわかりました。一部のプラットフォームでは PROT_EXEC を使用せずに malloc mmaps メモリを使用し、他のプラットフォームでは使用していると思われます。

そのため、後で mprotect で保護レベルを変更する必要があります。

不自由なこと、何をすべきかを見つけるのにしばらく時間がかかりました。

from ctypes import (
    cast, CFUNCTYPE, c_long, sizeof, addressof, create_string_buffer, pythonapi
)

PROT_NONE, PROT_READ, PROT_WRITE, PROT_EXEC = 0, 1, 2, 4
mprotect = pythonapi.mprotect

buffer = ''.join(map(chr, [ #0000000000000000 <add>:
    0x55,                     # push   %rbp
    0x48, 0x89, 0xe5,         # mov    %rsp,%rbp
    0x48, 0x89, 0x7d, 0xf8,   # mov    %rdi,-0x8(%rbp)
    0x48, 0x8b, 0x45, 0xf8,   # mov    -0x8(%rbp),%rax
    0x48, 0x83, 0xc0, 0x0a,   # add    $0xa,%rax
    0xc9,                     # leaveq 
    0xc3,                     # retq
]))

pagesize = pythonapi.getpagesize()
cbuffer = create_string_buffer(buffer)#c_char_p(buffer)
addr = addressof(cbuffer)
size = sizeof(cbuffer)
mask = pagesize - 1
if mprotect(~mask&addr, mask&addr + size, PROT_READ|PROT_WRITE|PROT_EXEC) < 0:
    print "mprotect failed?"
else:
    fptr = cast(cbuffer, CFUNCTYPE(c_long, c_long))
    print repr(fptr(1234))
于 2008-11-09T00:29:39.740 に答える
4

最初に実行可能として設定しないと、割り当てられたメモリを自由に実行できないと思います。私は自分で試したことはありませんが、unix 関数を確認することをお勧めしますmprotect

http://linux.about.com/library/cmd/blcmdl2_mprotect.htm

VirtualProtectWindowsでも同じことをしているようです:

http://msdn.microsoft.com/en-us/library/aa366898(VS.85).aspx

于 2008-11-08T22:51:24.777 に答える
0

Pythonはそのような使用法を許可していますか? だったら勉強すればいいのに…

インタープリターは、レジスターが変更されることを期待していないと思います。このようなアセンブラー出力を使用する予定がある場合は、関数内で使用するレジスターを保存してみてください。

ところで、x86_64 の呼び出し規約は通常の x86 とは異なります。スタック ポインターの位置合わせが失われ、他のツールで生成された外部オブジェクトが混在すると、問題が発生する可能性があります。

于 2008-11-08T22:21:43.417 に答える
0

私が考え出したより簡単なアプローチがありますが、最近では mprotect は関係ありません。プログラムの実行可能スペースを直接 mmap します。最近の python にはまさにこれを行うためのモジュールがありますが、コードのアドレスを取得する方法が見つかりませんでした。つまり、文字列バッファーを使用して間接的に実行フラグを設定する代わりに、mmap を呼び出してメモリを割り当てます。これはより簡単で安全です。コードのみを実行できるようになります。

于 2009-11-28T13:22:43.813 に答える