吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 11468|回复: 44
收起左侧

[转贴] 转别人编写的压缩壳的过程及vc6源码

  [复制链接]
涛之雨 发表于 2018-8-6 22:55
本帖最后由 涛之雨 于 2018-8-14 22:36 编辑

上一篇:传送门

破解入门(八)-----算法分析与注册机编写


先附上百度上搜到的压缩壳的原理:
把文件的二进制代码压缩,把相邻的0,1代码减少,比如有000000,可以把它变成6个0 的写法60,来减少该文件的空间。由于计算机处理的信息是以二进制数的形式表示的,因此压缩软件就是把二进制信息中相同的字符串以特殊字符标记来达到压缩的目的。所有的计算机文件归根结底都是以“1”和“0”的形式存储的,和蓝色像点一样,只要通过合理的数学计算公式,文件的体积都能够被大大压缩以达到“数据无损稠密”的效果。压缩可以分为有损和无损压缩两种。如果丢失个别的数据不会造成太大的影响,这时忽略它们是个好主意,这就是有损压缩有损压缩广泛应用于动画、声音和图像文件中,典型的代表就是影碟文件格式mpeg、音乐文件格式mp3和图像文件格式jpg。但是更多情况下压缩数据必须准确无误,人们便设计出了无损压缩格式,比如常见的zip、rar等。压缩软件(compression software)自然就是利用压缩原理压缩数据的工具,压缩后所生成的文件称为压缩包(archive),体积只有原来的几分之一甚至更小。当然,压缩包已经是另一种文件格式了,如果想使用其中的数据,首先得用压缩软件把数据还原,这个过程称作解压缩。常见的压缩软件有winzip、winrar等。

所有的文件在存储中都是以0和1的形式存在的。
但如果你用“1{7}0{49}1{4}”来描述它(当然这只是为方便说明这样写,并不是真的是一种压缩算法),也能得到同样的信息,但却只有13个字节,
<span]一般压缩壳很少用自己独创的压缩算法,都是根据需要选用已经很成熟了的算法.在具体应用中,文件都相当复杂,根据一定的数学算法,
权衡把哪段字节用一个特定的更小字节代替,就可以实现数据最大程度的无损压缩所有有很多算法都是针对具体文件类型才有最大的效率,比如gzip,zlib,以及图形格式png使用deflate压缩算法,gif用lzw算法,等等.
这些算法也能用于可执行文件.压缩壳把全部文件资源(一般就是一个可执行程序)用一种或几种算法进行压缩,运行时再解压缩释放到内存,然后让系统运行它.






《《《《《《《《《《《《《《《《《《《《《《《《《《《原文开始》》》》》》》》》》》》》》》》》》》》》》》》》》》

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段,有的话,把区段信息设置为不压缩



[C] 纯文本查看 复制代码
  //保存被压缩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就有用了,不
需要压缩的区段,是不放入加壳后的第一个区段的,所以不用放入要压缩数据大小里面


[C] 纯文本查看 复制代码
//获得被压缩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
当然每个区段大小都需要对齐文件



[C] 纯文本查看 复制代码
   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头,方便后面修改,然后修改镜像大小



[C] 纯文本查看 复制代码
 //拷贝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;


添加区段表:


[C] 纯文本查看 复制代码
 //添加区段表
 
    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);


拷贝区段:


[C] 纯文本查看 复制代码
//拷贝.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的偏移

[C] 纯文本查看 复制代码
//设置数据目录
    //记录被压缩程序的导入表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



[C] 纯文本查看 复制代码
//设置新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:



[C] 纯文本查看 复制代码
//修正资源表
    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自带重定位表,比较方便


[C] 纯文本查看 复制代码
 //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
所以这里处理了即可


[C] 纯文本查看 复制代码
//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处



[C] 纯文本查看 复制代码
 //记录要恢复的区段信息,原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中的处理:


[C] 纯文本查看 复制代码
__asm pushad
           __asm pushfd
//解压数据
decompress();
//恢复IAT
RecoverIAT();
//看是否有TLS函数 如果有 则调用
CallTls();
           __asm popfd
           __asm popad
__asm jmp g_dwOep

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


[C] 纯文本查看 复制代码
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++;
             
        }
    }
     
}



最后送大家一份我收集的一些语言的程序,写这个壳时都是用这些程序测试的,方便大家写壳时测试
原文源码:
源码.7z (60.9 KB, 下载次数: 95)
各种语言程序.7z (459.21 KB, 下载次数: 107)

密码:【www.52pojie.cn
屌丝看这:到原网站免费下载

《《《《《《《《《《《《《《《《《《《《《《《《《《《原文结束》》》》》》》》》》》》》》》》》》》》》》》》》》》




ps:原地址:https://bbs.pediy.com/thread-161315.htm


下一篇







因为最近看官方教程,而且没有看到什么比较好的帖子。。。就没发贴。。
至于原创。。。。等我出师吧。。。


虽然是转帖,但是还是恳求各位看过学过走过路过的同学同窗朋友好友点下下面的
【免费评分】
fen.png

先谢过。。。。
我会继续努力分享&(争取尽早)原创滴。。。。

免费评分

参与人数 7吾爱币 +4 热心值 +7 收起 理由
iteamo + 1 + 1 热心回复!
寒蝉鸣泣之时 + 1 + 1 热心回复!
RoB1n_Ho0d + 1 热心回复!
liphily + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
chenjingyes + 1 + 1 谢谢楼主分享,谢谢开源!
mefcl + 1 + 1 有密码啊,大兄弟
让导弹飞 + 1 不错呀,希望能继续写下去

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| 涛之雨 发表于 2018-8-7 08:00
alicc 发表于 2018-8-7 00:24
楼主不放一份成品嘛?

自认为源码比成品好,一来我是转载的;二来源码可以方便学习、分析、找自己的问题;三来可以防止某些伸手党(虽然只要下载后编译一下就行了,但是伸手党会很大程度上减少)
 楼主| 涛之雨 发表于 2018-11-12 19:48
西北狼 发表于 2018-11-12 19:34
每个细节  都复制   好好看看这么好的文章

目前没有能力发原创的不过会努力的
复制其实也不是很容易的。。
只是省的打字和截图了。。
一样要重拍版的
 楼主| 涛之雨 发表于 2018-8-6 22:55
头像被屏蔽
freemoredoom1 发表于 2018-8-6 23:12
提示: 作者被禁止或删除 内容自动屏蔽
Angel_Hacker 发表于 2018-8-6 23:12
建议楼主改一下标题。。
woainipojie 发表于 2018-8-6 23:17
谢谢分享好东西啊
吾爱看雪 发表于 2018-8-6 23:28
佩服大神,可以免杀啦~~
ycdear 发表于 2018-8-6 23:32
不错,正好用来学习
1727782690 发表于 2018-8-6 23:39
膜拜大神
白蓝 发表于 2018-8-6 23:54
楼主功力深厚啊,佩服,学习了
chenjingyes 发表于 2018-8-7 00:05
谢谢楼主分享,谢谢开源!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 12:27

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表