picoCTF,Reverse Engineering,逆向类,42/55

article/2025/4/22 21:59:48

picoCTF,Reverse Engineering,42/55

  • 2019 picoCTF
    • 01、vault-door-training,50分
    • 02、vault-door-1,100分
    • 03、vault-door-3,200分
    • 04、vault-door-4,250分
    • 05、vault-door-5,300分
    • 06、vault-door-6,350分
    • 07、vault-door-7,400分
    • 08、vault-door-8,450分
    • 09、asm1,200分
    • 10、asm2,250分
    • 11、asm3,300分
    • 12、droids0,300分
    • 13、droids1,350分
    • 14、OTP Implementation,300分
  • 2020 Mini-Competition
    • 01、reverse_cipher,300分
  • 2021 picoCTF
    • 01、Transformation,20分
    • 02、crackme-py,30分
    • 03、keygenme-py,30分
    • 04、ARMssembly 0,40分
    • 05、Shop,50分
    • 06、speeds and feeds,50分
    • 07、ARMssembly 1,70分
    • 08、ARMssembly 2,90分
    • 09、Hurry up! Wait!,100分
    • 10、gogo,110分
    • 11、ARMssembly 3,130分
    • 12、Let's get dynamic,150分
    • 13、Easy as GDB,160分
    • 14、ARMssembly 4,170分
    • 15、Powershelly,180分
  • 2021 redpwn
    • 01、not crypto,150分
    • 02、breadth,200分
  • 2022 picoCTF
    • 01、file-run1,100分
    • 02、file-run2,100分
    • 03、GDB Test Drive,100分
    • 04、patchme.py,100分
    • 05、Safe Opener,100分
    • 06、unpackme.py,100分
    • 07、bloat.py,200分
    • 08、Fresh Java,200分
    • 09、Bbbbloat,300分
    • 10、unpackme,300分

题目站点链接 https://play.picoctf.org/

最初将所有题解放在一个帖子里,帖子太长了不便于阅读,
为了方便后期编辑和阅读。2023年02月10日,将帖子拆分,按照题目类型分为六类:

picoCTF-General Skills,基本技能类
picoCTF-Cryptography,密码类
picoCTF-Forensics,取证类
picoCTF-Web Exploitation,网页开发类
picoCTF-Reverse Engineering,逆向类
picoCTF-Binary Exploitation,二进制类

下面,给出题解,逐步完善中……
注意:很多题目flag是变化的,每一个账号解题得到的flag不一样,所以,下面帖子里的flag仅供参考,但解题思路方法是一样的。



2019 picoCTF

01、vault-door-training,50分

代码里有检测函数,checkPassword(),函数里直接写了flag。
在这里插入图片描述

picoCTF{w4rm1ng_Up_w1tH_jAv4_be8d9806f18}

02、vault-door-1,100分

打开文件就看到一个密码表,顺序变了,仔细的把顺序排好就行了,
我把数组拷贝到et里转至就行了

picoCTF{d35cr4mbl3_tH3_cH4r4cT3r5_75092e}

03、vault-door-3,200分

按字符位置做了转换,写一个逆向函数,就可以了。

#include <stdio.h>
char cipher[] = "jU5t_a_sna_3lpm18g947_u_4_m9r54f";
char buff[32];
int main( void )
{ int i;for ( i = 0 ; i < 32 ; i++ ) {buff[i] = 'F';}for ( i = 0 ; i < 8 ; i++ ) {buff[i] = cipher[i];}for ( i = 8 ; i < 16 ; i++ ) {buff[i] = cipher[23 - i];}for ( i = 16 ; i < 32 ; i+=2 ) {buff[i] = cipher[46 - i];}for ( i = 31 ; i >= 17 ; i -=2 ) {buff[i] = cipher[i];}printf( "%s\n", buff );return 0;
}

picoCTF{jU5t_a_s1mpl3_an4gr4m_4_u_79958f}

04、vault-door-4,250分

一小段java

class test {public static void main(String[] args) {byte[] myBytes = {106, 85, 53, 116, 95, 52, 95, 98,0x55, 0x6e, 0x43, 0x68, 0x5f, 0x30, 0x66, 0x5f,0142, 0131, 0164, 063, 0163, 0137, 070, 0146,'4', 'a', '6', 'c', 'b', 'f', '3', 'b',};StringBuilder str_flag = new StringBuilder();for (byte myByte : myBytes) str_flag.append((char) myByte);System.out.println("picoCTF{" + str_flag + "}");}
}

picoCTF{jU5t_4_bUnCh_0f_bYt3s_8f4a6cbf3b}

05、vault-door-5,300分

打开程序有一段密码验证。
JTYzJTMwJTZlJTc2JTMzJTcyJTc0JTMxJTZlJTY3JTVmJTY2JTcyJTMwJTZkJTVmJTYyJTYxJTM1JTY1JTVmJTM2JTM0JTVmJTY1JTMzJTMxJTM1JTMyJTYyJTY2JTM0
解码后是flag

picoCTF{c0nv3rt1ng_fr0m_ba5e_64_e3152bf4}

06、vault-door-6,350分

验证密码函数写了数组

ublic boolean checkPassword(String password) {if (password.length() != 32) {return false;}byte[] passBytes = password.getBytes();byte[] myBytes = {0x3b, 0x65, 0x21, 0xa , 0x38, 0x0 , 0x36, 0x1d,0xa , 0x3d, 0x61, 0x27, 0x11, 0x66, 0x27, 0xa ,0x21, 0x1d, 0x61, 0x3b, 0xa , 0x2d, 0x65, 0x27,0xa , 0x6c, 0x61, 0x6d, 0x37, 0x6d, 0x6d, 0x6d,};for (int i=0; i<32; i++) {if (((passBytes[i] ^ 0x55) - myBytes[i]) != 0) {return false;}}return true;}

把数组考出来,用python写了一个反函数,注意,a^b=c,c^b=a

