一、 背景
这一题很有迷惑性,设置了很多陷阱,看似正常的逻辑其实是假象。里面用到了Webassembly逆向,我也是第一次遇到这种,之前没有触过wasm汇编语言,看到代码也是无从下手,也是一边学习一边算法逆向。
二、 代码分析
java层分析
从代码可以看出满足native int check_key(String str) 为真时候,则验证通过,同时也调用了sayHello(),调用的具体作用还看不出来。

so层分析
从导出函数列表中,找到我们的目标函数:

Java_com_example_assemgogogo_gogogoJNI_sayHello
这段代码的意思就是返回字符串:http://127.0.0.1:8000

Java_com_example_assemgogogo_gogogoJNI_check_key

主要步骤如下:
通过 JNI 调用 Java 中的方法(通过函数指针调用),返回结果存储在 result 变量中。
如果调用成功,进入密钥检查的逻辑。
使用 srand(0x32u) 设置随机数生成器的种子。
在循环中,生成随机数 v8,然后根据一些条件检查当前密钥字节是否符合预期。
如果所有密钥字节都通过检查,返回 0;否则,关闭套接字并返回 1。
v8是一个随机数,if ( *(_BYTE *)(v6 + v7) != (v8 - (v9 & 0xFFFFFF80) != (unsigned __int8)aD584a68d4e213d[v7]) )
结果肯定是不相等的,那么通过分析返回值永远都是0,那么不成立,这个函数岂不是无解?
三、hook验证
既然通过代码分析,我们知道他们调用了_check_key、sayHello这个2个函数,那么我们就hook他们,验证下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | function hook_jiami() { Java.perform(function () { var gogogoJNI = Java.use("com.example.assemgogogo.gogogoJNI"); gogogoJNI["sayHello"].implementation = function () { console.log("gogogoJNI.sayHello is called"); var result = this["sayHello"](); console.log("gogogoJNI.sayHello result=${result}", result); return result; }; gogogoJNI["check_key"].implementation = function (str) { console.log("gogogoJNI.check_key is called: str=${str}", str); var result = this["check_key"](str); console.log("gogogoJNI.check_key result=${result}", result); return result; }; });} |

通过结果可以看出,只调用了sayHello函数并没有调用check_key函数,结合之前的分析可以得出,此处并非是真正的位置,check_key函数又在哪调用的呢?
再来看java层代码中:
((WebView) findViewById(R.id.text1View)).loadUrl(this.u);
((WebView) findViewById(R.id.text1View)).getSettings().setJavaScriptEnabled(true);
this.u = :http://127.0.0.1:8000 setJavaScriptEnabled 启用了JavaScript。接着分析:
在so文件JNI_OnLoad中调用了inti_proc函数:

使用一个循环,将一系列向量进行初始化,涉及到按位异或操作。
使用一系列操作配置了套接字相关的参数,包括套接字类型、协议等。
通过 getaddrinfo 获取了本地主机(IP地址为0)在端口8000上的地址信息,并将结果存储在 v19 中。
在获取的地址信息上循环,尝试创建套接字并设置一些选项。
套接字创建和配置成功,创建了多个线程。
返回值 nullsub_

mm0 是关键 我们hook之
1 2 3 4 5 6 7 8 9 10 11 12 | function hook_jiami() { Java.perform(function () { var module_addr = Module.findBaseAddress("libgogogo.so"); var mm0__addr = Module.findExportByName("libgogogo.so","mm0"); console.log("module_addr = ", module_addr); var get_html = mm0__addr.readByteArray(35000); console.log("mm0__addr_addr 3333 ",get_html) });} |

