2021津门杯逆向

2021津门杯逆向

一:easyRe

调用lua的程序逆向

找到main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int i; // [rsp+4h] [rbp-1Ch]
  char *input; // [rsp+10h] [rbp-10h]
  _DWORD *v6; // [rsp+18h] [rbp-8h]

  input = (char *)malloc(0x21uLL);
  printf("input the a str:");
  __isoc99_scanf("%s", input);
  if ( (unsigned int)strlen(input) != 32 )
  {
    printf("oh,no!");
    exit(0);
  }
  v6 = encrypt(~(53 * (2 * input[6] + input[15] + 3 * input[29])) & 0xFFF, (__int64)input);
  for ( i = 0; i <= 31; ++i )
  {
    if ( dword_63A380[i] != v6[i] )
    {
      printf("oh,no!");
      exit(0);
    }
  }
  puts("Success!");
  return 0;
}

输入长度为32的字符串,然后从中截取三个字符来计算得到encrypt函数的参数1,并将字符串作为参数2传入函数

因为是会调用lua,所以我们先在linux平台下编译好一份lua

curl -R -O http://www.lua.org/ftp/lua-5.3.0.tar.gz

tar zxf lua-5.3.0.tar.gz

cd lua-5.3.0

make linux test

make install

然后cp /usr/local/bin/lua /home/ub20/Desktop(将/usr/local/bin/lua这个文件复制到桌面)

然后将其从虚拟机拖到windows本机即可

使用ida先载入lua,退出后生成lua.i64

重新打开ida,载入re1这个elf文件,ctrl+6
在这里插入图片描述

Diff Database载入我们之前生成的lua.i64(记住路径千万不要有中文)

然后再次ctrl+6,点击Import Symbols and Comments…

并设置confidence和similarity为0.5

效果如下:

在这里插入图片描述

可以很准确的识别出lua的函数

lua文档链接:http://www.lua.org/manual/5.4/

下面我们分析encrypt函数:(请查看注释)

void *__fastcall encrypt(unsigned int a1, __int64 a2)
{
  _BYTE *functionname; // rax
  double lua_func_parameter; // xmm0_8
  __int64 v4; // rcx
  __int64 v5; // r8
  __int64 v6; // r9
  int i; // [rsp+20h] [rbp-70h]
  int j; // [rsp+24h] [rbp-6Ch]
  int k; // [rsp+28h] [rbp-68h]
  FILE *fp; // [rsp+38h] [rbp-58h]
  __int64 size; // [rsp+40h] [rbp-50h]
  void *ptr; // [rsp+48h] [rbp-48h]
  const char *decode_file; // [rsp+50h] [rbp-40h]
  __int64 L; // [rsp+58h] [rbp-38h]
  void *s; // [rsp+70h] [rbp-20h]
  void *final; // [rsp+78h] [rbp-18h]

  setvbuf(stdout, 0LL, 2, 0LL);
  fp = fopen("my.lua", "rb");
  if ( !fp )
    exit(0);
  fseek(fp, 0LL, 2);
  size = ftell(fp);
  ptr = malloc(size);
  rewind(fp);
  fread(ptr, 1uLL, size, fp);                   // 读取文件
  decode_file = xorfunc((__int64)ptr, size);    // 对文件进行异或解密
  L = luaL_newstate();                          // luaL_newstate创建lua状态机
  luaopen_base(L);                              // luaopen_base调用基本库
  luaL_openlibs(L);                             // /* 打开之前luaopen_base调用的lua状态机中所有Lua标准库 */
  if ( !L )
    exit(0);
  if ( (unsigned int)luaL_loadstring(L, decode_file) )// Loads a string as a Lua chunk. 
    exit(0);
  s = malloc(132uLL);
  memset(s, 0, 132uLL);
  for ( i = 0; i <= 32; ++i )                   // 根据传入的a1值,循环33次生成一个序列
  {
    *((_DWORD *)s + i) = (0x1ED0675 * (unsigned __int64)a1 + 0x6C1) % 254;
    a1 = *((_DWORD *)s + i);
  }
  final = malloc(0x100uLL);
  memset(final, 0, 0x100uLL);
  for ( j = 0; j <= 31; ++j )
  {
    for ( k = 0; k <= 32; ++k )
    {
      *((_DWORD *)final + j + k) += *(char *)(j + a2) ^ *((_DWORD *)s + k);
      lua_pcallk(L, 0, 0, 0, 0LL, 0LL);         // Calls a function (or a callable object) in protected mode
      functionname = xorfunc((__int64)off_63A360, 7);// 对这个字符串进行异或解密
      lua_getglobal(L, (__int64)functionname);  // lua_getglobal 要调用的lua的函数名
      lua_func_parameter = (double)*((int *)final + j + k);
      lua_pushnumber(L, lua_func_parameter);    // 参数入栈
      lua_pcallk(L, 1, 1, 0, 0LL, 0LL);
      lua_tonumberx(L, 0xFFFFFFFFLL, 0LL, v4, v5, v6);// lua_tonumberx  取出返回值
      *((_DWORD *)final + j + k) = (int)lua_func_parameter;
    }
  }
  return final;
}