myBytesArr = [0x3b, 0x65, 0x21, 0xa, 0x38, 0x0, 0x36, 0x1d, 0xa, 0x3d, 0x61, 0x27, 0x11, 0x66, 0x27, 0xa,0x21, 0x1d, 0x61, 0x3b, 0xa, 0x2d, 0x65, 0x27, 0xa, 0x6c, 0x61, 0x6d, 0x37, 0x6d, 0x6d, 0x6d, ]
flag = ''
for i in range(32):chr_number = myBytesArr[i] ^ 0x55print(str(i).zfill(2), myBytesArr[i], chr_number, chr(chr_number))flag = flag + chr(chr_number)
print('picoCTF{' + flag + '}')

特别注意,这个小段子,我一次编写运行成功。

picoCTF{n0t_mUcH_h4rD3r_tH4n_x0r_948b888}

07、vault-door-7,400分

<< 是向左移位操作,逆运算是>>,向右移位。程序里密码验证有密码数组,写一个逆函数

pass_arr = [1096770097, 1952395366, 1600270708, 1601398833, 1716808014, 1734304867, 942695730, 942748212]
pass_str = ''
for pass_int in pass_arr:pass_bin = bin(pass_int)pass_bin = pass_bin[2:].zfill(32)pass_str = pass_str + chr(int(pass_bin[:8], 2))pass_str = pass_str + chr(int(pass_bin[8:16], 2))pass_str = pass_str + chr(int(pass_bin[16:24], 2))pass_str = pass_str + chr(int(pass_bin[24:], 2))print(str(pass_int).zfill(10), pass_bin, pass_str)
print('picoCTF{'+pass_str+'}')

picoCTF{A_b1t_0f_b1t_sh1fTiNg_dc80e28124}

08、vault-door-8,450分

还是和以前一样编写逆函数,这次是java写的

class test {public static void main(String[] args) {char[] expected = {0xF4, 0xC0, 0x97, 0xF0, 0x77, 0x97, 0xC0, 0xE4,0xF0, 0x77, 0xA4, 0xD0, 0xC5, 0x77, 0xF4, 0x86,0xD0, 0xA5, 0x45, 0x96, 0x27, 0xB5, 0x77, 0xE0,0x95, 0xF1, 0xE1, 0xE0, 0xA4, 0xC0, 0x94, 0xA4};String flag = "picoCTF{" + String.valueOf(unscramble(String.valueOf(expected))) + "}";System.out.println(flag);}static public char[] unscramble(String input) {char[] a = input.toCharArray();for (int b = 0; b < a.length; b++) {char c = a[b];c = switchBits(c, 6, 7);c = switchBits(c, 2, 5);c = switchBits(c, 3, 4);c = switchBits(c, 0, 1);c = switchBits(c, 4, 7);c = switchBits(c, 5, 6);c = switchBits(c, 0, 3);c = switchBits(c, 1, 2);a[b] = c;}return a;}static public char switchBits(char c, int p1, int p2) {char mask1 = (char) (1 << p1);char mask2 = (char) (1 << p2);char bit1 = (char) (c & mask1);char bit2 = (char) (c & mask2);char rest = (char) (c & ~(mask1 | mask2));char shift = (char) (p2 - p1);char result = (char) ((bit1 << shift) | (bit2 >> shift) | rest);return result;}
}

picoCTF{s0m3_m0r3_b1t_sh1fTiNg_2e762b0ab}

09、asm1,200分

仔细的研究汇编命令,一条一条的看,

已知输入0X2E0 (736)
asm1:<+0>:   push   ebp   #进栈<+1>:   mov    ebp,esp   #esp寄存器(0x2e0)放入ebp<+3>:   cmp    DWORD PTR [ebp+0x8],0x3fb #内存地址ebp+0x8的数据(0x2e0) 与  0x3fb 比较<+10>:  jg     0x512 <asm1+37>  #比较结果为大于,跳转<+12>:  cmp    DWORD PTR [ebp+0x8],0x280 #比较 0x2e00x280 比较<+19>:  jne    0x50a <asm1+29>  #比较结果为不等于,跳转<+21>:  mov    eax,DWORD PTR [ebp+0x8] #内存地址ebp+0x8的数据(0x2e0) 放入 eax <+24>:  add    eax,0xa   #eax中的数据+0xa<+27>:  jmp    0x529 <asm1+60>  #跳转到asm1+60<+29>:  mov    eax,DWORD PTR [ebp+0x8] #内存地址ebp+0x8的数据(0x2e0) 放入 eax<+32>:  sub    eax,0xa   #eax中的数据-0xa  =2d6<+35>:  jmp    0x529 <asm1+60>  #跳转到asm1+60<+37>:  cmp    DWORD PTR [ebp+0x8],0x559 #内存地址ebp+0x8的数据(0x2e0) 与  0x559 比较<+44>:  jne    0x523 <asm1+54>  #比较结果为不等于,跳转<+46>:  mov    eax,DWORD PTR [ebp+0x8] #内存地址ebp+0x8的数据(0x2e0) 放入 eax<+49>:  sub    eax,0xa   #eax中的数据-0xa          <+52>:  jmp    0x529 <asm1+60>  #跳转到asm1+60<+54>:  mov    eax,DWORD PTR [ebp+0x8] #内存地址ebp+0x8的数据(0x2e0) 放入 eax<+57>:  add    eax,0xa   #eax中的数据+0xa<+60>:  pop    ebp   #弹出栈   0x2d6<+61>:  ret        #结束 

然后一个巨坑,这个flag不要标志符,也就是不用flag{}包围
flag是
0x2d6
这个巨坑,不用flag{}包围。

10、asm2,250分

和asm1一样,一条一条语句的分析

注意flag不需要标志包围

0xa3

11、asm3,300分

题目提示是registers,寄存器问题。
给了asm3函数,传入三个值,获取返回值。
把asm3函数写成汇编源文件

.intel_syntax noprefix
.global asm3asm3:push   ebpmov    ebp,espxor    eax,eaxmov    ah,BYTE PTR [ebp+0xa]shl    ax,0x10sub    al,BYTE PTR [ebp+0xc]add    ah,BYTE PTR [ebp+0xd]xor    ax,WORD PTR [ebp+0x10]noppop    ebpret    

在编写一个main的c语言函数调用asm3

#include <stdio.h>
int asm3(int, int, int);
int main(int argc, char* argv[])
{printf("0x%x\n", asm3(0xd73346ed,0xd48672ae,0xd3c8b139));return 0;
}

