polar靶场reverse区简单难度题目详解
1438911687251843 发表于 江西 CTF 1032浏览 · 2024-05-29 08:44

shell

32位upx壳exe文件

脱UPX壳

丢进ida,进入main_0函数

int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  char Str1[60]; // [esp+D0h] [ebp-78h] BYREF
  char Str2[21]; // [esp+10Ch] [ebp-3Ch] BYREF
  int v6; // [esp+121h] [ebp-27h]
  int v7; // [esp+125h] [ebp-23h]
  int v8; // [esp+129h] [ebp-1Fh]
  int v9; // [esp+12Dh] [ebp-1Bh]
  int v10; // [esp+131h] [ebp-17h]
  int v11; // [esp+135h] [ebp-13h]
  int v12; // [esp+139h] [ebp-Fh]
  char v13; // [esp+13Dh] [ebp-Bh]

  __CheckForDebuggerJustMyCode(&unk_546005);
  strcpy(Str2, "flag{crack_is_funny}");
  v6 = 0;
  v7 = 0;
  v8 = 0;
  v9 = 0;
  v10 = 0;
  v11 = 0;
  v12 = 0;
  v13 = 0;
  j__memset(Str1, 0, 0x32u);
  sub_45A4C4("please input flag\r\n");
  sub_45A9A1(&unk_519E50, (char)Str1);
  if ( !j__strcmp(Str1, Str2) )
    sub_45A4C4("success\r\n");
  else
    sub_45A4C4("error\r\n");
  j__system("pause");
  return 0;
}

flag{crack_is_funny}

PE结构

查壳查不出什么东西

用010editor打开,显然PE头被修改

修改回MZ即可

ida进入main_0函数

int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  size_t i; // [esp+D0h] [ebp-48h]
  char Str[39]; // [esp+DCh] [ebp-3Ch] BYREF
  int v6; // [esp+103h] [ebp-15h]
  int v7; // [esp+107h] [ebp-11h]
  __int16 v8; // [esp+10Bh] [ebp-Dh]
  char v9; // [esp+10Dh] [ebp-Bh]

  __CheckForDebuggerJustMyCode(&unk_41A009);
  strcpy(Str, "eobdx55:;4bgg30`:;b;e3`b`42f7`be`1b5b~");
  v6 = 0;
  v7 = 0;
  v8 = 0;
  v9 = 0;
  for ( i = 0; i < j_strlen(Str); ++i )
    sub_411050("%c", Str[i] ^ 3);
  system("pause");
  return 0;
}

str每一位异或3

解密脚本

enc = 'eobdx55:;4bgg30`:;b;e3`b`42f7`be`1b5b~'
flag = ''
for i in enc:
    flag += chr(ord(i)^3)
print(flag)
# flag{66987add03c98a8f0cac71e4cafc2a6a}

拼接

32位无壳exe文件

丢进ida

int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  char v4; // [esp+0h] [ebp-160h]
  char v5; // [esp+0h] [ebp-160h]
  char Str1[60]; // [esp+D0h] [ebp-90h] BYREF
  char Destination[60]; // [esp+10Ch] [ebp-54h] BYREF
  char *v8; // [esp+148h] [ebp-18h]
  char *Source; // [esp+154h] [ebp-Ch]

  __CheckForDebuggerJustMyCode(&unk_41B009);
  Source = "flag{";
  v8 = "03ff6cf238c5cd8e7b4ee1e9567ad5a4}";
  j_memset(Destination, 0, 0x32u);
  j_memset(Str1, 0, 0x32u);
  j_strcpy(Destination, "flag{");
  j_strcat(Destination, v8);
  sub_411050("please input flag\r\n", v4);
  sub_4110AA("%s", (char)Str1);
  if ( !j_strcmp(Str1, Destination) )
    sub_411050("congratulation\r\n", v5);
  else
    sub_411050("error\r\n", v5);
  return 0;
}

把两部分拼起来即可

flag{03ff6cf238c5cd8e7b4ee1e9567ad5a4}

加加减减

32位无壳exe文件

丢进ida,进入main_0函数

