欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]我写的一个压缩壳

[原创]我写的一个压缩壳

VC6写的一个简单的压缩壳,我把我的心得体会分享给大家.
因为本人的水平有限,很多东西都是靠自己的理解来实现的,其中错误难免,拍砖、喷我,我都能Hold住,
但是一定要指出来,我会及时修正,因为我不想让文中的错误传遍互联网,祸害大家.
大致思路:
把被加壳程序的所有区段,放到压缩后放到第一个区段,资源和TLS段我选择不压缩,就直接拷贝到后面的
段中,添加一个shell段来做引导,解压压缩过的所有区段
结构为:
.oldDat
.shell
.rsrc(如果有的话)
.tls(如果有的话)
.info(一些shell用到的必要信息)
实现起来有几点要注意的地方:
1.为什么那么多壳都是选择把被加壳程序的区段放到第一个区段?,因为不放入第一个区段,变相的相当
于更改了ImageBase,导入被加壳程序用到的全局数据偏移都要修正,并且EXE基本上没有重定位表,这时
解决方法,需要特征码扫描被加壳程序需要重定位的数据偏移,在shell引导程序中修正偏移,
首先构造重定位表已经很累很难了,再者就是增加shell代码, 不过写loader应该是必须要做的(当然不
排除你能申请到你想要的ImageBase),第二种解决方法是修正EXE的ImageBase, 我就直接放到第一区段
了,何必麻烦呢
2.资源段的处理:资源段我选择全部不压缩,当然也可以压缩运行后才需要到的资源数据,但是感觉太麻
烦了(自己太菜-_-), 把资源段放到后面需要注意,要修正数据目录的RVA,并且资源的结构是三层目录结
构,要修正最后一层数据RVA
3.TLS的处理:TLS需要拷贝到后面区段,同时修正数据目录的RVA, 当TLS有回调函数时, 则shell解压数
据后,首先调用TLS回调函数,然后再跳到OEP,TLS还有个要处理的是.TLS区段有可能包含全局数据,所以
shell需要把TLS区段再拷贝到原始地方,以便被加壳程序能正确引用
4.被加壳程序的导入表处理:我的做法把他的导入表清空,区段也清除,当运行加壳后的程序使用shell
来填写原始IAT即可, 而应用程序加载后,即使没有导入表也是默认加载ntdll和kernel32(有了这两个,
飞机都能造了),当然最后一个面临问题就是shell用的Api的怎么填写, 就是我们写的程序,事先把
dll的IAT写入shell要用的api即可,这样就事先了,加壳后的程序没有导入表也可正常运行
5.shell用什么来写:可以考虑用exe,但是首选是dll,因为dll默认会有重定位表,上面说到生成后的程序
的基址与被加壳程序的基址一致是最方便的做法, 而dll又有重定位表, 只需要修正dll的重定位地址即
可
具体实现:
注:
文中的m_TargetPeTag,m_ShellPeTag,m_PressPeTag
分别是被加壳程序,dll写的shell,要生成的加壳程序
我用结构体成员管理了内存映射文件的句柄和地址,方便最后释放资源
首先用个数组保存被加壳程序区段信息,以及区段内容.以便后面查找和压缩用
判断被加壳程序是否有资源段和tls段,有的话,把区段信息设置为不压缩


	//保存被压缩PE的区段信息

	StorePressSecInfo();

	//被压缩程序的TLS和资源区段要保持不变 首先判断是否有TLS表和资源表

	DWORD dwResRVA = m_TargetPeTag.m_lpNtHeader->OptionalHeader.DataDirectory

