当前位置:网站首页>复盘网鼎杯Re-Signal Writeup
复盘网鼎杯Re-Signal Writeup
2022-07-06 09:33:00 【TiggerRun】
0x0 引言
比赛时没能做出来这道题,当时我使用OD进行动态调试,分析出启动程序时初始化了0x72数组,并且含有一个12分支的switch被嵌套在循环中,最终没能搞定,今天就复盘这道题。
0x1 查壳

无壳,并且编译时未启用ASLR技术,挺友好哈。
0x2 分析 main

这次学聪明了,先打开反汇编辅助插件查看伪代码(这对理解汇编逻辑有极大的帮助)。
可以看到在 main 函数中先调用了 __main()
随后复制内存到变量v4,随后v4被传入vm_operad()
容易想到,&unk_403040 是通过 __main() 得到的,随后的vm_operad()则是在计算flag,并在puts()中输出。
0x3 瞧一瞧 __main()
GO TO:0x0040176F
通过OD动态调试,F8步过发现寄存器中没有任何的变化,所以我认为之后的操作与__main()函数是无关的。
0x4 内存复制

rep movsd 根据百科上的定义,赋值edx次,即0x72次
其中esi指向的地址为0x403040,如下
再来看反汇编代码。
内存复制,从0x403040复制到变量v4,大小为0x1C8个字节。
可能存在疑问,在汇编代码中是0x72,为啥反汇编后是0x1C8呢?
其实很简单,因为数据类型为DWORD占4个字节,这从上文中数据硬编码处可以看出。0x1C8 = 0x72 * 0x4
还有一个很奇怪的现象,puts中只传入了字符串模板,而没有传入存储flag的地址…
0x5 分析 vm_operad
使用IDA自动反汇编 vm_operad(int *a1, int a2)
int * a1传入的是之前的v4首地址,a2为常数114
经过部分的调整与修改我得到了源代码如下:
#include<iostream>
#include<windows.h>
#include<string>
using namespace std;
size_t read(char *a1)
{
size_t result; // eax
printf("string:");
scanf("%s", a1);
result = strlen(a1);
if ( result != 15 )
{
puts("WRONG!\n");
exit(0);
}
return result;
}
int vm_operad(unsigned int *a1, int a2)
{
int result; // eax
char v3[100]; // [esp+13h] [ebp-E5h]
char v4[100]; // [esp+77h] [ebp-81h]
char v5; // [esp+DBh] [ebp-1Dh]
int v6; // [esp+DCh] [ebp-1Ch]
int v7; // [esp+E0h] [ebp-18h]
int v8; // [esp+E4h] [ebp-14h]
int v9; // [esp+E8h] [ebp-10h]
int v10; // [esp+ECh] [ebp-Ch]
v10 = 0;
v9 = 0;
v8 = 0;
v7 = 0;
v6 = 0;
while ( 1 )
{
result = v10;
if ( v10 >= a2 )
return result;
switch ( a1[v10] )
{
case 0:
case 9:
continue;
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] )
{
printf("what a shame...");
exit(0);
}
++v8;
v10 += 2;
break;
case 8:
v3[v6] = v5;
++v10;
++v6;
break;
case 10:
read(v3);
++v10;
break;
case 11:
v5 = v3[v9] - 1;
++v10;
break;
case 12:
v5 = v3[v9] + 1;
++v10;
break;
}
}
}
int main(){
unsigned int a[] ={
0x0A,0x04,0x10,0x08,0x03,0x05,0x01,0x04,0x20,0x08,0x05,0x03,0x01,0x03,0x02,0x08,0x0B,0x01,0x0C,0x08,0x04,0x04,0x01,0x05,0x03,0x08,0x03,0x21,0x01,0x0B,0x08,0x0B,0x01,0x04,0x09,0x08,0x03,0x20,0x01,0x02,0x51,0x08,0x04,0x24,0x01,0x0C,0x08,0x0B,0x01,0x05,0x02,0x08,0x02,0x25,0x01,0x02,0x36,0x08,0x04,0x41,0x01,0x02,0x20,0x08,0x05,0x01,0x01,0x05,0x03,0x08,0x02,0x25,0x01,0x04,0x09,0x08,0x03,0x20,0x01,0x02,0x41,0x08,0x0C,0x01,0x07,0x22,0x07,0x3F,0x07,0x34,0x07,0x32,0x07,0x72,0x07,0x33,0x7,0x18,0x7,0xffffffa7,0x7,0x31,0x7,0xfffffff1,0x7,0x28,0x7,0xffffff84,0x7,0xffffffc1,0x7,0x1e,0x7,0x7a};
cout << sizeof(a) / sizeof(a[0]);
vm_operad(&a,114);
return 0;
}
分析到这里有点懵逼,因为不知道啥是我们要的flag,我们只是拿到了源代码,而且这代码这么多的分支,感觉和我当时用OD调试没啥大的区别。
通过可视化,可以知道,如果想反向求解存在一定的困难,因为swich分支太多啦!
通过已得到的反汇编代码,v3v4 为长度100的字符数组,由于最后的输出中根本没有flag,所以猜测v3 v4可能就是我们想要的flag。


