buu-[网鼎杯 2020 青龙组]jocker

目录

堆栈不平衡:

 动态调试查看函数源码:


今天写了一道buu上的题目,学到新知识,记录一下

// positive sp value has been detected, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char Str[50]; // [esp+12h] [ebp-96h] BYREF
  char Destination[80]; // [esp+44h] [ebp-64h] BYREF
  DWORD flOldProtect; // [esp+94h] [ebp-14h] BYREF
  size_t v7; // [esp+98h] [ebp-10h]
  int i; // [esp+9Ch] [ebp-Ch]

  __main();
  puts("please input you flag:");
  if ( !VirtualProtect(encrypt, 0xC8u, 4u, &flOldProtect) )// 用于更改已分配的虚拟内存区域的访问权限。它的作用是保护或取消保护已分配的内存区域,以控制读、写或执行内存的能力。
    exit(1);
  scanf("%40s", Str);                           // 输入数据
  v7 = strlen(Str);
  if ( v7 != 24 )                               // 字符长度的判断
  {
    puts("Wrong!");
    exit(0);
  }
  strcpy(Destination, Str);                     // Str字符串复制到Destination中
  wrong(Str);                                   // 加密函数,得到假的flag
  omg(Str);                                     // 判断
  for ( i = 0; i <= 186; ++i )                  // 真正加密部分
    *(encrypt + i) ^= 0x41u;
  if ( encrypt(Destination) )
    finally(Destination);
  return 0;
}

打开附件,可以看到主函数逻辑,但是可以发现函数出现 positive sp value has been detected, the output may be wrong!字样,搜索可以发现出现此字符串是堆栈不平衡问题导致的。

堆栈不平衡:

某些函数在使用 f5 进行反编译时,会提示错误 "sp-analysis failed",导致无法正确反编译。原因可能是在代码执行中的 pop、push 操作不匹配,导致解析的时候 esp 发生错误。如用push + n条指令 + retn来实际跳转,而IDA会以为retn是函数要结束,结果它分析后发现调用栈不平衡,因此就提示sp analysis failed.

可以在Option->General->Disassembly中将Stack Point勾选上

buu-[网鼎杯 2020 青龙组]jocker_第1张图片

 操作完可以看到1处出现了一竖排的绿色数据,这就是程序的每步操作执行后对应的sp指针的偏移值。可以看到主函数在调用2处的encryptPc函数后,sp指针的偏移值发生了变化

修复堆栈不平衡:选中堆栈变化数据的上一个数据,按ALT+K,修改栈的偏移值为0

buu-[网鼎杯 2020 青龙组]jocker_第2张图片

 操作后可以看到sp指针的偏移值相同了,下面的finallyPc进行同样的操作。

修复好后,重新F5可以发现,函数没有出现sp-analysis failed的字样。

下面对main函数分析

  1. if ( !VirtualProtect(encrypt, 0xC8u, 4u, &flOldProtect) )  其中VirtualProtect函数查资料是修改虚拟内存区域的访问呢权限,还是不知道具体什么作用,好学长讲解后知道作用是取消下面encrypt函数所在区域的读写保护,为下方给函数脱壳做准备
  2. wrong函数部分貌似是加密函数,进入分析,是一段对奇数和偶数进行不同操作的加密部分
int __cdecl omg(char *a1)
{
  int v2[24]; // [esp+18h] [ebp-80h] BYREF
  int i; // [esp+78h] [ebp-20h]
  int v4; // [esp+7Ch] [ebp-1Ch]

  v4 = 1;
  qmemcpy(v2, &unk_4030C0, sizeof(v2));
  for ( i = 0; i <= 23; ++i )
  {
    if ( a1[i] != v2[i] )
      v4 = 0;
  }
  if ( v4 == 1 )
    return puts("hahahaha_do_you_find_me?");
  else
    return puts("wrong ~~ But seems a little program");
}

 3.omg函数是判断函数,对wrong函数加密后的数据进行判断

