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 = 1
和v4 = 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++逆向,对地址的理解等等,对新手很友好,尤其是我。