另类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上可用,不需要启动目标程序就可以得到公钥。至于随机数,看这篇有没有人关注,多的话会发,大概原理采用数学统计原理找到目标区域。
思路可以推广到其他强壳。 楼主详细讲解一下呗 原理懂了 获取到第一个节点 .sedata的偏移和大小然后枚举窗口 需要指定进程吗 目标程序需要打开吗 测试了一天 不知道后面的步骤怎么实现的 deadash 发表于 2024-11-18 16:14
你需要启动b进程,然后注入b进程dll,让b进程自己找自己的进程pid的窗口。或者你也可以在父进程定时查找b ...
感谢回复进程b是一个exe程序 打开就弹窗了如何用代码拦截呢 可以贴个完整代码吗 爆破se真的太有魅力了 就是后面几部搞的不太明白 谢谢分享 感谢大神分享代码,真的非常好! 感谢分享! 感觉长了点知识,但是又有点不明白!找到.sedata的区段然后 枚举符合条件的窗口-1073741819 是什么呢 本帖最后由 冰河洗剑 于 2024-11-14 13:28 编辑
不知道是BUG还是? 并不是所有SE都可以Keygen 有些样本无法正常解码 https://x.ws28.cn/f/fkf19l1quht 学习学习 跟帖学习一下 学到了 收藏一下 看看学习一下