编译它们
gcc -masm=intel -m32 -c asm3_test.S -o test.o
gcc -m32 -c main.c -o main.o
gcc -m32 test.o main.o -o main

如果编译报 fatal error: bits/libc-header-start.h: 没有那个文件或目录类似错误,
安装gcc-multilib
apt install gcc-multilib
apt install g++-multilib

最后运行
./main
得到flag
在这里插入图片描述
注意:25、asm130、asm232、asm3,它们的flag格式都是特别的,没有不用flag{}包围。
就是这个:0xc36b

12、droids0,300分

安卓调试,用Profile or Debug APK菜单打开APK文件。
运行它,可以配置虚拟机,也可以用真实机。
我找了一台Xiaomi Mi MIX 2S,小米的机器。
安装APK,然后运行它。
在这里插入图片描述
正式机器运行效果是这样的。
在这里插入图片描述
点击 HELLO, I AM BUTTON按钮,在 Android Studio.Run输出就会有一条flag出现。

picoCTF{a.moose.once.bit.my.sister}

13、droids1,350分

安卓继续
打开APK,在资源文件里找
在这里插入图片描述
重要的地方我都用红框标注来了
在这里插入图片描述
picoCTF{pining.for.the.fjords}

14、OTP Implementation,300分

ghidra反编译
在这里插入图片描述
获得enc
python程序:

import subprocess
import re
from pwn import unhex, xorflag = "a5d47ae6ffa911de9d2b1b7611c47a1c43202a32f0042246f822c82345328becd5b8ec4118660f9b8cdc98bd1a41141943a9"
enc = "lfmhjmnahapkechbanheabbfjladhbplbnfaijdajpnljecghmoafbljlaamhpaheonlmnpmaddhngbgbhobgnofjgeaomadbidl"key = ["0"] * 100for i in range(100):for j in "0123456789abcdef":key[i] = jp = subprocess.Popen(["ltrace", "-s", "1000", "./otp", "".join(key)], stdout=subprocess.PIPE,stderr=subprocess.STDOUT)re_text = p.communicate()[0].decode()# print(re_text)brute = re.findall(r"strncmp\(\"(.*?)\".*\)",re_text)[0]print(brute)if brute[i] == enc[i]:break
print(xor(unhex("".join(key)), unhex(flag)).decode())

获得flag:

picoCTF{cust0m_jumbl3s_4r3nt_4_g0Od_1d3A_e3647c08}

2020 Mini-Competition

01、reverse_cipher,300分

反编译得到main函数。
在这里插入图片描述

void    main(void){size_t    sVar1;char    local_58[23];char    local_41;int    local_2c;FILE * local_28;FILE * local_20;uint    local_14;int    local_10;char    local_9;local_20 = fopen("flag.txt", "r");local_28 = fopen("rev_this", "a");if (local_20 == (FILE *)0x0) {puts("No flag found, please make sure this is run on the server");}if (local_28 == (FILE * )0x0) {puts("please run this on the server");}sVar1 = fread(local_58, 0x18, 1, local_20);local_2c = (int)sVar1;if ((int)sVar1 < 1) {/ * WARNING: Subroutinedoesnotreturn * /exit(0);
}
for (local_10 = 0; local_10 < 8; local_10 = local_10 + 1) {local_9 = local_58[local_10];fputc((int)local_9, local_28);}for (local_14 = 8; (int)local_14 < 0x17; local_14 = local_14 + 1) {if ((local_14 & 1) == 0) {local_9 = local_58[(int)local_14] + '\x05';}else {local_9 = local_58[(int)local_14] + -2;}fputc((int)local_9, local_28);}local_9 = local_41;fputc((int)local_41, local_28);fclose(local_28);fclose(local_20);return;}

分析程序读取flag.txt经过一系列的运算,写入rev_this
写一个Python脚本

import os
import mmapdef memory_map(filename, access=mmap.ACCESS_READ):size = os.path.getsize(filename)fd = os.open(filename, os.O_RDONLY)return mmap.mmap(fd, size, access=access)with memory_map("rev_this") as bin_file:for i in range(8):print(chr(bin_file[i]), end='')for i in range(8, 23):if (i & 1) == 0:print(chr(bin_file[i] - 5), end='')else:print(chr(bin_file[i] + 2), end='')print(chr(bin_file[23]))

picoCTF{r3v3rs36ad73964}

2021 picoCTF

01、Transformation,20分

下载一个文件,是乱码,用python的Open打开,

flagStr = open("enc", encoding="utf-8").read()
flag = ""
for i in range(0, len(flagStr)):s1 = chr((ord(flagStr[i]) >> 8))s2 = chr(flagStr[i].encode('utf-16be')[-1])flag += s1flag += s2print(flag)

picoCTF{16_bits_inst34d_of_8_04c0760d}

02、crackme-py,30分

一个python程序,猜数字什么的,但是程序有一段代码没执行,就是下面这一段

bezos_cc_secret = "A:4@r%uL`M-^M0c0AbcM-MFE055a4ce`eN"# Reference alphabet
alphabet = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ" + \"[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"def decode_secret(secret):"""ROT47 decodeNOTE: encode and decode are the same operation in the ROT cipher family."""# Encryption keyrotate_const = 47# Storage for decoded secretdecoded = ""# decode loopfor c in secret:index = alphabet.find(c)original_index = (index + rotate_const) % len(alphabet)decoded = decoded + alphabet[original_index]print(decoded)

手动把这一段代码激活,在猜数字之后,加一行 decode_secret(bezos_cc_secret)
它就打印出flag了。

picoCTF{1|/|_4_p34||ut_dd2c4616}

03、keygenme-py,30分

仔细看了程序,一个星际旅行,一个星体的参数旅行测试什么的。
着重看了check_key部分,发现,flag的长度要一样,并且前面给定了一段,预计flag为picoCTF{1n_7h3_|<3y_of_********}
后面要比较
if key[i] != hashlib.sha256(username_trial).hexdigest()[4]:
看顺序是4、5、3、6、2、7、1、8
我直接加一个打印
print(hashlib.sha256(username_trial).hexdigest()[x]),注意x是位数
然后一个一个测试,得到了flag

picoCTF{1n_7h3_|<3y_of_54ef6292}

04、ARMssembly 0,40分

