2

tl;dr : 別のスニペットからいくつかのコードを動的に実行しようとしています。しかし、メモリ参照の処理に行き詰まっています (例: コードからのオフセットとして) 正しく解決されるmov 40200b, %rdiように、自分のコードまたは実行中のコードのスニペットにパッチを適用できますか?0x40200b200b


動的に実行されるコードを生成するには、(カーネル) オブジェクトから開始し、ld を使用して参照を解決します。

#!/usr/bin/python
import os, subprocess
if os.geteuid() != 0:
    print('Run this as root')
    exit(-1)
with open("/proc/kallsyms","r") as f:
    out=f.read()
sym= subprocess.Popen( ['nm', 'ebbchar.ko', '-u' ,'--demangle', '-fposix'],stdout=subprocess.PIPE) 
v=''
for sym in sym.stdout:
    s = " "+ sym.split()[0]+ "\n"
    off = out.find(s)
    v += "--defsym "+s.strip() + "=0x" +out[off-18:off -2]+" "
print(v)
os.system("ld ebbchar.ko "+ v +"-o ebbchar.bin");

次に、mmaped ファイルを介して実行するコードを送信します

int fd = open(argv[1], O_RDWR | O_SYNC);
address1 = mmap(NULL, page_size, PROT_WRITE|PROT_READ , MAP_SHARED, fd, 0);
int in=open(argv[2],O_RDONLY);
sz= read(in, buf+8,BUFFER_SIZE-8);
uint64_t entrypoint=atol(argv[3]);
*((uint64_t*)buf)=entrypoint;
write(fd, buf, min(sz+8, (size_t) BUFFER_SIZE));

このコードでコードを動的に実行します

struct mmap_info *info;
copy_from_user((void*)(&info->offset),buf,8);
copy_from_user(info->data, buf+8, sz-8);
unsigned long (*func)(void)  func= (void*) (info->data + info->offset);
int ret= func();

このアプローチは、次のようなメモリにアクセスしないコードに対して機能しますが"\x55\x48\x89\xe5\xc7\x45\xf8\x02\x00\x00\x00\xc7\x45\xfc\x03\x00\x00\x00\x8b\x55\xf8\x8b\x45\xfc\x01\xd0\x5d\xc3"、メモリが関係している場合に問題があります。

以下の例を参照してください。

関数 vm_close を動的に実行したくないと仮定しましょう。Objdump -d -S戻り値:

0000000000401017 <vm_close>:
{
  401017:   e8 e4 07 40 81          callq  ffffffff81801800 <__fentry__>
    printk(KERN_INFO "vm_close");
  40101c:   48 c7 c7 0b 20 40 00    mov    $0x40200b,%rdi
  401023:   e9 b6 63 ce 80          jmpq   ffffffff810e73de <printk>

実行時に、関数ポインターは正しいコードを指します。

(gdb) x/12x $rip
0xffffc90000c0601c:     0x48    0xc7    0xc7    0x0b    0x20    0x40    0x00    0xe9
0xffffc90000c06024:     0xb6    0x63    0xce    0x80
(gdb) x/2i $rip
=> 0xffffc90000c0601c:  mov    $0x40200b,%rdi
   0xffffc90000c06023:  jmpq   0xffffc8ff818ec3de

しかし、このコードは次の理由で失敗します:

1) 私のコンテキストでは、 $0x40200bはコードの先頭からでは$0x40200bなく、物理アドレスを指してoffset 200bいます。

2) 理由はわかりませんが、そこに表示されるアドレスは実際には正しいアドレス (0xffffc8ff818ec3de != ffffffff810e73de) とは異なるため、シンボルを指せず、クラッシュします。

私の2つの問題を解決する方法はありますか?

また、私の問題 (低レベルのメモリ解決) に関連する適切なドキュメントを見つけるのに苦労しました。


編集: カーネルでコードを実行するため、 gcc で許可されていない、-fPICまたは-fpiegcc で許可されていないコードを単純にコンパイルすることはできません ( cc1: error: code model kernel does not support PIC mode)