得到一串代码,可以看出他是一个html页面,看来交互就是和他进行了。我们在弄出完整的html页面看看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | function bytesToString(arr) { if (typeof arr === 'string') { return arr; } var str = ""; arr = new Uint8Array(arr); for (var i in arr) { str += String.fromCharCode(arr[i]); } return str}function hook_jiami() { Java.perform(function () { var module_addr = Module.findBaseAddress("libgogogo.so"); var mm0__addr = Module.findExportByName("libgogogo.so","mm0"); console.log("module_addr = ", module_addr); var get_html = mm0__addr.readByteArray(35000); var get_html1 = bytesToString(get_html); console.log("nmm0__addr_addr 4444 ",bytesToString(get_html)) });} |

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | <html><head><meta http-equiv="content-type" content="text/html; charset=UTF-8"> <meta charset="utf-8"> <style> body { background-color: rgb(255, 255, 255); } </style></head><script>var instance;WebAssembly.compile(new Uint8Array(` 00 61 73 6D 01 00 00 00 01 1B 05 60 00 00 60 047F 7F 7F 7F 01 7F 60 02 7F 7F 01 7F 60 01 7F 017F 60 00 01 7F 03 0E 0D 00 01 01 01 01 01 01 0101 02 03 04 04 04 05 01 70 01 01 01 05 03 01 0002 06 15 03 7F 01 41 D0 89 04 0B 7F 00 41 D0 89...00 00 00 83 01 04 6E 61 6D 65 01 7C 0D 00 11 5F5F 77 61 73 6D 5F 63 61 6C 6C 5F 63 74 6F 72 7301 01 6F 02 02 6F 6F 03 03 6F 6F 6F 04 04 6F 6F6F 6F 05 05 6F 6F 6F 6F 6F 06 06 6F 6F 6F 6F 6F6F 07 07 6F 6F 6F 6F 6F 6F 6F 08 08 6F 6F 6F 6F6F 6F 6F 6F 09 0E 73 65 74 5F 69 6E 70 75 74 5F66 6C 61 67 0A 12 73 65 74 5F 69 6E 70 75 74 5F66 6C 61 67 5F 6C 65 6E 0B 09 63 68 65 63 6B 5F6B 65 79 0C 03 78 78 78`.trim().split(/[\s\r\n]+/g).map(str => parseInt(str, 16)))).then(module => { new WebAssembly.instantiate(module).then(results => { instance = results;}).catch(console.error);})function check_flag(){ var value = document.getElementById("key_value").value; if(value.length != 32) { document.getElementById("tips").innerHTML = "Not Correct!"; return; } instance.exports.set_input_flag_len(value.length); for(var ii=0;ii<value.length;ii++){ instance.exports.set_input_flag(value[ii].charCodeAt(),ii); } var ret = instance.exports.check_key(); if (ret == 1){ document.getElementById("tips").innerHTML = "Congratulations!" } else{ document.getElementById("tips").innerHTML = "Not Correct!" }}</script><body> <div>Key: <input id="key_value" type="text" name="key" style="width:60%" ;="" value=""> <input type="submit" value="check" onclick="check_flag()"></div> <div> <label id="tips"></label></div></body></html> |
四、WebAssembly基本概念
WebAssembly(简称为Wasm)是一种面向Web的二进制指令格式。它是一种可移植、高性能的虚拟机,旨在在Web浏览器中执行底层代码,以提供接近本地代码执行速度的性能。以下是关于WebAssembly的基本概念:
二进制指令格式:
WebAssembly使用紧凑的二进制格式,可以更高效地传输和解码,同时减小加载时间。这使得WebAssembly成为Web平台上的一个通用执行格式。
独立于编程语言:
WebAssembly并不依赖于特定的编程语言,而是设计为与多种语言兼容。它可以被用作JavaScript之外的语言的目标,使得开发者可以使用各种语言编写Web应用的核心功能。
执行环境:
WebAssembly代码在浏览器中执行,但它不直接在JavaScript引擎上运行。相反,WebAssembly有自己的虚拟机,与浏览器的JavaScript引擎分开。这使得WebAssembly能够获得与本地代码相媲美的性能。
跨平台:
WebAssembly是一个跨平台的技术,可以在不同体系结构和操作系统上运行。这使得开发者可以编写一次代码,并在各种设备和环境中部署,而无需修改。
安全性:
WebAssembly被设计为在Web环境中运行,同时具有强大的安全性。它在一个受控的沙箱中运行,防止对计算机系统的恶意访问。此外,WebAssembly模块可以通过浏览器的同源策略限制来提高安全性。
JavaScript和WebAssembly的互操作性:
WebAssembly与JavaScript有良好的互操作性。JavaScript可以调用WebAssembly的函数,而WebAssembly也可以调用JavaScript的函数,使得两者可以共同工作。
使用场景:
WebAssembly广泛应用于需要高性能的Web应用程序,例如图形、游戏、模拟器、数据处理等。它允许开发者在Web上执行密集型计算任务,而不会损失性能。
工具链支持:
WebAssembly有完整的工具链,包括编译器和调试器,使得开发者能够方便地将其集成到现有的开发工作流程中。
五、处理 处理wasm文件
从github下载工具,对文件进行反编译 https://github.com/WebAssembly/wabt
1 | ./wasm2c /root/Desktop/hex.wasm -o web.c |
此时代码看上去是又多又长,分析起来比较吃力。gcc编译web.c获得中间文件web.o
1 | gcc -c web.c -o web.o |
将web.o拖到IDA中分析。
w2c_check_key()就是核心代码了

