2021年CISCN初赛re

glass.apk

直接分析jni的函数:

在这里插入图片描述
这里的话,三个有用函数,第一个sub_FFC((int)&v7, (int)&v6, v4);是RC4算法初始化函数,第二个sub_1088((int)&v7, v3, 39);RC4算法加密函数,第三个sub_10D4((int)v3, 39, (int)&v6, v4);进行一番异或操作,也就是说,只需要分析第三个函数即可。前面的两个函数直接用工具

 for ( i = 0; i < a2; i += 3 )
  {
    v5 = result + i;
    v6 = *(_BYTE *)(result + i + 2);
    v7 = *(_BYTE *)(result + i + 1);
    v8 = *(_BYTE *)(result + i) ^ v6;
    *(_BYTE *)(result + i) = v8;
    *(_BYTE *)(v5 + 2) = v6 ^ v7;
    *(_BYTE *)(v5 + 1) = v7 ^ v8;
  }
  for ( j = 0; j < a2; j += a4 )
  {
    for ( k = 0; (a4 & ~(a4 >> 31)) != k && j + k < a2; ++k )
      *(_BYTE *)(result + k) ^= *(_BYTE *)(a3 + k);
    result += a4;
  }

baby.bc

.bc转换为可执行程序

第一次拿到.bc的文件
在这里插入图片描述
查了一下,LLVM IR bitcode,二进制文件。想办法转换成.s,然后再转成可执行文件。(windows里面无法转成elf,最后编译成elf时,需要在linux里面进行
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
由此看出input长度为25;数组元素大小可取 0 ~ 5

fill_number(__int64)input)

v1 = (char *)(a1 + 4);
  v2 = 0LL;
  do
  {
    v3 = *(v1 - 4);
    if ( map[5 * v2] )
    {
      if ( v3 != 48 )
        return 0;
    }
    else
    {
      map[5 * v2] = v3 - 48;
    }
    v4 = *(v1 - 3);
    if ( byte_601051[5 * v2] )
    {
      if ( v4 != 48 )
        return 0;
    }
    else
    {
      byte_601051[5 * v2] = v4 - 48;
    }
    v5 = *(v1 - 2);
    if ( byte_601052[5 * v2] )
    {
      if ( v5 != 48 )
        return 0;
    }
    else
    {
      byte_601052[5 * v2] = v5 - 48;
    }
    v6 = *(v1 - 1);
    if ( byte_601053[5 * v2] )
    {
      if ( v6 != 48 )
        return 0;
    }
    else
    {
      byte_601053[5 * v2] = v6 - 48;
    }
    v7 = *v1;
    if ( byte_601054[5 * v2] )
    {
      if ( v7 != 48 )
        return 0;
    }
    else
    {
      byte_601054[5 * v2] = v7 - 48;
    }
    ++v2;
    v1 += 5;
  }
  while ( v2 < 5 );
  return 1;
}
unsigned char map[] =
{
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
    0,   0,   4,   0,   0,   0,   0,   0,   3,   0, 
    0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
    0,   0,   0,   0,   0,   1,   1,   0,   0,   0, 
    2,   0,   0,   1,   0,   0,   0,   0,   1,   0, 
    1,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
    0,   0,   0,   0
};

这个函数的作用也正如函数名所述,用来填充map数组中的0

 if ( map[5 * v2] )
    {
      if ( v3 != '0' )
        return 0;
    }
    else
    {
      map[5 * v2] = v3 - 48;
    }

map的起始地址是0x601050, 这个也就是如果map元素为0,那么就是用v3来做相应填充,而这里的v3也就是我们所做的input。else中减掉48,是想把字符元素转换为数字元素,然后赋值于map数组

 if ( v3 != '0' )
    return 0;

这里按照逻辑我觉得是当map中的元素不为0时,那么就来判断input相应输入位置,input这个输入位置必须为字符0,否则退出