int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  char v4; // [esp+0h] [ebp-154h]
  char v5; // [esp+0h] [ebp-154h]
  size_t i; // [esp+D0h] [ebp-84h]
  char Str[60]; // [esp+DCh] [ebp-78h] BYREF
  char Str2[39]; // [esp+118h] [ebp-3Ch] BYREF
  int v9; // [esp+13Fh] [ebp-15h]
  int v10; // [esp+143h] [ebp-11h]
  __int16 v11; // [esp+147h] [ebp-Dh]
  char v12; // [esp+149h] [ebp-Bh]

  __CheckForDebuggerJustMyCode(&unk_41C009);
  strcpy(Str2, "ek`fz5123086/ce7ac7/`4a81`6/87b`b28a5|");
  v9 = 0;
  v10 = 0;
  v11 = 0;
  v12 = 0;
  j_memset(Str, 0, 0x32u);
  sub_41104B("input\r\n", v4);
  sub_4110AA("%s", (char)Str);
  for ( i = 0; i < j_strlen(Str); ++i )
    --Str[i];
  if ( !j_strcmp(Str, Str2) )
    sub_41104B("success\r\n", v5);
  else
    sub_41104B("sorry\r\n", v5);
  return 0;
}

分析逻辑:

输入的str的每一位减1,得到的值与str2比较,如果相等,则输出success,否则输出sorry

解密脚本

enc = 'ek`fz5123086/ce7ac7/`4a81`6/87b`b28a5|'
str = ''
for i in enc:
    str += chr(ord(i)+1)
print(str)
# flag{62341970df8bd80a5b92a7098cac39b6}

康师傅

32位无壳exe

丢进ida,进入main_0函数

int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  size_t i; // [esp+D0h] [ebp-84h]
  char Str2[60]; // [esp+DCh] [ebp-78h] BYREF
  char Str1[39]; // [esp+118h] [ebp-3Ch] BYREF
  int v7; // [esp+13Fh] [ebp-15h]
  int v8; // [esp+143h] [ebp-11h]
  __int16 v9; // [esp+147h] [ebp-Dh]
  char v10; // [esp+149h] [ebp-Bh]

  __CheckForDebuggerJustMyCode(&unk_53B006);
  strcpy(Str1, "oehnr8>?;<?:9k>09;hj00o>:<o?8lh;8h9l;t");
  v7 = 0;
  v8 = 0;
  v9 = 0;
  v10 = 0;
  j__memset(Str2, 0, 0x32u);
  sub_45748D(&unk_510E84);
  sub_457929(&unk_510E50, (char)Str2);
  for ( i = 0; i < j__strlen(Str2); ++i )
    Str2[i] ^= 9u;
  if ( !j__strcmp(Str1, Str2) )
    sub_45748D("right");
  else
    sub_45748D("error");
  return 0;
}

分析逻辑:

str2为输入,即flag,str2的每一项都与9异或,把得到的值与str1比较,若相等,输出right,否则输出error

解密脚本

enc = 'oehnr8>?;<?:9k>09;hj00o>:<o?8lh;8h9l;t'
str = ''
for i in enc:
    str += chr(ord(i) ^ 9)
print(str)
# flag{17625630b7902ac99f735f61ea21a0e2}

另辟蹊径

CE附加进程

扫出次数的地址

改成1

再点击一次即可

flag{60983973c2ab87436914d71000e4b4e4}

use_jadx_open_it

apk文件,用jadx打开,进入MainActivity

flag{go_to_study_android}

re2

64位无壳elf文件

ida打开,进入main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  const char *v3; // rax
  const char *v4; // rax
  char v7[48]; // [rsp+0h] [rbp-170h] BYREF
  __int16 v8; // [rsp+30h] [rbp-140h]
  char v9[48]; // [rsp+40h] [rbp-130h] BYREF
  __int16 v10; // [rsp+70h] [rbp-100h]
  char dest[96]; // [rsp+80h] [rbp-F0h] BYREF
  int v12; // [rsp+E0h] [rbp-90h]
  char s2[96]; // [rsp+F0h] [rbp-80h] BYREF
  int v14; // [rsp+150h] [rbp-20h]
  unsigned __int64 v15; // [rsp+158h] [rbp-18h]

  v15 = __readfsqword(0x28u);
  memset(v7, 0, sizeof(v7));
  v8 = 0;
  memset(v9, 0, sizeof(v9));
  v10 = 0;
  memset(dest, 0, sizeof(dest));
  v12 = 0;
  memset(s2, 0, sizeof(s2));
  v14 = 0;
  strcat(v7, "flag{e10adc3949ba59abbe56e057f20f883e}");
  v3 = (const char *)base64_encode(v7);
  strcpy(dest, v3);
  puts("Please enter flag");
  __isoc99_scanf("%s", v9);
  v4 = (const char *)base64_encode(v9);
  strcpy(s2, v4);
  if ( !strcmp(dest, s2) )
    printf("flag = %s\n", v7);
  else
    puts("flag error!!!");
  return __readfsqword(0x28u) ^ v15;
}

