首页 > 其他 > 详细

[网鼎杯 2020 青龙组]singal

时间:2020-10-26 22:50:09      阅读:45      评论:0      收藏:0      [点我收藏+]

这个题目方法有很多,目的是练习算法的分析和vm练习,尝试了静态直接逆算法和动态静态结合的方法

方法1

拖入ida找到main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [sp+18h] [bp-1D4h]@1

  __main();
  qmemcpy(&v4, &unk_403040, 0x1C8u);//拷贝opcode表
  vm_operad(&v4, 114);//关键函数
  puts("good,The answer format is:flag {}");
  return 0;
}

找到关键函数

int __cdecl vm_operad(int *a1, int a2)
{
  int result; // eax@2
  char v3[100]; // [sp+13h] [bp-E5h]@4
  char v4[100]; // [sp+77h] [bp-81h]@5
  char v5; // [sp+DBh] [bp-1Dh]@5
  int v6; // [sp+DCh] [bp-1Ch]@1
  int v7; // [sp+E0h] [bp-18h]@1
  int v8; // [sp+E4h] [bp-14h]@1
  int v9; // [sp+E8h] [bp-10h]@1
  int v10; // [sp+ECh] [bp-Ch]@1

  v10 = 0;
  v9 = 0;
  v8 = 0;
  v7 = 0;
  v6 = 0;
  while ( 1 )
  {
    result = v10;
    if ( v10 >= a2 )
      return result;
    switch ( a1[v10] )                          // a1是opcode  v10是索引
    {
      case 10:                                  // 输入点
        read(v3);
        ++v10;
        break;
      case 1:
        v4[v7] = v5;
        ++v10;
        ++v7;
        ++v9;
        break;
      case 2:
        v5 = a1[v10 + 1] + v3[v9];
        v10 += 2;
        break;
      case 3:
        v5 = v3[v9] - LOBYTE(a1[v10 + 1]);
        v10 += 2;
        break;
      case 4:
        v5 = a1[v10 + 1] ^ v3[v9];
        v10 += 2;
        break;
      case 5:
        v5 = a1[v10 + 1] * v3[v9];
        v10 += 2;
        break;
      case 6:
        ++v10;
        break;
      case 7:
        if ( v4[v8] != a1[v10 + 1] )            // 最终判断 a1->v5->v4
        {
          printf("what a shame...");
          exit(0);
        }
        ++v8;
        v10 += 2;
        break;
      case 11:
        v5 = v3[v9] - 1;
        ++v10;
        break;
      case 12:
        v5 = v3[v9] + 1;
        ++v10;
        break;
      case 8:
        v3[v6] = v5;
        ++v10;
        ++v6;
        break;
      default:
        continue;
    }
  }
}

可以判断这是一个vm逆向,分析逻辑,输入点是case 10最后的判断位置是case 7。v4由v5得到,且必经过case 1,v5由opcode经过运算得到,所以我们可以模拟算法,先找出每次switch使用哪个索引值v10和整个过程中进行比较的opcode v4。
逆向算法如下

#include <stdio.h>
#include <string.h>
unsigned char opcode[] =
{
  0x0A, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00,
  0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
  0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00,
  0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
  0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00,
  0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00,
  0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
  0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00,
  0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
  0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x21, 0x00,
  0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
  0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00,
  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
  0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00,
  0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x51, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00,
  0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x0C, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0B, 0x00,
  0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00,
  0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x08, 0x00,
  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00,
  0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00,
  0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x01, 0x00,
  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
  0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00,
  0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x41, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0C, 0x00,
  0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
  0x22, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3F, 0x00,
  0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
  0x07, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x07, 0x00,
  0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
  0x33, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x18, 0x00,
  0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xA7, 0xFF, 0xFF, 0xFF,
  0x07, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x07, 0x00,
  0x00, 0x00, 0xF1, 0xFF, 0xFF, 0xFF, 0x07, 0x00, 0x00, 0x00,
  0x28, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x84, 0xFF,
  0xFF, 0xFF, 0x07, 0x00, 0x00, 0x00, 0xC1, 0xFF, 0xFF, 0xFF,
  0x07, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x07, 0x00,
  0x00, 0x00, 0x7A, 0x00, 0x00,
};
int *a = (int *)opcode;