int __cdecl omg(char *a1)
{
  int v2[24]; // [esp+18h] [ebp-80h] BYREF
  int i; // [esp+78h] [ebp-20h]
  int v4; // [esp+7Ch] [ebp-1Ch]

  v4 = 1;
  qmemcpy(v2, &unk_4030C0, sizeof(v2));
  for ( i = 0; i <= 23; ++i )
  {
    if ( a1[i] != v2[i] )
      v4 = 0;
  }
  if ( v4 == 1 )
    return puts("hahahaha_do_you_find_me?");
  else
    return puts("wrong ~~ But seems a little program");
}

尝试写脚本解密

#include
#include
int main(){
	int i;
unsigned char a[] =
{
  102,  107,     99,  100, 127, 
   97,   103,  100,  59,    86, 
  107,     97,   123,  38,   59, 
   80,    99,    95,     77,   90,  
  113,   12,    55,   102,   
};
for ( i = 0; i <= 23; ++i )
  {
    if ( (i & 1) != 0 )                         // (i & 1) != 0 奇数满足条件
      a[i] += i;                               // 奇数
    else
      a[i] ^= i;
	  printf("%c",a[i]);                               // 偶数
  }
	return 0;
}
//flag{fak3_alw35_sp_me!!}

哭...得到一个假flag,不过这个flag后面还有用,接着分析

4.下面可以看到encrypt和finallypt函数,这里是真正的加密部分

 动态调试查看函数源码:

但是函数无法打开,动调试试,需要注意的是,在动调时需要输入假flag,才能跳过上面函数的判断

 在此处下断点(之前尝试在24行处下断点但是失败了.花花枯萎.....),F8步过,F7单步进入encryptPc函数

将encryptPc处的数据里连通下面的一大块数据选中,按U取消定义,再按P重新创建函数,F5,得到函数源码 ,往下找finally函数,下面的finally函数同样操作,得到源码

encryptPc函数源码:

int __cdecl encrypt(char *a1)
{
  int v2[19]; // [esp+1Ch] [ebp-6Ch] BYREF
  int v3; // [esp+68h] [ebp-20h]
  int i; // [esp+6Ch] [ebp-1Ch]

  v3 = 1;
  qmemcpy(v2, &unk_403040, sizeof(v2));
  for ( i = 0; i <= 18; ++i )
  {
    if ( (a1[i] ^ Buffer[i]) != v2[i] )
    {
      puts("wrong ~");
      v3 = 0;
      exit(0);
    }
  }
  puts("come here");
  return v3;
}

不难看出a1数组与Buffer数组(跟进得到:hahahaha_do_you_find_me?)进行异或运算,异或后的值与v2进行比较判断,其中V2的值就是unk_403040地址处的值,直接写脚本解密

#include
#include
int main(){
	int i;
unsigned char a1[] =
{
   14,  13,  9,     6,   19, 
    5,   88,   86,   62,    6,  
   12,    60,     31,  87,   20,  
  107,   87,     89,    13,  
};
char Buffer[]="hahahaha_do_you_find_me?";
for(i=0;i<19;i++){
	a1[i]^=Buffer[i];
	printf("%c",a1[i]);
}
return 0;
}
//flag{d07abccf8a410c

只有一半,剩下的部分在finally函数中,进入finally函数 分析

finaly函数:

  unsigned int v1; // eax
  char v3[9]; // [esp+13h] [ebp-15h] BYREF
  int v4; // [esp+1Ch] [ebp-Ch]

  strcpy(v3, "%tp&:");
  v1 = time(0);
  srand(v1);
  v4 = rand() % 100;
  v3[6] = 0;
  *&v3[7] = 0;
  if ( (v3[v3[5]] != a1[v3[5]]) == v4 )
    return puts("Really??? Did you find it?OMG!!!");
  else
    return puts("I hide the last part, you will not succeed!!!");
}

一直看不懂怎么搞的,只能看出来有随机数参与,没看到有啥操作,看了其他大佬的wwp知道,此处是异或运算,异或后的密文是“%tp&:”可以通过flag最后一位一定是"}"来推算异或的key

直接写脚本,解密后面的数据

#include
#include
int main(){
  int i;
  char miwen[]="%tp&:";
  int key=':'^'}';
  for(i=0;i<5;i++){
  	miwen[i]^=key;
  	printf("%c",miwen[i]);
  }
return 0;
}
//b37a}

得到完整flag

你可能感兴趣的:(逆向工程,网络安全,安全)