本帖最后由 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++] 纯文本查看 复制代码 001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | #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] = {};
char flag[100];
char v4[100];
char v5;
int j;
int m;
int k;
int n;
int i;
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];
printf ( "%#X, " , v4[k]);
++k;
i += 2;
break ;
case 8:
flag[j] = v5;
++i;
++j;
break ;
case 10:
scanf ( "%s" , flag);
++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++] 纯文本查看 复制代码 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | int decode( int * opcode, int len_114)
{
int i;
unsigned char v4[] = { 0X22, 0X3F, 0X34, 0X32, 0X72, 0X33, 0X18, 0XFFFFFFA7, 0X31, 0XFFFFFFF1, 0X28, 0XFFFFFF84, 0XFFFFFFC1, 0X1E, 0X7A };
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] 纯文本查看 复制代码 1 2 3 4 5 6 | 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] 纯文本查看 复制代码 1 2 3 4 | 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] 纯文本查看 复制代码 1 2 3 | 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
|