这是一段汇编,比较两个数,1830628817,1765227561
返回大数,将大数1830628817转换成16进制得到6D1D2DD1

picoCTF{6D1D2DD1}

05、Shop,50分

一个模拟购物小程序。
查了很久才知道,购买数字写负数,可以增加钱数。
在这里插入图片描述
初始只有40元,买了-4个的苹果,增加了60元,就有100元了。
购买那个Quiches奶酪,至少要-6个,每个10元,-6个可以增加60元。
这样,钱就够了,可以买到flag了,是下面这个串:
Flag is: [112 105 99 111 67 84 70 123 98 52 100 95 98 114 111 103 114 97 109 109 101 114 95 98 97 54 98 56 99 100 102 125]
解码就行了。
说实话,我没那么脑筋急转弯,我是查了别人的题解,才知道购买数量写负数的。

picoCTF{b4d_brogrammer_ba6b8cdf}

06、speeds and feeds,50分

这个导出一个文件
用一个cncview的系统打开,得到一行字,cncview数控机床,工业制造方面的绘图系统
在这里插入图片描述
picoCTF{num3r1cal_c0ntr0l_a067637b}

07、ARMssembly 1,70分

先把chall_1.S编译成chall_1.o,然后,写一个代码测试。
编译命令是gcc chall_1.S -o chall_1.o,注意大小写。
还有,一定要注意,这个程序是ARM构架,arrch系统,关于如何获得ARM构架环境,参见我的文章
《在windows10系统中,用qemu软件创建ARM构架虚拟机》
python代码如下:

import os
import syscount = 0
while count < 3000:cmdStr = './chall_1.o ' + str(count)r = os.popen(cmdStr).readlines()rStr = "".join(r)rStr = rStr.strip()print(cmdStr, rStr)if rStr.find('win') > -1:sys.exit()count = count + 1

这样就得到了3个文件
在这里插入图片描述
python chall_1.py 来运行。
一开始,猜到1000都没有结果,有点失去信心了,后来朋友猜测,答案可能在1000以上,于是把循环上限调到3000,终于测出结果。
在这里插入图片描述
我的程序测出来是1813。转换成16进制715。我的朋友测出来是27,差别很大,每个账号的flag不一样。

picoCTF{00000715}

08、ARMssembly 2,90分

编译程序:gcc chall_2.S -o chall_2.o
运行程序,传入数字:./chall_2.o 3848786505
在这里插入图片描述
输出结果,得到一个大数字2956424923 转换成16进制得到 B03776DB。

picoCTF{B03776DB}

09、Hurry up! Wait!,100分

用ghidra,分析了0000298a函数,然后分析298a调用的每一个函数。
在这里插入图片描述
每一个函数都调用了 ada__text_io__put__4函数在这里插入图片描述
ada__text_io__put__4函数有两个参数,第一个参数在改变,每次不一样……在这里插入图片描述

在这里插入图片描述
一个一个的找出来,这些字母……

1:00002616:p
2:000024aa:i
3:00002372:c
4:000025e2:o
5:00002852:C
6:00002886:T
7:000028ba:F
8:00002922:{
9:000023a6:d
10:00002136:1
11:00002206:5
12:0000230a:a
13:00002206:5
14:0000257a:m
15:000028ee:_
16:0000240e:f
17:000026e6:t
18:00002782:w
19:000028ee:_
20:000023a6:d
21:0000240e:f
22:0000233e:b
23:000023a6:d
24:00002372:c
25:00002206:5
26:000023a6:d
27:00002956:}

一个一个字母找,太麻烦了。网上大神写了脚本……
用脚本管理,建立一个脚本,然后可以自动调试……
在这里插入图片描述
在这里插入图片描述

import sys
def getAddress(offset):return currentProgram.getAddressFactory().getDefaultAddressSpace().getAddress(offset)listing = currentProgram.getListing()
functionManager = currentProgram.getFunctionManager()main_func = getGlobalFunctions("FUN_0010298a")[0]# Iterate the instructions that FUN_0010298a() is composed of
for codeUnit in listing.getCodeUnits(main_func.getBody(), True):if not codeUnit.toString().startswith("CALL"):# Ignore anything that isn't a "call"continuecallee = functionManager.getFunctionAt(getAddress(str(codeUnit.getAddress(0))))if not callee.getName().startswith("FUN_"):# In practice - skip ada__calendar__delays__delay_for()continuefor cu in listing.getCodeUnits(callee.getBody(), True):# Iterate the instructions that the callee is composed ofif (not cu.toString().startswith("LEA RAX")):# Ignore anything that isn't LEA RAX, [addr]#  since that's the instruction that loads the flag character to be printedcontinue# Check what's at "addr" and print itsys.stdout.write(chr(getByte(getAddress(str(cu.getScalar(1))))))print("\n over")

运行效果如下:
在这里插入图片描述
这个解题方法我都是抄来的,自己复现了一遍,注意,ghidra代码不能有中文,不管是注释、还是打印、显示,全部都不能有中文

picoCTF{d15a5m_ftw_dfbdc5d}

10、gogo,110分

太麻烦,比较难……
用 Ghidra 逆向,找到了函数checkPassword

void main.checkPassword(int param_1,uint param_2){uint *puVar1;uint uVar2;int iVar3;int *in_GS_OFFSET;undefined4 local_40;undefined4 local_3c;undefined4 local_38;undefined4 local_34;undefined4 local_30;undefined4 local_2c;undefined4 local_28;undefined4 local_24;byte local_20 [28];undefined4 uStack4;puVar1 = (uint *)(*(int *)(*in_GS_OFFSET + -4) + 8);if (&stack0x00000000 < (undefined *)*puVar1 || &stack0x00000000 == (undefined *)*puVar1) {uStack4 = 0x80d4b72;runtime.morestack_noctxt();main.checkPassword();return;}if ((int)param_2 < 0x20) {os.Exit(0);}FUN_08090b18();local_40 = 0x38313638;local_3c = 0x31663633;local_38 = 0x64336533;local_34 = 0x64373236;local_30 = 0x37336166;local_2c = 0x62646235;local_28 = 0x39383338;local_24 = 0x65343132;FUN_08090fe0();uVar2 = 0;iVar3 = 0;while( true ) {if (0x1f < (int)uVar2) {if (iVar3 == 0x20) {return;}return;}if ((param_2 <= uVar2) || (0x1f < uVar2)) break;if ((*(byte *)(param_1 + uVar2) ^ *(byte *)((int)&local_40 + uVar2)) == local_20[uVar2]) {iVar3 = iVar3 + 1;}uVar2 = uVar2 + 1;}runtime.panicindex();do {invalidInstructionException();} while( true );
}

