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

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

解密脚本:

python 复制代码
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,然后与事先保存的字符串比较。可写出下列解密程序:

python 复制代码
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脚本:

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

脚本:

py 复制代码
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加壳,直接脱壳逆向。

py 复制代码
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反编译。

java 复制代码
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脱壳,然后逆向就行了,就是一个凯撒加密。

py 复制代码
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

直接逆,不解释了。

py 复制代码
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静态编译的文件,关键就是要理清楚一些库函数的功能。

c 复制代码
  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函数里面找到这段代码:

c 复制代码
  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;
    }
	...
  }

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

python 复制代码
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里面被引用了。

c 复制代码
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;
}

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

c 复制代码
__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了:

python 复制代码
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

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

c 复制代码
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;
}

直接上脚本:

python 复制代码
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)} ed≡1(mod(u−1)(v−1)),用扩展欧几里得来算。

python 复制代码
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加密之后又翻转了大小写。

python 复制代码
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

一个简单的异或。

python 复制代码
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查找相关字符串:

c 复制代码
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函数直接调用的:

c 复制代码
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关闭句柄的,因为线程句柄和线程本身的生命周期不同,线程句柄被关闭并不意味着线程立即结束,所以如果一个线程不需要任何干预,在创建之后就关闭句柄即可。第一个线程的函数如下:

c 复制代码
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);
  }
}
c 复制代码
// 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中有这一段处理,规定所有输入只能为字母,然后进行了处理。而另一个线程的代码如下:

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

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

c 复制代码
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);
}

直接上脚本:

python 复制代码
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。

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

Java 复制代码
    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方法,疑似是发送邮件。

Java 复制代码
    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,定位一下:

Java 复制代码
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程序,一个加密

python 复制代码
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}

相关推荐
一只乔哇噻8 分钟前
java后端工程师进修ing(研一版 || day41)
java·开发语言·学习·算法
愿时间能学会宽恕9 分钟前
SpringBoot后端开发常用工具详细介绍——SpringSecurity认证用户保证安全
spring boot·后端·安全
知识分享小能手22 分钟前
React学习教程,从入门到精通,React 使用属性(Props)创建组件语法知识点与案例详解(15)
前端·javascript·vue.js·学习·react.js·前端框架·vue
知识分享小能手7 小时前
React学习教程,从入门到精通, React 属性(Props)语法知识点与案例详解(14)
前端·javascript·vue.js·学习·react.js·vue·react
茯苓gao9 小时前
STM32G4 速度环开环,电流环闭环 IF模式建模
笔记·stm32·单片机·嵌入式硬件·学习
是誰萆微了承諾9 小时前
【golang学习笔记 gin 】1.2 redis 的使用
笔记·学习·golang
DKPT10 小时前
Java内存区域与内存溢出
java·开发语言·jvm·笔记·学习
Highcharts.js10 小时前
Highcharts 数据源安全最佳实践:保障数据安全,助力可视化可信部署
安全·highcharts
aaaweiaaaaaa10 小时前
HTML和CSS学习
前端·css·学习·html
Suckerbin11 小时前
LAMPSecurity: CTF5靶场渗透
笔记·安全·web安全·网络安全