XCTF-攻防世界CTF平台-Reverse逆向类——57、re5-packed-movement(linux32位ELF文件、movfuscator代码混淆)

先查看文件信息:
在这里插入图片描述

  是linux下的32位ELF文件,且被加了UPX的壳
  下载最新版的UPX脱壳,UPX脱壳的Githup地址:https://github.com/upx/upx/releases/tag/v3.91
  之后脱壳,指定加壳文件57,输出脱壳文件57move

upx -d 57 -o 57move

在这里插入图片描述

赋执行权限运行程序:
在这里插入图片描述

用IDA打开57move,找到字符串“Guess a flag:”
在这里插入图片描述

有两个位置使用了这个字符串:
在这里插入图片描述
在这里插入图片描述

同样地,我们查看“Wrong Flag!”字符串:
在这里插入图片描述

发现有70个引用的地方。
  但是我们可以看到程序的汇编代码全都是mov,肯定是被混淆过了,这里是用了movfuscator来混淆,。
  剑桥大学的Stephen Dolan证明了x86的mov指令是图灵完全的(论文《mov is Turing-complete》)。这意味着从理论上来讲,x86只要有mov这一条指令就可以完成几乎所有功能了(可能还需要jmp),其他指令都是“多余的”。受此启发,有个大牛做了一个虚拟机加密编译器。它是一个修改版的LCC编译器,输入是C语言代码,输出的obj里面直接包含了虚拟机加密后的代码。如它的名字,函数的所有代码只有mov指令,没有其他任何指令。对,完全没有,连call,jz,ret之类的都没有。
  开源项目地址:https://github.com/xoreaxeaxeax/movfuscator
  M/o/Vfuscator(简称“o”,听起来像“mobfuscator”)将程序编译成“mov”指令,并且只编译“mov”指令。算术、比较、跳转、函数调用,以及程序需要的一切,都是通过mov操作完成的;没有自修改代码,没有传输触发的计算,也没有其他形式的非 mov 作弊。
  继续直接分析原程序:
在这里插入图片描述

  这类movfuscator 混淆后的程序,MOV混淆是不会混淆函数的逻辑的,因此函数的逻辑还是不变的。大多都是通过逐个比较输入后的字符串的每个字符的方式,比较结果不对就输出“Wrong Flag!”,所以在程序当中就会出现单个的字符,例如:

.text:080493DB C7 05 68 20 06 08 41 00 00 00                   mov     R2, 41h ; 'A'

  movfuscator 混淆后的程序都是通过R2寄存器来存储常量字符,

方法一:搜索字节序列

  直接在IDA中搜索字节序列:字符串C7 05 68 20 06 08(“mov R2,”对应字节码)
在这里插入图片描述

得到程序中所有使用的常量字符:
在这里插入图片描述

  连起来就是程序的flag:ALEXCTF{M0Vfusc4t0r_w0rk5_l1ke_m4g1c}
  输入到程序验证:
在这里插入图片描述

Well Done,这就是正确的flag。

方法二:IDC脚本

  也可以用IDA中的IDC脚本和Python脚本来提取代码段中的常量字符:

#include<idc.idc>
static isAscii(p)  //判断符合条件的字符
{
	if(Byte(p)<='9' && Byte(p)>='0')
		return 1;
	if(Byte(p)<='z' && Byte(p)>='a')
		return 1;
	if(Byte(p)<='Z' && Byte(p)>='A')
		return 1;
	if(Byte(p)=='}' || Byte(p)=='{' || Byte(p)=='_' || Byte(p)=='@' || Byte(p)=='!' || Byte(p)=='#' || Byte(p)=='&' || Byte(p)=='*')
		return 1;
	return 0;
}
static main()
{
	auto start=0x0804829C,end=0x08060B32;  //代码段起止地址
	auto point=start;
	auto str="";
	while(point<=end)
	{
		if(isAscii(point) && Byte(point+1)==0 && Byte(point+2)==0 && Byte(point+3)==0)  
		{   //汇编中立即数一般都是DWORD
			Message("%X  %c\n",point,Byte(point));
			str=str+Byte(point);
		}
		point=point+1;
	}
	Message("%s\n",str);   //可能是flag
}

