tauri的逆向
大致分为2部分, 一部分是前端逆向, 关键在于提取前端的资源文件, tauri会用brotli将前端资源全部压缩, debug时压缩等级为2, release为9,
这些可以在tauri对资源打包的源代码找到, 另外就是后端部分的逆向
前端部分
目前有一个项目 tauri-dumper 可以把 windows 和 macos 平台tauri应用的前端资源直接dump出来, 对于其他平台还需要手动dump, 可以参考此文章
后端部分
当前端的某个函数长这样的时候
async function greet() {
greetMsgEl.textContent = await invoke("some_command", { name: greetInputEl.value });
}就说明前端调用了使用rust编写的后端函数
mod cry;
#[tauri::command]
fn some_command(name: &str) -> String {
let data = name.as_bytes().to_vec();
let cipher = cry::crypt(&data, &"ColorSkyFun");
if cipher
== vec![
78, 240, 77, 233, 128, 40, 110, 146, 222, 242, 138, 7, 84, 125, 236, 21, 87, 111, 144,
24, 55, 192, 93, 137, 123, 220, 76, 5,
]
{
format!("Yes~")
} else {
format!("No!")
}
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_opener::init())
.invoke_handler(tauri::generate_handler![some_command])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}generate_handler!宏会将#[tauri::command]派生的代码进行再次编程, 展开后得到这样的代码
tauri::Builder::default()
.plugin(tauri_plugin_opener::init())
.invoke_handler(move |__tauri_invoke__| {
let __tauri_cmd__ = __tauri_invoke__.message.command();
match __tauri_cmd__ {
"some_command" => {
({
move || {
#[allow(unused_imports)]
use ::tauri::ipc::private::*;
#[allow(unused_variables)]
let ::tauri::ipc::Invoke {
message: __tauri_message__,
resolver: __tauri_resolver__,
acl: __tauri_acl__,
} = __tauri_invoke__;
let result = some_command(
match ::tauri::ipc::CommandArg::from_command(::tauri::ipc::CommandItem {
plugin: ::core::option::Option::None,
name: "some_command",
key: "name",
message: &__tauri_message__,
acl: &__tauri_acl__,
}) {
Ok(arg) => arg,
Err(err) => {
__tauri_resolver__.invoke_error(err);
return true;
}
},
);
let kind = (&result).blocking_kind();
kind.block(result, __tauri_resolver__);
return true;
}
})()
}
_ => {
return false;
}
}
})下面这段代码最为重要
match ::tauri::ipc::CommandArg::from_command(
::tauri::ipc::CommandItem {
plugin: ::core::option::Option::None,
name: "some_command",
key: "name",
message: &__tauri_message__,
acl: &__tauri_acl__,
}) {
Ok(arg) => arg,
Err(err) => {
__tauri_resolver__.invoke_error(err);
return true;
}
},在编译为二进制后, 可以在ida里搜索some_command从而定位这段代码的位置
*(_QWORD *)&v20[12] = 0;
*(_QWORD *)v20 = aSomeCommand; // some_command
*(_QWORD *)&v20[2] = 12;
*(_QWORD *)&v20[4] = aName_0; // name
*(_QWORD *)&v20[6] = 4;
*(_QWORD *)&v20[8] = v18;
*(_QWORD *)&v20[10] = &v30;
v33 = 1;
sub_7FF63314F8B0(Src, (__int64 *)v20);接下来关注v20这个变量, 这样就可以定位到some_command这个函数具体的实现.
sub_7FF63300D380(
(unsigned int)v20,
(unsigned int)p_Size,
(unsigned int)&off_7FF63351B280,
(unsigned int)&off_7FF63351B2B0,
(__int64)&qword_7FF63351B2C0); // "ColorSkyFun"
nullsub_1();
v10 = sub_7FF632F41080(28, 1);
if ( !v10 )
sub_7FF6334FF7F1(1, 28);
*(__m128i *)v10 = _mm_load_si128((const __m128i *)&xmmword_7FF63351B030);
*(_QWORD *)(v10 + 16) = 0x895DC03718906F57uLL;
*(_DWORD *)(v10 + 24) = 88923259;
if ( *(_QWORD *)&v20[4] == 28
&& _mm_movemask_epi8(
_mm_and_si128(
_mm_cmpeq_epi8(
_mm_loadu_si128((const __m128i *)(v10 + 12)),
_mm_loadu_si128((const __m128i *)(*(_QWORD *)&v20[2] + 12LL))),
_mm_cmpeq_epi8(_mm_loadu_si128((const __m128i *)v10), _mm_loadu_si128(*(const __m128i **)&v20[2])))) == 0xFFFF )
{
// skip
}sub_7FF63300D380就是crypt函数, v10就是密文
Thanks for
https://blog.yllhwa.com/blog/tauri-static-assets-extraction/