欢迎光临
我们一直在努力

执行wasm2c翻译出来的c代码二

本帖最后由 Frhvjhhv 于 2022-10-3 11:25 编辑

承接上回,我们把wasm文件下来,然后执行wasm2c   www.wasm  -o   www.c  命令得到   www.c   和www.h文件

然后把wabt工具包里面的wasm-rt.h,wasm-rt-impl.c,wasm-rt-impl.h文件也拿出来

执行wasm2c翻译出来的c代码二



打开visualstudio,新建c++控制台项目(或者dll均可,为了方便演示我就建立=控制台项目了),然后把 www.c   和www.h,wasm-rt.h,wasm-rt-impl.c,wasm-rt-impl.h拷贝进去

执行wasm2c翻译出来的c代码二



组成工程文件,在主文件中导入www.h头文件,如下图所示:

然后编译一下:

执行wasm2c翻译出来的c代码二



发现抱错了。其中抱错的原因是我们加载wasm的过程中有导入函数,这些导入函数被反编译成了导入符号
www.h中的符号,分为导入 (import) 符号,和导出(export)符号。js 和 wasm 交互时,import 符号是 js 提供给 wasm 使用的。而 export 符号是 wasm 提供给 js 使用的。

执行wasm2c翻译出来的c代码二

其中导入符号是未定义的。就是我们要自己参考js中的导入函数去在www.c文件中实现

