Ginobili 发表于 2020-10-29 14:37

[网鼎杯 2020 青龙组]singal(补充)

这个题目方法有很多,目的是练习算法的分析和vm练习,尝试了静态直接逆算法和动态静态结合的方法
### 方法1
拖入ida找到main函数
```c
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // @1

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

```c
int __cdecl vm_operad(int *a1, int a2)
{
int result; // eax@2
char v3; // @4
char v4; // @5
char v5; // @5
int v6; // @1
int v7; // @1
int v8; // @1
int v9; // @1
int v10; // @1

v10 = 0;
v9 = 0;
v8 = 0;
v7 = 0;
v6 = 0;
while ( 1 )
{
    result = v10;
    if ( v10 >= a2 )
      return result;
    switch ( a1 )                        // a1是opcodev10是索引
    {
      case 10:                                  // 输入点
      read(v3);
      ++v10;
      break;
      case 1:
      v4 = v5;
      ++v10;
      ++v7;
      ++v9;
      break;
      case 2:
      v5 = a1 + v3;
      v10 += 2;
      break;
      case 3:
      v5 = v3 - LOBYTE(a1);
      v10 += 2;
      break;
      case 4:
      v5 = a1 ^ v3;
      v10 += 2;
      break;
      case 5:
      v5 = a1 * v3;
      v10 += 2;
      break;
      case 6:
      ++v10;
      break;
      case 7:
      if ( v4 != a1 )            // 最终判断 a1->v5->v4
      {
          printf("what a shame...");
          exit(0);
      }
      ++v8;
      v10 += 2;
      break;
      case 11:
      v5 = v3 - 1;
      ++v10;
      break;
      case 12:
      v5 = v3 + 1;
      ++v10;
      break;
      case 8:
      v3 = v5;
      ++v10;
      ++v6;
      break;
      default:
      continue;
    }
}
}
```
可以判断这是一个vm逆向,分析逻辑,输入点是case 10最后的判断位置是case 7。v4由v5得到,且必经过case 1,v5由opcode经过运算得到,所以我们可以模拟算法,先找出每次switch使用哪个索引值v10和整个过程中进行比较的opcode v4。
逆向算法如下

```c
#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 = {};//执行顺序
char flag; //
char v4; //
char v5; //
int m; //
int z; //
int y; //
int x; //
int i; //
int s = 0;
i = 0;
x = 0;
y = 0;
z = 0;
m = 0;
while (1)
{
    if ( i >= a2 )//继续循环
      break;
    switch ( opcode )
    {
      case 1:
      v4 = v5;
      ++i;
      ++z;
      ++x;
      break;
      case 2:
      v5 = opcode + flag;
      i += 2;
      break;
      case 3:
      v5 = flag - opcode;
      i += 2;
      break;
      case 4:
      v5 = opcode ^ flag;
      i += 2;
      break;

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

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

```
得到

```c
char order = {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};
```
确定了中间结果根据算法写逆向算法,注意:正向的算法逆回去需要从后往前来走

```c
int __cdecl vm_decode(int *opcode,int len_114)
{
char order = {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 = {}; //
int v5; //
int m; //
int z; //
int x; //
int i; //
x = 15;
z = 15;
m = 15;
for(int k=strlen(order) - 1;k>=0;k--)//从后往前
{
        i = order;
    switch ( opcode )
    {
      case 1:
                --x;
                --z;
      v5 = v4;
      break;
      case 2:
      flag = v5 - opcode;
      break;
      case 3:
      flag = v5 + opcode;
      break;
      case 4:
      flag = v5 ^ opcode;
      break;
      case 5:
      flag = v5 / opcode;
      break;
      case 6:
                break;
      case 8:
      v5 = flag[--m];
      break;
      case 11:
      flag = v5 + 1;
      break;
      case 12:
      flag = v5 - 1;
      break;
    }
}
printf("%s",flag);
return 0;
}
```
得到flag{757515121f3d478}
### 方法2
使用动态调试的方法得到opcode和算法。
回到vm算法,可以把断点断在case 7 的cmp上,这样可以根据eax和edx的值一步一步定位使用的opcode值和算法
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201026210313690.png#pic_center)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201026210344920.png#pic_center)

地址是004016E2,动态调试,使用od修改jz为jmp观察这个位置的寄存器数据为0x22
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201026210421656.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzk5MDMxMw==,size_16,color_FFFFFF,t_70#pic_center)

依次运行得到v4的值
unsigned char v4[] = {0x22, 0x3f, 0x34, 0x32, 0x72, 0x33, 0x18, 0xffffffa7, 0x31, 0xfffffff1, 0x28, 0xffffff84, 0xffffffc1, 0x1e, 0x7a};
下面分析算法,用ida从输入点进行分析,下面举第一个字符的例子:
1.首先走到case10读取字符串
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201026210955970.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzk5MDMxMw==,size_16,color_FFFFFF,t_70#pic_center)
2.走到case4执行抑或操作抑或的对象是第一个字符
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201026211056625.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzk5MDMxMw==,size_16,color_FFFFFF,t_70#pic_center)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201026211319869.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzk5MDMxMw==,size_16,color_FFFFFF,t_70#pic_center)
3.再往下到case8赋值操作
![在这里插入图片描述](https://img-blog.csdnimg.cn/2020102621142334.png#pic_center)
4.再往后到了case3,相减操作操作数是5
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201026211504991.png#pic_center)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201026211614717.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzk5MDMxMw==,size_16,color_FFFFFF,t_70#pic_center)
5.再往下到case1 case1是每个opcode的进行判段前的赋值,在这里结束第一个字符进入第二个字符
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201026211847564.png#pic_center)
这五步的计算结果就是0x22 = (flag ^ 0xa) - 5
依次动态调试分析出15个算法分别为

```python
0x22 = (flag ^ 0xa) - 5

0x3f = (flag ^ 0x20) * 3

0x34 = (flag - 2) - 1

0x32 = (flag + 1) ^ 4

0x72 = (flag * 3) - 0x21

0x33 = (flag - 1) - 1

0x18 = (flag ^ 9) - 0x20

0xffffffa7 = (flag + 0x51) ^ 0x24

0x31 = (flag +1 ) - 1

0xfffffff1 = (flag * 2) + 0x25

0x28 = (flag + 0x36) ^ 0x41

0xffffff84 = (flag + 0x20) * 1

0xffffffc1 = (flag *3) + 0x25

0x1e = (flag ^ 9) - 0x20

0x7a = (flag + 0x41) + 1
```
可以写脚本得到每个的值拼接

```python
#coding=utf-8
b=#(负数取了最低位,因此,直接取后面那部分)
flag=*15
flag=(b+5)^0x10
flag=(b//3)^0x20
flag=(b+3)
flag=(b^4)-1
flag=(b+0x21)//3
flag=b+2
flag=(b+0x20)^9
flag=(b^0x24)-0x51
flag=b
flag=(b-0x25)//2
flag=(b^0x41)-0x36
flag=(b-0x20)
flag=(b-0x25)//3
flag=(b+0x20)^9
flag=(b-1)-0x41
result = ""
for i in range(len(flag)):
      result += chr(flag)
print(result)
```
得到flag{757515121f3d478}
### 另外的思想
关键点在case 7和case1 指向了v4的来源
在case7中a1等于7,所以a1就是opcode中数值为7的下一个数字(int为四个字节)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201026213607162.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80Mzk5MDMxMw==,size_16,color_FFFFFF,t_70#pic_center)
可以直接静态分析出使用的opcode
除此之外,希望看点骚操作的师傅请移步李师傅的吾爱论坛还有小弟的论坛简单的符号执行讲解
https://www.52pojie.cn/thread-1176826-1-1.html
https://www.52pojie.cn/thread-1187493-1-1.html

小朋友呢 发表于 2020-11-2 15:46

楼主,我照着你的代码用python跑了一下,发现下面这个地方我们有点不同      case 2:
      flag = v5 - opcode; //逆运算时,这里应该是 - 1
      break;
      case 3:
      flag = v5 + opcode;
      break;
      case 4:
      flag = v5 ^ opcode;
      break;
      case 5:
      flag = v5 / opcode;
      break;

熙晓川 发表于 2020-10-29 14:45

6的看不懂。。。

panhaihaihai 发表于 2020-10-29 14:47

额,是个不错的操作我挺喜欢的。

一线码农 发表于 2020-10-29 14:50


看不懂。。。占个楼

aa77ss55dd 发表于 2020-10-29 14:53

这操作我喜欢

cydhr 发表于 2020-10-29 16:02

看不懂 但是很羡慕

莫丶小白 发表于 2020-10-29 16:05

完全看不懂。。。好尴尬。

timefy 发表于 2020-10-29 16:34

这个操作666

Baipg 发表于 2020-10-29 23:33

这才是高端操作

我是新手123 发表于 2020-10-30 09:03

这个是C语言逆向汇编?眼熟。。。
页: [1] 2 3 4 5 6 7
查看完整版本: [网鼎杯 2020 青龙组]singal(补充)