v3 变量在 case8 中被改变,在case10中被输入,输入长度为15

v4 在 case 1 中被改变,case7 中将他与a1元素进行比较,a1中的元素是已知的,为了方便,我把a1改名为array。
这里我将printf() 和 exit() 注释,并在read()中随机输入15个字符

多次执行后发现规律。
case7 中输出了v4每个元素的验证值 array[v10 + 1] 是常数,这就相当于我们已知了v4数组,而v4是在case 1中赋值为 v5,其中0xffffff不知道为啥显示不全,在OD中他的值为0xfffffff1
v4 = {
0x22,0x3f,0x34,0x32,0x72,0x33,0x18,0xffffffa7,0x31,0xf1,0x28,0xffffff84,0xffffffc1,0x1e,0x7a};
现在就得着重研究V5
0x6 调试计算 V5
#include<iostream>
#include<windows.h>
#include<string>
using namespace std;
size_t read(char *array)
{
for(int i=0;i<15;i++){
array[i] = rand() * 255;
}
return 15;
size_t result; // eax
printf("string:");
scanf("%s", array);
result = strlen(array);
if ( result != 15 )
{
puts("WRONG!\n");
exit(0);
}
return result;
}
void displayV9V10(int _case ,int v9,int v10){
return;
printf("case %d\tv9=%d\tv10=%d\n",_case,v9,v10);
}
int vm_operad(unsigned int *array, int a2)
{
int result; // eax
char v3[100]; // [esp+13h] [ebp-E5h]
char v4[100]; // [esp+77h] [ebp-81h]
char v5; // [esp+DBh] [ebp-1Dh]
int v6; // [esp+DCh] [ebp-1Ch]
int v7; // [esp+E0h] [ebp-18h]
int v8; // [esp+E4h] [ebp-14h]
int v9; // [esp+E8h] [ebp-10h]
int v10; // [esp+ECh] [ebp-Ch]
v10 = 0;
v9 = 0;
v8 = 0;
v7 = 0;
v6 = 0;
while ( 1 )
{
result = v10;
if ( v10 >= a2 ){
// cout << "v3: ";
// for(int i = 0; v3[i] != '\0';i++){
// cout << int(v3[i]) << " ";
// }
// cout << endl;
// cout << "v4: ";
// for(int i = 0; v4[i] != '\0';i++){
// cout << int(v4[i]) << " ";
// }
// cout << endl;
// cout << int(v5) << " "<<v6<<" "<<v7<<" "<<v8<<" "<<v9<<" "<<v10<<endl;
return result;
}
switch ( array[v10] )
{
case 0:
case 9:
continue;
case 1:
displayV9V10(1,v9,v10);
printf("v4[%d] = v5; \n\n\n",v7);
v4[v7] = v5;
++v10;
++v7;
++v9;
break;
case 2:
displayV9V10(2,v9,v10);
printf("v5 = array[%d] + v3[%d]; \n",v10 + 1,v9);
v5 = array[v10 + 1] + v3[v9];
v10 += 2;
break;
case 3:
displayV9V10(3,v9,v10);
printf("v5 = v3[%d] - LOBYTE(array[%d]); \n",v9 ,v10 + 1);
v5 = v3[v9] - LOBYTE(array[v10 + 1]);
v10 += 2;
break;
case 4:
displayV9V10(4,v9,v10);
printf("v5 = array[%d] ^ v3[%d]; \n",v10 + 1,v9);
v5 = array[v10 + 1] ^ v3[v9];
v10 += 2;
break;
case 5:
displayV9V10(5,v9,v10);
printf("v5 = array[%d] * v3[%d]; \n",v10 + 1,v9);
v5 = array[v10 + 1] * v3[v9];
v10 += 2;
break;
case 6:
++v10;
break;
case 7:
// 输出验证信息
cout << "case[7] " << "v4:" << v8 << "\t\t" << int(v4[v8]) << "\t\t" << int(array[v10 + 1]) <<endl << dec;
if ( v4[v8] != array[v10 + 1] )
{
// printf("what a shame...");
// exit(0);
}
++v8;
v10 += 2;
break;
case 8:
displayV9V10(8,v9,v10);
printf("v3[%d] = v5; \n",v6);
v3[v6] = v5;
++v10;
++v6;
break;
case 10:
read(v3);
++v10;
break;
case 11:
displayV9V10(11,v9,v10);
printf("v5 = v3[%d] - 1; \n",v9);
v5 = v3[v9] - 1;
++v10;
break;
case 12:
displayV9V10(12,v9,v10);
printf("v5 = v3[%d] + 1; \n",v9);
v5 = v3[v9] + 1;
++v10;
break;
}
}
}
int main(){
unsigned int a[] ={
0x0A,0x04,0x10,0x08,0x03,0x05,0x01,0x04,0x20,0x08,0x05,0x03,0x01,0x03,0x02,0x08,0x0B,0x01,0x0C,0x08,0x04,0x04,0x01,0x05,0x03,0x08,0x03,0x21,0x01,0x0B,0x08,0x0B,0x01,0x04,0x09,0x08,0x03,0x20,0x01,0x02,0x51,0x08,0x04,0x24,0x01,0x0C,0x08,0x0B,0x01,0x05,0x02,0x08,0x02,0x25,0x01,0x02,0x36,0x08,0x04,0x41,0x01,0x02,0x20,0x08,0x05,0x01,0x01,0x05,0x03,0x08,0x02,0x25,0x01,0x04,0x09,0x08,0x03,0x20,0x01,0x02,0x41,0x08,0x0C,0x01,0x07,0x22,0x07,0x3F,0x07,0x34,0x07,0x32,0x07,0x72,0x07,0x33,0x7,0x18,0x7,0xffffffa7,0x7,0x31,0x7,0xffffff,0x7,0x28,0x7,0xffffff84,0x7,0xffffffc1,0x7,0x1e,0x7,0x7a};
int test;
while(1){
unsigned int *b = new unsigned int[114];
for(int i = 0; i < 114;i++)
b[i] = a[i];
vm_operad(b,114);
delete [] b;
cout <<"input a int to try again:"<<endl;
cin >> test;
}
return 0;
}
打印运算V5的case 和 case 1
由于V4已知,所以可以求出对应的V3,以下为打印获取计算过程
v5 = array[2] ^ v3[0];
v3[0] = v5;
v5 = v3[0] - LOBYTE(array[5]);
v4[0] = v5;
v5 = array[8] ^ v3[1];
v3[1] = v5;
v5 = array[11] * v3[1];
v4[1] = v5;
v5 = v3[2] - LOBYTE(array[14]);
v3[2] = v5;
v5 = v3[2] - 1;
v4[2] = v5;
v5 = v3[3] + 1;
v3[3] = v5;
v5 = array[21] ^ v3[3];
v4[3] = v5;
v5 = array[24] * v3[4];
v3[4] = v5;
v5 = v3[4] - LOBYTE(array[27]);
v4[4] = v5;
v5 = v3[5] - 1;
v3[5] = v5;
v5 = v3[5] - 1;
v4[5] = v5;
v5 = array[34] ^ v3[6];
v3[6] = v5;
v5 = v3[6] - LOBYTE(array[37]);
v4[6] = v5;
v5 = array[40] + v3[7];
v3[7] = v5;
v5 = array[43] ^ v3[7];
v4[7] = v5;
v5 = v3[8] + 1;
v3[8] = v5;
v5 = v3[8] - 1;
v4[8] = v5;
v5 = array[50] * v3[9];
v3[9] = v5;
v5 = array[53] + v3[9];
v4[9] = v5;
v5 = array[56] + v3[10];
v3[10] = v5;
v5 = array[59] ^ v3[10];
v4[10] = v5;
v5 = array[62] + v3[11];
v3[11] = v5;
v5 = array[65] * v3[11];
v4[11] = v5;
v5 = array[68] * v3[12];
v3[12] = v5;
v5 = array[71] + v3[12];
v4[12] = v5;
v5 = array[74] ^ v3[13];
v3[13] = v5;
v5 = v3[13] - LOBYTE(array[77]);
v4[13] = v5;
v5 = array[80] + v3[14];
v3[14] = v5;
v5 = v3[14] + 1;
v4[14] = v5;
根据上面每一块内容写脚本计算V3
#include<iostream>
#include<windows.h>
#include<string>
using namespace std;
int main(){
unsigned long array[] ={
0x0A,0x04,0x10,0x08,0x03,0x05,0x01,0x04,0x20,0x08,0x05,0x03,0x01,0x03,0x02,0x08,0x0B,0x01,0x0C,0x08,0x04,0x04,0x01,0x05,0x03,0x08,0x03,0x21,0x01,0x0B,0x08,0x0B,0x01,0x04,0x09,0x08,0x03,0x20,0x01,0x02,0x51,0x08,0x04,0x24,0x01,0x0C,0x08,0x0B,0x01,0x05,0x02,0x08,0x02,0x25,0x01,0x02,0x36,0x08,0x04,0x41,0x01,0x02,0x20,0x08,0x05,0x01,0x01,0x05,0x03,0x08,0x02,0x25,0x01,0x04,0x09,0x08,0x03,0x20,0x01,0x02,0x41,0x08,0x0C,0x01,0x07,0x22,0x07,0x3F,0x07,0x34,0x07,0x32,0x07,0x72,0x07,0x33,0x7,0x18,0x7,0xffffffa7,0x7,0x31,0x7,0xfffffff1,0x7,0x28,0x7,0xffffff84,0x7,0xffffffc1,0x7,0x1e,0x7,0x7a};
unsigned long v4[] = {
0x22,0x3f,0x34,0x32,0x72,0x33,0x18,0xffffffa7,0x31,0xf1,0x28,0xffffff84,0xffffffc1,0x1e,0x7a};
unsigned long v3[15];
v3[0] = (v4[0] + LOBYTE(array[5])) ^ array[2];
v3[1] = (v4[1] / array[11]) ^ array[8];
v3[2] = (v4[2] + 1) + LOBYTE(array[14]);
v3[3] = (v4[3] ^ array[21]) - 1;
v3[4] = (v4[4] + LOBYTE(array[27])) / array[24];
v3[5] = (v4[5] + 1) + 1;
v3[6] = (v4[6] + LOBYTE(array[37])) ^ array[34];
v3[7] = (v4[7] ^ array[43]) - array[40];
v3[8] = (v4[8] + 1) - 1;
v3[9] = (v4[9] - array[53]) / array[50];
v3[10] = (v4[10] ^ array[59]) - array[56];
v3[11] = (v4[11] / array[65]) - array[62];
v3[12] = (v4[12] - array[71]) / array[68];
v3[13] = (v4[13] + LOBYTE(array[77])) ^ array[74];
v3[14] = (v4[14] - 1) - array[80];
cout << "flag{";
for(int i=0;i<15;i++){
cout <<char(v3[i]);
}
cout<<"}";
return 0;
}