docheck(input, input):z3约束

	v25 = 0;
    v24 = 0;
    v1 = (unsigned __int8)map[5 * v0];
    if ( *((_BYTE *)&v24 + v1) )
      break;
    *((_BYTE *)&v24 + v1) = 1;
    v2 = (unsigned __int8)byte_601051[5 * v0];
    if ( *((_BYTE *)&v24 + v2) )
      break;
    *((_BYTE *)&v24 + v2) = 1;
    v3 = (unsigned __int8)byte_601052[5 * v0];
    if ( *((_BYTE *)&v24 + v3) )
      break;
    *((_BYTE *)&v24 + v3) = 1;
    v4 = (unsigned __int8)byte_601053[5 * v0];
    if ( *((_BYTE *)&v24 + v4) )
      break;
    *((_BYTE *)&v24 + v4) = 1;
    if ( *((_BYTE *)&v24 + (unsigned __int8)byte_601054[5 * v0]) )
      break;

这里刚开始我也没看出有什么用,一调试才知道。这里的作用也就是把它所以的元素连续的五个分成五个一组,然后判断这组值中不能出现两两重复

		v23 = 0;
        v22 = 0;
        v6 = (unsigned __int8)map[v5];
        if ( *((_BYTE *)&v22 + v6) )
          break;
        *((_BYTE *)&v22 + v6) = 1;
        v7 = (unsigned __int8)byte_601055[v5];
        if ( *((_BYTE *)&v22 + v7) )
          break;
        *((_BYTE *)&v22 + v7) = 1;
        v8 = (unsigned __int8)byte_60105A[v5];
        if ( *((_BYTE *)&v22 + v8) )
          break;
        *((_BYTE *)&v22 + v8) = 1;
        v9 = (unsigned __int8)byte_60105F[v5];
        if ( *((_BYTE *)&v22 + v9) )
          break;
        *((_BYTE *)&v22 + v9) = 1;
        if ( *((_BYTE *)&v22 + (unsigned __int8)byte_601064[v5]) )
          break;

这里的话,也就是把连续的五个元素分成一组,总共有五组,每组有五个元素,然后每组同时选出一个相同下标的元素,判断五个组所选出的五个元素不能两两重复。

unsigned char row[] =
{
    0,   0,   0,   1,   1,   0,   0,   0,   2,   0, 
    0,   1,   0,   0,   0,   0,   1,   0,   1,   0, 
    0,   0,   0,   0,   0
};
			v11 = row[4 * v10];
            if ( v11 == 2 )
            {
              if ( (unsigned __int8)map[5 * v10] > (unsigned __int8)byte_601051[5 * v10] )
                return 0;
            }
            else if ( v11 == 1 && (unsigned __int8)map[5 * v10] < (unsigned __int8)byte_601051[5 * v10] )
            {
              return 0;
            }
            v12 = byte_601071[4 * v10];
            if ( v12 == 1 )
            {
              if ( (unsigned __int8)byte_601051[5 * v10] < (unsigned __int8)byte_601052[5 * v10] )
                return 0;
            }
            else if ( v12 == 2 && (unsigned __int8)byte_601051[5 * v10] > (unsigned __int8)byte_601052[5 * v10] )
            {
              return 0;
            }
            v13 = byte_601072[4 * v10];
            if ( v13 == 2 )
            {
              if ( (unsigned __int8)byte_601052[5 * v10] > (unsigned __int8)byte_601053[5 * v10] )
                return 0;
            }
            else if ( v13 == 1 && (unsigned __int8)byte_601052[5 * v10] < (unsigned __int8)byte_601053[5 * v10] )
            {
              return 0;
            }
            v14 = byte_601073[4 * v10];
            if ( v14 == 2 )
            {
              if ( (unsigned __int8)byte_601053[5 * v10] > (unsigned __int8)byte_601054[5 * v10] )
                return 0;
            }
            else if ( v14 == 1 && (unsigned __int8)byte_601053[5 * v10] < (unsigned __int8)byte_601054[5 * v10] )
            {
              return 0;
            }

