码迷,mamicode.com
首页 > 编程语言 > 详细

手工脱壳之 UPX 【随机基址】【模拟UPX部分算法】【手工C++重建重定位表】

时间:2018-03-20 20:43:23      阅读:306      评论:0      收藏:0      [点我收藏+]

标签:填充   目录   null   cti   lock   tor   src   on()   let   

一、工具及壳介绍

 使用工具:Ollydbg,PEID,ImportREC,LoadPE,010Editor

UPX壳 3.94:

技术分享图片

   

技术分享图片

   

技术分享图片

   

有了上篇ASPack壳的经验,先查看数据目录表:

可知是upx壳通过PE加载器修复自我重定位信息。

   

技术分享图片

   

二、脱壳

1、ESP定律

入口:

   

技术分享图片

   

通过ESP定律,ESP下硬件断点。

断下的第二次:

   

技术分享图片

   

跳过循环,单步几次。

OEP:

   

技术分享图片

   

技术分享图片

   

2、Dump内存

查看基址

   

技术分享图片

   

OPE - 加载基址 = OEP RVA

4BA9E1- 390000= 12A9E1

   

技术分享图片

   

技术分享图片

   

双击运行。

   

技术分享图片

   

查看出错位置。

加载基址 + 出错RVA = 出错地址

900000 + 131d90 = A31D90

   

技术分享图片

   

技术分享图片

   

技术分享图片

 

可见,重定位信息并没有修复。

   

三、寻找重定位表数据

目标:找到原本重定位表数据。

   

1、解压数据、解密重定位点、修复重定位信息 代码块

预判:程序先解压后 再修复重定位。

找到解压后的OEP入口处,还有附近的exe重定位点

程序重新加载,现加载基址 + OEP_RVA(12A9ED) == 现OEP。

   

技术分享图片

   

技术分享图片

   

技术分享图片

   

断在了壳的解压代码块

解密还未完成。

但是找到了似加密过的重定位点,找此点0xC0411A00再下硬件断点。

   

技术分享图片

   

断下来的地方,应该是修复exe重定位信息 代码块了。

似做了混淆。

   

技术分享图片

   

锁定疑似修改重定位点的代码块,

再次在原重定位点0xC0411A00下硬件断点

   

技术分享图片

   

技术分享图片

   

断下的地方进行分析,发现:

原重定位点的数据的确被加密

   

技术分享图片

   

详细分析:

EBX取出到EAX(0x88FC1A00地址),解密,得到RVA,加上text段地址,再放回去,完成修复exe重定位。

   

技术分享图片

   

技术分享图片

   

技术分享图片

   

2、回溯分析 寻重定位数据

它是怎么锁定当前重定位点的呢,往上分析,追踪EBX,找出重定位表。

从【EDI】中取出1字节的偏移值给AL,后续在将EBX和EAX相加。

   

技术分享图片

   

   

它的算法是:

EBX为当前重定位点的地址,【EDI】 --> EAX为下一重定位点的偏移

EBX+EAX方可定位到下一重定位点地址。

   

技术分享图片

   

查看EDI内存,内存储存着不同的偏移值。

   

技术分享图片

   

查看取出偏移值代码块:

可以看出代码有两处机制,可以推出偏移值有1字节和2字节之分。

   

技术分享图片

   

锁定分析EDI地址数据来源,也就是偏移值来源

最终分析,发现UPX其算法非常特别。

对偏移值区域数据的操作:

   

技术分享图片

   

也就是说EDX在加4之前,是0x15690F3,查看地址,发现真的是数据覆盖。

   

技术分享图片

   

3、总结及实施方案

总结upx的几点:

  1. 对原需要 修复重定位点 进行加密。
  2. 采用重定位点地址 加 偏移值 修复下一重定位点。
  3. 算出偏移值的算法 奇特。

   

而且通过原demo的重定位表数据,搜索字节码,发现无论在脱壳前还是脱壳后,都搜寻不到.reloc的数据。

可推断,upx在加壳时就已经将.reloc Section加密了,在壳中再通过其算法解析出偏移值,再通过偏移值修复重定位点

   

  4.加壳时已对重定位表数据加密。

   

加壳 --> .reloc加密,重定位点加密 --> 壳运行 --> .reloc解密 --> 算出偏移值 -->

对加密的重定位点解密 --> 采用偏移值修复重定位点信息。

   

现在有两种方案:

  1. UPX是开源的,研究其源码算法,对偏移值数据逆向处理,得到原本重定位表。
  2. 分析算法代码块,抠出偏移值数据,模拟算法,将偏移值转换为重定位表数据。

最后重建重定位表,为程序添加.reloc Section

   

这里时间紧迫,选择方案2。

   

四、模拟算法转换数据

1、分析算法代码块:

可见是从EDI所指向的地址,取出偏移值给EAX,再进行JA判断,最终加上EBX。

编写C++代码 进行模拟算法计算,转换,并输出。

   

技术分享图片

   

2、取偏移值区块数据:

数据首:

   

技术分享图片

   

数据末:

   

技术分享图片

   

抠出数据:

 

技术分享图片

   

3、写C++代码转换数据

   

//模拟重定位表结构,转换重定位表数据。
//参考,重定位表结构体:
/*typedef struct _IMAGE_BASE_RELOCATION {
DWORD   VirtualAddress;
DWORD   SizeOfBlock;
//  WORD    TypeOffset[1];
} IMAGE_BASE_RELOCATION;    */

typedef vector<WORD> _TypeOffset;