w2c_o内部逻辑都是一样的。

函数签名:
函数名为 w2c_o。
返回一个64位整数 __int64。
使用 __fastcall 调用约定,表明一些参数可能会通过寄存器传递。
接受四个无符号整数参数 a1、a2、a3、a4。
局部变量和参数:
使用了一系列整数变量,如 v4、v5、v6、...、v28。
v26、v17、v16、v15、v23、v21 等变量在后续的运算中被使用。
内存操作:
使用 i32_load8_u 和 i32_load8_s 从内存中加载8位整数。
使用 i32_store8 将8位整数写入内存。
逻辑运算和条件判断:
对变量进行递增、逻辑与、逻辑或等操作,生成一系列的逻辑条件。
根据条件判断执行不同的逻辑分支。
循环:
使用 do-while 循环,在循环体内执行一系列的操作。
返回值:
最终返回一个64位整数 v18。
调用 wasm_rt_trap 函数:
在代码开头,使用 wasm_rt_call_stack_depth 变量判断调用深度,如果超过一定深度,就会调用 wasm_rt_trap 函数,并传递参数7。
数据处理和异或运算:
对参数进行一系列复杂的逻辑运算,包括位异或运算,与运算,或运算等。
条件判断:
根据条件的不同执行不同的逻辑分支。
最后的返回:
根据条件计算得到的最终结果 v18 作为函数的返回值。
w2c_xxx() 是一个32元方程组

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | import numpy as npfrom scipy.linalg import solvea = np.array( [[108, 111, 92, 194, 124, 240, 126, 81, 144, 103, 161, 50, 67, 15, 127, 232, 188, 19, 233, 153, 231, 40, 112, 106, 135, 90, 67, 20, 248, 45, 48, 174], [227, 78, 195, 81, 10, 248, 186, 171, 148, 194, 40, 180, 17, 212, 104, 90, 178, 26, 225, 209, 32, 169, 94, 156, 154, 56, 244, 149, 120, 131, 13, 101], [83, 44, 95, 131, 30, 55, 46, 36, 67, 109, 69, 251, 8, 248, 40, 154, 251, 86, 112, 9, 174, 197, 38, 14, 202, 60, 117, 188, 136, 145, 240, 53], [152, 162, 112, 57, 102, 182, 10, 139, 30, 7, 145, 127, 148, 5, 165, 109, 110, 234, 113, 33, 192, 45, 65, 105, 140, 116, 35, 48, 155, 25, 234, 25], [101, 189, 236, 118, 141, 148, 197, 7, 108, 104, 45, 130, 39, 164, 88, 241, 108, 107, 76, 34, 210, 29, 156, 90, 139, 151, 10, 97, 209, 46, 82, 113], [182, 13, 50, 102, 155, 230, 3, 225, 237, 163, 38, 176, 115, 105, 203, 26, 72, 111, 96, 240, 139, 117, 153, 120, 151, 25, 49, 90, 98, 7, 179, 72], [170, 150, 226, 101, 110, 99, 127, 101, 203, 209, 187, 100, 226, 186, 252, 39, 65, 67, 225, 174, 1, 187, 214, 22, 74, 99, 129, 254, 13, 97, 156, 61], [1, 88, 118, 232, 60, 252, 133, 177, 185, 222, 32, 48, 1, 242, 240, 218, 81, 22, 73, 171, 139, 72, 106, 62, 156, 134, 220, 19, 77, 94, 154, 117], [189, 173, 41, 39, 26, 232, 75, 75, 95, 7, 117, 96, 211, 130, 228, 143, 91, 247, 43, 122, 131, 52, 48, 29, 111, 38, 19, 242, 162, 70, 220, 151], [236, 136, 147, 104, 79, 204, 220, 25, 38, 233, 165, 20, 174, 120, 214, 18, 233, 119, 244, 143, 126, 226, 77, 33, 189, 5, 150, 160, 14, 112, 231, 92], [191, 38, 193, 250, 212, 175, 39, 94, 183, 172, 171, 163, 129, 165, 64, 170, 199, 2, 167, 2, 216, 252, 184, 187, 97, 109, 98, 135, 192, 88, 50, 203], [203, 81, 252, 104, 248, 156, 199, 46, 208, 240, 149, 155, 102, 95, 51, 208, 208, 62, 58, 117, 72, 23, 193, 193, 226, 217, 106, 147, 136, 16, 43, 196], [144, 69, 224, 107, 225, 83, 15, 10, 214, 152, 24, 136, 165, 208, 38, 67, 201, 180, 158, 75, 111, 65, 211, 220, 135, 125, 216, 105, 122, 112, 80, 49], [143, 68, 127, 51, 152, 88, 153, 9, 149, 107, 178, 166, 190, 177, 99, 71, 63, 233, 58, 132, 109, 75, 152, 95, 74, 195, 90, 251, 205, 8, 76, 129], [209, 146, 59, 38, 40, 56, 182, 245, 67, 202, 177, 183, 26, 126, 161, 95, 133, 123, 163, 30, 88, 219, 5, 86, 183, 156, 253, 97, 43, 128, 31, 102], [146, 223, 137, 228, 226, 155, 170, 92, 77, 17, 22, 128, 20, 171, 142, 170, 192, 49, 200, 178, 154, 42, 5, 159, 251, 152, 7, 247, 145, 39, 91, 136], [169, 204, 244, 26, 77, 134, 221, 205, 149, 47, 1, 197, 82, 195, 123, 219, 116, 80, 13, 231, 173, 192, 220, 224, 108, 104, 56, 152, 84, 226, 121, 205], [184, 45, 176, 126, 118, 161, 142, 171, 215, 83, 233, 184, 171, 182, 126, 111, 118, 67, 92, 219, 70, 252, 194, 21, 245, 204, 48, 150, 39, 85, 73, 95], [48, 224, 164, 138, 92, 3, 191, 94, 19, 50, 34, 167, 75, 72, 238, 15, 111, 216, 84, 40, 145, 112, 140, 204, 154, 195, 175, 250, 202, 169, 170, 120], [112, 19, 189, 50, 247, 240, 164, 5, 139, 56, 19, 4, 23, 172, 96, 254, 63, 247, 149, 183, 128, 147, 213, 243, 172, 144, 246, 25, 106, 176, 170, 68], [184, 22, 183, 128, 149, 174, 227, 113, 65, 159, 74, 170, 186, 174, 211, 1, 223, 156, 253, 223, 241, 252, 148, 93, 41, 125, 27, 136, 78, 248, 41, 31], [155, 237, 242, 10, 145, 99, 239, 105, 3, 43, 46, 155, 208, 75, 140, 181, 197, 140, 10, 170, 142, 212, 186, 27, 105, 118, 198, 243, 13, 113, 82, 39], [207, 206, 127, 58, 91, 87, 7, 17, 63, 180, 40, 96, 202, 185, 68, 72, 240, 36, 139, 199, 76, 229, 159, 136, 94, 19, 3, 87, 45, 6, 136, 50], [115, 215, 40, 166, 87, 83, 74, 202, 235, 149, 114, 76, 204, 218, 63, 123, 9, 172, 38, 138, 35, 200, 221, 144, 235, 108, 1, 245, 153, 184, 90, 12], [123, 190, 55, 180, 84, 231, 81, 116, 61, 3, 94, 85, 190, 187, 142, 62, 225, 240, 179, 150, 77, 85, 196, 12, 144, 122, 28, 224, 248, 143, 114, 36], [2, 202, 40, 224, 154, 65, 30, 241, 13, 213, 176, 122, 30, 158, 14, 191, 80, 116, 74, 70, 32, 189, 76, 95, 158, 103, 7, 201, 204, 91, 190, 122], [42, 154, 223, 165, 155, 101, 75, 95, 253, 14, 158, 199, 110, 89, 205, 202, 83, 162, 67, 30, 115, 83, 27, 31, 118, 160, 248, 66, 88, 44, 5, 176], [34, 168, 72, 160, 243, 41, 146, 29, 62, 235, 185, 180, 10, 150, 208, 140, 125, 114, 35, 34, 38, 123, 163, 208, 5, 29, 207, 111, 72, 65, 125, 84], [18, 11, 26, 175, 44, 128, 32, 100, 21, 116, 253, 213, 67, 16, 171, 178, 97, 7, 162, 152, 78, 167, 177, 97, 26, 155, 127, 21, 243, 188, 140, 197], [140, 110, 164, 208, 72, 113, 9, 47, 179, 166, 51, 34, 91, 184, 89, 162, 233, 127, 156, 127, 244, 183, 193, 138, 242, 90, 193, 7, 252, 113, 152, 7], [133, 105, 75, 146, 173, 27, 97, 142, 164, 15, 10, 177, 239, 141, 189, 67, 153, 108, 206, 210, 171, 252, 84, 249, 7, 168, 100, 30, 196, 244, 197, 75], [147, 221, 57, 186, 69, 230, 167, 3, 220, 63, 218, 235, 156, 146, 75, 198, 204, 197, 59, 61, 179, 47, 221, 127, 210, 218, 241, 135, 196, 185, 53, 79], ])b = np.array([359512,387514,301487,296549,344514,346892,386678,348667,316884,372620,413102,428661,371484,350848,334408,382822,420160,402263,366968,384909,425203,372162,297509,372215,370337,314564,325974,307088,322340,380716,393331,430295])x = solve(a, b)print(x) |

对相应位进行异或
1 2 3 4 5 6 7 8 9 | ss = "S0m3time_l1tt1e_c0de_1s_us3ful33"yh=[0x18,9,3,0x6b,1,0x5a,0x32,0x57,0x30,0x5d,0x40,0x46,0x2b,0x46,0x56,0x3d,2,0x43,0x17,0,0x32,0x53,0x1f,0x26,0x2a,1,0,0x10,0x10,0x1e,0x40,0]temp=[]for i in range(32): x=ord(ss[i])^yh[i] temp.append(chr(x))print(''.join(temp))# 得到结果:K9nXu3_2o1q2_w3bassembly_r3vers3 |