另类SE壳找私钥
> SE壳破解已经有各种教程了,本文提供一个另类的思路来找公钥和随机key(下次发)。## 公钥获取
可以确定的是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解析代码例子如下
```rust
// 读取输入文件
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);
```
其中,拦截弹框并返回
```cpp
// 添加新的窗口枚举回调函数
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) {
DWORD processId;
GetWindowThreadProcessId(hwnd, &processId);
if (processId == GetCurrentProcessId()) {
char title = {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;
GetWindowTextW(child, wideText, textLength);
int mbLength = WideCharToMultiByte(CP_UTF8, 0, wideText, -1, NULL, 0, NULL, NULL);
char* mbText = new char;
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));
}
}
```
判断返回
```rust
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上可用,不需要启动目标程序就可以得到公钥。至于随机数,看这篇有没有人关注,多的话会发,大概原理采用数学统计原理找到目标区域。
思路可以推广到其他强壳。 本帖最后由 冰河洗剑 于 2024-11-14 13:28 编辑
不知道是BUG还是? 并不是所有SE都可以Keygen 有些样本无法正常解码 https://x.ws28.cn/f/fkf19l1quht 感觉长了点知识,但是又有点不明白!找到.sedata的区段然后 枚举符合条件的窗口-1073741819 是什么呢 谢谢分享 感谢大神分享代码,真的非常好! 感谢分享! 学习学习 跟帖学习一下 学到了 收藏一下 看看学习一下
页:
[1]
2