[JavaScript] 纯文本查看 复制代码
function initSync(A) {        var i = {            wbg: {}        };        return i.wbg.__wbg_new_59cb74e423758ede = function() {            return addHeapObject(new Error)        }        ,        i.wbg.__wbg_stack_558ba5917b466edd = function(A, i) {            var g = passStringToWasm0(getObject(i).stack, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc)              , i = WASM_VECTOR_LEN;            getInt32Memory0()[A / 4 + 1] = i,            getInt32Memory0()[A / 4 + 0] = g        }        ,        i.wbg.__wbg_error_4bb6c2a97407129a = function(A, i) {            try {                console.error(getStringFromWasm0(A, i))            } finally {                wasm.__wbindgen_free(A, i)            }        }        ,        i.wbg.__wbindgen_object_drop_ref = function(A) {            takeObject(A)        }        ,        i.wbg.__wbindgen_throw = function(A, i) {            throw new Error(getStringFromWasm0(A, i))        }        ,        i = loadSync(A, i).instance,        wasm = i.export

参考上面的js代码,
__wbg_new_59cb74e423758ede是维护一个栈结构,并将err元素入栈

_wbg_stack_558ba5917b466edd 中的getObject(i).stack是取出栈里面的err元素

其中throw error的直接赋值为NULL即可

[C] 纯文本查看 复制代码
void _558ba5917b466eddZ_vii(u32 a, u32 b);u32(*Z_wbgZ___wbg_new_59cb74e423758edeZ_iv)(void) = NULL;void (*Z_wbgZ___wbg_stack_558ba5917b466eddZ_vii)(u32, u32) = *_558ba5917b466eddZ_vii;void (*Z_wbgZ___wbg_error_4bb6c2a97407129aZ_vii)(u32, u32) = NULL;void (*Z_wbgZ___wbindgen_object_drop_refZ_vi)(u32) = NULL;void (*Z_wbgZ___wbindgen_throwZ_vii)(u32, u32) = NULL;

其中_558ba5917b466eddZ_vii函数的定义如下:

[C++] 纯文本查看 复制代码
typedef u32* wbindgen_malloc(u32 w2c_p0);typedef u32* wbindgen_realloc(u32 w2c_p0, u32 w2c_p1, u32 w2c_p2);int WASM_VECTOR_LEN;int passStringToWasm0(char* A, wbindgen_malloc i, wbindgen_realloc g) {    int B = strlen(A);    u32  Q = i(B);    int e = 0;    for (e; e < B; e++) {        int o = (int)e;        if (127 < o)            break;        w2c_memory.data[Q + e] = o;    }    u32 Qq = g(Q, B, e);    WASM_VECTOR_LEN = e;    return Qq;}void _558ba5917b466eddZ_vii(u32 a, u32 b){    int g = passStringToWasm0("error", w2c___wbindgen_malloc, w2c___wbindgen_realloc);    int  i = WASM_VECTOR_LEN;    (w2c_memory.data)[a + 1] = i;    (w2c_memory).data[a + 0] = g;}u32(*Z_wbgZ___wbg_new_59cb74e423758edeZ_iv)(void) = NULL;void (*Z_wbgZ___wbg_stack_558ba5917b466eddZ_vii)(u32, u32) = *_558ba5917b466eddZ_vii;void (*Z_wbgZ___wbg_error_4bb6c2a97407129aZ_vii)(u32, u32) = NULL;void (*Z_wbgZ___wbindgen_object_drop_refZ_vi)(u32) = NULL;void (*Z_wbgZ___wbindgen_throwZ_vii)(u32, u32) = NULL;

其中w2c_memory.data,w2c___wbindgen_malloc,u32等等均在www.c文件中已经定义。
对于build_in符号不用管他,因为wasm2c是用gcc编译的所以我们也要用gcc来编译和调试(不要用vs,会报一大堆错)
此时已经把环境补好了,然后我们把主文件即包含main入口点的c文件引入www.h

执行wasm2c翻译出来的c代码二

编译一下:

[Asm] 纯文本查看 复制代码
gcc -o www ConsoleApplication5.c  wasm-rt-impl.c

执行wasm2c翻译出来的c代码二



编译通过,表明环境已经补完

接下来我们来分析一下如何调用:
我们转到www.c文件:

执行wasm2c翻译出来的c代码二



可以看到两个静态全局变量static wasm_rt_memory_t w2c_memory;   static wasm_rt_table_t w2c_T0;  其中 w2c_memory即相当于web里面WebAssembly.Memory,w2c_T0相当于 js 中的 WebAssembly.Table.
转到wasm_rt_memory_t这个结构体的定义可以看到:

[C++] 纯文本查看 复制代码
typedef struct {  /** The linear memory data, with a byte length of `size`. */  uint8_t* data;  /** The current and maximum page count for this Memory object. If there is no   * maximum, `max_pages` is 0xffffffffu (i.e. UINT32_MAX). */  uint32_t pages, max_pages;  /** The current size of the linear memory, in bytes. */  uint32_t size;} wasm_rt_memory_t;

其中data就相当于

WebAssembly.Memory.buffer的uint8array形式。wasm_rt_memory_t还定义了其线性内存空间的大小和页数

然后我们在转到整个wasm的入口点:

[C++] 纯文本查看 复制代码
void WASM_RT_ADD_PREFIX(init)(void) {  init_func_types();  init_globals();  init_memory();  init_table();  init_exports();}

即调用上面这四个函数,即可实现wasm环境的初始化。这四个函数的定义均在www.c文件中。(本次直接调用即可。但是如果外部对wasm的初始化有导入函数,就要分析了,下面是对四个函数的分析)

执行wasm2c翻译出来的c代码二



init_func_types的作用是注册函数原型。

[C++] 纯文本查看 复制代码
static void init_globals(void) {  w2c_g0 = 1048576u;}

init_globals初始化全局变量

[C] 纯文本查看 复制代码
static void init_memory(void) {  wasm_rt_allocate_memory((&w2c_memory), 17, 65536);  LOAD_DATA(w2c_memory, 1048576u, data_segment_data_0, 8009);  LOAD_DATA(w2c_memory, 1057704u, data_segment_data_1, 1);}

init_memory及其重要。allocate_memory就是为wasm环境分配页数为17,最大大小为65536的内存空间

LOAD_DATA就是加载一些字符串或者常量向wasm的内存中。

其中data_segment_data_0的定义如下:


执行wasm2c翻译出来的c代码二


他就相当于wat 的 S 表达式形式的下面字符串常量


执行wasm2c翻译出来的c代码二


我们来看一下 load_data的定义:

[Asm] 纯文本查看 复制代码
static inline void load_data(void *dest, const void *src, size_t n) {  memcpy(dest, src, n);}#define LOAD_DATA(m, o, i, s) load_data(&(m.data[o]), i, s)#define DEFINE_LOAD(name, t1, t2, t3) 

其四个参数分别是w2c_memory,要写入w2c_memory.data处的内存地址(注意,不是真实的内存地址,而是相对与w2c_memory.data这个uint8_t*的地址,即相对地址,也可以理解为w2c_memory.data=[1,2,3,4,4,,5,5,5],a[5]就是w2c_memory.data相对地址5处的数据) ,要写入的数据,数据长度。最终调用memcpy来把数据拷贝到内存空间中。


执行wasm2c翻译出来的c代码二



init_table初始化函数指针数组。Table 可以看成函数指针的数组,数组的每一项存放着函数的签名和函数的地址。这个数组可以放任何不同类型的函数指针,有了函数签名这信息,在函数调用的时候就可动态检查参数是否对应。wasm 可以往 Table 中放函数,js 也可以往 Table 中放函数。放置好函数之后,就可以通过 Table 的索引去间接调用。于是 wasm 和 js,只需要知道 Table 的函数索引,没有必要使用函数指针。


然后我们可以看到导出函数init_exports的定义:

[C] 纯文本查看 复制代码
static void init_exports(void) {  /* export: 'memory' */  WASM_RT_ADD_PREFIX(Z_memory) = (&w2c_memory);  /* export: '__wbg_rsapublickeypair_free' */  WASM_RT_ADD_PREFIX(Z___wbg_rsapublickeypair_freeZ_vi) = (&w2c___wbg_rsapublickeypair_free);  /* export: 'rsapublickeypair_new' */  WASM_RT_ADD_PREFIX(Z_rsapublickeypair_newZ_iv) = (&w2c_rsapublickeypair_new);  /* export: 'rsapublickeypair_init' */  WASM_RT_ADD_PREFIX(Z_rsapublickeypair_initZ_vi) = (&w2c_rsapublickeypair_init);  /* export: 'rsapublickeypair_encode' */  WASM_RT_ADD_PREFIX(Z_rsapublickeypair_encodeZ_viiii) = (&w2c_rsapublickeypair_encode);  /* export: '__wbindgen_malloc' */  WASM_RT_ADD_PREFIX(Z___wbindgen_mallocZ_ii) = (&w2c___wbindgen_malloc);  /* export: '__wbindgen_realloc' */  WASM_RT_ADD_PREFIX(Z___wbindgen_reallocZ_iiii) = (&w2c___wbindgen_realloc);  /* export: '__wbindgen_free' */  WASM_RT_ADD_PREFIX(Z___wbindgen_freeZ_vii) = (&w2c___wbindgen_free);

这里面实现了在www.h头文件中申明的导出函数。

分析之后。我们开始调用:

[C] 纯文本查看 复制代码
char gccc[1000];void Yyyyyyy() {    init_func_types();    init_globals();    init_memory();    init_table();    init_exports();}char* Gou(u32* text, int length){    Yyyyyyy();}

首先在www.c文件中定义一个返回值为char*的函数返回加密的字符串。函数参数为待加密字符串,字符串长度,然后调用Yyyyyyy函数初始化wasm环境

[C] 纯文本查看 复制代码
    u32 aa = w2c_rsapublickeypair_new();//1176460    w2c_rsapublickeypair_init(aa);    u32 nei = w2c___wbindgen_malloc(length);//1176428    int le = 0;    for (le; le < length; le++)    {        w2c_memory.data[nei + le] = text[le];    }    //LOAD_DATA(w2c_memory, nei, text, length);    // memcpy(&w2c_memory, text, length);    w2c_rsapublickeypair_encode(8, aa, nei, length);

然后加密流程与js一样:rsapublickeypair_new初始化,rsapublickeypair_init初始化公钥。然后直接用w2c_memory.data[nei + le] = text[le];向wasm的内存空间中赋值。

[C] 纯文本查看 复制代码
 u32 bbb = i32_load(&w2c_memory, 8);    u32 bb = i32_load(&w2c_memory, 12);      #include <stdio.h>    printf("%dn", bbb);    printf("%dn", i32_load(&w2c_memory, 12));    // char* src;         //src= https://www.52pojie.cn/(char*)malloc((int)bb);>

这里用i32_load(&w2c_memory, 12);直接取出内存处12的值。同样取出内存处为8的值,然后打印出这两个值然后取出内存处bbb,长度为bb的字符串即可。。在www.h头文件中导出函数 extern char* Gou(char*, int);然后在主程序mian函数中调用Gou函数:
[C] 纯文本查看 复制代码
int main(){    const int length = 12;    char* str = "666666890ftt";    u32 cc[12];    for (int i = 0; i < length; i++)    {        cc[i] = (u32)(str[i]);    }    //Gou(cc, 6);   // printf("%sn", cc);    char* ccc;    //int ccc;    ccc = Gou(cc, length);    printf("%s", ccc);    //free(ccc);    getch();}

我们输入字符串666666890ftt,返回加密结果

现在来编译一下:

[Asm] 纯文本查看 复制代码
gcc -o www ConsoleApplication5.c wasm-rt-impl.c  www.c

执行wasm2c翻译出来的c代码二



编译成功,然后运行一下,返回了字符串fb32ca7c2863964f2438fa50a50d8e3f

执行wasm2c翻译出来的c代码二



返回了加密结果
打开之前爬取的html,输入加密字符串,验证一下:

执行wasm2c翻译出来的c代码二



正确。至此就结束了。我们可以写出c++   dll供其他语言调用。
参考:执行wasm转换出来的c代码https://zhuanlan.zhihu.com/p/43986042

赞(0) 打赏
未经允许不得转载:哈哈网 » 执行wasm2c翻译出来的c代码二

相关推荐

  • 暂无文章

评论 抢沙发

觉得文章有用就打赏一下文章作者

非常感谢你的打赏,我们将继续提供更多优质内容,让我们一起创建更加美好的网络世界!

支付宝扫一扫打赏

微信扫一扫打赏