Lub*_*osD 6 linux assembly x86-64
我正在尝试在64位Linux进程中运行32位代码。32位代码是完全独立的,它可以直接进行IA32系统调用。如果要在32位进程中加载此代码,它将运行良好。
最初,我以为我可以为32位代码分配一个堆栈,切换到该堆栈,一切都可以正常运行,但是效果并不理想。主要是因为与堆栈相关的指令(POP / PUSH / ...)正在执行8字节移位而不是4字节移位。
通过谷歌搜索,我了解到可以通过切换到段选择器0x23转换到32位模式。不幸的是,细分是我所知甚少的东西。
我可以使用以下内容(嵌入式AT&T程序集)转换为32位模式:
movl $0x23, 4(%%rsp) // segment selector 0x23
movq %0, %%rax
movl %%eax, (%%rsp) // target 32-bit address to jump to
lret
Run Code Online (Sandbox Code Playgroud)
其中%0包含代码映射位置的32位地址。代码开始运行,我可以看到PUSH / POP现在可以按应有的方式工作,但是它比我在看似无害的指令上以64位模式运行代码时更早崩溃:
0x8fe48201 mov 0xa483c(%rbx),%ecx
Run Code Online (Sandbox Code Playgroud)
在哪里%rbx(或更像%ebx因为该代码已经是32位,GDB根本不知道)包含0x8fe48200。它尝试从(0x8feeca3c)读取的地址是有效且可读的(根据/proc/XXX/maps),当我从GDB中读取时,它包含期望值。
但是,Linux会SIGSEGV根据此指令将a发送到进程,并且错误地址是0(由strace或p $_siginfo._sifields._sigfault.si_addr内部报告gdb)。在某种程度上,这似乎0x8feeca3c不是32位域中的有效地址。
任何想法如何进行?
更新:我已经写了一个最小的示例,该段会读取地址0,尽管并未真正引用地址0。尽管堆栈操作可以正常工作,但似乎读取内存中的任何地址都将失败(甚至无法读取刚刚执行的指令的地址!)。
movl $0x23, 4(%%rsp) // segment selector 0x23
movq %0, %%rax
movl %%eax, (%%rsp) // target 32-bit address to jump to
lret
Run Code Online (Sandbox Code Playgroud)
好问题:)
问题是它ds仍然设置为零,在 64 位模式下它不被使用。因此,您需要重新加载它,它就会起作用。将初始测试推送/弹出更改为push $0x2b; pop %ds即可解决问题:
const unsigned char instructions[] = {
0x6a, 0x2b, // push $0x2b
0x1f, // pop %ds
Run Code Online (Sandbox Code Playgroud)
我0x2b从 32 位程序中提取了该值。我只是一直想知道为什么push有效。仔细观察,ss也设置为 64 位模式,因此将其复制到ds以及es.