0


upx脱壳

参考链接:手动去upx特征_upx -d-CSDN博客
参考链接:linux 下 upx 脱壳笔记
参考链接:三叶草二进制招新培训
参考链接:如何用x64dbg UPX手动脱壳(64位)

upx壳简介

upx壳是一种压缩壳,在CTF比赛中比较常见。

针对upx壳有专业的脱壳工具。

当然我们也可以直接手脱,手动脱壳我们就需要找到加壳程序的OEP然后dump文件内存。

OEP原始程序入口点。EP(Entry Point),意即程序的入口点。而OEP是程序的原始入口点,一个正常的程序只有EP,只有入口点被修改的程序(加壳等),才会拥有OEP。

工具脱壳

UPX(Ultimate Packer for eXecutables)是一个开源的可执行文件压缩器,用于减小可执行文件的大小,同时保持其功能。UPX 支持多种操作系统,包括 Linux、Windows 和 macOS。

upx工具使用请参考这篇文章。

upx工具使用:【逆向】UPX工具使用及加壳_upx.exe-CSDN博客

常规upx工具脱壳

直接使用 upx -d 进行脱壳

#exe文件
upx -d demo.exe
#elf文件
upx -d demo
例题

[SWPUCTF 2023 秋季新生赛]UPX

分析

exeinfo查看发现upx加壳。

在这里插入图片描述

脱壳

直接使用upx -d进行脱壳。

可以看到脱壳成功。

在这里插入图片描述

exeinfo查看确认一下

确认脱壳成功

在这里插入图片描述

ida打开查看反编译代码,发现flag

在这里插入图片描述

upx魔改壳

常规upx壳只要利用upx工具直接执行upx -d命令即可脱壳。

不过只要做一些简单的修改就可以让upx工具失效。

这种魔改壳在CTF中很常见。

1.修改区段名

​ 我们先来查看一下未修改前的文件信息。

在这里插入图片描述

​ 可以看到图中显示了upx1和3.91 upx这两个特征标识。

在这里插入图片描述

​ 然后查看一下加壳后的区段窗口,发现只有三个区段。最明显的特征upx0和upx1.

​ 我们只需要修改它,就可以让upx工具无法脱壳。

​ 我们利用二进制编辑工具将upx改成rpx
在这里插入图片描述
在这里插入图片描述

尝试脱壳

​结果:

在这里插入图片描述

​可以看到失败了。

​只要改回原样就可以进行脱壳。

2.修改标识

在这里插入图片描述

​可以看到3.96.UPX!特征码。我们可以修改3.96版本号开始24个字节的内容。都不会对程序运行产生影响。

​修改标识之后,upx工具就无法脱壳。不过可以利用UPX Unpacker for Dummies工具进行脱壳也可以直接手脱。

后面我们会讲如何手动脱壳。

例题

[LitCTF 2024]hello_upx

分析

exe文件,查壳发现存在upx壳。
在这里插入图片描述

直接使用脱壳机,脱壳失败。
在这里插入图片描述

提示信息文件被修改,用010_Editor打开

发现有四处地方被修改

在这里插入图片描述

这里我们列出正常upx加壳文件的区段信息进行对比

在这里插入图片描述

UPX0和UPX1是加UPX壳后的两个区段名。其中UPX1区段包含了需要解压的数据块。
.rsrc是程序资源信息区段名,这个区段含有原资源段的完整头部以及图标、Manifest、版本等未被压缩的资源,当然还有UPX自身需要的导入信息等(如果程序自身不含资源段,加壳后就是UPX2)

分析修改

1. upx0被改成了小写
2. upx1被改成了小写
3. upx2被改成了小写
4. upx!被改成了小写
恢复

将upx0、upx1、upx2和upx!全部修改为大写

在这里插入图片描述

脱壳

在这里插入图片描述

可以看到脱壳成功。

分析代码逻辑
在这里插入图片描述

exp

根据代码逻辑构造exp

data=[0x4C,0x68,0x72,0x40,0x50,0x41,0x75,0x70,0x2B,0x63,0x59,0x25,0x61,0x58,0x51,0x65,0x20,0x4E,0x5A,0x1E,0x60,0x4E,0x5E,0x4F,0x65]
flag=""for i inrange(len(data)):
    flag+=chr((data[i]+i))print(flag)#LitCTF{w3lc0me_t0_l1tctf}

手脱

exe

自己写的程序加壳,手脱。
在这里插入图片描述

入口不是pushad,只能一步一步单步步过。

pushad意味着upx壳解压缩代码的入口。
64位程序中没有puahad,而是用几个push汇编代码替代。32位程序中存在pushad。