其中这个

  local_40 = 0x38313638;local_3c = 0x31663633;local_38 = 0x64336533;local_34 = 0x64373236;local_30 = 0x37336166;local_2c = 0x62646235;local_28 = 0x39383338;local_24 = 0x65343132;

看起来就那么特别,把后面的数值接下来
3831363831663633643365336437323637336166626462353938333865343132
解码
81681f63d3e3d72673afbdb59838e412
可是没什么用

用gdb调试,试了很多种方法,后来,查看逆向地址
在这里插入图片描述
异或操作的地方,0x080d4b21,0x080d4b28
设置断点b * 0x080d4b28
hexdump $esp+4查看内存的值
在这里插入图片描述
得到,861836f13e3d627dfa375bdb8389214e,正好和前面那个解码是倒过来的
紧接着后面的数据就是异或操作的数据,也就是密码
在这里插入图片描述
把两段数据都取出来,做异或,就可以得到密码了,写了下面的程序。

from pwnlib.util.fiddling import unhex, xor
a = unhex('3836313833366631336533643632376466613337356264623833383932313465')
b = unhex('4a53475d414503545d025a0a5357450d05005d555410010e4155574b45504601')
print(a)
print(b)
print(xor(a, b))

得到如下结果

b'861836f13e3d627dfa375bdb8389214e'
b'JSG]AE\x03T]\x02Z\nSWE\r\x05\x00]UT\x10\x01\x0eAUWKEPF\x01'
b'reverseengineericanbarelyforward'

明显结果是可识别的一句有意义的文字,
翻译成中文是逆向工程让我无法前进
程序在输入密码后,还要求输入key,
显然,直接输入861836f13e3d627dfa375bdb8389214e无效。
查看main.ambush函数

void main.ambush(undefined4 param_1,undefined4 param_2)
{uint *puVar1;char cVar2;uint uVar3;int *in_GS_OFFSET;int local_88;uint local_84;uint local_80;undefined local_70 [16];undefined4 local_60;undefined4 local_5c;undefined4 local_58;undefined4 local_54;undefined4 local_50;undefined4 local_4c;undefined4 local_48;undefined4 local_44;undefined local_40 [32];undefined local_20 [12];undefined local_14 [16];undefined4 uStack4;puVar1 = (uint *)(*(int *)(*in_GS_OFFSET + -4) + 8);if (local_14 < (undefined *)*puVar1 || local_14 == (undefined *)*puVar1) {uStack4 = 0x80d4e4b;runtime.morestack_noctxt();main.ambush();return;}runtime.stringtoslicebyte(local_40,param_1,param_2);crypto/md5.Sum(local_88,local_84,local_80);FUN_08091008();FUN_08090b18();local_60 = 0x38313638;local_5c = 0x31663633;local_58 = 0x64336533;local_54 = 0x64373236;local_50 = 0x37336166;local_4c = 0x62646235;local_48 = 0x39383338;local_44 = 0x65343132;uVar3 = 0;while( true ) {if (0xf < (int)uVar3) {return;}encoding/hex.EncodeToString(local_70,0x10,0x10);if (local_84 <= uVar3) break;cVar2 = *(char *)(uVar3 + local_88);local_88 = 0x20;runtime.slicebytetostring(local_20,&local_60,0x20);if (local_80 <= uVar3) break;if (cVar2 != *(char *)(local_84 + uVar3)) {os.Exit(0);}uVar3 = uVar3 + 1;}runtime.panicindex();do {invalidInstructionException();} while( true );
}

也有那一段文字,一样的,861836f13e3d627dfa375bdb8389214e
其中

crypto/md5.Sum(local_88,local_84,local_80);

表示MD5加密。 cyberchef 解密没成功
找到一个在线的MD5解密网站
https://pmd5.com/
类似网站有很多,大家可以试试别的。
在这里插入图片描述
解码为goldfish
至此就基本结束了。连上服务器,输入密码:
reverseengineericanbarelyforward
key:
goldfish
在这里插入图片描述
picoCTF{p1kap1ka_p1c05729981f}

11、ARMssembly 3,130分

把文件chall3_S运行起来就行了,48 转换成16进制00000030

picoCTF{00000030}

12、Let’s get dynamic,150分

编译运行,用gdb调试。
break *(main+385),在main+385的地方设置断点
x/s $rsi查看寄存器rsi内容。
在这里插入图片描述
picoCTF{dyn4m1c_4n4ly1s_1s_5up3r_us3ful_56e35b54}

13、Easy as GDB,160分

用gdb调用python,不要搞到了。gdb -q -x mycode.py

import gdb
import string
from queue import Queue, EmptyMAX_FLAG_LEN = 0x200class Checkpoint(gdb.Breakpoint):def __init__(self, queue, target_hitcount, *args):super().__init__(*args)self.silent = Trueself.queue = queueself.target_hitcount = target_hitcountself.hit = 0def stop(self):res = []self.hit += 1# print(f"\nhit {self.hit}/{self.target_hitcount}")if self.hit == self.target_hitcount:al = gdb.parse_and_eval("$al")dl = gdb.parse_and_eval("$dl")self.queue.put(al == dl)return Falseclass Solvepoint(gdb.Breakpoint):def __init__(self, *args):super().__init__(*args)self.silent = Trueself.hit = 0def stop(self):# gdb.execute("q")self.hit += 1return Falsegdb.execute("set disable-randomization on")
gdb.execute("delete")
gdb.execute("file brute")sp = Solvepoint("*0x56555a71")
queue = Queue()flag = ""
ALPHABET = string.ascii_letters + string.digits + "{}_"for i in range(len(flag), MAX_FLAG_LEN):for c in ALPHABET:bp = Checkpoint(queue, len(flag) + 1, '*0x5655598e')gdb.execute("run <<< " + flag + c)try:result = queue.get(timeout=1)bp.delete()if result:flag += c# print("\n\n{}\n\n".format(flag))print('\033[1;31m' + "\n\n{}\n\n".format(flag) + '\033[0m')breakexcept Empty:print("Error: Empty queue!")gdb.execute("q")if sp.hit > 0:print("Found flag: {}".format(flag))gdb.execute("q")

