本帖最后由 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文件也拿出来
打开visualstudio,新建c++控制台项目(或者dll均可,为了方便演示我就建立=控制台项目了),然后把 www.c 和www.h,wasm-rt.h,wasm-rt-impl.c,wasm-rt-impl.h拷贝进去
组成工程文件,在主文件中导入www.h头文件,如下图所示:
然后编译一下:
发现抱错了。其中抱错的原因是我们加载wasm的过程中有导入函数,这些导入函数被反编译成了导入符号
www.h中的符号,分为导入 (import) 符号,和导出(export)符号。js 和 wasm 交互时,import 符号是 js 提供给 wasm 使用的。而 export 符号是 wasm 提供给 js 使用的。
其中导入符号是未定义的。就是我们要自己参考js中的导入函数去在www.c文件中实现
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即可
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函数的定义如下:
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
编译一下:
gcc -o www ConsoleApplication5.c wasm-rt-impl.c
编译通过,表明环境已经补完
接下来我们来分析一下如何调用:
我们转到www.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这个结构体的定义可以看到:
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就相当于
void WASM_RT_ADD_PREFIX(init)(void) { init_func_types(); init_globals(); init_memory(); init_table(); init_exports();}
init_func_types的作用是注册函数原型。
static void init_globals(void) { w2c_g0 = 1048576u;}
init_globals初始化全局变量
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的内存空间
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来把数据拷贝到内存空间中。
然后我们可以看到导出函数init_exports的定义:
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头文件中申明的导出函数。
分析之后。我们开始调用:
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环境
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的内存空间中赋值。
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函数:
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,返回加密结果
现在来编译一下:
gcc -o www ConsoleApplication5.c wasm-rt-impl.c www.c
编译成功,然后运行一下,返回了字符串fb32ca7c2863964f2438fa50a50d8e3f
返回了加密结果
打开之前爬取的html,输入加密字符串,验证一下:
正确。至此就结束了。我们可以写出c++ dll供其他语言调用。
参考:执行wasm转换出来的c代码https://zhuanlan.zhihu.com/p/43986042