遵循单步定律,向下跳转允许实现,向上跳转不允许实现。

向下的红色小箭头就是向下的跳转,线为红色即是跳转成立,线为白色就是跳转不成立。
同理向上的红色小箭头就是向上的跳转。

在这里插入图片描述

一直单步步过,直到发现这样的多个push指令为止。
在这里插入图片描述

根据esp定律下断点寻找OEP

f8单步执行一下,让rsp发送变化。
在这里插入图片描述

查看寄存器窗口,发现rsp产生变化

在这里插入图片描述

右击rsp,选中在栈中转到。

右击栈顶,选中断点,选择硬件访问断点,选择4字节。
在这里插入图片描述

设置硬件断点后f9运行。

之后发现下面有一个比较大幅度的jmp跳转,并且已经显示文件特征。

判断这个特征会跳转到OEP。

选中jmp指令,然后f4。

在这里插入图片描述

之后直接f8跳到oep

在这里插入图片描述

在这里插入图片描述

接下来dump文件

点击dump之后将文件保存。

但是这样dump后的文件是无法执行的,所以我们要修复文件。

在这里插入图片描述

修复文件

先点击 IAT Autosearch,再点击 Get Imports ,在 Imports 列表中右键delete删掉带有红叉的。

之后点击 Fix Dump 选中之前的Dump文件,修复成功。

在这里插入图片描述

在这里插入图片描述

选中之前dump的文件修复既可。
在这里插入图片描述

脱壳之后运行程序测试。
成功执行程序,dump文件成功。在这里插入图片描述

exeinfo确认脱壳

发现仍然保留特征信息

在这里插入图片描述

ida打开确认

确认已脱壳
在这里插入图片描述

elf

elf脱壳的过程
寻找OEP

目标:找到原始程序入口地址(OEP)

​从启动函数start开始设置断点,一步一步单步步过。

​发现ret指令直接F4然后F7,直到发现endbr64指令(即源程序代码开头)。

​endbr64是elf程序开头的汇编指令,即是OEP

​然后查看程序代码,发现将函数地址送入rdi

判断函数地址为main函数地址

判断这是源程序代码,进入

c将数据解释为代码,p创建函数

f5反编译,得到main函数伪代码

dump内存

根据OEP利用脚本dump内存到文件,并修复运行。

例题

自己写一段代码编译加壳。

#include<stdio.h>intmain()1{char buf[10]={0};puts("input:");read(0,buf,10);printf("%s",buf);return0;}

编译,这里需要静态编译,要不然文件太小加不了壳。

在这里插入图片描述

加壳,可以看到加壳成功
在这里插入图片描述

exeinfo扫一下,可以看到显示upx壳

在这里插入图片描述

寻找oep

ida打开文件

直接在启动函数位置下断点,然后动态调试。

在这里插入图片描述

在这里插入图片描述

下翻,翻到ret指令直接f4运行到这里,然后f8。

重复以上过程,如果没找到ret指令就执行最近的jmp跳转。

当看到下面这个汇编中断代码时我们就快到OEP了。

之后f4运行到ret位置,之后f8单步。

在这里插入图片描述

跳转到OEP

看起来都是数据,按快捷键c将数据解释为代码。

在这里插入图片描述

之后看到汇编代码endbr64,elf64位程序的入口。

是由它来调用初始化程序,进而调用main函数。

这里被传地址给rdi的byte_401775就是原程序main函数。

在这里插入图片描述

进入main函数查看

按c解释为代码,然后在endbr64处按快捷键p创建函数。

在这里插入图片描述

之后就可以f5反编译了。

在这里插入图片描述

main函数反编译代码

CTF中一般就可以在这里分析程序代码解题了。

不过接下来我们要学习如何dump文件。

在这里插入图片描述

dump内存到文件

先回到OEP这里。

确保eip执向endbr64汇编代码。

在这里插入图片描述

然后,alt+f7快捷键打开并运行脚本。

dump64位程序选用64位文件(idc脚本代码下面有)。

在这里插入图片描述

执行,等待执行完毕。
在这里插入图片描述

执行完毕
在这里插入图片描述

dump文件的存放路径在脚本中设置。

执行文件

可执行,则dump文件成功。

在这里插入图片描述

exeinfo扫一下,确认是否脱壳。

显示无壳,则脱壳成功。

在这里插入图片描述

