攻防世界 easyvm
拿到程序后使用exeinfo查看信息,得知是64位程序,使用ida查看
首先是判断输入的flag长度,长度为32
其次是调用(**v3)(v3, ary1, ary2, s) 返回值为1则代表验证通过
而至于v3是什么,首先查看sub_400C1E(v3);函数
__int64 __fastcall sub_400C1E(__int64 a1)
{
__int64 result; // rax
*a1 = off_4010A8; //存放指令集地址
*(a1 + 8) = 0LL;
*(a1 + 16) = 0;
*(a1 + 17) = 0;
*(a1 + 18) = 0;
*(a1 + 20) = 0;
*(a1 + 24) = 0LL;
result = a1;
*(a1 + 32) = 0LL;
return result;
}
**v3则代表off_4010A8 也就是函数sub_400806,继续跟进函数sub_400806
因此就可以将主函数中的函数调用翻译出来sub_400806(v3, ary1, ary2, s)
a1[24] = a3的地址 a1[32] = 输入s的地址
注意**(a1+8)和*(*a1+8)的区别
而后就是逐个跟进函数,分析各个函数具体作用
ary1指的是a2 --- 0xA0,0XA1.......,0XAF数组
ary2指的是a3 ----0xF4, 0x0A, 0xF7,....0x7C数组
a1[8]=&ary1[9] a1[24]=&ary2[0]
ary1[9]=0xA9: *(a1 + 16) = *(*(a1 + 32) + *(a1 + 18));
a1[16]=flag[0];
a1[8]=a1[8]-6; ---ary1[3]
ary1[3]=0xA3; *(a1 + 32)
a1[16] = a1[16] - a1[18];
a1[8]=a1[8]+2; ---ary1[5]
ary[5]=0xA5:
a1[17]=a1[17]^a1[16];
a1[8]++; ary1[6];
ary1[6]=0xA6:
*(a1 + 16) = -51;
a1[8]-2; ary1[4];
ary1[4]:0xA4
a1[16]=a1[16]^a1[17];
a1[8]+7 ary1[11];
ary1[11]=0xAB a1[16]==ary2[a1[18]]
ary1[7] *(a1 + 17) = *(a1 + 16); 保存结果参与下一轮异或运算
ary1[14] a1[20]!=0, return 0LL; 进行判断
else -》ary1[2] a1[18]++;
ary1[13] *(a1 + 20) = *(a1 + 18) > 0x1Fu //判断是否越界
ary1[15] 前者判断是否越界,否则(a1[20]!=1)继续回滚程序
ary1[9] ............................ 循环进行
写出脚本
#include<stdio.h>
int main()
{
char flag[]="";
int ary2[] =
{
0xF4, 0x0A, 0xF7, 0x64, 0x99, 0x78, 0x9E, 0x7D, 0xEA, 0x7B,
0x9E, 0x7B, 0x9F, 0x7E, 0xEB, 0x71, 0xE8, 0x00, 0xE8, 0x07,
0x98, 0x19, 0xF4, 0x25, 0xF3, 0x21, 0xA4, 0x2F, 0xF4, 0x2F,
0xA6, 0x7C
};
int m=0;
int n=0;
for(int i=0;i<32;i++)
{
m=(ary2[i]^(-51)^n)+i;
n=ary2[i];
printf("%c",m);
/*
a1[16]=input[a1[18];
a1[16]-=a1[18];
a1[17]=a1[16] ^ a1[17];
a1[16]=-51;
a1[16]=a1[16]^a1[17];
a1[17]=a1[16];
*/
}
UNCTF{942a4115be2359ffd675fa6338ba23b6}