本来想动态调试,让程序自己载入my.lua进行解密,但是拖入winhex查看,发现OEP(e_entry)被修改过,直接./re1,会提示非法指令

只能静态实现它对文件的异或解密了

# _*_ coding: utf-8 _*_
# editor: SYJ
# function: Reversed By SYJ
# describe:
fp = open("D:\\CTF\\COMPETITIONS\\2021jingmen\\REVERSE\\easyRe\\my.lua", "rb")
data = fp.read()
xor_arr = [2, 3, 5]
def xor_func(data):
    for i in range(len(data)):
        print(chr((data[i] ^ xor_arr[i % 3]) & 0xff), end='')

print("下面是调用xorfuncd对文件解密的结果")
xor_func(data)
print("\n下面是循环中调用xorfunc对off_63A360位置的字符串cgfffce解密的结果")
xor2 = [0x63, 0x67, 0x66, 0x66, 0x66, 0x63, 0x65]
xor_func(xor2)

运行可以得到:

下面是调用xorfuncd对文件解密的结果
function BitXOR(a,b)
    local p,c=1,0
    while a>0 and b>0 do
        local ra,rb=a%2,b%2
        if ra~=rb then c=c+p end
        a,b,p=(a-ra)/2,(b-rb)/2,p*2
    end
    if a<b then a=b end
    while a>0 do
        local ra=a%2
        if ra>0 then c=c+p end
        a,p=(a-ra)/2,p*2
    end
    return c
end

function adcdefg(j)  
    return BitXOR(5977654,j)
end
下面是循环中调用xorfunc对字符串cgfffce解密的结果
adcdefg

分析这段lua:

function BitXOR(a,b)
    local p,c=1,0
    while a>0 and b>0 do
        local ra,rb=a%2,b%2         # 取最后一bit
        if ra~=rb then c=c+p end    # 如果最后一bit不相等,c = c+p
        a,b,p=(a-ra)/2,(b-rb)/2,p*2  # a等于除开最后一bit后还有多少个2,b等于除开最后一bit后还有多少个2,p=p*2,二进制进位
    end
    if a<b then a=b end      # 如果上面那个循环执行完了还在未判断完,就让a里面存放a和b里面大的那个数
    while a>0 do
        local ra=a%2           
        if ra>0 then c=c+p end    # a的最后一bit如果为1,就和b对应的bit(因为b小于a,这个bit必为0)异或,等于1,要二进制进位
        a,p=(a-ra)/2,p*2               # a等于除开最后一bit后还有多少个2,p二进制进位
    end
    return c
end

function adcdefg(j)  
    return BitXOR(5977654,j)      # 所以这个函数就是一个bit一个bit的进行判断,得到最后异或的值
end

其实就是实现的按bit异或,跟函数名BitXOR照应,所以function abcdefg(j)就是5977654 ^ j

然后返回最后的结果,与内存中的一段数据进行比较

seg023:000000000063A380 ; int dword_63A380[32]
seg023:000000000063A380 dword_63A380    dd 5B368Bh, 136h, 5B37CBh, 0E0Ch
seg023:000000000063A380                                         ; DATA XREF: main+D3↑r
seg023:000000000063A380                 dd 5B396Ah, 39h, 5B32A5h, 0CBAh
seg023:000000000063A380                 dd 5B2683h, 0CB9h, 5B3777h, 0E47h
seg023:000000000063A380                 dd 5B33BDh, 1F49h, 5B34A8h, 4D1h
seg023:000000000063A380                 dd 5B27E7h, 0D76h, 5B3BDFh, 2EFh
seg023:000000000063A380                 dd 5B3CD3h, 1F6Eh, 5B3D90h, 3DAh
seg023:000000000063A380                 dd 5B3184h, 524h, 5B330Eh, 1A10h
seg023:000000000063A380                 dd 5B2E84h, 0D3h, 5B3DA1h, 2F7Bh

