4

私はアセンブリで手動のシステムコールで遊んでいます。以前は正しく起動させることができましたが、nullを削除した後、syscallを実行させることができません/bin/date。これが私がAT&T構文で書いたコードです。

.global main
main:
    jmp     two

one:
    # zero rax and rdx
    xor     %rax,%rax 
    mov     %rax,%rdx

    # save string location
    mov     (%rsp),%rbx

    # push argv array onto the stack
    add     $16, %rsp
    push    %rax
    push    %rbx
    # assign argv pointer
    mov     %rsp,%rcx

    # execve call
    mov     $0xb, %al 
    int     $0x80

    # exit on failure
    xor     %rax,%rax
    xor     %rbx,%rbx
    movb    $0x1,%al
    int     $0x80

two:
    # get address of the string
    call    one
    .string "/bin/date"

私が正しい場合は%rbx、起動するプログラムを指定する文字列を直接指す必要があります。プログラムのを%rcx表すnullで終了するポインタの配列を指す必要があり、環境を指すので、ここではnullのままにしておきます。そして、もちろん、syscall番号(この場合)を保持します。argv%rdx%rax0x0b

(gdb) info registers 
rax            0xb  11
rbx            0x4000a0 4194464
rcx            0x7fffffffe968   140737488349544
rdx            0x0  0

(gdb) x/s $rbx
0x4000a0:    "/bin/date"

(gdb) x/s *$rcx
0x4000a0:    "/bin/date"

それにもかかわらず、syscallはプログラムを実行せず、-14を返します。これは、 EFAULT(segfault)に変換されます。何を見落としているのかわかりません。助けていただければ幸いです。


したがって、知覚的な読者は、上記のコードが64ビットシステムで32ビットのsyscall規則(およびfriendsを使用)を使用していることに気付いたかもしれませ%ebxint $0x80。32ビット規則は32ビットコードの実行を有効にするためにのみサポートされているため、これはエラーでした。64ビットシステム用に記述されたコードでは、syscallは、、、、、、および命令%rdiを使用します。64ビットシステム(nullfree)の修正されたコードは次のとおりです。%rsi%rdx%r10%r8%r9syscall

.global main
main:
    jmp     two

one:
    # zero rax and rdx
    xor     %rax,%rax 
    mov     %rax,%rdx

    # save string location, note that %rdi is used instead of %rbx
    pop     %rdi

    # push argv array onto the stack
    add     $16, %rsp
    push    %rax
    push    %rdi
    # assign argv pointer, using %rsi instead of %rcx
    mov     %rsp,%rsi

    # execve call, note that the syscall number is different than in 32bit
    mov     $0x3b, %al 
    syscall

two:
    # get address of the string
    call    one
    .string "/bin/date"

ただし、32ビットのsyscall規則64ビットシステムでサポートされており(したがって、32ビットの実行可能ファイルを実行できます)、execveこのシステムで32ビットの呼び出し規則を使用して他のコマンドを正常に実行できました。実際、x86_64システムで調べた「シェルコード」の大部分は32ビット規則を使用していました。それで、私の質問はまだ立っています:なぜ32ビットの呼び出し規約が上記のコードで機能しなかったのですか?

4

2 に答える 2

2

execve()呼び出しにはプロトタイプがあります

int execve(const char *filename,
           char *const argv[],
           char *const envp[]);

NULL通常、ポインタは渡されないことに注意してください。ただし、envp[]配列を空にする場合は、環境変数の終わりとして機能するNULLへのポインタを渡す必要があります。同様にargv[]、NULLポインタにすることはできません。少なくとも、argv[0]プログラム名が含まれている必要があります。通常filename、最初の要素として配置するだけで十分です。シェルの機能に完全に準拠するには、パスを削除し、最後のコンポーネントのみをargv [0]として渡しdateます(この例では)。

あなたの例に相当するCコードは次のとおりです。

char *filename = "/bin/date";
char *argv [2] = {filename, NULL};
char *envp [1] = {NULL};

execve (filename, argv, envp);

多分私はそれを見逃していますが、あなたは(32ビットと64ビットの両方)と同等のことをしているようです

char *filename = "/bin/date";

execve (filename, NULL);   // note missing third parameter, and malformed argv[]

スタック操作がわかりません。プッシュ、プッシュ、プッシュ、システムコールで十分なはずです。スタックポインタを直接操作する理由がわかりません。

于 2012-06-13T06:12:10.067 に答える
1

上記のコードで32ビットの呼び出し規約が機能しなかったのはなぜですか?

あなたはあなた自身の質問に答えました:

32ビット規則は、32ビットコードの実行を有効にするためにのみサポートされています。

64ビットコードからも呼び出すことができるのは偶然であり、カーネルはチェックしません。呼び出しは32ビットコードで発生することを想定しており、インターフェイスは32ビットレジスタのみを使用するため、ゼロは引数付きの32ビットレジスタを64ビットに拡張します。これはia32entry.Sで行われます(64ビットモードでの32ビット演算はレジスタの上位32ビットを自動的にクリアすることに注意してください)。

/*
 * Emulated IA32 system calls via int 0x80.
 *
 * Arguments:
 * eax  system call number
 * ebx  arg1
 * ecx  arg2
 * edx  arg3
 * esi  arg4
 * edi  arg5
 * ebp  arg6    (note: not saved in the stack frame, should not be touched)
 *
 * Notes:
 * Uses the same stack frame as the x86-64 version.
 * All registers except eax must be saved (but ptrace may violate that).
 * Arguments are zero extended. For system calls that want sign extension and
 * take long arguments a wrapper is needed. Most calls can just be called
 * directly.
 * Assumes it is only called from user space and entered with interrupts off.
 */
[...]    
ia32_do_call:
    /* 32bit syscall -> 64bit C ABI argument conversion */
    movl %edi,%r8d  /* arg5 */
    movl %ebp,%r9d  /* arg6 */
    xchg %ecx,%esi  /* rsi:arg2, rcx:arg4 */
    movl %ebx,%edi  /* arg1 */
    movl %edx,%edx  /* arg3 (zero extension) */
    call *ia32_sys_call_table(,%rax,8) # xxx: rip relative

そのため、32ビットに収まらない引数を渡すために使用することはできません。rcx値はに切り捨て0x7fffffffe968られ0xffffe968、最終的にはになりEFAULTます。

rcxもちろん、から設定されrsp、通常の64ビットアドレス空間では、スタックがアドレス空間の正の半分の上から下に向かって成長し、32ビット範囲外になります。これを証明するために、低メモリにあるスタックに切り替えることができます。次のコードは正常に機能します。

.lcomm stack, 4096
.global main
main:
    movl    $stack, %esp
    jmp     two

one:
    # zero rax and rdx
    xor     %rax,%rax
    mov     %rax,%rdx

    # save string location
    mov     (%rsp),%rbx

    # push argv array onto the stack
    add     $16, %rsp
    push    %rax
    push    %rbx
    # assign argv pointer
    mov     %rsp,%rcx

    # execve call
    mov     $0xb, %al
    int     $0x80

    # exit on failure
    xor     %rax,%rax
    xor     %rbx,%rbx
    movb    $0x1,%al
    int     $0x80

two:
    # get address of the string
    call    one
    .string "/bin/date"
于 2015-04-17T13:40:24.413 に答える