这里的话,把map数组照样分成五组,连续的五个元素为一组,然后把row作为4个元素为一组(5个元素,相邻两个相比较,只需要比较四次),因为input长度为25,所以map也就是25,map分小组可以分为5组,那么row也就可以分为5组:

  1. row第1组的第四个元素为1;
 else if ( v14 == 1 && (unsigned __int8)byte_601053[5 * v10] < (unsigned __int8)byte_601054[5 * v10] )
            {
              return 0;
            }

这是第一组,也就代表了map[3]>map[4];

  1. row第2组的第一个元素为1;
 else if ( v11 == 1 && (unsigned __int8)map[5 * v10] < (unsigned __int8)byte_601051[5 * v10] )
            {
              return 0;
            }

这是第二组,也就代表了map[5]>map[6];

  1. row第3组的第一个元素为2;第四个元素为1
 if ( v11 == 2 )
            {
              if ( (unsigned __int8)map[5 * v10] > (unsigned __int8)byte_601051[5 * v10] )
                return 0;
            }



 else if ( v14 == 1 && (unsigned __int8)byte_601053[5 * v10] < (unsigned __int8)byte_601054[5 * v10] )
            {
              return 0;
            }

这是第三组,也就代表了map[10]<map[11];map[13]>map[14]

  1. row第5组的第一个元素为1;第三个元素为1
 else if ( v11 == 1 && (unsigned __int8)map[5 * v10] < (unsigned __int8)byte_601051[5 * v10] )
            {
              return 0;
            }

 else if ( v13 == 1 && (unsigned __int8)byte_601052[5 * v10] < (unsigned __int8)byte_601053[5 * v10] )
            {
              return 0;
            }

这是第五组,也就代表了map[20]>map[21];map[22]>map[23]

row[]约束总结为:

map[3]>map[4];
map[5]>map[6];
map[10]<map[11];map[13]>map[14];
map[20]>map[21];map[22]>map[23]
unsigned char col[] =
{
  0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 
  0, 0, 0, 1, 0, 0, 1, 0, 0, 1
};
				v17 = col[5 * v15];
                if ( v17 == 2 )
                {
                  if ( (unsigned __int8)map[5 * v15] < (unsigned __int8)byte_601055[5 * v15] )
                    return 0;
                }
                else if ( v17 == 1 && (unsigned __int8)map[5 * v15] > (unsigned __int8)byte_601055[5 * v15] )
                {
                  return 0;
                }
                v18 = byte_601091[5 * v15];
                if ( v18 == 1 )
                {
                  if ( (unsigned __int8)byte_601051[5 * v15] > (unsigned __int8)byte_601056[5 * v15] )
                    return 0;
                }
                else if ( v18 == 2 && (unsigned __int8)byte_601051[5 * v15] < (unsigned __int8)byte_601056[5 * v15] )
                {
                  return 0;
                }
                v19 = byte_601092[5 * v15];
                if ( v19 == 2 )
                {
                  if ( (unsigned __int8)byte_601052[5 * v15] < (unsigned __int8)byte_601057[5 * v15] )
                    return 0;
                }
                else if ( v19 == 1 && (unsigned __int8)byte_601052[5 * v15] > (unsigned __int8)byte_601057[5 * v15] )
                {
                  return 0;
                }
                v20 = byte_601093[5 * v15];
                if ( v20 == 2 )
                {
                  if ( (unsigned __int8)byte_601053[5 * v15] < (unsigned __int8)byte_601058[5 * v15] )
                    return 0;
                }
                else if ( v20 == 1 && (unsigned __int8)byte_601053[5 * v15] > (unsigned __int8)byte_601058[5 * v15] )
                {
                  return 0;
                }
                v21 = byte_601094[5 * v15];
                if ( v21 == 2 )
                {
                  if ( (unsigned __int8)byte_601054[5 * v15] < (unsigned __int8)byte_601059[5 * v15] )
                    return 0;
                }
                else if ( v21 == 1 && (unsigned __int8)byte_601054[5 * v15] > (unsigned __int8)byte_601059[5 * v15] )
                {
                  return 0;
                }

