给程序加壳就是给程序动手术,一点也马虎不得,稍微一不留神就出问题.
本人一直学习使用c/c++做壳,所以bambam就是最好的参考(源码下载中有所有源码).
学习的过程就是读别人的代码,确实原理上不难,但实际一操作还挺麻烦的.这里讲一个给bambam调错的亲身经历吧.
背景:
bambam是bedrock根据bigboot的文章做的一个壳,代码写的很不错了.但他有的地方处理的不太合理.TLS是这个壳一个亮点,他使用了tls代理技术,巧妙的处理了tls,这个东西还是很实用的,尽管99%的程序不用他.borland编译的程序都有tls,但多数都是0,所以其实也没用.但如果这16个字节处理不好也会有问题.
下面是bambam的代码,
inline void FinaliseTlsStuff()
{
PIMAGE_DOS_HEADER pDosHdr;
PIMAGE_NT_HEADERS pNtHdr;
DWORD dwKatSup;
LONG lJmp;
pDosHdr = (PIMAGE_DOS_HEADER)dwLoadAddress;
lJmp = pDosHdr->e_lfanew;
dwKatSup = (DWORD)pDosHdr;
dwKatSup += lJmp;
pNtHdr = (PIMAGE_NT_HEADERS)dwKatSup;
if(pNtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress != 0)
{
gev.tlsOriginal.AddressOfIndex = (DWORD*)dwTlsSlotIndex;
void* pvTlsData;
__asm
{
mov ecx, DWORD PTR dwTlsSlotIndex;
mov edx, DWORD PTR fs:[02ch]
mov ecx, DWORD PTR [edx+ecx*4]
mov pvTlsData, ecx
}
int size = gev.tlsOriginal.EndAddressOfRawData - gev.tlsOriginal.StartAddressOfRawData;
CopyMemory(pvTlsData, (void*)gev.tlsOriginal.StartAddressOfRawData, size);
FillMemory((void*)gev.tlsOriginal.EndAddressOfRawData, gev.tlsOriginal.SizeOfZeroFill, 0);
}
fSafeToCallback = true;
if(fDelayedTlsCallback)
{
TlsCallback(tlsDllHandle, tlsReason, tlsReserved);
}
}
看上去这个代码写的很漂亮,而且运行起来也没问题,至少在windows9x-2003都可以很好的工作.但其实里面有个bug.
发现bug:
bug发现的很偶然,因为一般运行的时候都没问题.但有一次我在stub中开了一个线程,又在这个线程中开了第二个线程,这样的壳在win2000系列的平台是没问题的,但在win98下会报错.而且只有delphi的程序报错.
起初我以为是stub中线程的错误,就反覆修改,但还是同样的报错.就这样让我郁闷了很长时间.我觉的问题和tls有关,但就是找不到问题.
注意,win98下tls也是先启动的,所以可以确认win98是有tls支持的.
我甚至pediy一个exe,没有tls,他就不报错了.所以现在可以确定bug就在这段代码中.说实话,找这个bug很郁闷,因为很多的地方很难猜测系统运行的过程,因为系统loader报的错,就是olly能做的也很有限了.
只能从代码入手,一点点的看,突然,你可以看到有一个地方似乎有点古怪,gev.tlsOriginal.AddressOfIndex = (DWORD*)dwTlsSlotIndex; 实际上语法是没有问题的,但dwTlsSlotIndex是变量,能这么搞吗?当然可以这样做,其他的地方这样赋值很多,但这里似乎是错误的,应该改为gev.tlsOriginal.AddressOfIndex = (DWORD)&dwTlsSlotIndex;, 这样win98下就ok了.
这个问题修改是成功了,但我心理还是有些疑问,为什么win2000,xp就没事呢?
总结:
打壳就是动手术,一点都不能马虎,否则出了问题不好解决,主要是症状不好调试.这个文章写了我真实的经历,献给那些自己动手做壳的人吧.
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)