直接出现flag

flag{e10adc3949ba59abbe56e057f20f883e}

layout

apk文件,jadx打开

出现flag,但是发现是错误的

想到题目名字是layout,到layout文件看看

找到flag

flag{andoird_re}

Why32

64位无壳exe

扔进ida,进入start函数

void __cdecl Start()
{
  Input();
  if ( Judge() == 1 )
    puts("Wrong input");
  else
    Do();
}

Input函数为输入并计算长度

void __cdecl Input()
{
  printf(&Format);
  scanf("%s", str);
  nLen = strlen(str);
}

Do函数为加密部分

void __cdecl Do()
{
  char cAry[34]; // [rsp+20h] [rbp-30h] BYREF
  int flag; // [rsp+48h] [rbp-8h]
  int i; // [rsp+4Ch] [rbp-4h]

  strcpy(cAry, "2gfe8c8c4cde574f7:c6c;:;3;7;2gf:");
  cAry[33] = 0;
  flag = 0;
  for ( i = 0; i <= 31; ++i )
  {
    if ( str[i] != cAry[i] - 2 )
    {
      puts("you are wrong");
      flag = 1;
      break;
    }
  }
  if ( !flag )
    puts("Half right,think more");
}

分析逻辑,输入的str的ascll码每一项减2

解密脚本

enc = '2gfe8c8c4cde574f7:c6c;:;3;7;2gf:'
str = ''
for i in enc:
    str += chr(ord(i)-2)
print(str)
# 0edc6a6a2abc352d58a4a98919590ed8

但是这里输出的是"Half right,think more",事实也证明这个答案是错误的

由于数据为32位,且只有数字和字母,猜测经过了md5加密,到在线网站解密

flag{F1laig}

? 64

64位无壳exe

ida打开,进入main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 v4[2]; // [rsp+20h] [rbp-40h]
  int v5; // [rsp+30h] [rbp-30h]
  _WORD v6[12]; // [rsp+40h] [rbp-20h] BYREF
  int v7; // [rsp+58h] [rbp-8h]
  int i; // [rsp+5Ch] [rbp-4h]

  _main(argc, argv, envp);
  strcpy((char *)v6, "YlwAY08ob1gkTlT<");
  HIBYTE(v6[8]) = 0;
  v6[9] = 0;
  v4[0] = 0i64;
  v4[1] = 0i64;
  v5 = 0;
  v7 = www((char *)v6);
  for ( i = 0; i < v7; ++i )
  {
    *((_BYTE *)v4 + i) = *((_BYTE *)v6 + i) + 1;
    putchar(*((char *)v4 + i));
  }
  return 0;
}
__int64 __fastcall www(char *a1)
{
  if ( *a1 )
    return www(a1 + 1) + 1;
  else
    return 0i64;
}

分析:

www定义了一个递归函数,作用是计算a1的长度

enc = 'YlwAY08ob1gkTlT<'
str = ''
for i in enc:
    str += chr(ord(i)+1)
print(str)
# ZmxBZ19pc2hlUmU=

这看起来像base64加密后得到的

import base64
enc = 'ZmxBZ19pc2hlUmU='
print(base64.b64decode(enc))
# b'flAg_isheRe'

这个答案仍然不对,显然已经无法继续下去了。由于题目Why32这题使用了MD5加密,猜测这题也需要

flag{5d15777a411724ee5d029caca1ca7298}

Sign Up

64位无壳exe

ida打开,进入main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  _main();
  Input_Data();
  Check_Data();
  return 0;
}

