19

この指示に従って、サイズ a.out で 528 バイトしか生成できませんでした (gcc main.c が最初に 8539 バイトの大きなファイルを与えたとき)。

main.c は次のとおりです。

int main(int argc, char** argv) {

    return 42;
}

代わりに、このアセンブリ ファイルから a.out をビルドしました。

メイン:

; tiny.asm
  BITS 64
  GLOBAL _start
  SECTION .text
  _start:
                mov     eax, 1
                mov     ebx, 42  
                int     0x80

と:

me@comp# nasm -f elf64 tiny.s
me@comp# gcc -Wall -s -nostartfiles -nostdlib tiny.o
me@comp# ./a.out ; echo $?
42
me@comp# wc -c a.out
528 a.out

マシンコードが必要なため、次のようにします。

objdump -d a.out

a.out:     file format elf64-x86-64


Disassembly of section .text:

00000000004000e0 <.text>:
  4000e0:   b8 01 00 00 00          mov    $0x1,%eax
  4000e5:   bb 2a 00 00 00          mov    $0x2a,%ebx
  4000ea:   cd 80                   int    $0x80

># objdump -hrt a.out

a.out:     file format elf64-x86-64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
 0 .note.gnu.build-id 00000024  00000000004000b0  00000000004000b0  000000b0 2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 1 .text         0000000c  00000000004000e0  00000000004000e0  000000e0 2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
SYMBOL TABLE:
no symbols

ファイルはリトル エンディアン規則になっています。

me@comp# readelf -a a.out
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x4000e0
  Start of program headers:          64 (bytes into file)
  Start of section headers:          272 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         2
  Size of section headers:           64 (bytes)
  Number of section headers:         4
  Section header string table index: 3

今、私はこれを次のように実行したいと思います:

#include <unistd.h>
 // which version is (more) correct?
 // this might be related to endiannes (???)
char code[] = "\x01\xb8\x00\x00\xbb\x00\x00\x2a\x00\x00\x80\xcd\x00";
char code_v1[] = "\xb8\x01\x00\x00\x00\xbb\x2a\x00\x00\x00\xcd\x80\x00";

int main(int argc, char **argv)
{
/*creating a function pointer*/
int (*func)();
func = (int (*)()) code;
(int)(*func)();

return 0;
}

ただし、セグメンテーション違反が発生します。私の質問は次のとおりです。テキストのこのセクションはありますか

  4000e0:   b8 01 00 00 00          mov    $0x1,%eax
  4000e5:   bb 2a 00 00 00          mov    $0x2a,%ebx
  4000ea:   cd 80                   int    $0x80

(このマシンコード) 本当に必要なものは? 私が間違っていること(エンディアン??)、おそらくSIGSEGV以来、これを別の方法で呼び出す必要がありますか?

4

2 に答える 2

28

コードは、実行権限のあるページにある必要があります。デフォルトでは、スタックおよび読み書き静的データ (非 const グローバルなど) は、セキュリティ上の理由から、exec パーミッションなしでマップされたページにあります。

最も簡単な方法は、 を使用してコンパイルすることです。これにより、スタック変数グローバル変数 (静的ストレージ) が実行可能ページにマップされるgcc -z execstackようにプログラムがリンクされ、 .malloc


すべてを実行可能にせずにこれを行う別の方法は、このバイナリ マシン コードを実行可能バッファにコピーすることです。

#include <unistd.h>
#include <sys/mman.h>
#include <string.h>

char code[] = {0x55,0x48,0x89,0xe5,0x89,0x7d,0xfc,0x48,
    0x89,0x75,0xf0,0xb8,0x2a,0x00,0x00,0x00,0xc9,0xc3,0x00};
/*
00000000004004b4 <main> 55                          push   %rbp
00000000004004b5 <main+0x1>  48 89 e5               mov    %rsp,%rbp
00000000004004b8 <main+0x4>  89 7d fc               mov    %edi,-0x4(%rbp)
00000000004004bb <main+0x7>  48 89 75 f0            mov    %rsi,-0x10(%rbp)
'return 42;'
00000000004004bf <main+0xb>  b8 2a 00 00 00         mov    $0x2a,%eax
'}'
00000000004004c4 <main+0x10> c9                     leaveq 
00000000004004c5 <main+0x11> c3                     retq 
*/

int main(int argc, char **argv) { 
    void *buf;

    /* copy code to executable buffer */    
    buf = mmap (0,sizeof(code),PROT_READ|PROT_WRITE|PROT_EXEC,
                MAP_PRIVATE|MAP_ANON,-1,0);
    memcpy (buf, code, sizeof(code));
    __builtin___clear_cache(buf, buf+sizeof(code)-1);  // on x86 this just stops memcpy from optimizing away as a dead store

    /* run code */
    int i = ((int (*) (void))buf)();
    printf("get this done. returned: %d", i);

    return 0;
}

出力:

これをやりなさい。返された: 42

RUN SUCCESSFUL (合計時間: 57ms)

がない__builtin___clear_cacheと、最適化を有効にすると壊れる可能性があります。これは、gcc が を無効なmemcpyストアと見なし、最適化して削除するためです。x86 用にコンパイルする場合、実際にはキャッシュをクリア__builtin___clear_cacheしません。余分な命令はありません。メモリを「使用済み」としてマークするだけなので、メモリへのストアは「デッド」とは見なされません。( gcc マニュアルを参照してください。)


別のオプションはmprotect、配列を含むページchar code[]に、それを与えることPROT_READ|PROT_WRITE|PROT_EXECです。これは、ローカル配列 (スタック上) であるか、.data.

または、それconst char code[].rodataセクションにある場合は、単にそれを与えることができますPROT_READ|PROT_EXEC.

ld( 2019 年頃からの binutils のバージョンでは.rodata、 は と同じセグメントの一部としてリンクされ.text、既に実行可能ファイルにマップされていました。しかし、最近ldでは別のセグメントが提供されるため、実行権限なしでマップできるためconst char code[]、実行可能ファイルは提供されません。配列はもうありませんが、以前はこの古いアドバイスを他の場所で使用していた可能性があります。)

于 2013-08-27T23:27:06.753 に答える