idc dump内存文件代码
64位程序dump内存代码
#include <idc.idc>
#define PT_LOAD              1
#define PT_DYNAMIC           2
static main(void)
{
         auto ImageBase,StartImg,EndImg;
         auto e_phoff;
         auto e_phnum,p_offset;
         auto i,dumpfile;
         ImageBase=0x400000;
         StartImg=0x400000;
         EndImg=0x0;
         if (Dword(ImageBase)==0x7f454c46 || Dword(ImageBase)==0x464c457f )
  {
    if(dumpfile=fopen("G:\\dumpfile","wb")) //这里可以更改路径
    {
      e_phoff=ImageBase+Qword(ImageBase+0x20);
      Message("e_phoff = 0x%x\n", e_phoff);
      e_phnum=Word(ImageBase+0x38);
      Message("e_phnum = 0x%x\n", e_phnum);
      for(i=0;i<e_phnum;i++)
      {
         if (Dword(e_phoff)==PT_LOAD || Dword(e_phoff)==PT_DYNAMIC)
                         { 
                                 p_offset=Qword(e_phoff+0x8);
                                 StartImg=Qword(e_phoff+0x10);
                                 EndImg=StartImg+Qword(e_phoff+0x28);
                                 Message("start = 0x%x, end = 0x%x, offset = 0x%x\n", StartImg, EndImg, p_offset);
                                 dump(dumpfile,StartImg,EndImg,p_offset);
                                 Message("dump segment %d ok.\n",i);
                         }    
         e_phoff=e_phoff+0x38;
      }

      fseek(dumpfile,0x3c,0);
      fputc(0x00,dumpfile);
      fputc(0x00,dumpfile);
      fputc(0x00,dumpfile);
      fputc(0x00,dumpfile);

      fseek(dumpfile,0x28,0);
      fputc(0x00,dumpfile);
      fputc(0x00,dumpfile);
      fputc(0x00,dumpfile);
      fputc(0x00,dumpfile);
      fputc(0x00,dumpfile);
      fputc(0x00,dumpfile);
      fputc(0x00,dumpfile);
      fputc(0x00,dumpfile);

      fclose(dumpfile);
        }else Message("dump err.");
 }
}
static dump(dumpfile,startimg,endimg,offset) 
{
        auto i;
        auto size;
        size=endimg-startimg;
        fseek(dumpfile,offset,0);
        for ( i=0; i < size; i=i+1 ) 
        {
        fputc(Byte(startimg+i),dumpfile);
        }
}
32位程序dump内存代码
#include <idc.idc>
#define PT_LOAD              1
 
#define PT_DYNAMIC           2
static main(void)
{
    auto ImageBase,StartImg,EndImg;    //基址 08048000
    auto e_phoff;
    auto e_phnum,p_offset;    //paddr 0xc 地址,pmemsz ox14大小,p_offset 0x4
    auto i,dumpfile;
    ImageBase=0x08048000;
    StartImg=0x08048000;
    EndImg=0x0;
    Message("%8x\n",Dword(ImageBase));
    if (Dword(ImageBase)==0x7f454c46 || Dword(ImageBase)==0x464c457f )
    {
        if(dumpfile=fopen("G:\\dumpfile","wb"))//这里可以更改路径
        {
            e_phoff=ImageBase+Word(ImageBase+0x1c);
            e_phnum=Word(ImageBase+0x2c);
            for(i=0;i<e_phnum;i++)
            {
                 if (Dword(e_phoff)==PT_LOAD || Dword(e_phoff)==PT_DYNAMIC)
                     
                    {   p_offset=Dword(e_phoff+0x4);               
                        StartImg=Dword(e_phoff+0xc);
                        EndImg=Dword(e_phoff+0xc)+Dword(e_phoff+0x14);
                             
                                dump(dumpfile,StartImg,EndImg,p_offset);
                                Message("dump LOAD%d ok.\n",i);
                    }      
                     
                e_phoff=e_phoff+0x20;
            }
            fseek(dumpfile,0x30,0);
            fputc(0x00,dumpfile);      
            fputc(0x00,dumpfile);      
            fputc(0x00,dumpfile);      
            fputc(0x00,dumpfile);
            fclose(dumpfile);
        }else Message("dump err.");
    }
 
}
static dump(dumpfile,startimg,endimg,offset)
{
    auto i;
    auto size;
    size=endimg-startimg;
    fseek(dumpfile,offset,0);
    for ( i=0; i < size; i=i+1 )
    {
            fputc(Byte(startimg+i),dumpfile);
        }
}
标签: 安全

本文转载自: https://blog.csdn.net/m0_62280492/article/details/140280845
版权归原作者 南行* 所有, 如有侵权,请联系我们删除。

“upx脱壳”的评论:

还没有评论