得到 Flag
0x7 总结
这道题不像往常刷的题那样去破解算法本身,由于他分支复杂,所以应该有内在的规律。
边栏推荐
- How to generate six digit verification code
- TCP的三次握手和四次挥手
- DOS 功能调用
- DS18B20數字溫度計系統設計
- mysql的合计/统计函数
- Introduction to spring trick of ByteDance: senior students, senior students, senior students, and the author "brocade bag"
- Some instructions on whether to call destructor when QT window closes and application stops
- 8086 memory
- arithmetic operation
- 学习投资大师的智慧
猜你喜欢

Install docker under windows10 (through Oracle VM VirtualBox)

IDEA断点调试技巧,多张动图包教包会。

MySQL字符串函数

Flink 解析(四):恢复机制

Yao BanZhi and his team came together, and the competition experts gathered together. What fairy programming competition is this?

Eight part essay that everyone likes

Shawshank's sense of redemption

1. JVM入门介绍

Activiti directory (IV) inquiry agency / done, approved

Set up the flutter environment pit collection
随机推荐
MySQL digital function
Train 100 pictures for 1 hour, and the style of the photos changes at will. There is a demo at the end of the article | siggraph 2021
Flink 解析(五):State与State Backend
Take you hand-in-hand to do intensive learning experiments -- knock the level in detail
冯诺依曼体系结构
JVM运行时数据区之程序计数器
Shell_ 05_ operator
Interview collection library
Learn the wisdom of investment Masters
Coursera cannot play video
Ruoyi-Cloud 踩坑的BUG
Fdog series (VI): use QT to communicate between the client and the client through the server (less information, recommended Collection)
SQL tuning notes
Shell_ 01_ data processing
How to generate six digit verification code
Many papers on ByteDance have been selected into CVPR 2021, and the selected dry goods are here
Activiti目录(四)查询代办/已办、审核
Design of DS18B20 digital thermometer system
Log4j2 major vulnerabilities and Solutions
Flink源码解读(一):StreamGraph源码解读