看到大量中文编码,在选项-常规-字串-编码-插入gbk编码

int Input_Data(void)
{
  puts("请输入账号和密码:");
  gets((__int64)num);
  gets((__int64)password);
  puts(num);
  return puts(password);
}
int Check_Data(void)
{
  int j; // [rsp+20h] [rbp-10h]
  int i; // [rsp+24h] [rbp-Ch]
  int v3; // [rsp+28h] [rbp-8h]
  int v4; // [rsp+2Ch] [rbp-4h]

  v4 = 1;
  v3 = 1;
  for ( i = 0; i <= 6; ++i )
  {
    if ( num[i] + 1 != key_num[i] )
      v4 = 0;
  }
  for ( j = 0; j <= 3; ++j )
  {
    if ( password[j] + 2 != key_password[j] )
      v3 = 0;
  }
  if ( !v4 && !v3 )
    return puts("账号密码错误:");
  if ( !v4 )
    return puts("账号错误:");
  if ( v3 )
    return printf("密码正确!");
  return puts("密码错误:");
}

分析:

在Input_Data函数输入num和password,在Check_Data函数如果想输出"密码正确!",需要同时满足v3 = 1v4 = 1

num为key_num每一位ascll码减1,password为key_password每一位ascll码减2

这里要注意mun遍历的是前7位,最后2位没有遍历,所以不需要改变

key_num = '1921681'
key_password = 'root'
num = ''.join(chr(ord(i)-1) for i in key_num)
password = ''.join(chr(ord(j)-2) for j in key_password)
print(num,'09',password,sep='')
# 081057009pmmr

转MD5

flag{aa07caa2ff9e5b774bfca3b1f20c3ea0}

easyre1

32位无壳elf文件

shift+F12进入字符串,发现一串数字,双击进入

进入main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __isoc99_scanf("%s", flag);
  enkey();
  reduce();
  check();
  return 0;
}

enkey函数,把flag的每一项和key的对应项异或

因为这里的0x804A0A0代表地址,正好是key的地址

int enkey()
{
  int i; // [esp+Ch] [ebp-4h]

  for ( i = 0; i <= 31; ++i )
    *(_BYTE *)(i + 0x804A0A0) ^= *(_BYTE *)(i + 0x804A040);
  return 0;
}

reduce函数,这里是把flag的每一项的ascll码减一,因为这里的0x804A0A0就是flag的地址

int reduce()
{
  int i; // [esp+Ch] [ebp-Ch]

  for ( i = 0; i <= 30; ++i )
    --*(_BYTE *)(i + 0x804A0A0);
  putchar(10);
  return 0;
}

check函数,验证加密后的flag

int check()
{
  if ( !strcmp(flag, "d^XSAozQPU^WOBU[VQOATZSE@AZZVOF") )
    return puts("you are right");
  else
    return puts("no no no");
}

解密脚本

enc = 'd^XSAozQPU^WOBU[VQOATZSE@AZZVOF'
key = '5055045045055045055045055045055'
flag=''
for i,j in zip(enc,key):
    i = ord(i) + 1
    flag += chr(i ^ ord(j))
print(flag)
# PolarDNbecomesbiggerandstronger

flag{PolarDNbecomesbiggerandstronger}

babyRE

64位无壳exe文件

ida进入main函数

c++写的程序,if判断flag

int __cdecl main(int argc, const char **argv, const char **envp)
{
  std::ostream *v3; // rax
  char v5[48]; // [rsp+20h] [rbp-20h] BYREF

  _main();
  endoce();
  std::string::basic_string(v5);
  std::operator>><char>(refptr__ZSt3cin);
  if ( (unsigned __int8)std::operator==<char>(v5, &flag[abi:cxx11]) )
    v3 = (std::ostream *)std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "Ok");
  else
    v3 = (std::ostream *)std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "Err");
  refptr__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(v3);
  std::string::~string(v5);
  return 0;
}

endoce函数是把flag每一位加2