这里的话,把map数组照样分成五组,连续的五个元素为一组,然后把col作为5个元素为一组(5个元素,分别和对应组相比较,需要比5次),因为input长度为25,所以map也就是25,map分小组可以分为5组,五组之间,对应元素比较,只需要比4次,那么col也就可以分为4组,每组5个元素:

  1. col第一组的第三个元素为2;第五个元素为2
if ( v19 == 2 )
                {
                  if ( (unsigned __int8)byte_601052[5 * v15] < (unsigned __int8)byte_601057[5 * v15] )
                    return 0;
                }

if ( v21 == 2 )
                {
                  if ( (unsigned __int8)byte_601054[5 * v15] < (unsigned __int8)byte_601059[5 * v15] )
                    return 0;
                }

这是第一组,也就代表了map[2]>map[7];map[4]>map[9]

  1. col第三组的第四个元素为1;
 i else if ( v20 == 1 && (unsigned __int8)byte_601053[5 * v15] > (unsigned __int8)byte_601058[5 * v15] )
                {
                  return 0;
                }

这是第三组,也就代表了map[13]<map[18];

  1. col第4组的第二个元素为1;第五个元素为1
 if ( v18 == 1 )
                {
                  if ( (unsigned __int8)byte_601051[5 * v15] > (unsigned __int8)byte_601056[5 * v15] )
                    return 0;
                }
                
else if ( v21 == 1 && (unsigned __int8)byte_601054[5 * v15] > (unsigned __int8)byte_601059[5 * v15] )
                {
                  return 0;
                }

这是第三组,也就代表了map[16]<map[21];map[19]<map[24]

col[]约束总结为:

map[2]>map[7];map[4]>map[9]
map[13]<map[18];
map[16]<map[21];map[19]<map[24]
### 关于2024 CISCN 初赛逆向工程题目解析 #### 逆向工程题目的背景介绍 在2024的全国大学生信息安全竞赛(CISCN)初赛中,逆向工程作为一项重要的考察技能,在多个题目中有所体现。这类题目通常涉及对二进制文件、脚本或加密算法的理解与分析,旨在测试参赛者对于程序内部逻辑的掌握程度。 #### 解题思路概述 针对逆向工程类题目,一般采用如下方法论来解决问题: - **工具准备**:选择合适的反编译器和调试环境是成功的关键之一。常用的工具有IDA Pro、Ghidra等高级静态分析平台;以及OllyDbg这样的动态跟踪软件[^1]。 - **初步探索**:通过观察给定样本的行为模式(如启动参数、网络请求),可以获取一些线索帮助定位核心功能模块的位置[^2]。 - **深入挖掘**:利用上述提到的各种工具和技术手段逐步揭开目标应用程序的真实面目。此过程可能涉及到字符串查找、函数调用图绘制等工作[^3]。 - **验证假设**:当形成了一套完整的理论框架之后,则需设计实验加以检验其正确与否。比如尝试修改某些特定条件下的输入数据看是否会触发预期的结果变化[^4]。 #### 实际案例分享 以其中一个具体实例为例说明整个流程的应用情况。该挑战要求选手破解一个简单的加壳可执行文件并找出其中隐藏的信息。经过一系列操作后发现原生代码被混淆处理过,增加了阅读难度。不过借助强大的辅助工具还是能够还原出大致结构,并最终锁定了关键位置完成任务。 ```bash # 使用strings命令查看可能存在有用信息的地方 $ strings binary_file | grep "flag" ``` 这段shell指令展示了如何快速扫描二进制文件中的ASCII字符序列寻找潜在标志位(flag),这是许多CTF比赛中常见的技巧之一。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寻梦&之璐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值