easyre
下载附件,无壳的64位程序
使用IDA打开,进入main函数
int __fastcall main(int argc, const char **argv, const char **envp)
{
int b; // [rsp+28h] [rbp-8h] BYREF
int a; // [rsp+2Ch] [rbp-4h] BYREF
_main();
scanf("%d%d", &a, &b);
if ( a == b )
printf("flag{this_Is_a_EaSyRe}");
else
printf("sorry,you can't get flag");
return 0;
}
直接就有flag
reverse1
64位无壳,IDA打开
进入main函数
int __fastcall main_0(int argc, const char **argv, const char **envp)
{
char *v3; // rdi
__int64 i; // rcx
size_t v5; // rax
char v7; // [rsp+0h] [rbp-20h] BYREF
int j; // [rsp+24h] [rbp+4h]
char Str1[224]; // [rsp+48h] [rbp+28h] BYREF
__int64 v10; // [rsp+128h] [rbp+108h]
v3 = &v7;
for ( i = 82i64; i; --i )
{
*(_DWORD *)v3 = -858993460;
v3 += 4;
}
for ( j = 0; ; ++j )
{
v10 = j;
if ( j > j_strlen(Str2) )
break;
if ( Str2[j] == 'o' )
Str2[j] = '0';
}
sub_1400111D1("input the flag:");
sub_14001128F("%20s", Str1);
v5 = j_strlen(Str2);
if ( !strncmp(Str1, Str2, v5) )
sub_1400111D1("this is the right flag!\n");
else
sub_1400111D1("wrong flag\n");
return 0;
}
查看Str2
Str2 db '{hello_world}',0
将Str2中的o替换成0:flag{hell0_w0rld}
reverse2
64位的ELF文件
用IDA打开,进入main函数
int __fastcall main(int argc, const char **argv, const char **envp)
{
int stat_loc; // [rsp+4h] [rbp-3Ch] BYREF
int i; // [rsp+8h] [rbp-38h]
__pid_t pid; // [rsp+Ch] [rbp-34h]
char s2[24]; // [rsp+10h] [rbp-30h] BYREF
unsigned __int64 v8; // [rsp+28h] [rbp-18h]
v8 = __readfsqword(0x28u);
pid = fork();
if ( pid )
{
waitpid(pid, &stat_loc, 0);
}
else
{
for ( i = 0; i <= strlen(&flag); ++i )
{
if ( *(&flag + i) == 'i' || *(&flag + i) == 'r' )
*(&flag + i) = '1';
}
}
printf("input the flag:");
__isoc99_scanf("%20s", s2);
if ( !strcmp(&flag, s2) )
return puts("this is the right flag!");
else
return puts("wrong flag!");
}
查看if比较处的&flag:
.data:0000000000601080 flag db '{' ; DATA XREF: main+34↑r
.data:0000000000601080 ; main+44↑r ...
.data:0000000000601081 aHackingForFun db 'hacking_for_fun}',0
将flag中的‘i’和‘r’替换成1:flag{hack1ng_fo1_fun}
内涵的软件
下载附件,32位无壳exe,进入IDA后查找字符串+交叉引用直接就能看见flag
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
char v4[4]; // [esp+4Ch] [ebp-Ch] BYREF
const char *v5; // [esp+50h] [ebp-8h]
int v6; // [esp+54h] [ebp-4h]
v6 = 5;
v5 = "DBAPP{49d3c93df25caad81232130f3d2ebfad}";
while ( v6 >= 0 )
{
printf(&byte_4250EC, v6);
sub_40100A();
--v6;
}
printf(asc_425088);
v4[0] = 1;
scanf("%c", v4);
if ( v4[0] == 'Y' )
{
printf(aOd);
return sub_40100A();
}
else
{
if ( v4[0] == 'N' )
printf(&byte_425034);
else
printf(&byte_42501C);
return sub_40100A();
}
}
新年快乐
下载附件,发现是一个32位带有UPX壳的程序,使用工具脱壳后进入IDA
int __cdecl main(int argc, const char **argv, const char **envp)
{
char Str2[14]; // [esp+12h] [ebp-3Ah] BYREF
char Str1[44]; // [esp+20h] [ebp-2Ch] BYREF
__main();
strcpy(Str2, "HappyNewYear!");
memset(Str1, 0, 32);
printf("please input the true flag:");
scanf("%s", Str1);
if ( !strncmp(Str1, Str2, strlen(Str2)) )
return puts("this is true flag!");
else
return puts("wrong!");
}
Str2就是我们的flag:flag{HappyNewYear!}
xor
64位无壳,从名字就可以看出这题涉及到异或运算,用IDA打开
int __fastcall main(int argc, const char **argv, const char **envp)
{
int i; // [rsp+2Ch] [rbp-124h]
char __b[264]; // [rsp+40h] [rbp-110h] BYREF
memset(__b, 0, 0x100uLL);
printf("Input your flag:\n");
get_line(__b, 256LL);
if ( strlen(__b) != 33 )
goto LABEL_7;
for ( i = 1; i < 33; ++i )
__b[i] ^= __b[i - 1];
if ( !strncmp(__b, global, 0x21uLL) )
printf("Success");
else
LABEL_7:
printf("Failed");
return 0;
不难看出我们的输入从第二位开始每一位都与前一位做异或运算,然后与global作比较
查看global的值:
__cstring:0000000100000F6E aFKWOXZUPFVMDGH db 'f',0Ah ; DATA XREF: __data:_global↓o
__cstring:0000000100000F70 db 'k',0Ch,'w&O.@',11h,'x',0Dh,'Z;U',11h,'p',19h,'F',1Fh,'v"M#D',0Eh,'g'
__cstring:0000000100000F89 db 6,'h',0Fh,'G2O',0
写出解密脚本:
num=[0x66, 0x0A, 0x6B, 0x0C, 0x77, 0x26, 0x4F, 0x2E, 0x40, 0x11,
0x78, 0x0D, 0x5A, 0x3B, 0x55, 0x11, 0x70, 0x19, 0x46, 0x1F,
0x76, 0x22, 0x4D, 0x23, 0x44, 0x0E, 0x67, 0x06, 0x68, 0x0F,
0x47, 0x32, 0x4F,]
flag="f"
for i in range(1,33):
flag+=chr(num[i]^num[i-1])
print(flag)
flag{QianQiuWanDai_YiTongJiangHu}
reverse3
32位无壳,IDA打开
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
size_t v3; // eax
const char *v4; // eax
size_t v5; // eax
char v7; // [esp+0h] [ebp-188h]
char v8; // [esp+0h] [ebp-188h]
signed int j; // [esp+DCh] [ebp-ACh]
int i; // [esp+E8h] [ebp-A0h]
signed int v11; // [esp+E8h] [ebp-A0h]
char Destination[108]; // [esp+F4h] [ebp-94h] BYREF
char Str[28]; // [esp+160h] [ebp-28h] BYREF
char v14[8]; // [esp+17Ch] [ebp-Ch] BYREF
for ( i = 0; i < 100; ++i )
{
if ( (unsigned int)i >= 0x64 )
j____report_rangecheckfailure();
Destination[i] = 0; // 初始化
}
sub_41132F("please enter the flag:", v7);
sub_411375("%20s", (char)Str);
v3 = j_strlen(Str);
v4 = (const char *)sub_4110BE(Str, v3, v14); // 这里是个Base64加密
strncpy(Destination, v4, 0x28u);
v11 = j_strlen(Destination);
for ( j = 0; j < v11; ++j )
Destination[j] += j;
v5 = j_strlen(Destination);
if ( !strncmp(Destination, Str2, v5) ) // 比较结果
sub_41132F("rigth flag!\n", v8);
else
sub_41132F("wrong flag!\n", v8);
return 0;
}
发现sub_4110BE函数是个Base64加密,同时查找字符串
很明显能看见base64input和base64码表,于是查看Str2的值
.data:0041A034 Str2 db 'e3nifIH9b_C@n@dH',0 ; DATA XREF: _main_0+142↑o
写出求解脚本:
import base64
# 要解码的 Base64 编码文本
encoded_text = ""
Str="e3nifIH9b_C@n@dH"
for i in range(16):
encoded_text+=chr(ord(Str[i])-i)
# 使用 base64 模块进行解码
decoded_bytes = base64.b64decode(encoded_text)
# 打印解码结果
print(decoded_bytes)
#b'{i_l0ve_you}'
flag{i_l0ve_you}
helloword
下载附件,是个.apk文件,也就是我们常说的安卓,使用JEB打开
进到主函数里面,还可以按tab键查看代码:
package com.example.helloword;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends ActionBarActivity {
public MainActivity() {
super();
}
protected void onCreate(Bundle arg5) {
super.onCreate(arg5);
this.setContentView(0x7F030018);
"flag{7631a988259a00816deda84afb29430a}".compareTo("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
}
public boolean onCreateOptionsMenu(Menu arg3) {
this.getMenuInflater().inflate(0x7F0C0000, arg3);
return 1;
}
public boolean onOptionsItemSelected(MenuItem arg3) {
boolean v1 = arg3.getItemId() == 0x7F05003C ? true : super.onOptionsItemSelected(arg3);
return v1;
}
}
flag直接就出来了
不一样的flag
题目已经给了提示,下载附件看看
无壳,32位,用IDA打开
main函数如下
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
_BYTE v3[29]; // [esp+17h] [ebp-35h] BYREF
int v4; // [esp+34h] [ebp-18h]
int v5; // [esp+38h] [ebp-14h] BYREF
int i; // [esp+3Ch] [ebp-10h]
_BYTE v7[12]; // [esp+40h] [ebp-Ch] BYREF
__main();
v3[26] = 0; // 初始化
*(_WORD *)&v3[27] = 0;
v4 = 0;
strcpy(v3, "*11110100001010000101111#"); // 地图
while ( 1 )
{
puts("you can choose one action to execute");// 分别代表上下左右
puts("1 up");
puts("2 down");
puts("3 left");
printf("4 right\n:");
scanf("%d", &v5);
if ( v5 == 2 )
{
++*(_DWORD *)&v3[25]; // [5][5]的二维数组,用于构建地图
}
else if ( v5 > 2 )
{
if ( v5 == 3 )
{
--v4;
}
else
{
if ( v5 != 4 )
LABEL_13:
exit(1);
++v4;
}
}
else
{
if ( v5 != 1 )
goto LABEL_13;
--*(_DWORD *)&v3[25];
}
for ( i = 0; i <= 1; ++i )
{
if ( *(_DWORD *)&v3[4 * i + 25] >= 5u )
exit(1);
}
if ( v7[5 * *(_DWORD *)&v3[25] - 41 + v4] == '1' )// 碰到1时就结束
exit(1);
if ( v7[5 * *(_DWORD *)&v3[25] - 41 + v4] == '#' )// 结束于#
{
puts("\nok, the order you enter is the flag!");
exit(0);
}
}
}
不难看出,这是一个迷宫题,将 *11110100001010000101111#
变成一个5*5
的迷宫,如下:
*1111
01000
01010
00010
1111#
碰到1就停止(即走0的路径),碰到#就得出flag(即#为出口处)
得到下下下右右上上右右下下下,即222441144222
所以flag就是flag{222441144222}
SimpleRev
64位ELF,用IDA打开
主函数:
int __fastcall __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // eax
char v4; // [rsp+Fh] [rbp-1h]
while ( 1 )
{
while ( 1 )
{
printf("Welcome to CTF game!\nPlease input d/D to start or input q/Q to quit this program: ");
v4 = getchar();
if ( v4 != 'd' && v4 != 'D' )
break;
Decry("Welcome to CTF game!\nPlease input d/D to start or input q/Q to quit this program: ", argv);
}
if ( v4 == 'q' || v4 == 'Q' )
Exit("Welcome to CTF game!\nPlease input d/D to start or input q/Q to quit this program: ", argv);
puts("Input fault format!");
v3 = getchar();
putchar(v3);
}
}
主函数没什么好看的,主要看主函数中引用的Decry函数
进入Decry函数:
我们可以看见src和v9,这里需要注意x86的大小端序问题,转为字符串之后应该反转过来
下面是整理过后且加过注释的Decry函数:
unsigned __int64 Decry()
{
char v1; // [rsp+Fh] [rbp-51h]
int v2; // [rsp+10h] [rbp-50h]
int v3; // [rsp+14h] [rbp-4Ch]
int i; // [rsp+18h] [rbp-48h]
int v5; // [rsp+1Ch] [rbp-44h]
char src[8]; // [rsp+20h] [rbp-40h] BYREF
__int64 v7; // [rsp+28h] [rbp-38h]
int v8; // [rsp+30h] [rbp-30h]
__int64 v9[2]; // [rsp+40h] [rbp-20h] BYREF
int v10; // [rsp+50h] [rbp-10h]
unsigned __int64 v11; // [rsp+58h] [rbp-8h]
v11 = __readfsqword(0x28u);
*(_QWORD *)src = 'SLCDN'; // NDCLS
v7 = 0LL;
v8 = 0;
v9[0] = 'wodah'; // hadow
v9[1] = 0LL;
v10 = 0;
text = (char *)join(key3, v9); // key3="kills" text=key3+v9
strcpy(key, key1); // key1="ADSFK"
strcat(key, src); // key=key1+src
v2 = 0;
v3 = 0;
getchar();
v5 = strlen(key);
for ( i = 0; i < v5; ++i )
{
if ( key[v3 % v5] > '@' && key[v3 % v5] <= 'Z' )// 如果key[]为大写字母
key[i] = key[v3 % v5] + 32; // 将key中的大写字母变为小写字母
++v3;
}
printf("Please input your flag:");
while ( 1 )
{
v1 = getchar();
if ( v1 == 10 )
break;
if ( v1 == 32 )
{
++v2;
}
else
{
if ( v1 <= '`' || v1 > 'z' )
{
if ( v1 > '@' && v1 <= 'Z' )
{
str2[v2] = (v1 - 39 - key[v3 % v5] + 97) % 26 + 97;
++v3;
}
}
else
{
str2[v2] = (v1 - 39 - key[v3 % v5] + 97) % 26 + 97;// 主要加密过程
++v3;
}
if ( !(v3 % v5) )
putchar(32);
++v2;
}
}
if ( !strcmp(text, str2) ) // 比较text于str2,其中text我们已经知道,str2可以由key得出
puts("Congratulation!\n");
else
puts("Try again!\n");
return __readfsqword(0x28u) ^ v11;
}
写出爆破脚本:
#include<stdio.h>
int main()
{
char key[] = "adsfkndcls";
char text[] = "killshadow";
int i;
int v3=10;//长度
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 128; j++)
{
if (j < 'A' || j > 'z' || j > 'Z' && j < 'a') //限制在字母内爆破
{
continue;
}
if ((j - 39 - key[v3 % 10] + 97) % 26 + 97 == text[i]) //满足判断条件则输出
{
printf("%c",j);
v3++;
break;
}
}
}
}
flag{KLDQCUDFZO}
[GXYCTF2019]luck_guy
ELF文件,IDA打开进入main函数:
int __fastcall main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+14h] [rbp-Ch] BYREF
unsigned __int64 v5; // [rsp+18h] [rbp-8h]
v5 = __readfsqword(0x28u);
welcome();
puts("_________________");
puts("try to patch me and find flag");
v4 = 0;
puts("please input a lucky number");
__isoc99_scanf("%d", &v4);
patch_me(v4);
puts("OK,see you again");
return 0;
}
进入patch_me函数,再进入get_flag函数:
unsigned __int64 get_flag()
{
unsigned int v0; // eax
int i; // [rsp+4h] [rbp-3Ch]
int j; // [rsp+8h] [rbp-38h]
__int64 s; // [rsp+10h] [rbp-30h] BYREF
char v5; // [rsp+18h] [rbp-28h]
unsigned __int64 v6; // [rsp+38h] [rbp-8h]
v6 = __readfsqword(0x28u);
v0 = time(0LL);
srand(v0);
for ( i = 0; i <= 4; ++i )
{
switch ( rand() % 200 )
{
case 1:
puts("OK, it's flag:");
memset(&s, 0, 0x28uLL);
strcat((char *)&s, f1); // f1="GXY{do_not_"
strcat((char *)&s, &f2);
printf("%s", (const char *)&s);
break;
case 2:
printf("Solar not like you");
break;
case 3:
printf("Solar want a girlfriend");
break;
case 4:
s = 0x7F666F6067756369LL; //注意大小端
v5 = 0;
strcat(&f2, (const char *)&s);
break;
case 5:
for ( j = 0; j <= 7; ++j )
{
if ( j % 2 == 1 )
*(&f2 + j) -= 2;
else
--*(&f2 + j);
}
break;
default:
puts("emmm,you can't find flag 23333");
break;
}
}
return __readfsqword(0x28u) ^ v6;
}
已知f1,再通过s求出f2的值拼接就可以得到flag了
exp
s=[0x69,0x63,0x75,0x67,0x60,0x6F,0x66,0x7F]
flag=""
for i in range(8):
if i%2==1:
s[i]=s[i]-2
else:
s[i]=s[i]-1
flag+=chr(s[i])
print(flag)
输出结果为:hate_me}
所以flag=GXY{do_not_hate_me}
Java逆向解密
下载附件,发现是一个.class文件,最开始我用JEB打开发现看不了,然后看看wp上都用的jadx,然后我想起来之前下过,就试了试用jadx打开:
package defpackage;
import java.util.ArrayList;
import java.util.Scanner;
/* renamed from: Reverse reason: default package */
/* loaded from: Reverse.class */
public class Reverse {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
System.out.println("Please input the flag :");
String str = s.next();
System.out.println("Your input is :");
System.out.println(str);
char[] stringArr = str.toCharArray();
Encrypt(stringArr);
}
public static void Encrypt(char[] arr) {
ArrayList<Integer> Resultlist = new ArrayList<>();
for (char c : arr) {
int result = (c + '@') ^ 32;
Resultlist.add(Integer.valueOf(result));
}
int[] KEY = {180, 136, 137, 147, 191, 137, 147, 191, 148, 136, 133, 191, 134, 140, 129, 135, 191, 65};
ArrayList<Integer> KEYList = new ArrayList<>();
for (int i : KEY) {
KEYList.add(Integer.valueOf(i));
}
System.out.println("Result:");
if (Resultlist.equals(KEYList)) {
System.out.println("Congratulations!");
} else {
System.err.println("Error!");
}
}
}
发现是KEY加上@的ASCII码再与32做异或运算得到flag,开始写脚本:
flag=""
key=[180, 136, 137, 147, 191, 137, 147, 191, 148, 136, 133, 191, 134, 140, 129, 135, 191, 65]
for i in range(18):
flag+=chr((key[i]-ord('@'))^32)
print(flag)
This_is_theflag!
最后换了个JEB,可以打开了
[BJDCTF2020]JustRE
法一:
下载附件之后点开这个程序,发现它居然只需要点击就可以得到flag
32位的程序,用IDA打开,查找字符串之后可以交叉引用到关键函数:
INT_PTR __stdcall DialogFunc(HWND hWnd, UINT a2, WPARAM a3, LPARAM a4)
{
CHAR String[100]; // [esp+0h] [ebp-64h] BYREF
if ( a2 != 272 )
{
if ( a2 != 273 )
return 0;
if ( (_WORD)a3 != 1 && (_WORD)a3 != 2 )
{
sprintf(String, Format, ++dword_4099F0);
if ( dword_4099F0 == 19999 )
{
sprintf(String, " BJD{%d%d2069a45792d233ac}", 19999, 0);
SetWindowTextA(hWnd, String);
return 0;
}
SetWindowTextA(hWnd, String);
return 0;
}
EndDialog(hWnd, (unsigned __int16)a3);
}
return 1;
}
很明显可以看见BJD开头的flag,再结合后面的19999和0就可以得到flag了
法二:
由于这题太过简单,做完之后我继续再思考有没有其他的解法,最后还是找到了
基于法一中给出的函数,我们可以分析得出当我们点击19999次时就可以得到flag了,于是在那句if判断是否点击19999次的地方下一个断点
启动动态调试:
一路F8运行,直到出现跳转处为止
这里可以看见有个jnz,我们需要使其往左边走输出flag,于是想到可以更改ZF寄存器的值,使判断可以通过
这时F8就可以看见程序往下走了:
然后继续F8
flag就出来了!
刮开有奖
这题涉及到WindowsAPI,我将其作为BUUCTF第一页Windows逆向的最后一题来给我学习的第一个阶段划上一个句号
用IDA打开,能看见很多API,而且没找到主函数,查找字符串看看
发现有一个base64表,猜测这题与base64加密有关,还看见了下面的U g3t 1T!,这代表着其所在位置很可能是我们这题的主逻辑所在地,交叉引用到调用它的函数:
INT_PTR __stdcall DialogFunc(HWND hDlg, UINT a2, WPARAM a3, LPARAM a4)
{
const char *v4; // esi
const char *v5; // edi
int v7[2]; // [esp+8h] [ebp-20030h] BYREF
int v8; // [esp+10h] [ebp-20028h]
int v9; // [esp+14h] [ebp-20024h]
int v10; // [esp+18h] [ebp-20020h]
int v11; // [esp+1Ch] [ebp-2001Ch]
int v12; // [esp+20h] [ebp-20018h]
int v13; // [esp+24h] [ebp-20014h]
int v14; // [esp+28h] [ebp-20010h]
int v15; // [esp+2Ch] [ebp-2000Ch]
int v16; // [esp+30h] [ebp-20008h]
CHAR String[65536]; // [esp+34h] [ebp-20004h] BYREF
char v18[65536]; // [esp+10034h] [ebp-10004h] BYREF
if ( a2 == 272 )
return 1;
if ( a2 != 273 )
return 0;
if ( (_WORD)a3 == 1001 )
{
memset(String, 0, 0xFFFFu);
GetDlgItemTextA(hDlg, 1000, String, 0xFFFF);
if ( strlen(String) == 8 )
{
v7[0] = 90;
v7[1] = 74;
v8 = 83;
v9 = 69;
v10 = 67;
v11 = 97;
v12 = 78;
v13 = 72;
v14 = 51;
v15 = 110;
v16 = 103;
sub_4010F0(v7, 0, 10);
memset(v18, 0, 0xFFFFu);
v18[0] = String[5];
v18[2] = String[7];
v18[1] = String[6];
v4 = (const char *)sub_401000(v18, strlen(v18));
memset(v18, 0, 0xFFFFu);
v18[1] = String[3];
v18[0] = String[2];
v18[2] = String[4];
v5 = (const char *)sub_401000(v18, strlen(v18));
if ( String[0] == v7[0] + 34
&& String[1] == v10
&& 4 * String[2] - 141 == 3 * v8
&& String[3] / 4 == 2 * (v13 / 9)
&& !strcmp(v4, "ak1w")
&& !strcmp(v5, "V1Ax") )
{
MessageBoxA(hDlg, "U g3t 1T!", "@_@", 0);
}
}
return 0;
}
if ( (_WORD)a3 != 1 && (_WORD)a3 != 2 )
return 0;
EndDialog(hDlg, (unsigned __int16)a3);
return 1;
}
上面的API函数和判断字符串不管,查看这段函数调用的第一个函数 sub_4010F0():
int __cdecl sub_4010F0(int a1, int a2, int a3)
{
int result; // eax
int i; // esi
int v5; // ecx
int v6; // edx
result = a3;
for ( i = a2; i <= a3; a2 = i )
{
v5 = 4 * i;
v6 = *(4 * i + a1);
if ( a2 < result && i < result )
{
do
{
if ( v6 > *(a1 + 4 * result) )
{
if ( i >= result )
break;
++i;
*(v5 + a1) = *(a1 + 4 * result);
if ( i >= result )
break;
while ( *(a1 + 4 * i) <= v6 )
{
if ( ++i >= result )
goto LABEL_13;
}
if ( i >= result )
break;
v5 = 4 * i;
*(a1 + 4 * result) = *(4 * i + a1);
}
--result;
}
while ( i < result );
}
LABEL_13:
*(a1 + 4 * result) = v6;
sub_4010F0(a1, a2, i - 1);
result = a3;
++i;
}
return result;
}
这段函数大致意思是将v7数组从小到大排列,即ASCII码排序,我们可以使用脚本将其还原:
#include <iostream>
using namespace std;
void restore(int* num, int size) {
for (int j = 0; j < size - 1; j++) {
for (int i = 0; i < size - 1 - j; i++) {
if (num[i] > num[i + 1]) {
int temp = num[i];
num[i] = num[i + 1];
num[i + 1] = temp;
}
}
}
}
int main() {
int v7[11] = { 90, 74, 83, 69, 67, 97, 78, 72, 51, 110, 103 };
restore(v7, 11);
for (int i = 0; i < 11; i++) {
cout << char(v7[i]);
}
return 0;
}
输出3CEHJNSZagn
再往下看看见一些赋值以及调用了两次sub_401000()函数对v4和v5进行赋值,而sub_401000()函数是一个base64编码函数,因此将下方的v4和v5解密:
v4="jMp",v5="WP1"
但是由于前面的判断字符串长度,flag显然是8位的,而我们解码出来只有6位,因此还要寻找两个字符并且对它们进行排序,这时候主函数中的最后那段if显然就是我们寻找flag的关键,String数组为flag
String[0] == v7[0] + 34
flag第一位是上面输出中第一位的ASCII码加上34的字符即51+34="U"
String[1] == v10
flag第二位v10是上面输出的第5位即为"J"
4 * String[2] - 141 == 3 * v8
flag第三位的ASCII码乘4减去141与上面输出的第三位v8乘3相等,则flag第三位为"W"
基于上述字符可以推断,String中的前三个元素为”UJW“,则WP1在jMp前面
因此可以得到,String="UJWP1jMp"
[ACTF新生赛2020]easyre
未魔改的UPX壳,就不多介绍了
int __cdecl main(int argc, const char **argv, const char **envp)
{
_BYTE v4[12]; // [esp+12h] [ebp-2Eh] BYREF
_DWORD v5[3]; // [esp+1Eh] [ebp-22h]
_BYTE v6[5]; // [esp+2Ah] [ebp-16h] BYREF
int v7; // [esp+2Fh] [ebp-11h]
int v8; // [esp+33h] [ebp-Dh]
int v9; // [esp+37h] [ebp-9h]
char v10; // [esp+3Bh] [ebp-5h]
int i; // [esp+3Ch] [ebp-4h]
__main();
qmemcpy(v4, "*F'\"N,\"(I?+@", sizeof(v4));
printf("Please input:");
scanf("%s", v6);
if ( v6[0] != 'A' || v6[1] != 'C' || v6[2] != 'T' || v6[3] != 'F' || v6[4] != '{' || v10 != '}' )
return 0;
v5[0] = v7;
v5[1] = v8;
v5[2] = v9;
for ( i = 0; i <= 11; ++i )
{
if ( v4[i] != _data_start__[*((char *)v5 + i) - 1] )
return 0;
}
printf("You are correct!");
return 0;
}
逻辑也很清晰_data_start__是一个表,直接搓脚本
v4 = [42, 70, 39, 34, 78, 44, 34, 40, 73, 63, 43, 64] #v4提出来的数据
flag = ''
__data_start__ = '~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)(\'&%$# !"'
for i in v4:
flag = flag + chr(__data_start__.find(chr(i)) + 1)
print(flag)
flag{U9X_1S_W6@T?}
简单注册器
下载附件,是个apk,用JEB打开看看,能直接看到main,tab看看代码
package com.example.flag;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBarActivity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View.OnClickListener;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends ActionBarActivity {
public static class PlaceholderFragment extends Fragment {
@Override // android.support.v4.app.Fragment
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(0x7F030018, container, false); // layout:fragment_main
}
}
@Override // android.support.v7.app.ActionBarActivity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(0x7F030017); // layout:activity_main
if(savedInstanceState == null) {
this.getSupportFragmentManager().beginTransaction().add(0x7F05003C, new PlaceholderFragment()).commit(); // id:container
}
Button button = (Button)this.findViewById(0x7F05003F); // id:button1
TextView textview = (TextView)this.findViewById(0x7F05003E); // id:textView1
button.setOnClickListener(new View.OnClickListener() {
@Override // android.view.View$OnClickListener
public void onClick(View v) {
int flag = 1;
String s = ((EditText)this.findViewById(0x7F05003D)).getText().toString(); // id:editText1
if(s.length() != 0x20 || s.charAt(0x1F) != 97 || s.charAt(1) != 98 || s.charAt(0) + s.charAt(2) - 0x30 != 56) {
flag = 0;
}
if(flag == 1) {
char[] arr_c = "dd2940c04462b4dd7c450528835cca15".toCharArray();
arr_c[2] = (char)(arr_c[2] + arr_c[3] - 50);
arr_c[4] = (char)(arr_c[2] + arr_c[5] - 0x30);
arr_c[30] = (char)(arr_c[0x1F] + arr_c[9] - 0x30);
arr_c[14] = (char)(arr_c[27] + arr_c[28] - 97);
int i;
for(i = 0; i < 16; ++i) {
char a = arr_c[0x1F - i];
arr_c[0x1F - i] = arr_c[i];
arr_c[i] = a;
}
textview.setText("flag{" + String.valueOf(arr_c) + "}");
return;
}
textview.setText("输入注册码错误");
}
});
}
@Override // android.app.Activity
public boolean onCreateOptionsMenu(Menu menu) {
this.getMenuInflater().inflate(0x7F0C0000, menu); // menu:main
return true;
}
@Override // android.app.Activity
public boolean onOptionsItemSelected(MenuItem item) {
return item.getItemId() == 0x7F050040 ? true : super.onOptionsItemSelected(item); // id:action_settings
}
}
显然中间这段就是主逻辑:
试试能不能写个脚本
最开始写出来是这样,发现运行报错了,然后去查了一下,应该是str类型的问题,要将其转换为列表才能进行正常的赋值运算,重新写脚本:
str=['d','d','2','9','4','0','c','0','4','4','6','2','b','4','d','d','7','c','4','5','0','5','2','8','8','3','5','c','c','a','1','5']
a=""
flag=""
str[2]=chr(ord(str[2])+ord(str[3])-50)
str[4]=chr(ord(str[2])+ord(str[5])-0x30)
str[30]=chr(ord(str[0x1F])+ord(str[9])-0x30)
str[14]=chr(ord(str[27])+ord(str[28])-97)
for i in range(16):
a=str[0x1F-i]
str[0x1F-i]=str[i]
str[i]=a
for i in range(len(str)):
flag+=chr(ord(str[i]))
print(flag)
59acc538825054c7de4b26440c0999dd
[GWCTF 2019]pyre
下载下来是一个pyc文件,直接用在线网站反编译了
#!/usr/bin/env python
# visit https://tool.lu/pyc/ for more information
# Version: Python 2.7
print 'Welcome to Re World!'
print 'Your input1 is your flag~'
l = len(input1)
for i in range(l):
num = ((input1[i] + i) % 128 + 128) % 128
code += num
for i in range(l - 1):
code[i] = code[i] ^ code[i + 1]
print code
code = [
'%1f',
'%12',
'%1d',
'(',
'0',
'4',
'%01',
'%06',
'%14',
'4',
',',
'%1b',
'U',
'?',
'o',
'6',
'*',
':',
'%01',
'D',
';',
'%',
'%13']
求解脚本:
# 假设的加密后的code列表,将特殊格式转换为对应的ASCII码
encrypted_code = [
0x1f, 0x12, 0x1d, ord('('), ord('0'), ord('4'), 0x01, 0x06,
0x14, ord('4'), ord(','), 0x1b, ord('U'), ord('?'), ord('o'),
ord('6'), ord('*'), ord(':'), 0x01, ord('D'), ord(';'), ord('%'), 0x13]
# 第一步:逆向异或操作
original_code = [0] * len(encrypted_code)
original_code[-1] = encrypted_code[-1] # 最后一个元素不变
for i in range(len(encrypted_code) - 2, -1, -1):
original_code[i] = encrypted_code[i] ^ original_code[i + 1]
# 第二步:逆向计算input1
input1 = [''] * len(original_code)
for i in range(len(original_code)):
num = original_code[i]
original_input1_i = (num - i) % 128
input1[i] = chr(original_input1_i)
# 打印原始输入
print(''.join(input1))
findit
使用JEB看看:
package com.example.findit;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.MenuItem;
import android.view.View.OnClickListener;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends ActionBarActivity {
@Override // android.support.v7.app.ActionBarActivity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(0x7F030018); // layout:activity_main
Button btn = (Button)this.findViewById(0x7F05003D); // id:widget3
EditText edit = (EditText)this.findViewById(0x7F05003E); // id:widget2
TextView text = (TextView)this.findViewById(0x7F05003F); // id:widget1
btn.setOnClickListener(new View.OnClickListener() {
@Override // android.view.View$OnClickListener
public void onClick(View v) {
char[] x = new char[17];
char[] y = new char[38];
int i;
for(i = 0; i < 17; ++i) {
if(new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}[i] < 73 && new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}[i] >= 65 || new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}[i] < 105 && new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}[i] >= 97) {
x[i] = (char)(new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}[i] + 18);
}
else if(new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}[i] >= 65 && new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}[i] <= 90 || new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}[i] >= 97 && new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}[i] <= 0x7A) {
x[i] = (char)(new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}[i] - 8);
}
else {
x[i] = new char[]{'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'}[i];
}
}
if(String.valueOf(x).equals(edit.getText().toString())) {
int v1;
for(v1 = 0; v1 < 38; ++v1) {
if(new char[]{'p', 'v', 'k', 'q', '{', 'm', '1', '6', '4', '6', '7', '5', '2', '6', '2', '0', '3', '3', 'l', '4', 'm', '4', '9', 'l', 'n', 'p', '7', 'p', '9', 'm', 'n', 'k', '2', '8', 'k', '7', '5', '}'}[v1] >= 65 && new char[]{'p', 'v', 'k', 'q', '{', 'm', '1', '6', '4', '6', '7', '5', '2', '6', '2', '0', '3', '3', 'l', '4', 'm', '4', '9', 'l', 'n', 'p', '7', 'p', '9', 'm', 'n', 'k', '2', '8', 'k', '7', '5', '}'}[v1] <= 90 || new char[]{'p', 'v', 'k', 'q', '{', 'm', '1', '6', '4', '6', '7', '5', '2', '6', '2', '0', '3', '3', 'l', '4', 'm', '4', '9', 'l', 'n', 'p', '7', 'p', '9', 'm', 'n', 'k', '2', '8', 'k', '7', '5', '}'}[v1] >= 97 && new char[]{'p', 'v', 'k', 'q', '{', 'm', '1', '6', '4', '6', '7', '5', '2', '6', '2', '0', '3', '3', 'l', '4', 'm', '4', '9', 'l', 'n', 'p', '7', 'p', '9', 'm', 'n', 'k', '2', '8', 'k', '7', '5', '}'}[v1] <= 0x7A) {
y[v1] = (char)(new char[]{'p', 'v', 'k', 'q', '{', 'm', '1', '6', '4', '6', '7', '5', '2', '6', '2', '0', '3', '3', 'l', '4', 'm', '4', '9', 'l', 'n', 'p', '7', 'p', '9', 'm', 'n', 'k', '2', '8', 'k', '7', '5', '}'}[v1] + 16);
if(y[v1] > 90 && y[v1] < 97 || y[v1] >= 0x7A) {
y[v1] = (char)(y[v1] - 26);
}
}
else {
y[v1] = new char[]{'p', 'v', 'k', 'q', '{', 'm', '1', '6', '4', '6', '7', '5', '2', '6', '2', '0', '3', '3', 'l', '4', 'm', '4', '9', 'l', 'n', 'p', '7', 'p', '9', 'm', 'n', 'k', '2', '8', 'k', '7', '5', '}'}[v1];
}
}
text.setText(String.valueOf(y));
return;
}
text.setText("答案错了肿么办。。。不给你又不好意思。。。哎呀好纠结啊~~~");
}
});
}
@Override // android.app.Activity
public boolean onOptionsItemSelected(MenuItem item) {
return item.getItemId() == 0x7F050040 ? true : super.onOptionsItemSelected(item); // id:action_settings
}
}
发现代码很乱,再用jadx看看:
package com.example.findit;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
/* loaded from: classes.dex */
public class MainActivity extends ActionBarActivity {
/* JADX INFO: Access modifiers changed from: protected */
@Override // android.support.v7.app.ActionBarActivity, android.support.v4.app.FragmentActivity, android.app.Activity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn = (Button) findViewById(R.id.widget3);
final EditText edit = (EditText) findViewById(R.id.widget2);
final TextView text = (TextView) findViewById(R.id.widget1);
final char[] a = {'T', 'h', 'i', 's', 'I', 's', 'T', 'h', 'e', 'F', 'l', 'a', 'g', 'H', 'o', 'm', 'e'};
final char[] b = {'p', 'v', 'k', 'q', '{', 'm', '1', '6', '4', '6', '7', '5', '2', '6', '2', '0', '3', '3', 'l', '4', 'm', '4', '9', 'l', 'n', 'p', '7', 'p', '9', 'm', 'n', 'k', '2', '8', 'k', '7', '5', '}'};
btn.setOnClickListener(new View.OnClickListener() { // from class: com.example.findit.MainActivity.1
@Override // android.view.View.OnClickListener
public void onClick(View v) {
char[] x = new char[17];
char[] y = new char[38];
for (int i = 0; i < 17; i++) {
if ((a[i] < 'I' && a[i] >= 'A') || (a[i] < 'i' && a[i] >= 'a')) {
x[i] = (char) (a[i] + 18);
} else if ((a[i] >= 'A' && a[i] <= 'Z') || (a[i] >= 'a' && a[i] <= 'z')) {
x[i] = (char) (a[i] - '\b');
} else {
x[i] = a[i];
}
}
String m = String.valueOf(x);
if (m.equals(edit.getText().toString())) {
for (int i2 = 0; i2 < 38; i2++) {
if ((b[i2] >= 'A' && b[i2] <= 'Z') || (b[i2] >= 'a' && b[i2] <= 'z')) {
y[i2] = (char) (b[i2] + 16);
if ((y[i2] > 'Z' && y[i2] < 'a') || y[i2] >= 'z') {
y[i2] = (char) (y[i2] - 26);
}
} else {
y[i2] = b[i2];
}
}
String n = String.valueOf(y);
text.setText(n);
return;
}
text.setText("答案错了肿么办。。。不给你又不好意思。。。哎呀好纠结啊~~~");
}
});
}
@Override // android.app.Activity
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
这个清楚多了
有两行可疑字符串,分别为ThisIsTheFlagHome
和pvkq{m164675262033l4m49lnp7p9mnk28k75}
然后我将花括号中的字符串作为flag交了上去,发现不对,这个字符串应该是经过了某种位移
使用工具看一下:
可以看见那行flag了:flag{c164675262033b4c49bdf7f9cda28a75}
[ACTF新生赛2020]rome
32位无壳,用IDA打开,没找到主要加密函数,查找字符串之后交叉引用到关键函数
进入func函数:
int func()
{
int result; // eax
int v1[4]; // [esp+14h] [ebp-44h]
unsigned __int8 v2; // [esp+24h] [ebp-34h] BYREF
unsigned __int8 v3; // [esp+25h] [ebp-33h]
unsigned __int8 v4; // [esp+26h] [ebp-32h]
unsigned __int8 v5; // [esp+27h] [ebp-31h]
unsigned __int8 v6; // [esp+28h] [ebp-30h]
int v7; // [esp+29h] [ebp-2Fh]
int v8; // [esp+2Dh] [ebp-2Bh]
int v9; // [esp+31h] [ebp-27h]
int v10; // [esp+35h] [ebp-23h]
unsigned __int8 v11; // [esp+39h] [ebp-1Fh]
char v12[29]; // [esp+3Bh] [ebp-1Dh] BYREF
strcpy(v12, "Qsw3sj_lz4_Ujw@l");
printf("Please input:");
scanf("%s", &v2);
result = v2;
if ( v2 == 'A' )
{
result = v3;
if ( v3 == 'C' )
{
result = v4;
if ( v4 == 'T' )
{
result = v5;
if ( v5 == 'F' )
{
result = v6;
if ( v6 == '{' )
{
result = v11;
if ( v11 == '}' )
{
v1[0] = v7;
v1[1] = v8;
v1[2] = v9;
v1[3] = v10;
*(_DWORD *)&v12[17] = 0;
while ( *(int *)&v12[17] <= 15 )
{
if ( *((char *)v1 + *(_DWORD *)&v12[17]) > '@' && *((char *)v1 + *(_DWORD *)&v12[17]) <= 'Z' )
*((_BYTE *)v1 + *(_DWORD *)&v12[17]) = (*((char *)v1 + *(_DWORD *)&v12[17]) - 51) % 26 + 65;
if ( *((char *)v1 + *(_DWORD *)&v12[17]) > '`' && *((char *)v1 + *(_DWORD *)&v12[17]) <= 'z' )
*((_BYTE *)v1 + *(_DWORD *)&v12[17]) = (*((char *)v1 + *(_DWORD *)&v12[17]) - 79) % 26 + 97;
++*(_DWORD *)&v12[17];
}
*(_DWORD *)&v12[17] = 0;
while ( *(int *)&v12[17] <= 15 )
{
result = (unsigned __int8)v12[*(_DWORD *)&v12[17]];
if ( *((_BYTE *)v1 + *(_DWORD *)&v12[17]) != (_BYTE)result )
return result;
++*(_DWORD *)&v12[17];
}
return printf("You are correct!");
}
}
}
}
}
}
return result;
}
分析函数:
主体加密为第一个while循环,两个if分别代表v12字符串中的大小写字符进行加密,最后与result作比较,正确的话就会输出correct
exp
#include<stdio.h>
int main()
{
char str[]="Qsw3sj_lz4_Ujw@l";
int i;
char flag[17]={0};
for(i=0;i<=15;i++)
{
if(str[i]>'@'&&str[i]<='Z')
{
flag[i]=str[i]-65+51;
if(flag[i]<64)
{
flag[i]+=26;
}
}
else if(str[i]>'`'&&str[i]<='z')
{
flag[i]=str[i]-97+79;
if(flag[i]<96)
{
flag[i]+=26;
}
}
else
flag[i]=str[i];
}
for(i = 0 ; i < 16 ; i ++)
{
printf("%c",flag[i]);
}
}
flag{Cae3ar_th4_Gre@t}
rsa
密码题别来沾边
[FlareOn4]login
下载附件,发现是个html,打开这个网站查看源代码:
<!DOCTYPE Html />
<html>
<head>
<title>FLARE On 2017</title>
</head>
<body>
<input type="text" name="flag" id="flag" value="Enter the flag" />
<input type="button" id="prompt" value="Click to check the flag" />
<script type="text/javascript">|
document.getElementById("prompt").onclick = function () {
var flag = document.getElementById("flag").value;
var rotFlag = flag.replace(/[a-zA-Z]/g, function(c){return String.fromCharCode((c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26);});
if ("PyvragFvqrYbtvafNerRnfl@syner-ba.pbz" == rotFlag) {
alert("Correct flag!");
} else {
alert("Incorrect flag, rot again");
}
}
</script>
</body>
</html>
是个JS代码,由于我没学,就直接丢给AI了:
这段HTML代码实现了一个简单的网页,包含一个文本输入框和一个按钮。用户可以在文本框中输入数据,并通过点击按钮来检查输入的数据是否符合特定条件。这个检查是通过JavaScript脚本实现的。
JavaScript代码的核心功能是对用户输入的数据进行ROT13加密,然后将加密后的结果与预设的字符串"PyvragFvqrYbtvafNerRnfl@syner-ba.pbz"
比较。如果两者相等,则弹出提示框显示“Correct flag!”;如果不相等,则提示“Incorrect flag, rot again”。
ROT13是一种简单的字符替换加密方法,它将字母表中的每个字母替换为它在字母表中13位之后的字母。这种加密方法是可逆的,即对加密后的文本再次应用ROT13加密,就能得到原文。
要找到正确的flag,我们需要对给定的字符串"PyvragFvqrYbtvafNerRnfl@syner-ba.pbz"
进行ROT13解密。
flag{ClientSideLoginsAreEasy@flare-on.com}
[WUSTCTF2020]level1
下载附件,有一个ELF和一个output.txt文件,先用IDA64查看ELF文件
进入main函数:
int __fastcall main(int argc, const char **argv, const char **envp)
{
int i; // [rsp+4h] [rbp-2Ch]
FILE *stream; // [rsp+8h] [rbp-28h]
char ptr[24]; // [rsp+10h] [rbp-20h] BYREF
unsigned __int64 v7; // [rsp+28h] [rbp-8h]
v7 = __readfsqword(0x28u);
stream = fopen("flag", "r");
fread(ptr, 1uLL, 0x14uLL, stream);
fclose(stream);
for ( i = 1; i <= 19; ++i )
{
if ( (i & 1) != 0 )
printf("%ld\n", (unsigned int)(ptr[i] << i));
else
printf("%ld\n", (unsigned int)(i * ptr[i]));
}
return 0;
}
- 通过一个for循环,从1迭代到19(包含),对
ptr
数组中的每个字符进行处理和输出。 - 如果
i
是奇数,将ptr[i]
的值左移i
位(即ptr[i] << i
),然后输出。 - 如果
i
是偶数,计算i
与ptr[i]
的乘积(即i * ptr[i]
),然后输出。
再看看那个.txt文件,里面是一串数字
那就应该是将flag文件进行上述运算最后得到output,然后就可以开始写逆向求解脚本了:
#include<stdio.h>
int main()
{
int output[]={1,198,232,816,200,1536,300,6144,984,51200,570,92160,1200,565248,756,1474560,800,6291456,1782,65536000};
int i;
char flag[20]="0";
for ( i = 1; i <= 19; ++i )
{
if ( (i & 1) != 0 )
flag[i]=char((output[i])>>i);
else
flag[i]=char((output[i])/i);
}
flag[i]='\0';
printf("%s",flag);
}
//(注意循环是从1开始的,txt中只有19位,首位没有,先随便写一个)
flag{d9-dE6-20c}
[GUET-CTF2019]re
简单的UPX壳,进去没有main函数,直接查找字符串
最终定位到主体
__int64 __fastcall sub_400E28(__int64 a1, int a2, int a3, int a4, int a5, int a6)
{
int v6; // edx
int v7; // ecx
int v8; // r8d
int v9; // r9d
__int64 result; // rax
__int64 v11; // [rsp+0h] [rbp-30h] BYREF
unsigned __int64 v12; // [rsp+28h] [rbp-8h]
v12 = __readfsqword(0x28u);
sub_40F950((unsigned int)"input your flag:", a2, a3, a4, a5, a6, 0LL, 0LL, 0LL, 0LL);
sub_40FA80((unsigned int)"%s", (unsigned int)&v11, v6, v7, v8, v9, v11);
if ( (unsigned int)sub_4009AE(&v11) )
sub_410350("Correct!");
else
sub_410350("Wrong!");
result = 0LL;
if ( __readfsqword(0x28u) != v12 )
sub_443550();
return result;
}
不难看出sub_4009AE是关键的函数,点进去发现是一个z3
_BOOL8 __fastcall sub_4009AE(char *a1)
{
if ( 1629056 * *a1 != 166163712 )
return 0LL;
if ( 6771600 * a1[1] != 731332800 )
return 0LL;
if ( 3682944 * a1[2] != 357245568 )
return 0LL;
if ( 10431000 * a1[3] != 1074393000 )
return 0LL;
if ( 3977328 * a1[4] != 489211344 )
return 0LL;
if ( 5138336 * a1[5] != 518971936 )
return 0LL;
if ( 7532250 * a1[7] != 406741500 )
return 0LL;
if ( 5551632 * a1[8] != 294236496 )
return 0LL;
if ( 3409728 * a1[9] != 177305856 )
return 0LL;
if ( 13013670 * a1[10] != 650683500 )
return 0LL;
if ( 6088797 * a1[11] != 298351053 )
return 0LL;
if ( 7884663 * a1[12] != 386348487 )
return 0LL;
if ( 8944053 * a1[13] != 438258597 )
return 0LL;
if ( 5198490 * a1[14] != 249527520 )
return 0LL;
if ( 4544518 * a1[15] != 445362764 )
return 0LL;
if ( 3645600 * a1[17] != 174988800 )
return 0LL;
if ( 10115280 * a1[16] != 981182160 )
return 0LL;
if ( 9667504 * a1[18] != 493042704 )
return 0LL;
if ( 5364450 * a1[19] != 257493600 )
return 0LL;
if ( 13464540 * a1[20] != 767478780 )
return 0LL;
if ( 5488432 * a1[21] != 312840624 )
return 0LL;
if ( 14479500 * a1[22] != 1404511500 )
return 0LL;
if ( 6451830 * a1[23] != 316139670 )
return 0LL;
if ( 6252576 * a1[24] != 619005024 )
return 0LL;
if ( 7763364 * a1[25] != 372641472 )
return 0LL;
if ( 7327320 * a1[26] != 373693320 )
return 0LL;
if ( 8741520 * a1[27] != 498266640 )
return 0LL;
if ( 8871876 * a1[28] != 452465676 )
return 0LL;
if ( 4086720 * a1[29] != 208422720 )
return 0LL;
if ( 9374400 * a1[30] == 515592000 )
return 5759124 * a1[31] == 719890500;
return 0LL;
}
然后用Python写了个z3求解,发现求不出来,查了一下说伪代码少了a1[6],而且a1[16],a1[17]反了,爆破出来a1[6]=1,可以写脚本了
from z3 import *
a1 = [Int(f'a1[{i}]') for i in range(32)]
flag=""
solver = Solver()
solver.add(1629056 * a1[0] == 166163712)
solver.add(6771600 * a1[1] == 731332800)
solver.add(3682944 * a1[2] == 357245568)
solver.add(10431000 * a1[3] == 1074393000)
solver.add(3977328 * a1[4] == 489211344)
solver.add(5138336 * a1[5] == 518971936)
solver.add(a1[6]==ord('1'))
solver.add(7532250 * a1[7] == 406741500)
solver.add(5551632 * a1[8] == 294236496)
solver.add(3409728 * a1[9] == 177305856)
solver.add(13013670 * a1[10] == 650683500)
solver.add(6088797 * a1[11] == 298351053)
solver.add(7884663 * a1[12] == 386348487)
solver.add(8944053 * a1[13] == 438258597)
solver.add(5198490 * a1[14] == 249527520)
solver.add(4544518 * a1[15] == 445362764)
solver.add(10115280 * a1[16] == 981182160)
solver.add(3645600 * a1[17] == 174988800)
solver.add(9667504 * a1[18] == 493042704)
solver.add(5364450 * a1[19] == 257493600)
solver.add(13464540 * a1[20] == 767478780)
solver.add(5488432 * a1[21] == 312840624)
solver.add(14479500 * a1[22] == 1404511500)
solver.add(6451830 * a1[23] == 316139670)
solver.add(6252576 * a1[24] == 619005024)
solver.add(7763364 * a1[25] == 372641472)
solver.add(7327320 * a1[26] == 373693320)
solver.add(8741520 * a1[27] == 498266640)
solver.add(8871876 * a1[28] == 452465676)
solver.add(4086720 * a1[29] == 208422720)
solver.add(9374400 * a1[30] == 515592000)
solver.add(5759124 * a1[31] == 719890500)
if solver.check() == sat:
model = solver.model()
solution = [model[a1[i]].as_long() for i in range(32)]
print("Solution found:")
print(solution)
else:
print("No solution found.")
for j in range(len(solution)):
flag+=chr(solution[j])
print(flag)
CrackRTF
扫了一眼,这题好像有点难度,于是一边看wp一边写写看
直接可以进入主逻辑函数:
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
DWORD v3; // eax
DWORD v4; // eax
char Str[260]; // [esp+4Ch] [ebp-310h] BYREF
int v7; // [esp+150h] [ebp-20Ch]
char String1[260]; // [esp+154h] [ebp-208h] BYREF
char Destination[260]; // [esp+258h] [ebp-104h] BYREF
memset(Destination, 0, sizeof(Destination));
memset(String1, 0, sizeof(String1));
v7 = 0;
printf("pls input the first passwd(1): ");
scanf("%s", Destination); // 输入第一个密码
if ( strlen(Destination) != 6 ) // 长度不为6退出
{
printf("Must be 6 characters!\n");
ExitProcess(0);
}
v7 = atoi(Destination);
if ( v7 < 100000 ) // 密码小于100000退出
ExitProcess(0);
strcat(Destination, "@DBApp"); // 拼接函数
v3 = strlen(Destination);
sub_40100A((BYTE *)Destination, v3, String1); // SHA-1
if ( !_strcmpi(String1, "6E32D0943418C2C33385BC35A1470250DD8923A9") )// 比较哈希值
{
printf("continue...\n\n");
printf("pls input the first passwd(2): ");
memset(Str, 0, sizeof(Str));
scanf("%s", Str); // 输入第二个密码
if ( strlen(Str) != 6 ) // 长度不为6退出
{
printf("Must be 6 characters!\n");
ExitProcess(0);
}
strcat(Str, Destination);
memset(String1, 0, sizeof(String1));
v4 = strlen(Str);
sub_401019((BYTE *)Str, v4, String1); // MD5
if ( !_strcmpi("27019e688a4e62a649fd99cadaafdb4e", String1) )
{
if ( !(unsigned __int8)sub_40100F(Str) ) // 生成包含flag的文件
{
printf("Error!!\n");
ExitProcess(0);
}
printf("bye ~~\n");
}
}
return 0;
}
在sub_40100A函数中可以通过哈希算法标识符判断出是SHA-1
第一个密码给了限制条件,可以通过哈希值爆破出第一个密码:
import hashlib
string='@DBApp'
for i in range(100000,999999):
flag=str(i)+string
x = hashlib.sha1(flag.encode("utf8"))
y = x.hexdigest()
if "6e32d0943418c2c33385bc35a1470250dd8923a9" == y:
print(flag)
break
爆破出来是密码是123321
往后看发现第二个密码只给了MD5值,并没有给其他的限制条件,因此爆破是很困难的,只知道是一个6位密码
在后面判断的时候有一个sub_40100F函数,应该是关键函数,进去看看
跳转到sub_4014D0函数
char __cdecl sub_4014D0(LPCSTR lpString)
{
LPCVOID lpBuffer; // [esp+50h] [ebp-1Ch]
DWORD NumberOfBytesWritten; // [esp+58h] [ebp-14h] BYREF
DWORD nNumberOfBytesToWrite; // [esp+5Ch] [ebp-10h]
HGLOBAL hResData; // [esp+60h] [ebp-Ch]
HRSRC hResInfo; // [esp+64h] [ebp-8h]
HANDLE hFile; // [esp+68h] [ebp-4h]
hFile = 0;
hResData = 0;
nNumberOfBytesToWrite = 0;
NumberOfBytesWritten = 0;
hResInfo = FindResourceA(0, (LPCSTR)0x65, "AAA");
if ( !hResInfo )
return 0;
nNumberOfBytesToWrite = SizeofResource(0, hResInfo);
hResData = LoadResource(0, hResInfo);
if ( !hResData )
return 0;
lpBuffer = LockResource(hResData);
sub_401005(lpString, (int)lpBuffer, nNumberOfBytesToWrite);
hFile = CreateFileA("dbapp.rtf", 0x10000000u, 0, 0, 2u, 0x80u, 0);
if ( hFile == (HANDLE)-1 )
return 0;
if ( !WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, &NumberOfBytesWritten, 0) )
return 0;
CloseHandle(hFile);
return 1;
}
看不懂这堆,只能问一下AI了:
再去查了一下,使用ResourceHacker可以查找到“AAA”的文件源,下面还有一个sub_401005函数:
unsigned int __cdecl sub_401420(LPCSTR lpString, int a2, unsigned int a3)
{
unsigned int result; // eax
unsigned int i; // [esp+4Ch] [ebp-Ch]
unsigned int v5; // [esp+54h] [ebp-4h]
v5 = lstrlenA(lpString);
for ( i = 0; ; ++i )
{
result = i;
if ( i >= a3 )
break;
*(_BYTE *)(i + a2) ^= lpString[i % v5];
}
return result;
}
发现是简单的异或,再去问了问学长,为了保证输出的文件可用,需保证其文件头为.rtf文件,于是寻找.rtf文件的文件头:{\rtf1
密码与“AAA”异或得到.rtf文件
再查看“AAA”中的前六位16进制
因为异或是可逆运算,所以就可以求解密码了
抄个脚本:
rtf = '{\\rtf1' \\需要注意,\r需要转义,变成\\r
A = [0x05, 0x7D, 0x41, 0x15, 0x26, 0x01]
password=''
for i in range(len(rtf)):
x = ord(rtf[i]) ^ A[i]
password+=chr(x)
print(password)
得到第二个密码是~!3a@0
这时就可以去程序中验证了,输入两次密码之后控制台就关掉了,然后桌面上就生成了一个.rtf文件
打开文件就是flag啦:Flag{N0_M0re_Free_Bugs}
[WUSTCTF2020]level2
下载附件,查壳发现有一个UPX壳
脱完壳之后用IDA打开
flag直接就送了,我没想到居然这么简单
[MRCTF2020]Transform
下载附件,用IDA打开,进去之后就是函数的主逻辑:
int __fastcall main(int argc, const char **argv, const char **envp)
{
char Str[104]; // [rsp+20h] [rbp-70h] BYREF
int j; // [rsp+88h] [rbp-8h]
int i; // [rsp+8Ch] [rbp-4h]
sub_402230(argc, argv, envp);
printf("Give me your code:\n");
scanf("%s", Str);
if ( strlen(Str) != 33 )
{
printf("Wrong!\n");
system("pause");
exit(0);
}
for ( i = 0; i <= 32; ++i )
{
result[i] = Str[init[i]];
result[i] ^= LOBYTE(init[i]);
}
for ( j = 0; j <= 32; ++j )
{
if ( byte_40F0E0[j] != result[j] )
{
printf("Wrong!\n");
system("pause");
exit(0);
}
}
printf("Right!Good Job!\n");
printf("Here is your flag: %s\n", Str);
system("pause");
return 0;
}
发现是一个打乱字符串在异或运算的操作,LOBYTE()的意思是取八位最低字节,如图所示:
最后还要加个00,然后我们就可以取得需要进行异或的两个数组了,由此写出脚本
result=[0x67,0x79,0x7B,0x7F,0x75,0x2B,0x3C,0x52,0x53,0x79,0x57,0x5E,0x5D,0x42,0x7B,0x2D,0x2A,0x66,0x42,0x7E,0x4C,0x57,0x79,0x41,0x6B,0x7E,0x65,0x3C,0x5C,0x45,0x6F,0x62,0x4D]
init =[0x09,0x0A,0x0F,0x17,0x07,0x18,0x0C,0x06,0x01,0x10,0x03,0x11,0x20,0x1D,0x0B,0x1E,0x1B,0x16,0x04,0x0D,0x13,0x14,0x15,0x02,0x19,0x05,0x1F,0x08,0x12,0x1A,0x1C,0x0E,0x00]
str=[0]*33
flag=""
for i in range(33):
result[i]^=init[i]
for j in range(33):
str[init[j]]=result[j]
for k in range(33):
flag+=chr(str[k])
print(flag)
MRCTF{Tr4nsp0sltiON_Clph3r_1s_3z}
[2019红帽杯]easyRE
最开始写了一下,逆天题目
elf文件,用IDA64打开,发现里面有很多函数,shift+F12查找一下
发现有一长串字符和一个表,看着就是base64
本来以为解密之后就能得到flag了,还是我想的太简单了
解密之后发现还是个base64加密,然后一直一直一直解密,10次之后得到一个网站,是看雪的帖子
被骗了!!!
然后从查找到的you found me入手,一直交叉引用找到主要函数:
__int64 sub_4009C6()
{
__int64 result; // rax
int i; // [rsp+Ch] [rbp-114h]
__int64 v2; // [rsp+10h] [rbp-110h]
__int64 v3; // [rsp+18h] [rbp-108h]
__int64 v4; // [rsp+20h] [rbp-100h]
__int64 v5; // [rsp+28h] [rbp-F8h]
__int64 v6; // [rsp+30h] [rbp-F0h]
__int64 v7; // [rsp+38h] [rbp-E8h]
__int64 v8; // [rsp+40h] [rbp-E0h]
__int64 v9; // [rsp+48h] [rbp-D8h]
__int64 v10; // [rsp+50h] [rbp-D0h]
__int64 v11; // [rsp+58h] [rbp-C8h]
char v12[13]; // [rsp+60h] [rbp-C0h] BYREF
char v13[4]; // [rsp+6Dh] [rbp-B3h] BYREF
char v14[19]; // [rsp+71h] [rbp-AFh] BYREF
char v15[32]; // [rsp+90h] [rbp-90h] BYREF
int v16; // [rsp+B0h] [rbp-70h]
char v17; // [rsp+B4h] [rbp-6Ch]
char v18[72]; // [rsp+C0h] [rbp-60h] BYREF
unsigned __int64 v19; // [rsp+108h] [rbp-18h]
v19 = __readfsqword(0x28u);
qmemcpy(v12, "Iodl>Qnb(ocy", 12);
v12[12] = 127;
qmemcpy(v13, "y.i", 3);
v13[3] = 127;
qmemcpy(v14, "d`3w}wek9{iy=~yL@EC", sizeof(v14));
memset(v15, 0, sizeof(v15));
v16 = 0;
v17 = 0;
sub_4406E0(0LL, v15, 37LL);
v17 = 0;
if ( sub_424BA0(v15) == 36 )
{
for ( i = 0; i < (unsigned __int64)sub_424BA0(v15); ++i )
{
if ( (unsigned __int8)(v15[i] ^ i) != v12[i] )
{
result = 4294967294LL;
goto LABEL_13;
}
}
sub_410CC0("continue!");
memset(v18, 0, 65);
sub_4406E0(0LL, v18, 64LL);
v18[39] = 0;
if ( sub_424BA0(v18) == 39 )
{
v2 = sub_400E44(v18);
v3 = sub_400E44(v2);
v4 = sub_400E44(v3);
v5 = sub_400E44(v4);
v6 = sub_400E44(v5);
v7 = sub_400E44(v6);
v8 = sub_400E44(v7);
v9 = sub_400E44(v8);
v10 = sub_400E44(v9);
v11 = sub_400E44(v10);
if ( !(unsigned int)sub_400360(v11, off_6CC090) )
{
sub_410CC0("You found me!!!");
sub_410CC0("bye bye~");
}
result = 0LL;
}
else
{
result = 4294967293LL;
}
}
else
{
result = 0xFFFFFFFFLL;
}
LABEL_13:
if ( __readfsqword(0x28u) != v19 )
sub_444020();
return result;
}
发现函数开头的地方做了一个位移异或运算,写个脚本解密一下:
v12="Iodl>Qnb(ocy\x7Fy.i\x7Fd`3w}wek9{iy=~yL@EC"
v15=""
for i in range(len(v12)):
v15+=chr(ord(v12[i])^i)
print(v15)
得到的结果是:Info:The first four chars are flag
前四个字节是flag……你不说我也知道的
再往下看就是base64了,但是这里并没有看见之前需要解密的字符串
if判断那里有一行
后面看了别人的wp说base64结束之后下面又调用了一个函数,就在这张图的下半部分
进入函数:
unsigned __int64 sub_400D35()
{
unsigned __int64 result; // rax
unsigned int v1; // [rsp+Ch] [rbp-24h]
int i; // [rsp+10h] [rbp-20h]
int j; // [rsp+14h] [rbp-1Ch]
unsigned int v4; // [rsp+24h] [rbp-Ch]
unsigned __int64 v5; // [rsp+28h] [rbp-8h]
v5 = __readfsqword(0x28u);
v1 = sub_43FD20(0LL) - qword_6CEE38;
for ( i = 0; i <= 1233; ++i )
{
sub_40F790(v1);
sub_40FE60();
sub_40FE60();
v1 = sub_40FE60() ^ 0x98765432;
}
v4 = v1;
if ( (v1 ^ byte_6CC0A0[0]) == 'f' && (HIBYTE(v4) ^ byte_6CC0A3) == 'g' )
{
for ( j = 0; j <= 24; ++j )
sub_410E90((byte_6CC0A0[j] ^ *(&v4 + j % 4)));
}
result = __readfsqword(0x28u) ^ v5;
if ( result )
sub_444020();
return result;
}
最开始我是不能理解这段代码的,于是只能去多找几个相对详细的wp去看看
HIBYTE()是取最高位字节的意思,而v4又是int型,所以v4
与byte_6CC0A0
数组的前四个字节异或就能得到flag头的字符串,而下文for循环又是byte_6CC0A0
数组与v4
数组中与4取模之后的元素异或,也就是对v4
数组中的四个元素循环异或,因此我们需要将byte_6CC0A0
数组中的前四个元素与“flag”异或得到作为“密钥”的v4
数组,直到这里我才发现前文解出的前四个字节为flag是有用的提示
这个时候就可以搓脚本了:
v4=""
byte_6CC0A0="@5 V]\x18\"E\x17/$nb<'THl$nr<2E["
xor="flag"
flag=""
for i in range(4):
v4+=chr(ord(byte_6CC0A0[i])^ord(xor[i]))
print(v4)
for j in range(len(byte_6CC0A0)):
flag+=chr(ord(byte_6CC0A0[j])^ord(v4[j%4]))
print(flag)
得到的结果就是:
很有意思的一道题,不过真让我头大,看着wp写都有点磕磕绊绊的
[SUCTF2019]SignIn
下载附件之后IDA打开,没看见主要函数,查找字符串之后发现有个input flag,交叉引用到具体函数:
__int64 __fastcall main(int a1, char **a2, char **a3)
{
char v4[16]; // [rsp+0h] [rbp-4A0h] BYREF
char v5[16]; // [rsp+10h] [rbp-490h] BYREF
char v6[16]; // [rsp+20h] [rbp-480h] BYREF
char v7[16]; // [rsp+30h] [rbp-470h] BYREF
char v8[112]; // [rsp+40h] [rbp-460h] BYREF
char v9[1000]; // [rsp+B0h] [rbp-3F0h] BYREF
unsigned __int64 v10; // [rsp+498h] [rbp-8h]
v10 = __readfsqword(0x28u);
puts("[sign in]");
printf("[input your flag]: ");
__isoc99_scanf("%99s", v8);
sub_96A(v8, v9);
__gmpz_init_set_str(v7, "ad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35", 16LL);
__gmpz_init_set_str(v6, v9, 16LL);
__gmpz_init_set_str(v4, "103461035900816914121390101299049044413950405173712170434161686539878160984549", 10LL);
__gmpz_init_set_str(v5, "65537", 10LL);
__gmpz_powm(v6, v6, v5, v4);
if ( (unsigned int)__gmpz_cmp(v6, v7) )
puts("GG!");
else
puts("TTTTTTTTTTql!");
return 0LL;
}
看见这个65537的时候我第一反应就是RSA,然后C是用16进制表示的,n是那长串大素数,先去在线网站分解一下n,然后去抄个RSA脚本就可以解密了:
import gmpy2
from Crypto.Util.number import long_to_bytes
q = 366669102002966856876605669837014229419
p = 282164587459512124844245113950593348271
e = 65537
c = 0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35
# n = 103461035900816914121390101299049044413950405173712170434161686539878160984549
n = q * p
# print(n)
d = gmpy2.invert(e, (p - 1) * (q - 1))
print("d=", d)
m = pow(c, d, n)
print(m)
print(long_to_bytes(m))
放虚拟机里面跑出来:
suctf{Pwn_@_hundred_years}
[ACTF新生赛2020]usualCrypt
下载附件,用IDA打开,查找字符串之后发现有一个base64表以及一个字符串
我第一反应是去解这个base64,结果发现这并不是一个有效的base64字符串
于是去看了眼wp的思路
进入_main()函数看看:
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // esi
int v5[3]; // [esp+8h] [ebp-74h] BYREF
__int16 v6; // [esp+14h] [ebp-68h]
char v7; // [esp+16h] [ebp-66h]
char v8[100]; // [esp+18h] [ebp-64h] BYREF
printf(&unk_40E140);
scanf("%s", v8);
memset(v5, 0, sizeof(v5));
v6 = 0;
v7 = 0;
base64_fun(v8, strlen(v8), v5);
v3 = 0;
while ( *(v5 + v3) == byte_40E0E4[v3] )
{
if ( ++v3 > strlen(v5) )
goto LABEL_6;
}
printf(aError);
LABEL_6:
if ( v3 - 1 == strlen(byte_40E0E4) )
return printf(aAreYouHappyYes);
else
return printf(aAreYouHappyNo);
}
部分函数我已重命名,进入base64_fun():
int __cdecl sub_401080(int a1, int a2, int a3)
{
int v3; // edi
int v4; // esi
int v5; // edx
int v6; // eax
int v7; // ecx
int v8; // esi
int v9; // esi
int v10; // esi
int v11; // esi
_BYTE *v12; // ecx
int v13; // esi
int v15; // [esp+18h] [ebp+8h]
v3 = 0;
v4 = 0;
sub_401000();
v5 = a2 % 3;
v6 = a1;
v7 = a2 - a2 % 3;
v15 = a2 % 3;
if ( v7 > 0 )
{
do
{
LOBYTE(v5) = *(a1 + v3);
v3 += 3;
v8 = v4 + 1;
*(v8 + a3 - 1) = aAbcdefghijklmn[(v5 >> 2) & 0x3F];
*(++v8 + a3 - 1) = aAbcdefghijklmn[16 * (*(a1 + v3 - 3) & 3) + ((*(a1 + v3 - 2) >> 4) & 0xF)];
*(++v8 + a3 - 1) = aAbcdefghijklmn[4 * (*(a1 + v3 - 2) & 0xF) + ((*(a1 + v3 - 1) >> 6) & 3)];
v5 = *(a1 + v3 - 1) & 0x3F;
v4 = v8 + 1;
*(v4 + a3 - 1) = aAbcdefghijklmn[v5];
}
while ( v3 < v7 );
v5 = v15;
}
if ( v5 == 1 )
{
LOBYTE(v7) = *(v3 + a1);
v9 = v4 + 1;
*(v9 + a3 - 1) = aAbcdefghijklmn[(v7 >> 2) & 0x3F];
v10 = v9 + 1;
*(v10 + a3 - 1) = aAbcdefghijklmn[16 * (*(v3 + a1) & 3)];
*(v10 + a3) = 61;
LABEL_8:
v13 = v10 + 1;
*(v13 + a3) = 61;
v4 = v13 + 1;
goto LABEL_9;
}
if ( v5 == 2 )
{
v11 = v4 + 1;
*(v11 + a3 - 1) = aAbcdefghijklmn[(*(v3 + a1) >> 2) & 0x3F];
v12 = (v3 + a1 + 1);
LOBYTE(v6) = *v12;
v10 = v11 + 1;
*(v10 + a3 - 1) = aAbcdefghijklmn[16 * (*(v3 + a1) & 3) + ((v6 >> 4) & 0xF)];
*(v10 + a3) = aAbcdefghijklmn[4 * (*v12 & 0xF)];
goto LABEL_8;
}
LABEL_9:
*(v4 + a3) = 0;
return sub_401030(a3);
}
函数头有一个sub_401000()函数:
int sub_401000()
{
int result; // eax
char v1; // cl
for ( result = 6; result < 15; ++result )
{
v1 = aAbcdefghijklmn[result + 10];
aAbcdefghijklmn[result + 10] = aAbcdefghijklmn[result];
aAbcdefghijklmn[result] = v1;
}
return result;
}
这里是对base64表进行操作,具体来说就是将表中6-14与16-24中的元素进行互换
发现函数最后还有一个返回值,进入sub_401030()函数:
int __cdecl sub_401030(const char *a1)
{
__int64 v1; // rax
char v2; // al
v1 = 0i64;
if ( strlen(a1) )
{
do
{
v2 = a1[HIDWORD(v1)];
if ( v2 < 'a' || v2 > 'z' )
{
if ( v2 < 'A' || v2 > 'Z' )
goto LABEL_9;
LOBYTE(v1) = v2 + 32;
}
else
{
LOBYTE(v1) = v2 - 32;
}
a1[HIDWORD(v1)] = v1;
LABEL_9:
LODWORD(v1) = 0;
++HIDWORD(v1);
}
while ( HIDWORD(v1) < strlen(a1) );
}
return v1;
}
这个函数的作用是将base64加密后的字符串中的大写字母转小写,小写字母转大写
由此就可以构造脚本解密了:
#include<iostream>
#include<string>
#include<cstring>
using namespace std;
void base64_fun() {
char b[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char result;
for (int i = 6; i < 15; i++) {
result = b[i];
b[i] = b[i + 10];
b[i + 10] = result;
}
cout<<b<<endl;
}
void exchange() {
char str[]="zMXHz3TIgnxLxJhFAdtZn2fFk3lYCrtPC2l9";
int len_str=strlen(str);
for (int i=0;i<len_str;i++) {
if (str[i] >= 'a' && str[i] <= 'z') {
str[i] = str[i] - 32;
} else if (str[i] >= 'A' && str[i] <= 'Z') {
str[i] = str[i] + 32;
}
}
cout<<str<<endl;
}
int main()
{
base64_fun();
exchange();
}
这样以来就可以得到原本的表和原本的字符串了:
然后使用Python的base64模块进行换表的解密:
import base64
import string
str1 = "ZmxhZ3tiGNXlXjHfaDTzN2FfK3LycRTpc2L9" # 待解秘字符串
string1 = "ABCDEFQRSTUVWXYPGHIJKLMNOZabcdefghijklmnopqrstuvwxyz0123456789+/" # 新表
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
print(base64.b64decode(str1.translate(str.maketrans(string1, string2))))
b'flag{bAse64_h2s_a_Surprise}'
[MRCTF2020]Xor
进去之后直接就是主函数,但是按F5之后会报错
flag其实储存在byte_4212C0中,看到 loc_4010B6 我们可以知道 edx 寄存器不断自增,最后退出循环,然后再与27 进行比较,这个时候我们就知道,flag的长度就为27,如果输入的长度 大于27 那么就提示错误,如果不小于 那么先与eax异或 然后 再与byte_41EA08进行比较,所以byte_41EA08就是异或后的flag,我们写脚本还原就行了
str1 = 'MSAWB~FXZ:J:`tQJ"N@ bpdd}8g'
flag = ""
for i in range(len(str1)):
flag+= chr(i ^ ord(str1[i]))
print(flag)
[HDCTF2019]Maze
下载附件,有一个简单的UPX壳:
直接用工具去除了,用IDA打开:
发现一处爆红,一大堆字节码以及一个jnz原地跳转,因为跳转的地址+1所以才是原地跳转(我猜的)
先按D将call的错误的地址转换为字节码:
又去查了一下,发现0E8h是添加花指令,将jnz与0E8h都nop掉:
这样就很正常了,但是还是不能F5,我们需要按P重新定义函数,选中需要定义的部分按P
然后就可以F5了:
int __cdecl main(int argc, const char **argv, const char **envp)
{
int i; // [esp+10h] [ebp-14h]
char v5[16]; // [esp+14h] [ebp-10h] BYREF
sub_401140(aGoThroughTheMa);
scanf("%14s", v5);
for ( i = 0; i <= 13; ++i )
{
switch ( v5[i] )
{
case 'a':
--*(_DWORD *)asc_408078;
break;
case 'd':
++*(_DWORD *)asc_408078;
break;
case 's':
--dword_40807C;
break;
case 'w':
++dword_40807C;
break;
default:
continue;
}
}
if ( *(_DWORD *)asc_408078 == 5 && dword_40807C == -4 )
{
sub_401140(aCongratulation);
sub_401140(aHereIsTheFlagF);
}
else
{
sub_401140(aTryAgain);
}
return 0;
}
可以看见我们用键盘中的adsw进行操作,而asc_408078和dword_40807C分别代表横坐标和纵坐标,则终点就是(5,-4)
结合题目名字和伪代码adsw的提示,这是一个迷宫题:
我们很容易可以查看到地图,且起点则是(7,0),地图总共占了70个字节,可以绘制地图了:
*******+**
******* **
**** **
** *****
** **F****
** ****
**********
ssaaasaassdddw
[MRCTF2020]hello_world_go
好像是go语言逆向,用IDA8.3打开就给flag