bad_python
看样子是pyc文件损坏了。利用工具打开,发现是MAGIC坏了。搜下也没有头绪。
攻防世界-难度1- bad_python - _rainyday - 博客园
python Magic Number对照表以及pyc修复方法 - iPlayForSG - 博客园
看WP才知道36已经提示了pyc版本了。参考第二个文章,除了魔法数字还有8字节的额外信息。
但是我填充之后还是bad magic,再看看WP咋说的。原来代表文件大小的地方还不能为0.也就是第12个字节。
我们只需要修复一下就可以了。 妈的,用FF也不行,看来必须中规中矩才行。用E5成功了。、
识别出是XTEA加密,写出解密即可。后期我这又出问题了,这一步就不搞了吧
ereere
左边没有main了,依据start必定会出现main函数,一个个点找到了疑似main的函数。
base64换表+RC4。思路同攻防世界RE_ereere_攻防世界ereere-CSDN博客
用cyberchef解决的
easyEZbaby_app
定位到关键代码。主要看checkpass和checkUsername函数
首先是username,密码是zhishixuebao的MD5,以2为步长取。结果是:7afc4fcefc616ebd
pass则是如下。长度显然是15位,注意算法要求是char数组=0,说明赋值等式右边值是0(也就是48)。写出Python代码即可。
a=''
for i in range(15):a+=chr(((255 - i) + 2) - 98-48)
print(a)
toddler_regs
导入pdb文件,放进IDA分析。
持续跟踪,首先看到stage1,要求参数必须是要求的值,只能动调修改这个参数。
但是看起来没啥用啊,看看stage2.
发现stage1的用处了,他会把黄框部分设置为23.我们直接找到这个对应的值即可。注意team是个字符串数组,我们导出来看看,直接在C里面运行吧。
第一个team输出了:0Int
第二个代码和上面一样,结果是:nTJnU
组合起来就是flag{0Int_1s_n1c3_but_nTJnU_is_we1rd}
上面显然是错误的,可能会遇到这个地方刚好是\0直接截断。保险的做法是先定义好数组大小(如char[512][10],再直接去掉&运行即可)
exp:
也可以动调做,我电脑不知为何运行不起来这个软件,只能静态分析了。
easyre-xctf
看题目简介估计有壳。是UPX原版壳,官方工具就可以脱壳了。
但并没有明显的算法,只在字符部分看到了疑似flag的后半段。双击这里有一个符号名part2。
动调看看有没有提示,拿拿数据。
在内存发现了疑似的部分
根据其地址定位到函数,这就是第一个flag。函数名字有个part1,对应着part2.两个16进制转换为字符后就是part1.
七分靠猜,这里有运气成分,正常来说估计得搜part1了。
CatFly(还没有写出来)
看主函数很复杂。慢慢来看吧。
运行出来是个这,输入的东西会在左下角显示。那么main函数有的部分就可以不看了。
看到一个提示:
要记住逃逸密码??
难道是迷宫,分析这部分,\x1Bxxx是终端转义啊,看着是打印图形用的。
百思不得姐,还是看看WP吧。
re学习(29)攻防世界-CatFly(复原反汇编)_攻防世界catfly-CSDN博客
粗略浏览下发现没有我想要的,不过突然想到可以给main函数拷贝到VC,删去已知功能的函数再仔细分析看看。
暂时跳过,下次再看。
IgniteMe
跟进到主函数。
首先str长度需要大于4.
先来一个循环把str循环复制给v7。后两个if判断意思是大写转小写,小写转大写。
最关键的在后院吗,str2我们已经知道了,这就是目标。byte我们也知道,目标就是求v7。接下来的sub_4013c0跟进一下。
是一个表达式,相当于v7^0x55+72.那就很明显了,写出对应exp:
str2='GONDPHyGjPEKruv{{pj]X@rF'
byte=[ 0x0D, 0x13, 0x17, 0x11, 0x02, 0x01, 0x20, 0x1D, 0x0C, 0x02, 0x19, 0x2F, 0x17, 0x2B, 0x24, 0x1F, 0x1E, 0x16, 0x09, 0x0F, 0x15, 0x27, 0x13, 0x26, 0x0A, 0x2F, 0x1E, 0x1A, 0x2D, 0x0C, 0x22, 0x04]
flag=''
#根据str2猜测长度是24
for i in range(24):flag+=chr(((byte[i]^ord(str2[i]))-72)^0x55)
print(flag)
print(flag.lower())
出来的flag都是大写,不要忘记两个if判断是大写转小写小写转大写,因此再次转为小写即可
BABYRE
main函数看着很简单
需要注意:在 C 语言中,(*judge)(s)
这种语法表示通过函数指针调用函数。在 C 语言中,函数名本质上是指向函数代码起始地址的指针。可以将这个地址存储在一个函数指针变量中,然后通过该变量调用函数。
也就是说这是一段自修改代码。judge是函数,需要解密这段judge。
先用AI生成(因为我不太会IDA python)一段解密的脚本在IDA运行,然后C--P加载为函数即可
import idautils
import idaapibase = 0x600b00# 处理182个字节(与原始代码一致,0到181共182次)
for i in range(182):# 读取原始字节original_byte = idaapi.get_byte(base + i)# 异或解密decrypted_byte = original_byte ^ 0xC# 写入解密后的字节idaapi.patch_byte(base + i, decrypted_byte)# 标记为数据(这一行删了就行)无用idaapi.create_data(base + i, FF_BYTE, 1, idaapi.BADADDR)print(f"已解密 {base:X} 开始的182个字节")
再次从main函数进入judge即可了。 直接点进去judge好像部分代码还是错误,重新从main进去就好了。
这个加密逻辑就很明显了。
a='fmcd\x7Fk7d;V`;np'
flag=''
for i in range(len(a)):flag+=chr(ord(a[i])^i)
print(flag)
parallel-comparator-200
这一题直接给了C代码。有点意思。
注意到代码的flag_len是20.
首先认识一个新函数
逐步分析,main函数调用了下图的highly_.....函数。
跟进此函数。根据is_ok的判断条件,着重关注此部分,说明result值是0.因为is_ok的条件为generated==just_a_string。
查找用到result的地方。注意到只有first_letter未知,user_string是我们的目标。
最后一行代码实际上经过上文知识点铺垫,调用了checking函数。
由于result==0,说明argument[0]+argument[1]严格相等于argument[2]。会循环20次这个checking函数(因为pthread_creat函数在for循环内部循环了20次)
0我们不知道,(起码知道是某个小写字母)2是目标,1知道了。而且2就等于1+0.但是这还不太够啊,看看还有没有其余条件,似乎没了。
先尝试写一个爆破脚本试试。这里要注意,一定要多生成几个flag,因为不同的frist_letter对应不同的flag,我们试着a~z都搞一遍。第一次没想到这样遍历。而且代码美观不如豆包写的,还得多写多练。
、注意{}是占位符,可以写任意的表达式。
a = [0, 9, -9, -1, 13, -13, -4, -11, -9, -1, -7, 6, -13, 13, 3, 9, -13, -11, 6, -7]# 尝试所有可能的基准值b (97-122)
for base in range(97, 123):flag = []valid = Truefor i in a:char_code = base + iif 32 <= char_code <= 126:flag.append(chr(char_code))else:valid = Falsebreak # 这个基准值无效if valid:print(f"基准值 {chr(base)} ({base}): {''.join(flag)}")
发现flag
很让我开心的是我找到了大致思路!哈哈哈哈哈。只是脚本功底还比较薄弱。
secret-galaxy-300
注意到程序是打印了类似于星球的东西。
但是观察函数发现其实并没有完全打印出来,a1这个数组的部分内容并没有完全用到,所以我打算动调拿一波数据看看。
动调看a1并没发现明显的字符串
攻防世界逆向高手题之secret-galaxy-300-CSDN博客
看了别人的wp,反思一下,还是思路错了,这时候可以结合题目的提示,隐藏的星系,确实发现一个没有被打印出来的星系,点击ctrl+x查看交叉引用:
(而且我说为啥有三个代码差不多的文件,原来对应不同平台)
其实这里就可以看出来这估计就是flag了,这里偷了个懒,使用了AI。
# 定义各个星系名称字符串
ngs_2366 = "NGS 2366"
andromeda = "Andromeda"
messier = "Messier"
sombrero = "Sombrero"
triangulum = "Triangulum"
dark_secret_gala = "DARK SECRET GALAXY"# 将各个星系名称转换为字符列表,方便按索引取值
galaxy_strings = {"ngs_2366": list(ngs_2366),"andromeda": list(andromeda),"messier": list(messier),"sombrero": list(sombrero),"triangulum": list(triangulum),"dark_secret_gala": list(dark_secret_gala)
}def build_result_string():"""构建结果字符串"""result_chars = [galaxy_strings["andromeda"][8],galaxy_strings["triangulum"][7],galaxy_strings["messier"][4],galaxy_strings["andromeda"][6],galaxy_strings["andromeda"][1],galaxy_strings["messier"][2],'_',galaxy_strings["andromeda"][8],galaxy_strings["andromeda"][3],galaxy_strings["sombrero"][5],'_',galaxy_strings["andromeda"][8],galaxy_strings["andromeda"][3],galaxy_strings["andromeda"][4],galaxy_strings["triangulum"][6],galaxy_strings["triangulum"][4],galaxy_strings["andromeda"][2],'_',galaxy_strings["triangulum"][6],galaxy_strings["messier"][3]]return ''.join(result_chars)# 直接构建并打印结果字符串
print("构建的结果字符串:", build_result_string())
其实直接动调很快
simple-check-100
打开也是三个文件,有了前面的经验,这里面的代码一定是一样的
不如直接打开exe,动调起也方便
怀疑这部分的result就是最终的flag。
为了便于调试分析,我想使用动调的方法来做这道题。目前有两个思路,一个是直接改汇编代码,让这个条件强制为真,另一个是利用check_key来找v8的值,但是很遗憾看代码这个是v7地址对应的数组,估计不方便寻找和爆破。方便起见,先直接改汇编代码看看。
好吧,这种思路看着不太行的样子。OD出来的是乱码。估计是GBK编码不可以处理,这个估计不是GBK编码,
需要注意用X32得用管理员模式,刚才我patch了估计就是没管理员模式,他不认,IDA动调的patch估计没有静态的好使,patch了还是说wrong.
参考了攻防世界-simple-check-100_攻防世界 simple-check-100-CSDN博客看来这种动调还是找专业的调试器吧
我看人家都是用GDB动调的。linux估计不乱码。
新学的调试指令:set $eax=1
这里需要注意在这里改:,估计箭头跑到这里意思就是这个指令执行完毕了已经。
果不其然出来了flag。
接下来试试静态分析
注意参数本来是字符,搞成了int,因此本来一字节,导致合并为4字节。
还要注意这是一个嵌套for循环。
这借鉴了攻防世界 simple-check-100(非常好的一篇wp,日后还得多多观看,受益匪浅)-CSDN博客
#include <stdio.h>
int main(int argc, char *argv[])
{unsigned char flag_data[] = {220, 23, 191, 91, 212, 10, 210, 27, 125, 218,167, 149, 181, 50, 16, 246, 28, 101, 83, 83,103, 186, 234, 110, 120, 34, 114, 211};char v7[] = {84, -56, 126, -29, 100, -57, 22, -102, -51, 17,101, 50, 45, -29, -45, 67, -110, -87, -99, -46,-26, 109, 44, -45, -74, -67, -2, 106};unsigned int v2;unsigned char *v3;// v11 = -478230444;// v12 = -1709783196;// v13 = 845484493;// v14 = 1137959725;// v15 = -761419374;// v16 = -752063002;// v17 = -74;// v18 = -67;// v19 = -2;// v20 = 106;// 方法一:类比写for (int i = 0; i <= 6; ++i){v2 = ((v7[4 * i] & 0x000000FF) + ((v7[4 * i + 1] & 0x000000FF) << 8) + ((v7[4 * i + 2] & 0x000000FF) << 16) + ((v7[4 * i + 3] & 0x000000FF) << 24)) ^ 0xDEADBEEF;v3 = (unsigned char *)&v2;for (int j = 3; j >= 0; --j){printf("%c", *(v3 + j) ^ flag_data[4 * i + j]);}}// 方法二:按位异或,小端字节序,低位在低地址// for (int i = 0; i <= 6; ++i) {// printf("%c", v7[4 * i + 3] ^ 0xDE ^ flag_data[4 * i + 3]);// printf("%c", v7[4 * i + 2] ^ 0xAD ^ flag_data[4 * i + 2]);// printf("%c", v7[4 * i + 1] ^ 0xBE ^ flag_data[4 * i + 1]);// printf("%c", v7[4 * i] ^ 0xEF ^ flag_data[4 * i]);// }return 0;
}
代码分析:
v2
的计算逻辑这行代码的核心是 将 4 个有符号字节组合成一个 32 位无符号整数,然后与
0xDEADBEEF
进行异或运算。让我逐步拆解这个过程:1. 为什么需要
& 0x000000FF
?
v7
是一个 有符号字符数组(char v7[]
),其中的负数(如-56
)在内存中以补码形式存储(例如-56
的补码是0xC8
,即十进制的200
)。问题:当将有符号数扩展为更大的类型(如
int
)时,符号位会被保留。例如:
v7[1]
的值是-56
(二进制0xC8
),如果直接转换为int
,会变成0xFFFFFFC8
(因为符号位扩展)。这会导致后续的位运算(如
<< 8
)结果错误。解决方法:通过
& 0xFF
将其截断为无符号字节:
-56 & 0xFF
→0xC8
(十进制200
),确保高 24 位为0
。2. 为什么要进行位移操作(
<< 8
,<< 16
,<< 24
)?这是 小端序(Little Endian)字节组合 的过程:
假设有 4 个字节
b0, b1, b2, b3
,对应的索引为4*i
到4*i+3
。在小端序中,低字节存放在低地址,高字节存放在高地址。因此,组合成 32 位整数的公式是:
plaintext
value = b0 + (b1 << 8) + (b2 << 16) + (b3 << 24)
示例:
假设v7[4*i]
到v7[4*i+3]
的值为[84, -56, 126, -29]
(即[0x54, 0xC8, 0x7E, 0xE3]
):
v7[4*i] & 0xFF
→0x54
(低字节)
v7[4*i+1] & 0xFF
→0xC8
v7[4*i+2] & 0xFF
→0x7E
v7[4*i+3] & 0xFF
→0xE3
(高字节)组合后的值:
0xE37EC854
(注意字节顺序颠倒,符合小端序)。
主要是因为将字符指针弄成整数的,再加上端序转换,搞得很麻烦。
re1-100
开局这些都是反调试
函数功能与原理
管道概念:管道是一种进程间通信(IPC,Inter - Process Communication)机制,用于在具有亲缘关系(如父子进程)的进程间传递数据。它本质上是一个特殊的文件,遵循先进先出(FIFO)原则。
函数作用:在许多编程语言和系统(如 C、C++ 等基于 POSIX 标准的系统编程中),
pipe
函数用于创建一个匿名管道。pipe
函数通常接受一个包含两个文件描述符的数组作为参数(这里pParentWrite
应该是数组首地址),其中一个文件描述符用于读取管道数据(通常是数组的第一个元素,记为fd[0]
),另一个用于向管道写入数据(通常是数组的第二个元素,记为fd[1]
- 成功时:返回 0。
- 失败时:返回 -1,并设置
errno
来指示具体错误类型。
由此我们已经知道pparentread/pparentwrite都是什么意思。
这个代码大概意思就是判断输入的是不是完整。根据我的判断。