int __cdecl vm_operad(int *opcode, int a2)
{
  int order[114] = {};//执行顺序
  char flag[100]; // [esp+13h] [ebp-E5h]
  char v4[100]; // [esp+77h] [ebp-81h]
  char v5; // [esp+DBh] [ebp-1Dh]
  int m; // [esp+DCh] [ebp-1Ch]
  int z; // [esp+E0h] [ebp-18h]
  int y; // [esp+E4h] [ebp-14h]
  int x; // [esp+E8h] [ebp-10h]
  int i; // [esp+ECh] [ebp-Ch]
  int s = 0;
  i = 0;
  x = 0;
  y = 0;
  z = 0;
  m = 0;
  while (1)
  {
    if ( i >= a2 )//继续循环
      break;
    switch ( opcode[i] )
    {
      case 1:
        v4[z] = v5;
        ++i;
        ++z;
        ++x;
        break;
      case 2:
        v5 = opcode[i + 1] + flag[x];
        i += 2;
        break;
      case 3:
        v5 = flag[x] - opcode[i + 1];
        i += 2;
        break;
      case 4:
        v5 = opcode[i + 1] ^ flag[x];
        i += 2;
        break;

      case 5:
        v5 = opcode[i + 1] * flag[x];
        i += 2;
        break;
      case 6:
        ++i;
      case 7:
        v4[y] = opcode[i + 1];
		printf("%#x, ",v4[y]);//打印比较的opcode
        ++y;
        i += 2;
        break;
      case 8:
        flag[m] = v5;
        ++i;
        ++m;
        break;
      case 10:
        scanf("%s",flag);//输入点
        ++i;
        break;
      case 11:
        v5 = flag[x] - 1;
        ++i;
        break;
      case 12:
        v5 = flag[x] + 1;
        ++i;
        break;
    }
    //printf("%d, ",i);//打印每次使用的索引
  }
}

int main()
{
	vm_operad(a,114);
	return 0;
}

得到

  char order[100] = {1, 3, 4, 6, 7, 9, 10, 12, 13, 15, 16, 17, 18, 19, 20, 22, 23, 25, 26, 28, 29, 30, 31, 32, 33, 35, 36, 38, 39, 41, 42, 44, 45, 46, 47, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 64, 66, 67, 69, 70, 72, 73, 75, 76, 78, 79, 81, 82, 83, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114};
  unsigned char v4[] = {0x22, 0x3f, 0x34, 0x32, 0x72, 0x33, 0x18, 0xffffffa7, 0x31, 0xfffffff1, 0x28, 0xffffff84, 0xffffffc1, 0x1e, 0x7a};

确定了中间结果根据算法写逆向算法,注意:正向的算法逆回去需要从后往前来走

int __cdecl vm_decode(int *opcode,int len_114)
{
  char order[100] = {1, 3, 4, 6, 7, 9, 10, 12, 13, 15, 16, 17, 18, 19, 20, 22, 23, 25, 26, 28, 29, 30, 31, 32, 33, 35, 36, 38, 39, 41, 42, 44, 45, 46, 47, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 64, 66, 67, 69, 70, 72, 73, 75, 76, 78, 79, 81, 82, 83, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114};
  unsigned char v4[] = {0x22, 0x3f, 0x34, 0x32, 0x72, 0x33, 0x18, 0xffffffa7, 0x31, 0xfffffff1, 0x28, 0xffffff84, 0xffffffc1, 0x1e, 0x7a};
  unsigned char flag[100] = {}; // [esp+13h] [ebp-E5h]
  int v5; // [esp+DBh] [ebp-1Dh]
  int m; // [esp+DCh] [ebp-1Ch]
  int z; // [esp+E0h] [ebp-18h]
  int x; // [esp+E8h] [ebp-10h]
  int i; // [esp+ECh] [ebp-Ch]
  x = 15;
  z = 15;
  m = 15;
  for(int k=strlen(order) - 1;k>=0;k--)//从后往前
  {
	i = order[k];
    switch ( opcode[i] )
    {
      case 1:
		--x;
		--z;
        v5 = v4[z];
        break;
      case 2:
        flag[x] = v5 - opcode[i + 1];
        break;
      case 3:
        flag[x] = v5 + opcode[i + 1];
        break;
      case 4:
        flag[x] = v5 ^ opcode[i + 1];
        break;
      case 5:
        flag[x] = v5 / opcode[i + 1];
        break;
      case 6:
		break;
      case 8:
        v5 = flag[--m];
        break;
      case 11:
        flag[x] = v5 + 1;
        break;
      case 12:
        flag[x] = v5 - 1;
        break;
    }
  }
  printf("%s",flag);
  return 0;
}

