EProtect Ver:1.0.6.4 脱壳
https://www.52pojie.cn/thread-1191986-1-1.html在吾爱破解论坛看到的,这壳又出新版了。
Step.1 寻找OEP
找到标题为 “进入软件” 这一按钮的按钮事件。
在按钮事件上下断点。
中断后,不断单步步过CALL指令,如果经过某CALL后,程序跑飞,则进入该CALL内部。继续单步步过CALL指令。
在重复几次后,我来到了这个CALL。
004B4B40 50 push eax ; ep_hello.0045D9A7
004B4B41 FF75 FC push dword ptr ss: ; ep_hello.004B693D
004B4B44 FF75 CC push dword ptr ss:
004B4B47 E8 C81D0000 call ep_hello.004B6914
然后打开内存页面
在代码区块上下一个访问断点,F9运行时到达OEP。
0045D9A7/.55 push ebp
用OD把它dump出来,不过把重建输入表去掉。
Step.2 IAT修复
随便找到一个API调用,解码,找到IAT地址,下一个硬件写入断点,中断后回溯,来到此处。
004B528A FF75 E4 push dword ptr
004B528D FF75 14 push dword ptr
004B5290 E8 66F1FFFF call 004B43FB
这里就在填充IAT,有两种类型,一种填进去的就是真实API地址,还有一类填进去的是一个stub函数地址,通过这个stub函数来解码出真实的API地址。
现在我选中的是真正无效的,删掉就好了。
这一次选中的是有效的,但是指向的是stub函数,我们要修复IAT,就必须解码出真实的API地址写入IAT表。
随便挑一个,给各位看一下stub函数的样子。
显然,stub函数经过ret指令后会跳转到真正的api地址,现在我们开始考虑如何进行跟踪。
简单说下我的思路:
1.搜集所有stub函数首地址
2.创建新线程执行stub函数
3.设置tf位,如果单步一次后程序的eip变化较大(比如>1000),这就说明程序肯定跳进其它模块里面了,这个时候取程序eip写进IAT里面就好了
P.S. :
程序每次运行时候stub函数的生成是随机的,也就是程序会随机挑选一些API生成stub函数。所以我给出的代码里记得修改getStubAddr()函数里的地址!!!!!!!!!
下面给出代码。
// FixIAT.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <windows.h>
#include <tlhelp32.h>
#include <stdlib.h>
#include <vector>
#define SE_DEBUG_PRIVILEGE 20
using namespace std;
typedef DWORD(WINAPI *PRtlAdjustPrivilege) (
ULONG Privilege,
BOOLEAN Enable,
BOOLEAN CurrentThread,
PBOOLEAN Enabled);
DWORD pid;
vector<DWORD> stub_addr;
vector<DWORD> iat_addr;
HANDLE hp;
void AdjustPrivilege() {
BOOLEAN Enabled;
PRtlAdjustPrivilege RtlAdjustPrivilege = (PRtlAdjustPrivilege)GetProcAddress(LoadLibraryA((LPCSTR)"ntdll.dll"), "RtlAdjustPrivilege");
RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, TRUE, FALSE, &Enabled);
}
bool getStubFunc() {
DWORD addr;
DWORD t;
for (DWORD i = 0x47D170; i <= 0x47D1D0; i += 4) {
ReadProcessMemory(hp, (LPCVOID)i, &addr, sizeof(DWORD), &t);
if (t != sizeof(DWORD)) {
printf("What the fuck stub addr?\r\n");
getchar();
return false;
}
stub_addr.push_back(addr);
iat_addr.push_back(i);
}
ReadProcessMemory(hp, (LPCVOID)0x47D1EC, &addr, sizeof(DWORD), &t);
stub_addr.push_back(addr);
iat_addr.push_back(0x47D1EC);
if (t != sizeof(DWORD)) {
printf("What the fuck stub addr?\r\n");
getchar();
return false;
}
ReadProcessMemory(hp, (LPCVOID)0x47D1E4, &addr, sizeof(DWORD), &t);
stub_addr.push_back(addr);
iat_addr.push_back(0x47D1E4);
if (t != sizeof(DWORD)) {
printf("What the fuck stub addr?\r\n");
getchar();
return false;
}
ReadProcessMemory(hp, (LPCVOID)0x47D310, &addr, sizeof(DWORD), &t);
stub_addr.push_back(addr);
iat_addr.push_back(0x47D310);
if (t != sizeof(DWORD)) {
printf("What the fuck stub addr?\r\n");
getchar();
return false;
}
/* ReadProcessMemory(hp, (LPCVOID)0x47D200, &addr, sizeof(DWORD), &t);
stub_addr.push_back(addr);
if (t != sizeof(DWORD)) {
getchar();
printf("What the fuck stub addr?\r\n");
return false;
}
ReadProcessMemory(hp, (LPCVOID)0x47D398, &addr, sizeof(DWORD), &t);
stub_addr.push_back(addr);
if (t != sizeof(DWORD)) {
getchar();
printf("What the fuck stub addr?\r\n");
return false;
}*/
return true;
}
bool getIAT() {
LPVOID addr;
DWORD tid;
DWORD t;
HANDLE ht;
CONTEXT ct;
DEBUG_EVENT dbg;
DWORD last_eip;
bool state;
unsigned char c3 = 0xCC;
char opcode;
for (unsigned int i = 0; i < stub_addr.size(); i++) {
addr = (LPVOID)stub_addr;
ht = CreateRemoteThread(hp, NULL, 0, (LPTHREAD_START_ROUTINE)addr, NULL, CREATE_SUSPENDED, &tid);
if (ht == INVALID_HANDLE_VALUE) {
printf("What the fuck CreateRemoteThread?\r\n");
return false;
}
/*ct.ContextFlags = CONTEXT_FULL;
GetThreadContext(ht, &ct);
last_eip = ct.Eip;
ct.EFlags &= 0x100; //set IF = 1
SetThreadContext(ht, &ct);*/
ReadProcessMemory(hp, addr, &opcode, 1, &t);
WriteProcessMemory(hp, addr, &c3, 1, &t);
ResumeThread(ht);
state = true;
while (state) {
WaitForDebugEvent(&dbg, INFINITE);
switch (dbg.dwDebugEventCode) {
case EXCEPTION_DEBUG_EVENT:
if (dbg.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_SINGLE_STEP) {
ct.ContextFlags = CONTEXT_FULL;
GetThreadContext(ht, &ct);
ct.EFlags |= 0x100; //set IF = 1
if (abs((int)last_eip - (int)ct.Eip) >= 1000) {
WriteProcessMemory(hp, (LPVOID)iat_addr, &ct.Eip, sizeof(DWORD), &t);
if (t != 4) {
printf("Fail: %08X, %08X\r\n", iat_addr, ct.Eip);
}
else {
printf("Succeed: %08X, %08X\r\n", iat_addr, ct.Eip);
}
TerminateThread(ht, 0);
state = false;
}
else {
last_eip = ct.Eip;
SetThreadContext(ht, &ct);
}
ContinueDebugEvent(pid, dbg.dwThreadId, DBG_CONTINUE);
}
else if (dbg.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT) {
if (dbg.u.Exception.ExceptionRecord.ExceptionAddress == LPVOID((DWORD)addr)) {
WriteProcessMemory(hp, addr, &opcode, 1, &t);
ct.ContextFlags = CONTEXT_FULL;
GetThreadContext(ht, &ct);
ct.Eip--;
last_eip = ct.Eip;
ct.EFlags |= 0x100; //set IF = 1
SetThreadContext(ht, &ct);
ContinueDebugEvent(pid, dbg.dwThreadId, DBG_CONTINUE);
}
else {
ContinueDebugEvent(pid, dbg.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
}
}
else {
ContinueDebugEvent(pid, dbg.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
}
break;
case EXIT_PROCESS_DEBUG_EVENT:
printf("Why does the fuck process has been terminated?\r\n");
ContinueDebugEvent(pid, dbg.dwThreadId, DBG_CONTINUE);
return false;
break;
case CREATE_THREAD_DEBUG_EVENT:
ContinueDebugEvent(pid, dbg.dwThreadId, DBG_CONTINUE);
break;
default:
ContinueDebugEvent(pid, dbg.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
}
}
CloseHandle(ht);
}
return true;
}
bool supAllThreads() {
HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
THREADENTRY32 te = {0};
HANDLE ht;
if (h == INVALID_HANDLE_VALUE) {
return false;
}
te.dwSize = sizeof(te);
BOOL state = Thread32First(h, &te);
while (state) {
if (te.th32OwnerProcessID != pid) {
state = Thread32Next(h, &te);
continue;
}
ht = OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID);
SuspendThread(ht);
state = Thread32Next(h, &te);
CloseHandle(ht);
}
CloseHandle(h);
return true;
}
int _tmain(int argc, _TCHAR* argv[])
{
AdjustPrivilege();
scanf("%d", &pid);
hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
supAllThreads();
DebugActiveProcess(pid);
if (hp == NULL) {
printf("What the fuck pid and uac?\r\n");
getchar();
return -1;
}
getStubFunc();
getIAT();
DebugActiveProcessStop(pid);
system("pause");
return 0;
}
运行以后输入进程ID,程序会列出修复结果。
修复完成后再用IMPORT REC重建程序导入表。
注意勾选这个选项,否则它会重建一份新IAT,等于白给。
脱壳后的程序:
链接: https://pan.baidu.com/s/1tDYeXdFRKFMMQNj07g8X9Q 提取码: zi9b
IAT Tree:
链接: https://pan.baidu.com/s/1ZgWev3N3vDolBQ8oR_kz7A 提取码: ms8e
FixIAT完整工程文件(VS2008):
链接: https://pan.baidu.com/s/1RaNPkoRpO-pq6e7lwJE82g 提取码: rqua
依旧羡慕玩壳的师傅~ 太强了 大佬牛逼 羡慕大佬 大佬牛逼 牛逼啊最新版 好东西学到了。thanks! 感谢分享,收藏慢慢学习 感谢分享收藏学习 有保存了文件的大哥没 学习了!感谢楼主的细致讲解!!!