【文章标题】:菜鸟脱壳---Themida
【文章作者】: xross
【下载地址】: TMKIHD.rar
第一次脱壳成功,还是来分享一下。
前几天看大牛脱Themida的壳,很有启发。
我果然是菜鸟,几天才脱壳成功,看了还是无法找出主要算法,主要它有跳转表,不知怎么办,。
也没有必要找出,只要找到OEP,IAT,API就可以了,果然我还是菜鸟。
先用PEID查壳,查不出来,但估计是Themida。
将程序运行一次,然后在CE中搜索4D5A,发现两个可疑地址,
在OD中,在10000000处查看PE文件头,它是DLL.查看窗口句柄,发现100012BC。OD中搜索"BC 12 00 10",100010FB.
在100012BA下断,断后,F8到返回。
查看ebp,跳到10002BD9附近看看,
在10002BCB改为:jmp 10002BD9,跳过CDCheck,
然后在代码设访问断点,断在2362261。删除内存断点,运行到下一指令,再在代码段设断,
断在
20C901F:
020C901F 8F02 pop dword ptr ds:[edx]
; 056B0000
020C9021 ^ E9 7DF6FFFF jmp RealLive.020C86A3 看看edx=5F20A8,猜想是对IAT的写入。
在GetCommandLineA处下硬件访问断点,返回到5CCA3A。
往上翻,到5CC928。堆栈中没有任何信息,猜想是jmp过来的。
搜索"jmp 5CC928",
找到:
005CCB08 E8 61520000 call RealLive.005D1D6E ;可能是OEP
005CCB0D ^ E9 16FEFFFF jmp RealLive.005CC928
在刚才的IAT处查看,可以确定IAT的范围为:
5F2000~5F2590
在kernel32的EAT下内存访问断点,
为了方便,直接写脚本:
bphwcall
bphws 10002BCB
esto
bphwc
mov [10002BCB],#EB0C#
bphws 5CCB08,"x" //OEP设硬件执行
bpcnd 20C901F,"edx>=5F20A8&&edx<5F2590"
esto
gma "kernel32",EDATATABLE
bprm $RESULT,A9B1
断在23712C6:
023712C6 66:AD lods word ptr ds:[esi]
023712C8 60 pushad
023712C9 60 pushad
023712CA 8BCE mov ecx,esi
023712CC E9 10000000 jmp RealLive.023712E1
023712D1 AC lods byte ptr ds:[esi]
023712D2 54 push esp
023712D3 90 nop
023712D4 E5 31 in eax,31
023712D6 0BDF or ebx,edi
023712D8 8F ??? ; 未知命令 Step Into下去,
023716DE 015A 4F add dword ptr ds:[edx+4F],ebx
023716E1 F5 cmc
023716E2 2130 and dword ptr ds:[eax],esi
023716E4 FFD3 call ebx
023716E6 60 pushad //此时eax为API,之前有出现过字符串
023716E7 81DB D595871B sbb ebx,1B8795D5
023716ED E9 06000000 jmp RealLive.023716F8
023716F2 BE 366874F3 mov esi,F3746836
尝试修复IAT:
var apiadd
var iatadd
bphwcall
bphws 10002BCB
esto
bphwc
mov [10002BCB],#EB0C#
bphws 5CCB08,"x"
bpcnd 20C9021,"edx>=5F20A8&&edx<5F2590"
bphws 23716E6,"x"
api:
esto
cmp eip,23716E6
jne fixiat
mov apiadd,eax
jmp api
fixiat:
cmp eip,20C9021
jne exit
cmp edx,63CA0000
jge api
mov [edx],apiadd
jmp api
exit:
bc
运行中发现还有无名地址5F20E4,下硬件写入断点。
bphwcall
bphws 10002BCB
esto
bphwc
mov [10002BCB],#EB0C#
bphws 5CCB08,"x"
bpcnd 20C9021,"edx>=5F20A8&&edx<5F2590"
esto
bc 20C9021
bphws 5F20E4,"w"
esto
断在2373D47:
02373D42 890C24 mov dword ptr ss:[esp],ecx
02373D45 8F00 pop dword ptr ds:[eax]
02373D47 60 pushad
重写修复IAT的脚本:
var apiadd
var iatadd
bphwcall
bphws 10002BCB
esto
bphwc
mov [10002BCB],#EB0C#
bphws 5CCB08,"x"
bpcnd 20C9021,"edx>=5F2000&&edx<5F25F0"
bphws 2373D47,"x"
bphws 23716E6,"x"
api:
esto
cmp eip,23716E6
jne fixiat1
mov apiadd,eax
jmp api
fixiat1:
cmp eip,20C9021
jne fixiat2
cmp [edx],63AC0000
jge api
mov [edx],apiadd
jmp api
fixiat2:
cmp eip,2373D47
jne exit
cmp [eax],63AC0000
jge api
mov [eax],apiadd
jmp api
exit:
bc
执行完后发现IAT确实都修复了,但无法用RecImport修复,原因未知,有谁知道吗。
看来只能手动修复了,
先确定各模块名的地址
5F2000:advapi32
5F2030:comctl32
5F203C:dsound
5F2044:gdi32
5F20A8:kernel32
5F22B8:oleaut32
5F22E8:shell32
5F22FC:user32
5F24D4:version
5F24E4:winmm
5F2554:ole32
再写个输出文件的脚本:
var addr
var str
mov addr,5F2000
mov str,0
advapi32:
wrta "C:\\qw.txt",str,0
cmp addr,5F2000
jne comctl32
mov str,"advapi32.dll"
add str,0
jmp loop
comctl32:
cmp addr,5F2030
jne dsound
mov str,"comctl32.dll"
add str,0
jmp loop
dsound:
cmp addr,5F203C
jne gdi32
mov str,"dsound.dll"
add str,0
jmp loop
gdi32:
cmp addr,5F2044
jne kernel32
mov str,"gdi32.dll"
add str,0
jmp loop
kernel32:
cmp addr,5F20A8
jne oleaut32
mov str,"kernel32.dll"
add str,0
jmp loop
oleaut32:
cmp addr,5F22B8
jne shell32
mov str,"oleaut32.dll"
add str,0
jmp loop
shell32:
cmp addr,5F22E8
jne user32
mov str,"shell32.dll"
add str,0
jmp loop user32:
cmp addr,5F22FC
jne version
mov str,"user32.dll"
add str,0
jmp loop version:
cmp addr,5F24D4
jne winmm
mov str,"version.dll"
add str,0
jmp loop
winmm:
cmp addr,5F24E4
jne ole32
mov str,"winmm.dll"
add str,0
jmp loop
ole32:
cmp addr,5F2554
jne exit
mov str,"ole32.dll"
add str,0 loop:
cmp addr,5F2590
jge exit
cmp [addr],0
add addr,4
je advapi32
gn [addr-4]
add str,11 //hint
add str,$RESULT_2
add str,0
jmp loop
exit:
此文件需要将30替换为0,31替换为1,ntdll的函数改为kernel32,写到227B0F0。
再次重写脚本:
bphwcall
bphws 10002BCB
esto
bphwc
mov [10002BCB],#EB0C#
bphws 5CCB08,"x"
esto
运行后,记录下伪IAT的值(我是菜鸟,不知如何用脚本修复nop地址,于是读文件修复nop地址)。
好,OD的工作完成了,接着就是最接近汇编的高级语言登场了。。
然后先是修复IAT,
HANDLE hFile=CreateFile("RealLive_fixed.exe",GENERIC_READ|GENERIC_WRITE,0,NULL,
OPEN_EXISTING,0,NULL);
HANDLE hFileMap=CreateFileMapping(hFile,NULL,PAGE_READWRITE,
0,0,NULL);
BYTE *p=(BYTE*)MapViewOfFile(hFileMap,FILE_MAP_READ|FILE_MAP_WRITE,0,
0,0);
CloseHandle(hFileMap);
CloseHandle(hFile);
BYTE *pBeg=p;
int arr[]=
{
0X1F2000,0X1F2030,0X1F203C,0X1F2044,
0X1F20A8,0X1F22B8,0x1F22E8,0X1F22FC,
0X1F24D4,0X1F24E4,0X1F2554
};
char *szModule[11]=
{
"advapi32.dll","comctl32.dll","dsound.dll","gdi32.dll",
"kernel32.dll","oleaut32.dll","shell32.dll","user32.dll",
"version.dll","winmm.dll","ole32.dll"
};
int i;
IMAGE_IMPORT_DESCRIPTOR* pIAT=(IMAGE_IMPORT_DESCRIPTOR*)(pBeg+0X227B000);
BYTE* pszModule=(BYTE*)(p+0X227b0f0);
for (i=0;i<11;i++)
{
pIAT->OriginalFirstThunk=pIAT->TimeDateStamp=pIAT->ForwarderChain=0;
while(1)
{
if(strstr((const char*)pszModule,szModule[i])!=NULL)
break;
}
pIAT->Name=pszModule-pBeg;
pIAT->FirstThunk=arr[i];
DWORD *pThunk=(DWORD*)(pIAT->FirstThunk+pBeg);
while(*pszModule!=0)
{
while(*pszModule!=0)pszModule++;
pszModule++;
if(*pszModule!=0)
*pThunk=pszModule-pBeg;
pThunk++;
}
pszModule++;
pIAT++;
}
UnmapViewOfFile(pBeg);
再修复nop地址,
DWORD iatapi[11][256]; //伪API地址
DWORD iatadd[11]=
{
0X5F2000,0X5F2030,0X5F203C,0X5F2044,
0X5F20A8,0X5F22B8,0X5F22E8,0X5F22FC,
0X5F24D4,0X5F24E4,0X5F2554
};
int i,j;
BYTE* pFile,*pBase;
BYTE *nopadd; //需要修复的地址
DWORD calladd; //call的地址
//坏习惯
HANDLE hFile=CreateFile("RealLive_fixed.exe",GENERIC_READ|
GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);
HANDLE hFileMap=CreateFileMapping(hFile,NULL,PAGE_READWRITE,0,0,NULL);
pFile=(BYTE*)MapViewOfFile(hFileMap,FILE_MAP_COPY|FILE_MAP_WRITE,0,0,0);
pBase=pFile;
pFile+=0X1000;
BOOL bCall; //是call还是jmp
//偷懒的做法
HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,7968);
for (i=0;i<11;i++)
{
j=0;
do
{
ReadProcessMemory(hProcess,(LPVOID)(iatadd[i]+4*j),&iatapi[i]
[j],4,NULL);
j++;
} while (iatapi[i][j]!=0);
}
CloseHandle(hProcess);
while(pFile<pBase+0x200000)
{
nopadd=NULL;
//pFile指向0X90(nop)
while(*pFile!=0X90)pFile++;
// call 地址 nop型
if(pFile[-5]==0XE8||pFile[-5]==0XE9)
{
bCall=(pFile[-5]==0XE8);
nopadd=pFile-5;
calladd=*(DWORD*)(nopadd+1)+5+(nopadd-pBase+0X400000);
}
//nop call地址 型
else if(pFile[1]==0XE8||pFile[1]==0XE9)
{
bCall=(pFile[1]==0XE8);
nopadd=pFile;
calladd=*(DWORD*)(nopadd+2)+5+(nopadd+1-pBase+0X400000);
}
if(nopadd!=NULL)
{
for (i=0;i<11;i++)
{
j=0;
//在IAT中查找call的地址
while(iatapi[i][j]!=0&&calladd!=iatapi[i][j])
j++;
if(iatapi[i][j]!=0)break;
}
if(i!=11)
{
*(WORD*)nopadd=bCall==1?0X15FF:0X25FF;
*(DWORD*)(nopadd+2)=iatadd[i]+4*j;
}
}
pFile++;
}
CloseHandle(hFileMap);
CloseHandle(hFile);
UnmapViewOfFile(pBase);
最后想问一下,ODScript中只能下硬件访问一个字节,有谁知道能下4个字节的断点。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!