得到flag{757515121f3d478}

方法2

使用动态调试的方法得到opcode和算法。
回到vm算法,可以把断点断在case 7 的cmp上,这样可以根据eax和edx的值一步一步定位使用的opcode值和算法
技术分享图片
技术分享图片

地址是004016E2,动态调试,使用od修改jz为jmp观察这个位置的寄存器数据为0x22
技术分享图片

依次运行得到v4的值
unsigned char v4[] = {0x22, 0x3f, 0x34, 0x32, 0x72, 0x33, 0x18, 0xffffffa7, 0x31, 0xfffffff1, 0x28, 0xffffff84, 0xffffffc1, 0x1e, 0x7a};
下面分析算法,用ida从输入点进行分析,下面举第一个字符的例子:
1.首先走到case10读取字符串
技术分享图片
2.走到case4执行抑或操作抑或的对象是第一个字符
技术分享图片
技术分享图片
3.再往下到case8赋值操作
技术分享图片
4.再往后到了case3,相减操作操作数是5
技术分享图片
技术分享图片
5.再往下到case1 case1是每个opcode的进行判段前的赋值,在这里结束第一个字符进入第二个字符
技术分享图片
这五步的计算结果就是0x22 = (flag[0] ^ 0xa) - 5
依次动态调试分析出15个算法分别为

0x22 = (flag[0] ^ 0xa) - 5

0x3f = (flag[1] ^ 0x20) * 3

0x34 = (flag[2] - 2) - 1

0x32 = (flag[3] + 1) ^ 4

0x72 = (flag[4] * 3) - 0x21

0x33 = (flag[5] - 1) - 1 

0x18 = (flag[6] ^ 9) - 0x20

0xffffffa7 = (flag[7] + 0x51) ^ 0x24

0x31 = (flag[8] +1 ) - 1

0xfffffff1 = (flag[9] * 2) + 0x25

0x28 = (flag[10] + 0x36) ^ 0x41

0xffffff84 = (flag[11] + 0x20) * 1

0xffffffc1 = (flag[12] *3) + 0x25

0x1e = (flag[13] ^ 9) - 0x20

0x7a = (flag[14] + 0x41) + 1 

可以写脚本得到每个的值拼接

#coding=utf-8
b=[34, 63, 52, 50,   114, 51, 24, 256-89,  49,256-15, 40,256-124,  256-63,  30,  122]#(负数取了最低位,因此,直接取后面那部分)
flag=[0]*15
flag[0]=(b[0]+5)^0x10
flag[1]=(b[1]//3)^0x20
flag[2]=(b[2]+3)
flag[3]=(b[3]^4)-1
flag[4]=(b[4]+0x21)//3
flag[5]=b[5]+2
flag[6]=(b[6]+0x20)^9
flag[7]=(b[7]^0x24)-0x51
flag[8]=b[8]
flag[9]=(b[9]-0x25)//2
flag[10]=(b[10]^0x41)-0x36
flag[11]=(b[11]-0x20)
flag[12]=(b[12]-0x25)//3
flag[13]=(b[13]+0x20)^9
flag[14]=(b[14]-1)-0x41
result = ""
for i in range(len(flag)):
        result += chr(flag[i])
print(result)

得到flag{757515121f3d478}

另外的思想

关键点在case 7和case1 指向了v4的来源
在case7中a1[v10]等于7,所以a1[v10 + 1]就是opcode中数值为7的下一个数字(int为四个字节)
技术分享图片
可以直接静态分析出使用的opcode
除此之外,希望看点骚操作的师傅请移步李师傅的吾爱论坛还有小弟的论坛简单的符号执行讲解
https://www.52pojie.cn/thread-1176826-1-1.html
https://www.52pojie.cn/thread-1187493-1-1.html

[网鼎杯 2020 青龙组]singal

原文:https://www.cnblogs.com/A-Niucw/p/13881541.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!