__int64 endoce(void)
{
  __int64 result; // rax
  __int64 v1; // [rsp+20h] [rbp-20h] BYREF
  __int64 v2; // [rsp+28h] [rbp-18h] BYREF
  _BYTE *v3; // [rsp+30h] [rbp-10h]
  void *v4; // [rsp+38h] [rbp-8h]

  v4 = &flag[abi:cxx11];
  v2 = std::string::begin(&flag[abi:cxx11]);
  v1 = std::string::end(&flag[abi:cxx11]);
  while ( 1 )
  {
    result = __gnu_cxx::operator!=<char *,std::string>(&v2, &v1);
    if ( !(_BYTE)result )
      break;
    v3 = (_BYTE *)__gnu_cxx::__normal_iterator<char *,std::string>::operator*(&v2);
    *v3 += 2;
    __gnu_cxx::__normal_iterator<char *,std::string>::operator++(&v2);
  }
  return result;
}

但是看不出来flag被初始化成了什么,也不知道flag有几位,这里选择动调

在这里下断点,等运行到这里F7步入,因为在这之前没有看到对flag的初始化,然后随便输入

size函数计算flag的长度,size对应地址加8就是flag的长度

__int64 __fastcall std::string::size(__int64 a1)
{
  return *(a1 + 8);
}

运行到这里时,双击a2

长度为0x10即16位

重新开启动调,输入16个1,运行到这里可以查看v4的值,运行到下一步可以看到v5的值

这里的逻辑显然是比较v4和v5的值,v3是长度,类似C语言的strncmp(v4,v5,v3)

由于到这一步我们的输入已经过了加密步骤,所以不需要对flag解密

flag{cufhiexdpolivnqr}

C^

32位无壳elf文件

ida进入main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [esp-Ah] [ebp-60h]
  int v5; // [esp-6h] [ebp-5Ch]
  _BYTE v6[48]; // [esp-2h] [ebp-58h] BYREF
  int v7; // [esp+2Eh] [ebp-28h]
  int v8; // [esp+32h] [ebp-24h]
  int v9; // [esp+36h] [ebp-20h]
  int v10; // [esp+3Ah] [ebp-1Ch]
  int *p_argc; // [esp+4Ah] [ebp-Ch]

  p_argc = &argc;
  init();
  *&v6[2] = 0;
  v7 = 0;
  memset(&v6[4], 0, 4 * (((&v6[2] - &v6[4] + 50) & 0xFFFFFFFC) >> 2));
  v10 = 0;
  v9 = 0;
  puts("Please enter flag\n");
  (__isoc99_scanf)("%s", &v6[2], v4, v5, *v6);
  v8 = strlen(&v6[2]);
  fun1(&v6[2], v8);
  if ( check(&v6[2], v8) )
    puts("RIGHT");
  else
    printf("Try again");
  return 0;
}

fun1函数对v6加密,异或1

int __cdecl fun1(int a1, int a2)
{
  int i; // [esp+Ch] [ebp-4h]

  for ( i = 0; i < a2; ++i )
    *(i + a1) ^= 1u;
  return 0;
}

check函数验证s与加密后的v6是否相等

int __cdecl check(int a1, int a2)
{
  char s[10]; // [esp+8h] [ebp-20h] BYREF
  __int16 v4; // [esp+12h] [ebp-16h]
  int v5; // [esp+14h] [ebp-14h]
  int v6; // [esp+18h] [ebp-10h]
  int i; // [esp+1Ch] [ebp-Ch]

  strcpy(s, "shfiu777");
  s[9] = 0;
  v4 = 0;
  v5 = 0;
  v6 = 0;
  if ( strlen(s) != a2 )
    return 0;
  for ( i = 0; i < a2; ++i )
  {
    if ( *(i + a1) != s[i] )
      return 0;
  }
  return 1;
}

写脚本解密

enc = 'shfiu777'
flag = ''
for i in enc:
    flag += chr(ord(i)^1)
print(flag)
# right666

md5加密

flag{f9239748ca798af5d838ac8699bb5d3d}

一个flag劈三瓣儿

32位无壳elf文件

int flag()
{
  printf("flag{HaiZI233");
  printf("N145wuD!");
  return printf("le112@666}");
}

拼起来即可

flag{HaiZI233N145wuD!le112@666}

总结

polar靶场的re简单难度部分非常基础,其中涉及大量基本计算,也包含了少量较难的知识,比如c++逆向,对地址的理解等等,对新手很友好,尤其是我。

0 条评论
某人
表情
可输入 255
目录