本帖最后由 L15263458908 于 2020-5-12 21:53 编辑
2020网鼎杯青龙组部分逆向题
signal
方法一
打开程序,可以很明显的看到,这是一个虚拟机指令的逆向
进入关键函数进行分析
进行观察,可以发现,v4 与 a1[v10 + 1]比较 是判断条件
a1是输入的opcode, 而v4来自v5,v5由flag进行运算得到
flag--->v5---->v4
所以我们要得到v4,然后推v5,在得到flag
我们复制IDA中的代码,进行修改,来得到v4的值和执行顺序,即上图的V10
代码如下
[C++] 纯文本查看 复制代码 #include "pch.h"
#include <stdio.h>
#include <string.h>
unsigned char vmcode[] =
{
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, 0x00
};
int * a = (int *)vmcode;
int __cdecl vm_operad(int *opcode, int len_114)
{
char order[114] = {}; //顺序 即i
char flag[100]; // [esp+13h] [ebp-E5h]
char v4[100]; // [esp+77h] [ebp-81h]
char v5; // [esp+DBh] [ebp-1Dh]
int j; // [esp+DCh] [ebp-1Ch]
int m; // [esp+E0h] [ebp-18h]
int k; // [esp+E4h] [ebp-14h]
int n; // [esp+E8h] [ebp-10h]
int i; // [esp+ECh] [ebp-Ch]
int s = 0;
i = 0;
n = 0;
k = 0;
m = 0;
j = 0;
while (1)
{
if (i >= len_114) // 成功的地方
break;
switch (opcode[i])
{
case 1:
v4[m] = v5;
++i;
++m;
++n;
break;
case 2:
v5 = opcode[i + 1] + flag[n];
i += 2;
break;
case 3:
v5 = flag[n] - opcode[i + 1];
i += 2;
break;
case 4:
v5 = opcode[i + 1] ^ flag[n];
i += 2;
break;
case 5:
v5 = opcode[i + 1] * flag[n];
i += 2;
break;
case 6:
++i;
break;
case 7:
v4[k] = opcode[i + 1]; // 打印关键数据V4
printf("%#X, ", v4[k]);
++k;
i += 2;
break;
case 8:
flag[j] = v5;
++i;
++j;
break;
case 10:
scanf("%s", flag); // 输入flag //长度是15
++i;
break;
case 11:
v5 = flag[n] - 1;
++i;
break;
case 12:
v5 = flag[n] + 1;
++i;
break;
}
order[s++] = i;
}
printf("执行顺序是: ");
for (int ss = 0; ss < strlen(order); ss++) {
printf("%d, ", order[ss]);
}
return 0;
}
int main()
{
vm_operad(a, 114);
return 0;
}
得到结果
v4 = { 0X22, 0X3F, 0X34, 0X32, 0X72, 0X33, 0X18, 0XFFFFFFA7, 0X31, 0XFFFFFFF1, 0X28, 0XFFFFFF84, 0XFFFFFFC1, 0X1E, 0X7A };
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 };
再写解密函数逆推flag
[C++] 纯文本查看 复制代码 int decode(int * opcode, int len_114)
{
int i;
//获取flag 的关键数据
unsigned char v4[] = { 0X22, 0X3F, 0X34, 0X32, 0X72, 0X33, 0X18, 0XFFFFFFA7, 0X31, 0XFFFFFFF1, 0X28, 0XFFFFFF84, 0XFFFFFFC1, 0X1E, 0X7A };
//执行opcode 的索引,即执行顺序
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 flag[100] = {};
int m = 15;
int n = 15;
int j = 15;
int v5;
for (int k = strlen(order) - 1; k >= 0 ; k--)
{
i = order[k];
switch (opcode[i]) // 倒序执行
{
case 1:
--m;
--n;
v5 = v4[m];
break;
case 2:
flag[n] = v5 - char(opcode[i + 1]) ;
break;
case 3:
flag[n] = v5 + char(opcode[i + 1]);
break;
case 4:
flag[n] = (v5 ^ opcode[i + 1]) & 0XFF;
break;
case 5:
flag[n] = v5 / opcode[i + 1];
break;
case 6:
break;
case 8:
v5 = flag[--j];
break;
case 11:
flag[n] = v5 + 1;
break;
case 12:
flag[n] = v5 - 1;
break;
}
}
printf("%s", flag);
return 0;
}
得到flag是 757515121f3d478
方法二:
通过Ponce插件符号执行来一步步获取flag
关于Ponce插件下介绍及下载->https://github.com/illera88/Ponce
方法三: 使用python angr符号执行解决
代码如下
[Python] 纯文本查看 复制代码 import angr
p = angr.Project('signal.exe')
st = p.factory.entry_state()
sm = p.factory.simulation_manager(st)
sm.explore(find=0x40175E, avoid=0x4016E6)
print(sm.found[0].posix.dumps(0))
jocker
打开程序,找到main函数,F5发现无法反编译
手动修复下栈指针
问题出现在401838那里
对401833那个位置按ALT+ K
修改栈偏移
F5看伪代码
wrong是对flag进行了一个简单的加密
omg对加密的flag进行判断,得到flag为flag{fak3_alw35_sp_me!!} 提交发现错误
然后调试起来,进入下面的encrypt函数
一个简单的异或判断
写脚本打印出flag的前一半
[Python] 纯文本查看 复制代码 a = [0XE, 0XD, 9, 6, 0X13, 5, 0X58, 0X56, 0X3E, 6, 0XC, 0X3C, 0X1F, 0X57, 0X14, 0X6B, 0X57, 0X59, 0XD]
b = "hahahaha_do_you_find_me?"
c = [chr((a[i] ^ ord(b[i]))& 0xff) for i in range(len(a))]
print "".join(c)
结果为:
flag{d07abccf8a410c
最后进入finally函数
根据提示说 我隐藏了后一半部分,,前面
flag{d07abccf8a410c是19个字符,而flag是24个字符,还差5个,最后一个肯定是 “}”
于是将v7 与 } 异或得到 71
写个脚本,将37,116,112,38,58 全部异或71
[Python] 纯文本查看 复制代码 a = [37,116,112,38,58]
flag = "".join([chr(i ^ 71) for i in a])
print flag
得到flag的后半部分
b37a}
拼接,即为flag--->flag{d07abccf8a410cb37a}
------------------------------
题目链接:
链接:https://pan.baidu.com/s/1atclBURdY8msXE889elidg
提取码:gemn
|