公钥获取

可以确定的是SE壳的公钥和随机Key是直接在PE文件中的,并且找到了这两个就可以直接修改并通过修补最后两个字节来达到修改授权的目的,其他内容在网站都可以找到就不说了。

目的:存在a.exe或dll为目标PE,我们要修改公钥并解密a.key来达到修改成我们自己的授权。

步骤如下:

  1. 构建一个PE为b.exe,已知它的公钥和随机Key地址
  2. a.key拷贝成b.key,也就是b.exe可以加载a文件的key
  3. 解析a.exe,得到第一个节点 .sedata ,找到非0连续地址,然后读取指定大小后启动b.exe
  4. 内存修改b.exe对应公钥地址为上面猜解值,启动b.exe并拦截弹框
  5. 循环执行4,直到弹框报错 ‘该授权文件与本程序不匹配。’, 这样就得到公钥地址+公钥。

参考代码如下

其中,PE解析代码例子如下

 复制代码 隐藏代码
    // 读取输入文件
    let mut input_file = File::open(&cli.input)?;
    let mut buffer = Vec::new();
    input_file.read_to_end(&mut buffer)?;

    let pe = PE::parse(&buffer).map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "Invalid PE file"))?;
    let (sedata_offset, sedata_size) = pe.sections.iter()
        .find(|s| s.name().unwrap_or("") == ".sedata")
        .map(|s| (s.pointer_to_raw_data, s.size_of_raw_data))
        .unwrap_or((0, 0));

    println!(".sedata Offset: 0x{:08X}, Size: 0x{:08X}", sedata_offset, sedata_size);

其中,拦截弹框并返回

 复制代码 隐藏代码
// 添加新的窗口枚举回调函数
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) {
    DWORD processId;
    GetWindowThreadProcessId(hwnd, &processId);

    if (processId == GetCurrentProcessId()) {
        char title[256] = {0};

        GetWindowTextA(hwnd, title, sizeof(title));

        HWND child = GetWindow(hwnd, GW_CHILD);
        int controlIndex = 0;

        while (child && controlIndex < 2) {
            child = GetWindow(child, GW_HWNDNEXT);
            controlIndex++;
        }

        if (child) {
            int textLength = GetWindowTextLengthW(child) + 1;
            if (textLength > 1) {
                wchar_t* wideText = new wchar_t[textLength];
                GetWindowTextW(child, wideText, textLength);

                int mbLength = WideCharToMultiByte(CP_UTF8, 0, wideText, -1, NULL, 0, NULL, NULL);
                char* mbText = new char[mbLength];

                WideCharToMultiByte(CP_UTF8, 0, wideText, -1, mbText, mbLength, NULL, NULL);

                // 输出JSON格式
                printf("{\"title\": \"%s\", \"text\": \"%s\"}\n", title, mbText);
                delete[] wideText;
                delete[] mbText;

                // 退出进程
                ExitProcess(-1);
            }
        }
    }
    return TRUE;
}

// 添加新的监控线程函数
void WindowMonitorThread() {
    while (isMonitorRunning) {
        EnumWindows(EnumWindowsProc, 0);
        std::this_thread::sleep_for(std::chrono::milliseconds(30));
    }
}

判断返回

 复制代码 隐藏代码
fn parse_execution_result(output: std::process::Output) -> ExecutionResult {
    match output.status.code().unwrap_or(-1) {
        -1073741819 => ExecutionResult::DecryptionFailed,
        -1 => {
            if let Ok(json_output) = serde_json::from_slice::<serde_json::Value>(&output.stdout) {
                if let Some(text) = json_output.get("text").and_then(|v| v.as_str()) {
                    match text {
                        "文件已损坏,无法运行。" => ExecutionResult::FileCorrupted,
                        "该授权文件与本程序不匹配。" => ExecutionResult::InvalidLicense,
                        _ => ExecutionResult::UnknownError(text.to_string()),
                    }
                } else {
                    ExecutionResult::UnknownError("无法解析 JSON 输出".to_string())
                }
            } else {
                ExecutionResult::UnknownError("标准输出无法解析为 JSON".to_string())
            }
        },
        0 | 1 => ExecutionResult::Success(
            String::from_utf8_lossy(&output.stdout).to_string(),
            String::from_utf8_lossy(&output.stderr).to_string()
        ),
        code => ExecutionResult::UnknownError(format!("未知错误码: {}", code)),
    }
}

结果

通过测试,所有的SE2加壳均可以快速(1-5分钟)找到key,并且测试在linux上可用,不需要启动目标程序就可以得到公钥。至于随机数,看这篇有没有人关注,多的话会发,大概原理采用数学统计原理找到目标区域。

思路可以推广到其他强壳。某滑块逆向分析插图

本站资源来自互联网收集,仅提供信息发布
一旦您浏览本站,即表示您已接受以下条约:
1.使用辅助可能会违反游戏协议,甚至违法,用户有权决定使用,并自行承担风险;
2.本站辅助严禁用于任何形式的商业用途,若被恶意贩卖,利益与本站无关;
3.本站为非营利性网站,但为了分担服务器等运营费用,收费均为赞助,没有任何利益收益。
死神科技 » 某滑块逆向分析

死神科技,因为专业,所以领先。

网站首页 24小时自动发卡
在线客服
24小时在线客服
阿里云自动发卡,购卡进群售后
12:01
您好,有任何疑问请与我们联系!

选择聊天工具: