【问题标题】:Executing self generated machine code on mobile platforms在移动平台上执行自行生成的机器代码
【发布时间】:2021-12-28 18:37:51
【问题描述】:

关于 SO 评估执行自生成代码的可能性已经存在几个问题。通用的答案是这是可能的,它是在 JIT 编译器中完成的,并通过动态库加载来完成。

应用程序用于优化稀疏卷积,其中稀疏结构应嵌入代码中,因为有条件地跳过乘以零比乘零慢。在数据中编码结构在性能方面也不可行:

 do { // actually: don't do this
     auto offset = *encoded_offset++;
     auto coeff = *encoded_coefficient++;
     accum += data[offset] * coeff;
 } while (end_of_data);   // e.g. offset < 0, or offset == previous offset

对比

...
ldr     q5, [x1], 16   ; load data
fmla    v0.4s, v5.4s, v8.s[0]
fmla    v1.4s, v5.4s, v8.s[1]
fmla    v2.4s, v5.4s, v8.s[2]
// fmla    v3.4s, v5.4s, v8.s[3]   ; omitted, when |weight| < threshold
fmla    v4.4s, v5.4s, v8.s[3]      ; we reuse the v8.s[3] with a weight that matters
ldr     q5, [x1], 16
// fmla    v0.4s, v5.4s, v9.s[0] ; omitted, when |weight| < threshold
// fmla    v1.4s, v5.4s, v9.s[0] ; omitted
fmla    v2.4s, v5.4s, v9.s[0]    
fmla    v3.4s, v5.4s, v9.s[1]
fmla    v4.4s, v5.4s, v9.s[2]

; + several kilobytes of instructions

可以通过模板生成代码,但如果代码是动态生成的,那么效率会高得多(并且考虑到逆向工程的安全性)。

现在的问题是,如何准确地在一些最重要的移动生态系统中调用生成的函数:Ios、android 和可能的 webassembly(使用偏离路线的 WASM (SIMD) 字节码)。 p>

需要发出哪些确切的系统调用才能合法地执行填充了代码的内存块?

【问题讨论】:

  • 我并不是说这是不可能的,但我认为您需要 root 或管理员权限才能绕过通常用于防止病毒和其他攻击的保护措施。
  • Metal 可以成为您在 ios 上的一个选项吗?
  • 就保护/执行而言,这不是通常使用mprotect 管理的吗?

标签: android c++ ios webassembly


【解决方案1】:

您会偶然发现的唯一问题是“正常”内存通常受到保护以防执行。您只需要在该内存上设置执行权限。

之后,您可以设置任何您想要的指令。例如,请参阅使用简单 JIT 机器的顶级 fizzbuzz 带宽赢家。

https://codegolf.stackexchange.com/a/236630/108147

但基本代码是这样的:

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

uint8_t code[] = { 0x89, 0xf8,       // mov eax, edi
                   0x0f, 0xaf, 0xc6, // imul eax, esi
                   0xc3 };           // ret

int main() {
    using multifn = int( int a, int b);
    size_t pagesize = getpagesize();
    size_t codesize = ((sizeof(code)-1)/pagesize+1)*pagesize;
    uint64_t codeaddr = uint64_t(code);
    codeaddr -= codeaddr % pagesize;
    mprotect((void*)codeaddr, codesize, PROT_READ | PROT_WRITE |PROT_EXEC);   
    multifn* m = reinterpret_cast<multifn*>(code);
    std::cout << "Result:" << m(2,3) << std::endl;
    return 0;
}

我只是以普通用户的身份在我的 Linux 机器上运行它,结果是:

Program stdout
Result:6

我假设由于 Android 基本上是 Linux,结果将是相同的。根据IOS,我必须在这里推测。

还有神箭:https://godbolt.org/z/hsMKWsjM9

如果你对不搞乱常规内存真的很挑剔,你可以为你生成的代码创建一个单独的页面

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

void* createExecPage( size_t size ) {
    size_t pagesize = getpagesize();
    size = ((size-1)/pagesize+1)*pagesize;
    std::cout << "Pagesize:" << pagesize << " size:" << size << std::endl;
    void* temp = nullptr;
    int res = posix_memalign(&temp, pagesize, size );
    if ( res!=0 ) {
        perror("posix_memalign");
    }
    res = mprotect(temp, size, PROT_READ | PROT_WRITE |PROT_EXEC);
    if ( res!=0 ) {
        perror( "mprotect" );
    }
    return temp;
}

uint8_t code[] = { 0x89, 0xf8,       // mov eax, edi
                   0x0f, 0xaf, 0xc6, // imul eax, esi
                   0xc3 };           // ret

int main()
{
    using multifn = int( int a, int b);
    void* xpage = createExecPage(sizeof(code));
    memcpy( xpage, code, sizeof(code));
    multifn* m = reinterpret_cast<multifn*>(xpage);
    std::cout << "Result:" << m(2,3) << std::endl;
    return 0;
}

https://godbolt.org/z/ebW76o1KP

【讨论】:

    猜你喜欢
    • 2021-06-19
    • 2017-07-19
    • 2021-07-10
    • 2015-03-30
    • 2023-03-22
    • 2016-08-17
    • 2016-11-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多