tl;dr : 別のスニペットからいくつかのコードを動的に実行しようとしています。しかし、メモリ参照の処理に行き詰まっています (例: コードからのオフセットとして) 正しく解決されるmov 40200b, %rdi
ように、自分のコードまたは実行中のコードのスニペットにパッチを適用できますか?0x40200b
200b
動的に実行されるコードを生成するには、(カーネル) オブジェクトから開始し、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
または-fpie
gcc で許可されていないコードを単純にコンパイルすることはできません ( 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
しかし、私のcallq
toでは、カーネルにセクションがないため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_address
relative_address = symbol_address - buffer_offset
非常に汚れていますが、このアプローチは実際に機能します。
ただし、バッファが別のアドレスに割り当てられる可能性があるため (そして割り当てられる予定)、実行するたびにバイナリを再リンクする必要があります。この問題を解決するには、実行可能ファイルを実際の位置に依存しない実行可能ファイルとしてビルドして、グローバル オフセット テーブルにパッチを適用し、モジュールを完全に再リンクしないようにするのが最善の方法だと思います。
しかし、そこで提供されたオプションを使用すると、リップ相対アドレスを取得できましたが、get/plt は取得できませんでした。したがって、モジュールを適切な PIE としてビルドする方法を見つけたいと思います。
この投稿は巨大で乱雑になり、元の質問から逸脱しています。したがって、そこに新しい簡略化された投稿を開きました。興味深い回答が得られたら、この投稿を編集して説明します。
注: 簡単にするために、安全性テストは表示されません。
注 2: 私の PoC が非常に珍しいものであり、悪い習慣になる可能性があることは十分承知していますが、とにかくやりたいと思っています。