picoCTF{I_5D3_A11DA7_e5458cbf}

14、ARMssembly 4,170分

启动ARM虚拟机
gcc chall_4.S -o chall_4.o
编译生成程序。
传入题目给定的数字,就会返回数字
在这里插入图片描述
转换16进制得到ec4e2911

picoCTF{ec4e2911}

15、Powershelly,180分

下载得到两个文件,是一个windows的shell脚本程序,程序是读一个input.txt,生成一个output.txt,题目给出了输出文件output.txt。

脚本阅读有点麻烦,其中:
-eq :equal(相等)
-ne :not equal(不等)
-gt :greater than(大于)
-ge :greater than or equal(大于或等于)
-lt :less than(小于)
-le :less than or equal(小于或等于)

$input = ".\input.txt"$out = Get-Content -Path $input
$enc = [System.IO.File]::ReadAllBytes("$input")
$encoding = [system.Text.Encoding]::UTF8
$total = 264
$t = ($total + 1) * 5
$numLength = ($total * 30 ) + $t
if ($out.Length -gt 5 -or $enc.count -ne $numLength)
{Write-Output "Wrong format 5"Exit
}else
{for($i=0; $i -lt $enc.count ; $i++){if (($enc[$i] -ne 49) -and ($enc[$i] -ne 48) -and ($enc[$i] -ne 10) -and ($enc[$i] -ne 13) -and ($enc[$i] -ne 32)){Write-Output "Wrong format 1/0/"Exit}}
}

从这一段可以看出,输入文件必须是5行。
用enc获得以字节读取文件数组,并且enc的长度做了精确限定。
初步判定:
enc是5行、每组6个字符、每行264组,并且是二进制0和1。

接着,程序把文件字符放入blocks变量,进行一系列的变换,写入output.txt

我们要做的就是逆向,从output.txt得到input.txt

找了一个逆向程序:

import sysprint("Randoms: ")
genNumbers = []
for i in range(1, 264 + 1, 1):y = (((i * 327) % 681) + 344) % 313genNumbers.append(y)print(genNumbers)print("Seeds: ")
seeds = []
for i in range(1, 264 + 1, 1):seeds.append((i * 127) % 500)
# print(seeds)
contents = []
with open("output.txt") as f:contents = f.read().split("\n")result = 0
finalArray = []
for blockCount in range(0, len(contents)):output = int(contents[blockCount]) ^ result ^ genNumbers[blockCount]output = bin(output)[2:].zfill(60)# print(y)# print(output)finalOutput = ""finalSplit = []for x in range(0, len(output), 2):finalSplit.append(output[x:x + 2])# finalSplit.length = $raw.lengthusedCounter = 0for x in range(0, len(finalSplit), 1):y = (x * seeds[blockCount]) % len(finalSplit)# print(y[x])# print("Length " + str(len(finalSplit)))current = finalSplit[y]if current == "X":while current == "X":y = (y + 1) % len(finalSplit)current = finalSplit[y]if current == "11":finalOutput += "1"finalSplit[y] = "X"usedCounter += 1else:finalOutput += "0"finalSplit[y] = "X"usedCounter += 1print(finalSplit)print(usedCounter)result = int(contents[blockCount])finalArray.append(finalOutput)columnOutput = []
for x in range(0, len(finalArray), 1):current = finalArray[x]counter = 0# print(current)for y in range(0, len(current), 6):  # go down each rowcurrentSplit = current[y:y + 6].zfill(6)if x == 0:columnOutput.append([currentSplit])else:columnOutput[counter].append(currentSplit)counter += 1# print(columnOutput)with open("input.txt", "w") as f:for x in columnOutput:print(len(x))joined = ' '.join(x)f.write(joined + "\r\n")
with open("input.txt", "rb") as f:print(len(f.read()))# print(columnOutput[0])

生成了input.txt在这里插入图片描述将0和1按点阵排列,写一个代码:

with open("input.txt") as fs:contents = fs.read().split('\n')
for content in contents:content = content.strip()if content != '':# print(content)v = ''for point in content:if point == '0':v = v + '█'else:v = v + ' 'print(v)

得到输出:
在这里插入图片描述
这个输出,正好拼接成一个二进制数列……
把它们抄下来,按照8位,分成一段,是这样的:
01110000 01101001 01100011 01101111 01000011 01010100 01000110 01111011 00110010 00110000 00110001 00111000 01101000 01101001 01100111 01101000 01110111 01000000 01111001 01011111 00110010 01011111 01110000 01101111 01110111 00110011 01110010 00100100 01101000 01100101 01101100 00100001 01111101

这个数列其实不用显示5行,从第一行就能判断,连续4个黑色块是0,连续2个黑色块是1,换算就是,100001为0,110011为1,也可以写代码,判断第一行就可以了……,有时间在搞……

把这个长数列放到010editor中,得到flag。
在这里插入图片描述
这题很精彩,绕了几道弯,我也是查了大量资料,才做出来的。也有借鉴网上大神的代码。
特别是用点阵拼出二进制,更是想不到。

picoCTF{2018highw@y_2_pow3r$hel!}

2021 redpwn

01、not crypto,150分

Ghidra 反编译,获得一个很长的函数。其中:memcmp函数比较输入数据的
在这里插入图片描述
gdb调试,b memcmp断点,在554000段。
Ghidra查看内存找到函数,在5553b9
在这里插入图片描述
gdb调试,在b *0x5555555553b9断点
在这里插入图片描述
运行程序后,在0x5555555553b9断点,看到flag,在rdi寄存器。
x/s $rdi查看寄存器。

picoCTF{c0mp1l3r_0pt1m1z4t10n_15_pur3_w1z4rdry_but_n0_pr0bl3m?}

02、breadth,200分