在这里插入图片描述

运行结果:
在这里插入图片描述
得到flag:ALEXCTF{M0Vfusc4t0r_w0rk5_l1ke_m4g1c}

方法三:Python脚本:

start=0x0804829C
end=0x08060B32
flag = ""
while start<end:
    if (Byte(start) <= ord('9') and Byte(start)>=ord('0')) or (Byte(start)<=ord('z') and Byte(start)>=ord('a')) or (Byte(start)<=ord('Z') and Byte(start)>=ord('A')) or (Byte(start) == ord('}')) or (Byte(start) == ord('{')) or (Byte(start) == ord('_')) or (Byte(start) == ord('@')) or (Byte(start) == ord('!')) or (Byte(start) == ord('#')) or (Byte(start) == ord('&')) or (Byte(start) == ord('*')):
        if Byte(start) and (Byte(start+1)==0) and (Byte(start+2)==0) and (Byte(start+3)==0):
            print(chr(Byte(start)))
            flag += chr(Byte(start))
    start += 1
print(flag)

在这里插入图片描述

运行结果:
在这里插入图片描述

  脚本找到的运行结果flag中都出现了字符2,这是因为脚本在判断的时候的逻辑是:从代码段.text起止地址0x0804829C到0x08060B32,只要字符的ASCII值在数字、大小写字母和几个特殊字符中,即可认为是flag中的一个字符,所以这中判断逻辑是可能出现误判的:
在这里插入图片描述
得到flag:ALEXCTF{M0Vfusc4t0r_w0rk5_l1ke_m4g1c}

方法四:bgrep工具

  我们从前面的IDA中知道.text:080493DB上的字节序列:字符串C7 05 68 20 06 08(“mov R2,”对应字节码),表明标志的字符是硬编码在二进制文件中的:
在这里插入图片描述

bgrep工具:
用法:
bgrep [-B 字节] [-A 字节] [-C 字节] [ […]]
-B:bytes_before
-A:bytes_after
-C:bytes_before 和 bytes_after
先安装:
在这里插入图片描述

bgrep "C70568200608" ./57move

查找程序当中字节序列:C70568200608
在这里插入图片描述

  得到的是所有出现的字节序列C70568200608的文件偏移地址,之后+6是字节序列C70568200608的长度是6,再从当前的文件读取指针位置读取一个字节的字符,拼接起来所有的字符就是flag。
Python脚本:

#!/usr/bin/env python2

offsets = [0x000013db, 0x00001dde, 0x000027e1, 0x000031e4, 0x00003b9c, 0x0000459f, 0x00004fa2, 0x000059a5, 0x00006357, 0x00006d5a, 0x0000775d, 0x00008160, 0x00008b0c, 0x0000950f, 0x00009f12, 0x0000a915, 0x0000b2bb, 0x0000bcbe, 0x0000c6c1, 0x0000d0c4, 0x0000da64, 0x0000e467, 0x0000ee6a, 0x0000f86d, 0x00010207, 0x00010c0a, 0x0001160d, 0x00012010, 0x000129a4, 0x000133a7, 0x00013daa, 0x000147ad, 0x0001513b, 0x00015b3e, 0x00016541, 0x00016f44, 0x000178cc]

with open('./57move') as f:
    flag = ''
    for offset in offsets:
        # "mov R2, 41h" is "C70568200608"+"41000000" (the immediate starts after the 6th byte)
        # 移动文件读取指针到指定位置
        f.seek(offset + 0x6)
        #从当前的文件读取指针位置读取一个字节的字符,+6是字节序列C70568200608的长度是6
        flag += f.read(1)
print flag

运行结果:
在这里插入图片描述
得到flag:ALEXCTF{M0Vfusc4t0r_w0rk5_l1ke_m4g1c}

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值