2

私は以下に小さなシェルコードを書きました:

#include <stdlib.h>

int main()
{
    __asm__("jmp calloffset\n"
        "poploffset: popl %%esi\n"
        "movl $1,%%eax\n"
        "movl $6,%%ebx\n"
        "int $0x80\n"
        "calloffset: call poploffset\n"
        ".string \"/bin/bash\"\n":::"esi");

    exit(1);
}

シェルコードが機能すると、6が返されます。実際には、上記のコードは適切に機能し、メイン関数は実際に6を返します。

次に、コードをCプログラムに埋め込みます。

#include <stdlib.h>
#include <unistd.h>

char shellcode[]="\xeb\x0d\x5e\xb8\x01\x00\x00\x00\xbb\x06\x00\x00\x00\xcd\x80\xe8\xee\xff\xff\xff";

void func()
{
    int * ret;
    ret=(int *)&ret+0x08;
    *ret=(int *)shellcode;

}

int main()
{
    func();
    exit(0);
}

通常の状況では、コードは6を返すはずですが、常に0を返します。

私のコードは間違っているとは思いません。それをお見せします。

まず、gdbからvalretのアドレスを取得します。

(gdb) print &ret
$1 = (int **) 0xbffff2f4

そして、私はメインの次の呼び出し命令のアドレスを取得します:

(gdb) disass main
Dump of assembler code for function main:
   0x08048ccb <+0>: push   %ebp
   0x08048ccc <+1>: mov    %esp,%ebp
   0x08048cce <+3>: and    $0xfffffff0,%esp
   0x08048cd1 <+6>: sub    $0x10,%esp
   0x08048cd4 <+9>: call   0x8048cb0 <func>
   0x08048cd9 <+14>:    movl   $0x0,(%esp)
   0x08048ce0 <+21>:    call   0x80495c0 <exit>
End of assembler dump.

明らかに、それは0x08048cd9です。

次に、上記のアドレスをスタックに格納するアドレスを取得します。

(gdb) x/16xw $esp
0xbffff2e8: 0xbffff3bc  0x00000001  0x00000000  0x08049460
0xbffff2f8: 0xbffff318  0x08048cd9  0x0804972f  0x080d6044
0xbffff308: 0x08049797  0x00000000  0x08049460  0x080493c0
0xbffff318: 0x00000000  0x08048e91  0x00000001  0xbffff3b4

明らかに、アドレスは0xbffff2f8 + 0x04=0xbffff2fcです。そして、valretのアドレスは0xbffff2f4です。

したがって、ret=(int *)&ret+0x08正しいアドレスを取得する必要があります。そして*ret=(int *)shellcode、シェルコードのアドレスをスタックに挿入する必要があります。そして、プログラムはシェルコードにぶつかり、プログラムが戻ったときに最後に6を取得します。

私が間違っている?

私は間違った場所を見つけたようです:

(gdb) disass func
Dump of assembler code for function func:
   0x08048cb0 <+0>: push   %ebp
   0x08048cb1 <+1>: mov    %esp,%ebp
   0x08048cb3 <+3>: sub    $0x28,%esp
   0x08048cb6 <+6>: lea    -0xc(%ebp),%eax
   0x08048cb9 <+9>: add    $0x20,%eax
   0x08048cbc <+12>:    mov    %eax,-0xc(%ebp)
   0x08048cbf <+15>:    mov    -0xc(%ebp),%eax
   0x08048cc2 <+18>:    mov    $0x80d6028,%edx
   0x08048cc7 <+23>:    mov    %edx,(%eax)
   0x08048cc9 <+25>:    movl   $0x1,(%esp)
   0x08048cd0 <+32>:    call   0x8053380 <sleep>
   0x08048cd5 <+37>:    leave  
   0x08048cd6 <+38>:    ret    
End of assembler dump.

指示add $0x20,%eaxがおかしい。これはどのように起こりますか?

4

2 に答える 2

2

指示add $0x20,%eaxがおかしい。これはどのように起こりますか?

int * ret;
ret=(int *)&ret+0x08;

これがCポインタの計算の仕組みです。この加算はバイト単位で変化retします。0x08 * sizeof(int)そこ0x20から来ています。しかし、Andy Rossの見解は正しいです。コンパイラーはスタックフレームを自由に配置できるため、特にコンパイラー設定が異なる場合は、再コンパイルによってフレームレイアウトを変更できます。

于 2012-04-28T04:12:10.240 に答える
2

コンパイラは、func()のスタックフレーム内の任意の場所に「ret」変数を自由に配置できます。おそらく(私は逆アセンブルから数学を計算するのが面倒です)、8のオフセットは単に間違っています。40バイトのフレームが設定されていることに注意してください。

于 2012-04-28T04:00:15.767 に答える