下载得到两个文件,V1和V2版本。
ida逆向一个可以看到有很多flag,
在这里插入图片描述
估计V1和V2的不同的地方就是flag修改的地方。
用010editor比较两个文件有3个不同的地方,2D4h9504Bh253753h
在这里插入图片描述
反编译查看地址
2D4在文件开头没什么意义
9504B是数据部分。
在这里插入图片描述
双击字符串,得到完整的flag
在这里插入图片描述
picoCTF{VnDB2LUf1VFJkdfDJtdYtFlMexPxXS6X}

2022 picoCTF

01、file-run1,100分

下载文件运行,就行了

picoCTF{U51N6_Y0Ur_F1r57_F113_47cf2b7b}

02、file-run2,100分

下载文件用二进制打开,搜索到了

picoCTF{F1r57_4rgum3n7_f65ed63e}

03、GDB Test Drive,100分

$ chmod +x gdbme
$ gdb gdbme
(gdb) layout asm
(gdb) break *(main+99)
(gdb) run
(gdb) jump *(main+104)

gdb 设置两个断点,然后……

picoCTF{d3bugg3r_dr1v3_7776d758}

04、patchme.py,100分

验证密码在程序里,写入密码,程序返回一个flag
picoCTF{p47ch1ng_l1f3_h4ck_21d62e33}

05、Safe Opener,100分

代码里有密码比较语句

String encodedkey = "cGwzYXMzX2wzdF9tM18xbnQwX3RoM19zYWYz";if (password.equals(encodedkey)) {System.out.println("Sesame open");return true;}

用CyberChef解码就行了。

picoCTF{pl3as3_l3t_m3_1nt0_th3_saf3}

06、unpackme.py,100分

密码写在程序里,一下就看到了,在代码里加一行,print(plain),打印出plain,里面有一行验证flag就是了

picoCTF{175_chr157m45_85f5d0ac}

07、bloat.py,200分

arg444 = arg132()
# arg432 = arg232()
# arg133(arg432)
# arg112()
arg423 = arg111(arg444)
print(arg423)

picoCTF{d30bfu5c4710n_f7w_5e14b257}

08、Fresh Java,200分

用反编译工具jd-gui反编译,有一个密码验证函数,是按位验证的

if (str.length() != 34) {System.out.println("Invalid key");return;} if (str.charAt(33) != '}') {System.out.println("Invalid key");return;} if (str.charAt(32) != 'd') {System.out.println("Invalid key");return;
……if (str.charAt(3) != 'o') {System.out.println("Invalid key");return;} if (str.charAt(2) != 'c') {System.out.println("Invalid key");return;} if (str.charAt(1) != 'i') {System.out.println("Invalid key");return;} if (str.charAt(0) != 'p') {System.out.println("Invalid key");return;} System.out.println("Valid key");}

取出重要的句子,举例如下:

