tauri的逆向

大致分为2部分, 一部分是前端逆向, 关键在于提取前端的资源文件, tauri会用brotli将前端资源全部压缩, debug时压缩等级为2, release为9, 这些可以在tauri对资源打包的源代码找到, 另外就是后端部分的逆向

前端部分

目前有一个项目 tauri-dumper 可以把 windowsmacos 平台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/

https://github.com/Mas0nShi/tauri-dumper