写出最后的解密脚本

# decrypt部分
from z3 import *

dest_enc = [0x005B368B, 0x00000136, 0x005B37CB, 0x00000E0C, 0x005B396A, 0x00000039, 0x005B32A5, 0x00000CBA, 0x005B2683, 0x00000CB9, 0x005B3777, 0x00000E47, 0x005B33BD, 0x00001F49, 0x005B34A8, 0x000004D1, 0x005B27E7, 0x00000D76, 0x005B3BDF, 0x000002EF, 0x005B3CD3, 0x00001F6E, 0x005B3D90, 0x000003DA, 0x005B3184, 0x00000524, 0x005B330E, 0x00001A10, 0x005B2E84, 0x000000D3, 0x005B3DA1, 0x00002F7B]
for seed in range(0xfff):
    xor_data = []

    for i in range(33):        # 循环33次生成序列
        r = (0x1ED0675 * seed + 0x6c1) % 0xfe
        xor_data.append(r)
        seed = r

    s=Solver()

    flag = [BitVec(('x%d' % i), 8) for i in range(32)]
    xor_result = [0 for i in range(64)]
    for i in range(32):
        for j in range(33):
            a = flag[i] ^ xor_data[j]
            xor_result[i + j] += a
            xor_result[i+j] = (xor_result[i+j] ^ 5977654)

    for i in range(0, 32):
        s.add(flag[i] <= 127)
        s.add(flag[i] >= 32)
        s.add(xor_result[i] == dest_enc[i])

    if s.check() == sat:
        model = s.model()
        str = [chr(model[flag[i]].as_long().real) for i in range(32)]
        print("".join(str))
        exit()

# flag{cfcd208495d565ef66e7dff9f98764da}

二:GoodRE

这道题不知道为什么和强网杯的LongTimeAgo很像

逻辑分析,关键部分就是TEA,key直接动调之后看即可

delta是tea加密函数内部生成的

  sub_55C46D20A408((__int64)v7, 0x830A5376);
  sub_55C46D20A408((__int64)v8, 0x1D3D2ACF);
  xor(delta, v8, v7);

可以得到delta就是

num1 = 0x830A5376
num2 = 0x1D3D2ACF
print(hex(num1 ^ num2)[2:])     # 9e3779b9

9e3779b9

一个标准的TEA并没有魔改过,只是不知道用了什么库,将+,<<, >>, ^等都分开成用函数实现的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zr2GjGii-1626758207675)(2021%E6%B4%A5%E9%97%A8%E6%9D%AF%E9%80%86%E5%90%91%20222b8f36769c42b0b92b1fd76d121063/Untitled%202.png)]

解密脚本:

#include <stdio.h>
#include <stdint.h>

void decrypt (uint32_t* v, uint32_t* k) {
    uint32_t delta=0x9E3779B9;                     /* a key schedule constant */
    uint32_t v0=v[0], v1=v[1], sum=(delta*32)&0xffffffff, i;  /* set up */
    uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];   /* cache key */
    for (i=0; i<32; i++) {                         /* basic cycle start */
        v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
        v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
        sum -= delta;
    }
    //v0 = v0 ^data1;
    //v1 = v1 ^data2;
    v[0]=v0; v[1]=v1;   //现在v0和v1已经解密成功
}

int main()
{
    uint32_t array[] = {0x79AE1A3B,0x596080D3,0x80E03E80,0x846C8D73,0x21A01CF7,0xC7CACA32,0x45F9AC14,0x0C5F5F22F};
    uint32_t key[4] = {0x11,0x11,0x11,0x11};
    int i = 0;
    for(i=0;i<8;i+=2)
    {
        uint32_t temp[2];        //定义来解密
        temp[0] = array[i];
        temp[1] = array[i+1];
        decrypt(temp, key);
        printf("%X%X",temp[0],temp[1]);
    }
    return 0;
}
# 7DEA3F6D3B3D6C0C620864ADD2FA2AE1A61F2736F0060DA0B97E8356D017CE59

得到flag为:flag{7DEA3F6D3B3D6C0C620864ADD2FA2AE1A61F2736F0060DA0B97E8356D017CE59}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值