if (str.charAt(0) != 'p') {
if (str.charAt(1) != 'i') {
if (str.charAt(10) != '0') {
if (str.charAt(11) != 'l') {
if (str.charAt(12) != '1') {
if (str.charAt(13) != 'n') {
if (str.charAt(14) != 'g') {
if (str.charAt(15) != '_') {
if (str.charAt(16) != 'r') {
if (str.charAt(17) != '3') {
if (str.charAt(18) != 'q') {
if (str.charAt(19) != 'u') {
if (str.charAt(2) != 'c') {
if (str.charAt(20) != '1') {
if (str.charAt(21) != 'r') {
if (str.charAt(22) != '3') {
if (str.charAt(23) != 'd') {
if (str.charAt(24) != '_') {
if (str.charAt(25) != '2') {
if (str.charAt(26) != 'b') {
if (str.charAt(27) != 'f') {
if (str.charAt(28) != 'e') {
if (str.charAt(29) != '1') {
if (str.charAt(3) != 'o') {
if (str.charAt(30) != 'a') {
if (str.charAt(31) != '0') {
if (str.charAt(32) != 'd') {
if (str.charAt(33) != '}') {
if (str.charAt(4) != 'C') {
if (str.charAt(5) != 'T') {
if (str.charAt(6) != 'F') {
if (str.charAt(7) != '{') {
if (str.charAt(8) != '7') {
if (str.charAt(9) != '0') {

排好顺序,取出字符就OK了,注意,上面的排序没有严格按照数字大小排序,大家自己调整。

picoCTF{700l1ng_r3qu1r3d_2bfe1a0d}

09、Bbbbloat,300分

反编译:
在这里插入图片描述
16进制86187 就是十进制 549255 。就是这个数字
在这里插入图片描述

picoCTF{cu7_7h3_bl047_44f74a60}

10、unpackme,300分

UPX 可以有效地对可执行文件进行压缩,并且压缩后的文件可以直接由系统执行,支持多系统和平台。
使用 UPX 来压缩可执行文件是一种减少发布包大小的有效方式。数字是754635,测了一下upx压缩或者不压缩,程序都能运行,输入754635,都能得出flag

picoCTF{up><_m3_f7w_5769b54e}


http://chatgpt.dhexx.cn/article/T1xPILan.shtml

相关文章

Java学习(12)(String类、String的查找方法、字符串转化、 替换、拆分、截取、trim方法、字符串的不可变性、StringBuilder和StringBuffer)

接上次博客&#xff1a;Java学习&#xff08;11&#xff09;&#xff1a;Java实现图书馆书库管理系统_di-Dora的博客-CSDN博客 目录 String类 构造字符串的方法&#xff1a; String 对象的比较 1、比较是否引用了同一个对象。 2、比较字符串内容是否相同 3、"int co…

渗透测试工具实战技巧合集

本文为作者总结自己在渗透测试中常用的一些小技巧。原文分为两部分&#xff0c;译者将其合二为一&#xff0c;方便大家查阅。 最好的 NMAP 扫描策略 # 适用所有大小网络最好的 nmap 扫描策略# 主机发现&#xff0c;生成存活主机列表 $ nmap -sn -T4 -oG Discovery.gnmap 192.1…

Netty堆外内存泄漏排查,这一篇全讲清楚了

上篇文章介绍了Netty内存模型原理&#xff0c;由于Netty使用不当会导致堆外内存泄漏&#xff0c;网上关于这方面的资料比较少&#xff0c;所以写下这篇文章&#xff0c;基于Netty4.1.43.Final&#xff0c;专门介绍排查Netty堆外内存相关的知识点&#xff0c;诊断工具&#xff0…

分支选择结构

分支语句又被称为选择结构&#xff0c;选择结构就是根据一个boolean表达式,来决定执行哪一些代码(代码块)。比较常见的分支语句有if语句和switch语句&#xff0c;下面将以代码的形式说明if和switch语句的基本构成以及其作用。 1、if语句&#xff1a;if语句作为选择语句&#xf…

选择结构。

1. 掌握if(如果)选择(分支)结构 注意&#xff1a; 经常会用到标志位&#xff0c;定义标志位的时候 也得考虑其位置 是局部变量还是全局变量 掌握基本if if(条件){// 条件成立时执行的内容 }掌握if-else(互斥) if(条件){// 条件成立 }else{// 条件不成立 }一般流程图也是这样…

C#选择结构

选择结构 概述IF选择语句if单分支选择结构if…else双分支选择结构if…else if…else多分支选择结构 switch选择语句基本语法 this关键字 概述 在程序当中&#xff0c;也存在选择语句&#xff0c;C#中共有2个选择语句&#xff1a;if和switch&#xff0c;if是单分支选择语句&…

5、选择结构

什么是选择结构 C程序的执行通常是按照顺序从上到下逐行执行。 而选择结构是根据某个变量或表达式的值来选择哪些语句执行&#xff0c;哪些语句不执行。 选择结构需要先进行条件判断&#xff08;对表达式的值判断&#xff09;&#xff0c;根据表达式的值选择是否执行。 三木运…

Python的选择结构

Python的选择结构—— if 语句 Python 中的选择结构就是判断真假。 Python 中的 if 语句就是选取要执行的结果&#xff0c;从一些备选的操作中进行要选择的语句。 if 语句后面跟着一个或多个可选的 elif &#xff08;“ else if ”&#xff09;&#xff0c;以及一个最终可选的…

初识C语言—— 选择结构程序设计

文章目录 一、if语句二、if语句的基本形式1.if语句形式2.if...else语句形式3.else if语句形式 三、if的嵌套形式四、条件运算符五、switch语句1switch语句的基本形式2.多路开关模式的switch语句 六、if...else语句和switch语句的区别七、总结 一、if语句 在C语言程序中&#x…

顺序结构 与 选择结构

1、顺序结构 流程&#xff1a;就是"程序代码的执行"顺序。 流程控制&#xff1a; 规定的语句&#xff0c;按照 哪几种方式 执行。&#xff08;这几种方式 指的是 顺序结构、选择结构、循环结构&#xff09; 顺序结构&#xff1a;按照程序&#xff08;自上而下&…

选择结构语句

if条件语句 ifelse的多层叠加 格式流程图代码console结论 ifelse ifelse语句 格式流程图代码console结论 if条件语句 1.if&#xff08;&#xff09;…else的多层叠加 格式 if&#xff08;判断语句&#xff09;{ 执行if语句1 }else{ 执行esle语句1} if&#xff08;判断语句…

C语言中的选择结构

C语言中的条件控制语句选择结构 在C语言中&#xff0c;一共有三种程序结构&#xff1a;顺序结构、选择结构&#xff08;分支结构&#xff09;和循环结构。顺序结构&#xff0c;从头到尾一句接着一句的执行下来&#xff0c;直到执行完最后一句&#xff1b;选择结构&#xff0c;…

顺序结构、选择结构

1、顺序结构 计算机执行程序的步骤是从上到下依次执行。 2、选择结构 选择结构也叫条件控制语句&#xff0c;主要有if语句、if…else语句、条件表达式exp1? exp2&#xff1a;exp3、switch语句。 1&#xff09;if语句、if…else语句 if语句主要有单分支、双分支和多分支三…

if选择结构

if选择结构一共有三种分别是 if 型 if else 型 if else if else型 第一种 if型 使用场景&#xff1a;需要进行判断时使用&#xff1b; int a 1, b 1; if (a b){System.out.println(&qu…

选择结构(一)

一 流程控制 1.有三种基本结构:顺序结构、选择结构、循环结构 顺序结构 选择结构 循环结构 2.流程控制语句是控制程序中各语句执行顺序的语句 二 选择结构 1.根据一定的条件有选择地执行或跳过特定的语句 2.选择结构可以分为单分支、双分支、多分支 3.语法:if ( 表达式 ) {…

C语言 选择结构

一.选择结构 1. 单分支选择结构 2. 双分支选择结构 3. 多分支选择结构 1. 单分支选择结构 1.1何为单分支选择结构&#xff1f; 从开始到结束之间只有一条路可供选择。 1.2何为双分支结构&#xff1f; 从开始到结束之间有两条路可供选择。 1.3何为多分支结构&#xff1f; 从开…

C语言——选择结构

选择结构和条件判断 &#xff08;一&#xff09;if语句——实现两个分支 if 语句可以写成不同形式 if &#xff08;表达式&#xff09; 语句1——可以没有else语句if &#xff08;表达式&#xff09; 语句 1 else 语句 2 ——————————有else语句if &#xff08;表达式…

c语言 选择结构例题

目录 1、if 语句的例题 2、switch 语句例题 前面我有一篇讲选择结构的文章&#xff0c;同学们可以先看一下&#xff0c;温习温习。 应该就在前面几篇&#xff0c;我复制不了链接了&#xff0c;尴尬了。 1、if 语句的例题 先来一张图&#xff0c;将if 语句步骤简单的走一下…

python选择结构

1.选择结构 1.1单分支结构 1.1.1 语法&#xff1a; if 表达式:语句块 1.1.2. 流程图 1.1.3 例&#xff1a;输入2个数&#xff0c;输出最大值&#xff1a; aint(input()) bint(input()) maxa if a<b:maxb print(max) 1.1.4.注意&#xff1a; 表达式&#xff1a;一般表…

C语言的选择结构

选择结构 C语言有两种选择语句:(1)if语句:用来实现两个分支的选择结构 (2)switch语句:用来实现多分支条件的选择结构 例子:计算ax^2 + bx + c = 0的方程的根。 流程图: 代码: #include<stdio.h>#include<math.h>//计算ax^2 + bx + c = 0的方程的根。…