//自实现重定位结构体
typedef struct _MyRELOCATION {
DWORD   VirtualAddress;
DWORD   SizeOfBlock;
_TypeOffset TypeOffset;
} MyRELOCATION,*PMyRELOCATION;


int _tmain(int argc, _TCHAR* argv[])
{
    ///////////////////////////////////////////////////////////////////////////////////////
    //////前期工作

    //打开文件
    FILE* DataFile = NULL;
    DWORD error = fopen_s(&DataFile, "Section_Data", "rb");

    if (error != NULL)
    {
        printf("失败1");
        getchar();
        return 0;
    }

    //重建重定位表
    PMyRELOCATION relocData = new MyRELOCATION();
    //新建重定位表数据文件
    HANDLE HNewFile = CreateFile(L"reloc_Section", GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (HNewFile == INVALID_HANDLE_VALUE)
    {
        printf("失败2");
        getchar();
        return 0;
    }

    //读取数据
    fseek(DataFile, 0, SEEK_END);
    DWORD FileSize = ftell(DataFile);
    fseek(DataFile, 0, SEEK_SET);

    //存放偏移值缓冲区
    BYTE* FileBuff = new BYTE[FileSize]();
    fread_s(FileBuff, FileSize, FileSize, 1, DataFile);

    ///////////////////////////////////////////////////////////////////////////////////////
    /////////模拟算法

    BYTE* BuffPos = FileBuff;        //Buff指针
    DWORD Curshifting = 0;        //当前偏移值 用于运算
    DWORD AddShifting = 0;        //本区段累计偏移值 用于判断跨区段
    DWORD Count = 1;            //跨区段个数  //由重复逆向分析可可知,TEXE段的RVA恒定0x1000
    DWORD RelocOfNumber = 0;    //重定位数量

    for (DWORD i = 0; BuffPos[i] != 0;)
    {
        Curshifting = BuffPos[i];

        if (Curshifting > 0xEF)        //取出两字节
        {
            Curshifting &= 0xF;
            Curshifting << 0x10;
            Curshifting = *(WORD*)&BuffPos[i + 1];

            if (i == 0)
                Curshifting -= 4;            //首字节减去4字节

            i += 1 + 2;
        }
        else
        {                            //取出1字节

            if (i == 0)
                Curshifting -= 4;            //首字节减去4字节

            ++i;
        }

        ///////////////////////////////////////////////////////////////////////////////////////
        //////////转换成重定位表数据

        //判断是否跨区段
        ////当本区段累计值 与 当前偏移值 之和 超过0x1000时,表示跨区段
        if (AddShifting + Curshifting > 0x1000)
        {
            //跨区段
            Count += 1;
            relocData->VirtualAddress = 0x1000 * Count;
            relocData->SizeOfBlock = ((relocData->TypeOffset.size()*sizeof(WORD)) + sizeof(_IMAGE_BASE_RELOCATION));

            //输出到重定位表数据文件
            DWORD Relity = 0;
            WriteFile(HNewFile, &(relocData->VirtualAddress), 4, (LPDWORD)&Relity, NULL);
            WriteFile(HNewFile, &(relocData->SizeOfBlock), 4, (LPDWORD)&Relity, NULL);
            RelocOfNumber += relocData->TypeOffset.size();
            for (DWORD j = 0; j < relocData->TypeOffset.size(); j++)
            {
                WriteFile(HNewFile, &(relocData->TypeOffset[j]), 2, (LPDWORD)&Relity, NULL);
            }

            //清空结构体


            //计算新区段累加值
            AddShifting = (AddShifting + Curshifting) % 0x1000;
        }

        AddShifting += Curshifting;        //添加进本区段累计偏移值

        //记录每个重定位区域 重定位点 的偏移值,高4位重定位属性3,低12位偏移值。
        WORD y = (0x3000 | *(WORD*)&AddShifting);                    //位运算符是把数据加载,运算后,再小端存储
        relocData->TypeOffset.push_back((0x3000 | *(WORD*)&AddShifting));
    }

    //最后重定位表结构体数组以0结尾
    DWORD Relity2 = 0;
    WriteFile(HNewFile, &Relity2, 4, (LPDWORD)&Relity2, NULL);

    //打印重定位数量:
    printf("%X", RelocOfNumber);

    ///////////////////////////////////////////////////////////////////////////////////////

    fclose(DataFile);
    CloseHandle(HNewFile);
    delete[]relocData;
    delete FileBuff;

    getchar();
}

 

重定位数量:

技术分享图片

   

转换后数据:

技术分享图片

   

 4、添加.reloc区段 

先填充一个区段头表为零:

 

技术分享图片

   

最末尾粘贴上.reloc数据。

 

技术分享图片

   

.reloc数据一共0xE99CD8大小。

   

技术分享图片

   

LoadPE添加区段,填上相应数值。 

 

技术分享图片

技术分享图片

   

成功!

 

技术分享图片

   

 个人总结:

1. 四部分的难度是逐步上升的,多休息,多吃苹果,保持好心态(这是我对自己说的)。

2. 实现随机基址,主要是想尽量还原程序的运行环境,觉得改掉随机标志没什么意思,但是,很累压...

3. 静态编译的MFC,有点大...

附件:

 UPX_MFC_Reloc

 

KID

手工脱壳之 UPX 【随机基址】【模拟UPX部分算法】【手工C++重建重定位表】

标签:填充   目录   null   cti   lock   tor   src   on()   let   

原文地址:https://www.cnblogs.com/KIDofot/p/8611891.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!