編集 24/09: @Peter Cordes のコメントによるとmcmodel=small -fpie -mno-red-zone -mnosse、Makefile ( /lib/modules/$(uname -r)fixed/build/Makefile) に追加して再コンパイルしました。これは、リンク前に生成されたコードが次のようになっているため、元のバージョンよりも優れています。

0000000000000018 <vm_close>:
{
  18:   ff 15 00 00 00 00       callq  *0x0(%rip)        # 1e <vm_close+0x6>
    printk(KERN_INFO "vm_close");
  1e:   48 8d 3d 00 00 00 00    lea    0x0(%rip),%rdi        # 25 <vm_close+0xd>
  25:   e8 00 00 00 00          callq  2a <vm_close+0x12>
}
  2a:   c3                      retq   So thanks to rip-relative addressing 

これで、スクリプトの他の変数にアクセスできるようになりました…</p>

したがって、リンク後、バッファ内に埋め込まれた変数に正常にアクセスできます。

40101e: 48 8d 3d e6 0f 00 00    lea    0xfe6(%rip),%rdi        # 40200b

それでも、1 つの問題が残っています。

アクセスしたいシンボル ( printk) と実行可能バッファは、たとえば次のように異なるアドレス空間にあります。

printk=0xffffffff810e73de:
Executable_buffer=0xffffc9000099d000

しかし、私のcallqtoでは、カーネルにセクションがないためprintk、オフセットとして呼び出すアドレスを書き込むのに 32 ビットしかありません。これは、printk内に配置する必要があることを意味します。しかし、そうではありません。$rip.got[$rip-2GO, $rip+2GO]

printk アドレスはバッファから 2GO 以上離れていますが (使用しようとしmcmodel=mediumましたが、生成されたコードに違いは見られませんでした)、たとえば gcc オプションを変更して、バイナリが実際に.gotセクションがありますか?

または、実行可能ファイル潜在的に大きすぎる kmallocバッファーを強制的に割り当てる信頼できる方法はありますか[0xffffffff00000000 ; 0xffffffffffffffff] range?(私は現在使用しています__vmalloc(BUFFER_SIZE, GFP_KERNEL, PAGE_KERNEL_EXEC);)


編集 27/09:エクスポートされていない関数を (汚い) ハックとして[0xffffffff00000000 ; 0xffffffffffffffff]使用して、範囲内 にバッファを割り当てることに成功しました。 __vmalloc_node_range

IMPORTED(__vmalloc_node_range)(BUFFER_SIZE, MODULE_ALIGN,
                MODULES_VADDR + get_module_load_offset(),
                MODULES_END, GFP_KERNEL,
                PAGE_KERNEL_EXEC, 0, NUMA_NO_NODE,
                __builtin_return_address(0));

次に、実行可能バッファのアドレスとカーネル シンボルのアドレスがわかったら ( を解析して)、のオプションwhere/proc/kallsymsを使用してバイナリにパッチを適用できます。ld--defsym symbol=relative_addressrelative_address = symbol_address - buffer_offset

非常に汚れていますが、このアプローチは実際に機能します。

ただし、バッファが別のアドレスに割り当てられる可能性があるため (そして割り当てられる予定)、実行するたびにバイナリを再リンクする必要があります。この問題を解決するには、実行可能ファイルを実際の位置に依存しない実行可能ファイルとしてビルドして、グローバル オフセット テーブルにパッチを適用し、モジュールを完全に再リンクしないようにするのが最善の方法だと思います。

しかし、そこで提供されたオプションを使用すると、リップ相対アドレスを取得できましたが、get/plt は取得できませんでした。したがって、モジュールを適切な PIE としてビルドする方法を見つけたいと思います。

この投稿は巨大で乱雑になり、元の質問から逸脱しています。したがって、そこに新しい簡略化された投稿を開きました。興味深い回答が得られたら、この投稿を編集して説明します。


注: 簡単にするために、安全性テストは表示されません。

注 2: 私の PoC が非常に珍しいものであり、悪い習慣になる可能性があることは十分承知していますが、とにかくやりたいと思っています。

4

0 に答える 0