[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;

	DWORD dwTLSRVA = m_TargetPeTag.m_lpNtHeader->OptionalHeader.DataDirectory

[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress;

	MySecInfo* lpResSecInfo = NULL;

	if (dwResRVA != 0)

	{

		lpResSecInfo = GetSecInfoByRVA(dwResRVA, m_TargetPeTag.m_lpNtHeader-

>OptionalHeader.SectionAlignment);

		//资源段不压缩

		lpResSecInfo->m_isNeedPress = false;

	}

	MySecInfo* lpTLSSecInfo = NULL;

	if (dwTLSRVA != 0)

	{

		lpTLSSecInfo = GetSecInfoByRVA(dwTLSRVA, m_TargetPeTag.m_lpNtHeader-

>OptionalHeader.SectionAlignment);

		//TLS段不压缩

		lpTLSSecInfo->m_isNeedPress = false;

	}


获得被加壳程序的镜像大小:
镜像大小为 pe头 + 各区段虚拟大小之和,当然都是对齐内存后的值
特殊的说一下,如果某个区段的虚拟大小为0,则windows加载器会使用文件大小对齐内存对齐后来算,如
果文件大小对齐后的值正好符合应该的虚拟大小,则程序会正常运行的.
需要说明这里我并没有处理
然后申请块空间,把要压缩的数据拷贝到这块空间 拷贝完成后使用aplib压缩:
每个区段的文件大小之和,当然都是对齐文件后的大小,这里刚刚设置的标志m_isNeedPress就有用了,不
需要压缩的区段,是不放入加壳后的第一个区段的,所以不用放入要压缩数据大小里面

	//获得被压缩PE的所有区段文件大小和(去除不能压缩的资源,TLS)

	DWORD dwPressSize = GetPressSize();

	//申请要压缩数据的空间

	char* lpSrcData = new char[dwPressSize];

	if (lpSrcData == NULL)

	{

		AfxMessageBox("new Error");

		return false;

	}

	RtlZeroMemory(lpSrcData, dwPressSize);

	//拷贝要压缩的数据

	CopyPressData(lpSrcData);

        //aplib压缩数据

        DWORD dwAfterPressSize = MyCompress(lpSrcData, &m_lpPressData, dwPressSize);

        if (dwAfterPressSize == APLIB_ERROR)

	{

		if (lpSrcData != NULL)

		{

			delete[] lpSrcData;

		}	

		return false;

	}


然后开始生成加壳后的PE文件,首先得知道要生成的大小,用内存映射文件方法创建的文件,方便构造PE
成成的文件大小 = PE头 + 压缩后的PE大小 + shell引导大小 + 不能压缩的资源段(如果有) +
不能压缩的tls(如果有) + .info
当然每个区段大小都需要对齐文件

	DWORD dwFileAlign = m_TargetPeTag.m_lpNtHeader->OptionalHeader.FileAlignment;

	DWORD dwResFileSize = 0;

	if (lpResSecInfo != NULL)

	{

		dwResFileSize = Align(dwFileAlign, lpResSecInfo-

>m_SecHeader.SizeOfRawData);

	}

	DWORD dwTlsFileSize = 0;

	if (lpTLSSecInfo != NULL)

	{

		dwTlsFileSize = Align(dwFileAlign, lpTLSSecInfo-

>m_SecHeader.SizeOfRawData);

	}

	DWORD dwFileSize = m_TargetPeTag.m_lpNtHeader->OptionalHeader.SizeOfHeaders +

						Align(dwFileAlign, dwAfterPressSize) + 

Align(dwFileAlign, m_ShellPeTag.m_lpNtHeader->OptionalHeader.SizeOfCode) +

						dwResFileSize + dwTlsFileSize + 

dwFileAlign;


接下来就是构造PE:
首先拷贝PE头,方便后面修改,然后修改镜像大小

	//拷贝PE头

	memcpy(m_PressPeTag.m_FileMapTag.m_lpFileData,

		m_TargetPeTag.m_FileMapTag.m_lpFileData, 

		m_TargetPeTag.m_lpNtHeader->OptionalHeader.SizeOfHeaders);

	m_PressPeTag.m_lpDosHeader =(IMAGE_DOS_HEADER*)

m_PressPeTag.m_FileMapTag.m_lpFileData;

	m_PressPeTag.m_lpNtHeader = (IMAGE_NT_HEADERS*)

(m_PressPeTag.m_FileMapTag.m_lpFileData + m_PressPeTag.m_lpDosHeader->e_lfanew);

	//生成程序的映像大小 = pe头 + 被压缩PE镜像大小 + .shell + rsrc + tls + info

	DWORD dwSecAlign = m_PressPeTag.m_lpNtHeader->OptionalHeader.SectionAlignment;

	DWORD dwResSize = 0;

	if (lpResSecInfo != NULL)

	{

		dwResSize = Align(dwSecAlign, lpResSecInfo->m_SecHeader.SizeOfRawData);

	}

	DWORD dwTlsSize = 0;

	if (lpTLSSecInfo != NULL)

	{

		dwTlsSize = Align(dwSecAlign, lpTLSSecInfo->m_SecHeader.SizeOfRawData);

	}

	DWORD dwImageSize = Align(dwSecAlign, m_PressPeTag.m_lpNtHeader-

>OptionalHeader.SizeOfHeaders) +

		dwTargetImageSize + Align(dwSecAlign, m_ShellPeTag.m_lpNtHeader-

>OptionalHeader.SizeOfCode) +

		dwResSize + dwTlsSize + Align(dwSecAlign, dwFileAlign);

	m_PressPeTag.m_lpNtHeader->OptionalHeader.SizeOfImage = dwImageSize;


添加区段表:

	//添加区段表

	m_PressPeTag.m_lpNtHeader->FileHeader.NumberOfSections = 0;

	AddSec(".OldDat", dwAfterPressSize, m_PressPeTag.m_FileMapTag.m_lpFileData);

	AddSec(".Shell", m_ShellPeTag.m_lpNtHeader->OptionalHeader.SizeOfCode, 

m_PressPeTag.m_FileMapTag.m_lpFileData);

	if (lpResSecInfo != NULL)

	{

		AddSec(".rsrc", lpResSecInfo->m_SecHeader.SizeOfRawData, 

m_PressPeTag.m_FileMapTag.m_lpFileData);

	}

	if (lpTLSSecInfo != NULL)

	{

		AddSec(".Tls", lpTLSSecInfo->m_SecHeader.SizeOfRawData, 

m_PressPeTag.m_FileMapTag.m_lpFileData);

	}

	AddSec(".Info", dwFileAlign, m_PressPeTag.m_FileMapTag.m_lpFileData);


拷贝区段:

//拷贝.OldDat区段

	DWORD dwCurPos = m_PressPeTag.m_lpNtHeader->OptionalHeader.SizeOfHeaders;

	memcpy((m_PressPeTag.m_FileMapTag.m_lpFileData + dwCurPos), 

			m_lpPressData, 

			dwAfterPressSize);

	//拷贝.Shell区段

	dwCurPos += Align(dwFileAlign, dwAfterPressSize);

	memcpy((m_PressPeTag.m_FileMapTag.m_lpFileData + dwCurPos), 

			(LPVOID)((DWORD)hRes + RVA2FA((char*)hRes, 

m_ShellPeTag.m_lpNtHeader->OptionalHeader.BaseOfCode)),

			m_ShellPeTag.m_lpNtHeader->OptionalHeader.SizeOfCode);

	dwCurPos += Align(dwFileAlign, m_ShellPeTag.m_lpNtHeader-

>OptionalHeader.SizeOfCode);

	//拷贝.rsrc区段

	if (lpResSecInfo != NULL)

	{

		memcpy((m_PressPeTag.m_FileMapTag.m_lpFileData + dwCurPos), 

			(m_TargetPeTag.m_FileMapTag.m_lpFileData + lpResSecInfo-

>m_SecHeader.PointerToRawData),

			lpResSecInfo->m_SecHeader.SizeOfRawData);

		dwCurPos += Align(dwFileAlign, lpResSecInfo->m_SecHeader.SizeOfRawData);

	}

	//拷贝.tls区段

	if (lpTLSSecInfo != NULL)

	{

		memcpy((m_PressPeTag.m_FileMapTag.m_lpFileData + dwCurPos), 

			(m_TargetPeTag.m_FileMapTag.m_lpFileData + lpTLSSecInfo-

>m_SecHeader.PointerToRawData), 

			lpTLSSecInfo->m_SecHeader.SizeOfRawData);

	}


开始设置数据目录:
数据目录全部清空,如果有资源或tls,则修正资源,tls的偏移

//设置数据目录

	//记录被压缩程序的导入表RVA

	DWORD dwOldImpAddr = m_PressPeTag.m_lpNtHeader->OptionalHeader.DataDirectory

[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;

	//把所有数据目录项都清空

	ClearDataDir(m_PressPeTag.m_FileMapTag.m_lpFileData);

//资源表

	DWORD dwRVA = 0;

	DWORD dwSize = 0;

	GetResRVA(m_PressPeTag.m_FileMapTag.m_lpFileData, dwRVA, dwSize);

	m_PressPeTag.m_lpNtHeader->OptionalHeader.DataDirectory

[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = dwRVA;

	m_PressPeTag.m_lpNtHeader->OptionalHeader.DataDirectory

[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = dwSize;

	//TLS

	//某些时候 TLS和别的区段连在一起 则要加上一个相差偏移

	DWORD dwPressTLSRVA = 0;

	if (lpTLSSecInfo != NULL)

	{

		DWORD dwTLSSize = 0;

		GetTLSRVA(m_PressPeTag.m_FileMapTag.m_lpFileData, dwPressTLSRVA, 

dwTLSSize);

		dwPressTLSRVA += dwTLSRVA - lpTLSSecInfo->m_SecHeader.VirtualAddress;

		m_PressPeTag.m_lpNtHeader->OptionalHeader.DataDirectory

[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress = dwPressTLSRVA;

		m_PressPeTag.m_lpNtHeader->OptionalHeader.DataDirectory

[IMAGE_DIRECTORY_ENTRY_TLS].Size = dwTLSSize;

	}


设置oep到shell段代码入口处:
注意保存旧oep

//设置新oep

	DWORD dwOep = 0;

	DWORD dwOldOep = m_PressPeTag.m_lpNtHeader->OptionalHeader.AddressOfEntryPoint;

	GetShellRVA(m_PressPeTag.m_FileMapTag.m_lpFileData, dwOep, dwSize);

	m_PressPeTag.m_lpNtHeader->OptionalHeader.AddressOfEntryPoint

		= m_ShellPeTag.m_lpNtHeader->OptionalHeader.AddressOfEntryPoint - 

m_ShellPeTag.m_lpNtHeader->OptionalHeader.BaseOfCode + dwOep;


接下来开始修正资源数据RVA:

//修正资源表

	if (lpResSecInfo != NULL)

	{

		GetResRVA(m_PressPeTag.m_FileMapTag.m_lpFileData, dwResRVA, dwSize);

		DWORD dwResFA = RVA2FA(m_PressPeTag.m_FileMapTag.m_lpFileData, dwResRVA);

		IMAGE_RESOURCE_DIRECTORY* lpIMAGE_RESOURCE_DIRECTORY = 

(IMAGE_RESOURCE_DIRECTORY*)(m_PressPeTag.m_FileMapTag.m_lpFileData + dwResFA);

		WORD nCount = lpIMAGE_RESOURCE_DIRECTORY->NumberOfIdEntries + 

lpIMAGE_RESOURCE_DIRECTORY->NumberOfNamedEntries;

		IMAGE_RESOURCE_DIRECTORY_ENTRY* lpRESOURCE_DIRECTORY = (struct 

_IMAGE_RESOURCE_DIRECTORY_ENTRY *)(lpIMAGE_RESOURCE_DIRECTORY + 1);

		int n = sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY);

		for (int i = 0 ; i < nCount; i ++)

		{

			if (lpRESOURCE_DIRECTORY->DataIsDirectory == 1)

			{

				//第二层目录

				DWORD RESOURCEAdder = lpRESOURCE_DIRECTORY-

>OffsetToDirectory + (DWORD)lpIMAGE_RESOURCE_DIRECTORY;

				IMAGE_RESOURCE_DIRECTORY* lpSecond = 

(IMAGE_RESOURCE_DIRECTORY*)RESOURCEAdder;

				IMAGE_RESOURCE_DIRECTORY_ENTRY* lpSecond2 = 

(IMAGE_RESOURCE_DIRECTORY_ENTRY*)(lpSecond + 1);

				for (int l = 0; l < lpSecond->NumberOfNamedEntries; l++)

				{

					//第三层数据

					IMAGE_RESOURCE_DIRECTORY* lpThrid = 

(IMAGE_RESOURCE_DIRECTORY*)(lpSecond2->OffsetToDirectory + (DWORD)

lpIMAGE_RESOURCE_DIRECTORY);

					IMAGE_RESOURCE_DIRECTORY_ENTRY* lpThridEntry = 

(IMAGE_RESOURCE_DIRECTORY_ENTRY*)(lpThrid + 1);

					for (int k = 0; k < lpThrid->NumberOfIdEntries; 

k++)

					{

						DWORD* lpDwAddress =  (DWORD*)

(lpThridEntry->OffsetToData + (DWORD)lpIMAGE_RESOURCE_DIRECTORY);

						//修改偏移 = 基址 + 差值

						*lpDwAddress = dwResRVA + *lpDwAddress - 

lpResSecInfo->m_SecHeader.VirtualAddress;

						lpThridEntry++;

					}

					lpSecond2++;

				}

				for (int j = 0; j < lpSecond->NumberOfIdEntries; j++)

				{

					//第三层数据

					IMAGE_RESOURCE_DIRECTORY* lpThrid = 

(IMAGE_RESOURCE_DIRECTORY*)(lpSecond2->OffsetToDirectory + (DWORD)

lpIMAGE_RESOURCE_DIRECTORY);

					IMAGE_RESOURCE_DIRECTORY_ENTRY* lpThridEntry = 

(IMAGE_RESOURCE_DIRECTORY_ENTRY*)(lpThrid + 1);

					for (int k = 0; k < lpThrid->NumberOfIdEntries; 

k++)

					{

						DWORD* lpDwAddress =  (DWORD*)

(lpThridEntry->OffsetToData + (DWORD)lpIMAGE_RESOURCE_DIRECTORY);

						//修改偏移 = 基址 + 差值

						*lpDwAddress = dwResRVA + *lpDwAddress - 

lpResSecInfo->m_SecHeader.VirtualAddress;

						lpThridEntry++;

					}

					lpSecond2++;

				}

			}

			lpRESOURCE_DIRECTORY++;

		}

	}


shell的重定位处理:
因为我的shell是dll写的,所以dll自带重定位表,比较方便

	//DLL写的shell重定位处理

	DWORD dwDllRelocRVA = m_ShellPeTag.m_lpNtHeader->OptionalHeader.DataDirectory

[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;

	DWORD dwDllRelocFA = RVA2FA((char*)hRes, dwDllRelocRVA);

	m_ShellPeTag.m_lpBaseReloc = (IMAGE_BASE_RELOCATION*)(DWORD(hRes) + dwDllRelocFA);

	DWORD dwShellRVA = 0;

	DWORD dwShellSize = 0;

	GetShellRVA(m_PressPeTag.m_FileMapTag.m_lpFileData, dwShellRVA, dwShellSize);

	DWORD dwShellFA =  RVA2FA( m_PressPeTag.m_FileMapTag.m_lpFileData, dwShellRVA);

	DWORD dwCurLoadAddress = dwShellRVA;

	DWORD dwLoadAddress = m_ShellPeTag.m_lpNtHeader->OptionalHeader.ImageBase;

	while (TRUE)

	{

		DWORD base = m_ShellPeTag.m_lpBaseReloc->VirtualAddress;

		USHORT* lpReloc = (USHORT*)((DWORD)m_ShellPeTag.m_lpBaseReloc + 8);

		if (m_ShellPeTag.m_lpBaseReloc->SizeOfBlock == 0)

		{

			break;

		}

		DWORD i = 0;

		DWORD dwSize = m_ShellPeTag.m_lpBaseReloc->SizeOfBlock - 8;

		while (i < dwSize)

		{

			DWORD type = (*lpReloc & 0xf000) >> 12;

			if (type == IMAGE_REL_BASED_HIGHLOW)

			{

				USHORT offset = *lpReloc & 0xfff;

				DWORD relocoffset = base + offset - 

m_ShellPeTag.m_lpNtHeader->OptionalHeader.BaseOfCode;

				DWORD* lpNeedReloc = (DWORD*)(dwShellFA + (DWORD)

m_PressPeTag.m_FileMapTag.m_lpFileData + relocoffset);

				*lpNeedReloc = 	dwCurLoadAddress + *lpNeedReloc - 

					dwLoadAddress - m_ShellPeTag.m_lpNtHeader-

>OptionalHeader.BaseOfCode + 

					m_PressPeTag.m_lpNtHeader-

>OptionalHeader.ImageBase;

			}

			lpReloc++;

			i += 2;

		}

		m_ShellPeTag.m_lpBaseReloc = (IMAGE_BASE_RELOCATION*)

(m_ShellPeTag.m_lpBaseReloc->SizeOfBlock + (DWORD)m_ShellPeTag.m_lpBaseReloc);

	}


shell的导入表处理:
这里需要填写IAT,因为我加壳后的程序是没有导入表信息的, shell里必然用到api
所以这里处理了即可

//dll写的shell 填写IAT

	//dll导入表处理

	DWORD dwDllImportRVA = m_ShellPeTag.m_lpNtHeader->OptionalHeader.DataDirectory

[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;

	DWORD dwDllImportFA = RVA2FA((char*)hRes, dwDllImportRVA);

	m_ShellPeTag.m_lpImport = (IMAGE_IMPORT_DESCRIPTOR*)(DWORD(hRes) + dwDllImportFA);

	int DllNameOffset = 0;

    int ThunkRVA = 0;

    HMODULE hDll = NULL;

    int* lpIAT  = NULL;

    while (m_ShellPeTag.m_lpImport->Name)

    {

        DllNameOffset = RVA2FA((char*)hRes, m_ShellPeTag.m_lpImport->Name) + DWORD(hRes);

        hDll = LoadLibrary((char*)DllNameOffset);

        if (m_ShellPeTag.m_lpImport->FirstThunk == 0)

        {

            break;

        }

        lpIAT =(int*)( RVA2FA((char*)hRes, m_ShellPeTag.m_lpImport->FirstThunk) + DWORD

(hRes));

        if (m_ShellPeTag.m_lpImport->OriginalFirstThunk == 0)

        {

            ThunkRVA = m_ShellPeTag.m_lpImport->FirstThunk;

        }

        else

        {

            ThunkRVA = m_ShellPeTag.m_lpImport->OriginalFirstThunk;

        }

        m_ShellPeTag.m_lpThunkData = (IMAGE_THUNK_DATA*)(DWORD(hRes) + RVA2FA((char*)hRes, 

ThunkRVA));

        int funAddress = 0;

        int FunName = 0;

        while (m_ShellPeTag.m_lpThunkData->u1.Ordinal != 0)

        {

            //名字导出

            if ((m_ShellPeTag.m_lpThunkData->u1.Ordinal & 0x80000000) == 0)

            {

                m_ShellPeTag.m_ImprotName = (IMAGE_IMPORT_BY_NAME*)(DWORD(hRes) + RVA2FA

((char*)hRes, m_ShellPeTag.m_lpThunkData->u1.Ordinal));

                FunName = (int)&(m_ShellPeTag.m_ImprotName->Name);

            }

            else

            {

                FunName = m_ShellPeTag.m_lpThunkData->u1.Ordinal & 0xffff;

            }

            int funAddress = (int)GetProcAddress(hDll,(char*)FunName);

			DWORD* dwCurIATaddress = (DWORD*)((DWORD)lpIAT - (DWORD)hRes - 

RVA2FA((char*)hRes, m_ShellPeTag.m_lpNtHeader->OptionalHeader.BaseOfCode) 

				+ m_PressPeTag.m_FileMapTag.m_lpFileData + RVA2FA

(m_PressPeTag.m_FileMapTag.m_lpFileData, dwCurLoadAddress));

            *(dwCurIATaddress) = funAddress;

            lpIAT++;

            m_ShellPeTag.m_lpThunkData++;

        }

        m_ShellPeTag.m_lpImport++;

    }


最后保存信息:
保存shell需要用的信息到.info段
shell第一个用到的就是上面提到的,如果tls区段里有被加壳程序用到的数据,则shell需要拷贝到原来
的TLS的RVA处,然后就是区段信息要给shell, shell解压数据后,需要拷贝数据到原来每个区段的正确RVA处
最后是原oep和原IAT的RVA
shell解压后数据 需要填写被加壳程序的IAT,然后跳到原oep处

	//记录要恢复的区段信息,原OEP,原导入表地址

	//tls要记录 如果被压缩程序tls区段和别的区段是合并状态 也就是别的区段中的数据也

存在在tls段中

	//则要拷贝回原tls节区

	DWORD dwInfoRVA = 0;

	DWORD dwInfoFA = 0;

	DWORD dwInfoSize = 0;

	GetInfoRVA(m_PressPeTag.m_FileMapTag.m_lpFileData, dwInfoRVA, dwInfoSize);

	dwInfoFA = RVA2FA(m_PressPeTag.m_FileMapTag.m_lpFileData, dwInfoRVA);

	DWORD* lpDwAddress = (DWORD*)(m_PressPeTag.m_FileMapTag.m_lpFileData + dwInfoFA);

	//TLS

	if (lpTLSSecInfo != NULL)

	{

		*lpDwAddress = lpTLSSecInfo->m_SecHeader.SizeOfRawData;

		lpDwAddress++;

		*lpDwAddress = lpTLSSecInfo->m_SecHeader.VirtualAddress;

		lpDwAddress++;

		DWORD dwTLSSize = 0;

		GetTLSRVA(m_PressPeTag.m_FileMapTag.m_lpFileData, dwTLSRVA, dwTLSSize);

		*lpDwAddress = dwTLSRVA;

		lpDwAddress++;

		*lpDwAddress = dwPressTLSRVA;

		lpDwAddress++;

	}

	else

	{

		*lpDwAddress = 0;

		lpDwAddress++;

		*lpDwAddress = 0;

		lpDwAddress++;

		*lpDwAddress = 0;

		lpDwAddress++;

		*lpDwAddress = 0;

		lpDwAddress++;

	}

	DWORD* lpSecCount = lpDwAddress;

	DWORD dwSecCount = 0;

	lpDwAddress++;

	vector<MySecInfo*>::iterator it2 = m_SecVector.begin();

	while(it2 != m_SecVector.end())

	{

		if ((*it2)->m_isNeedPress == true)

		{

			*lpDwAddress = (*it2)->m_SecHeader.SizeOfRawData;

			lpDwAddress++;

			*lpDwAddress = (*it2)->m_SecHeader.VirtualAddress;

			lpDwAddress++;

			dwSecCount++;

		}

		it2++;

	}

	*lpDwAddress = dwOldOep + m_PressPeTag.m_lpNtHeader->OptionalHeader.ImageBase;

	lpDwAddress++;

	*lpDwAddress = dwOldImpAddr;

	*lpSecCount = dwSecCount;


下面接着看shell需要做什么:
我在shell中的处理:

	__asm pushad

               __asm pushfd

	//解压数据

	decompress();

	//恢复IAT

	RecoverIAT();

	//看是否有TLS函数 如果有 则调用

	CallTls();

               __asm popfd

               __asm popad

	__asm jmp g_dwOep


其中解压数据与恢复IAT都很简单
我保存.info段中有原TLS的 RVA,这里我判断TLS是否有回调函数,如果有则先调用TLS回调函数

void CallTls()

{

	//解压完数据看是否有TLS函数 如果有 则调用

	if (g_lpTlsDir != NULL)

	{

		HMODULE hModule = GetModuleHandle(NULL);

		PIMAGE_TLS_CALLBACK* lptlsFun = (PIMAGE_TLS_CALLBACK*)g_lpTlsDir-

>AddressOfCallBacks;

		while (lptlsFun[0] != NULL)

		{

			lptlsFun[0](hModule, DLL_PROCESS_ATTACH, NULL);

			lptlsFun++;

		}

	}

}


最后送大家一份我收集的一些语言的程序,写这个壳时都是用这些程序测试的,方便大家写壳时测试

上传的附件 源码.rar
各种语言程序.rar

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功