Feb_Note
[TOC]
RE做题过程零散笔记
工具使用相关
IDA
- shift+E:提取数据
X64dbg
markdown语法
- 折叠代码块:
summary后写折叠后显示的标题,同时注意代码前后各空一行
Android逆向
Python逆向
pyc
pyc文件就是由Python文件经过编译后所生成的文件,.py文件编译成pyc文件后加载速度更快而且提高了代码的安全性。pyc的内容与python的版本相关,不同版本编译的pyc文件不一样
直接对pyc文件进行逆向分析是很难的,所以针对这些脚本语言,通常方法为反编译为源码
在线pyc转py
1 | #py to pyc |
py to exe
py文件打包成exe文件:pyinstxtractor
1 | #py to exe |
需要注意的是转换出来的pyc文件不具有Magic Number
即魔术头,需根据Python版本自行补全
Python版本对应Magic Number
- 在Python3.7及以上版本的编译后二进制文件中,头部除了四字节Magic Number,还有四个字节的空位和八个字节的时间戳+大小信息,后者对文件反编译没有影响,全部填充0即可;
- Python3.3 - Python3.7(包含3.3)版本中,只需要Magic Number和八位时间戳+大小信息
- Python3.3 以下的版本中,只有Magic Number和四位时间戳
Magic Number对照表
1 | Known values: |
大小端序
- 大端序: 高位存放在低地址,低位存放在高地址。数据字节位随着内存地址的增长而减小。正常的存储模式,便于数据类型的符号判断,因为最低地址位数据即为符号位,可以直接判断数据的正负号。
- 小端序: 高位存放在高地址,低位存放在低地址。数据字节位随着内存地址的增长而增长。将数字逆序存储。强制转换数据不需要调整字节内容。做数值四则运算时从低位每次取出相应字节运算,最后直到高位,并且最终把符号位刷新,这样的运算方式会更高效。
- 字符串数字等在x86内存中是反向存放的,如果用地址来取的话要反向,如果用数组下标来取的话才是正向。
可执行文件
PE文件
种类 | 主扩展名 |
---|---|
可执行系列 | EXE、XER |
库系列 | DLL、OCX、CPL、DRV |
驱动程序系列 | SYS、VXD |
对象文件系列 | OBJ |
PE文件的全称是Portable Executable,意为可移植的可执行的文件
PE文件是指32位可执行文件,也称为PE32。64位的可执行文件称为PE+或PE32+,是PE(PE32) 的一种扩展形式(请注意不是PE64)。
PE官方文档
ELF文件
可执行与可链接格式,一种用于二进制文件、可执行文件、目标代码、共享库和核心转储格式文件,是UNIX系统实验室作为应用程序二进制接口(Application BinaryInterface,ABI)而开发和发布的,也是Linux的主要可执行文件格式。1999年,被86open项目选为x86架构上的类Unix操作系统的二进制文件标准格式,用来取代COFF。因其可扩展性与灵活性,也可应用在其它处理器、计算机系统架构的操作系统上。
ELF节的分类
- .text节 是保存了程序代码指令的代码节。一段可执行程序,如果存在Phdr,则.text节就会存在于text段中。由于.text节保存了程序代码,所以节类型为SHT_PROGBITS。
- .rodata节 保存了只读的数据,如一行C语言代码中的字符串。由于.rodata节是只读的,所以只能存在于一个可执行文件的只读段中。因此,只能在text段(不是data段)中找到.rodata节。由于.rodata节是只读的,所以节类型为SHT_PROGBITS.
- .plt节(过程链接表) 也称为过程链接表(Procedure Linkage Table),其包含了动态链接器调用从共享库导入的函数所必需的相关代码。由于.plt节保存了代码,所以节类型为SHT_PROGBITS。
- .data节 存在于data段中,其保存了初始化的全局变量等数据。由于.data节保存了程序的变量数据
,所以节类型为SHT_PROGBITS。 - ELF官方文档点击查看更多
一些常见汇编指令
- cmp a,b 比较a与b
- mov a,b 把b的值送给a
- call与ret(调用一个子程序/返回主程序)
- Inc和dec 自增/自减
- and和xor
- lea 用于将源操作数的实际地址加载到目的操作数中
- Int 向处理器抛出一个系统终端信号,常用的中断信号是0x80,它用于向内核发送系统调用。
- MOV AX,2000H; 将16位数据2000H传送到AX寄存器
- MOV AL,20H; 将8位数据20H传送到AL寄存器
- MOV AX,BX; 将BX寄存器的16位数据传送到AX寄存器
- MOV AL,[2000H]; 将2000H单元的内容传送到AL寄存器
- je或jz若相等则跳(机器码74或0F84)
- jne或jnz若不相等则跳(机器码75或0F85)
- jmp无条件跳(机器码EB)
- jz相同则跳同je
- jb若小于则跳
- ja若大于则跳
- jg若大于则跳
- jge若大于等于则跳
- jI若小于则跳
- jIe若小于等于则跳
- MOV EAX, 222; eax = 222
- MOV EBX, 100; ebx = 100
- ADD EBX EAX; ebx + = eax;
- SUB EBX EAX; ebx - = eax;
- push/pop 入栈和出栈
- SHR(右移)指令使目的操作数逻辑右移一位,最高位用 0 填充。最低位复制到进位标志位,而进位标志位中原来的数值被丢弃;位元除法,数值进行右移(向 LSB 移动)即执行了位元除法(Bitwise Division)。将一个无符号数右移 n 位,即将该数除以 2^n^。
- SHL(左移)指令使目的操作数逻辑左移一位,最低位用 0 填充。最高位移入进位标志位,而进位标志位中原来的数值被丢弃:位元乘法,数值进行左移(向 MSB 移动)即执行了位元乘法(Bitwise Multiplication)。任何操作数左移 n 位,即将该数乘以 2^n^。
WP
Helloworld
题目文件下载下来发现Apk文件
那就是Android逆向
学习一番,采用APKIDE
使用前需要Java环境搭建与配置
用APKIDE打开题目的APK
直接搜索flag
搜索到在MainActivity.smali里面
flag{7631a988259a00816deda84afb29430a}
Java逆向解密
Java逆向,百度一下采用Jadx-gui
打开后得到如下代码
1 | package defpackage; |
核心代码就是int result = (c + '@') ^ 32;
对所输入字符串字符+64 ^ 32后若和KEY中的值相同即为正确的flag
脚本如下
点击查看
1 | key = [180, 136, 137, 147, 191, 137, 147, 191, 148, 136, 133, 191, 134, 140, 129, 135, 191, 65] |
得到flagThis_is_the_flag_!
reverse3
查一下,无壳32位
用IDA32打开
直接看main_0函数
1 | int __cdecl main_0(int argc, const char **argv, const char **envp) |
分析可知逻辑如下
- 先读入一串字符串str
- 然后经过sub_4110BE这个函数加密后赋值给v4
- v4复制给Destination,然后Destination再经过for循环的简单加密
- 最后判断Destination和Str2是否相等
Str2即为flag经过变换后的字符串点进去可以得到Str2为e3nifIH9b_C@n@dH
现在关键就是分析sub_4110BE
所以我们点进去看看
点开查看
1 | void *__cdecl sub_411AB0(char *a1, unsigned int a2, int *a3) |
难懂没关系,我们简单分析一下
我们点开aAbcdefghijklmn这个字符串看看
1 | .rdata:00417B30 aAbcdefghijklmn db 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' |
看到这个字符串应该敏锐地想到Base64加密
同时我们的猜测可以通过这个函数前面的/3 *4和字符串界面得到验证
所以可以确定就是Base64加密
当然更严谨可以通过动态调试验证。
利用python或者在线解密网站解密就可以得出flag为{i_l0ve_you}
Python代码
1 | from base64 import * |
game
尝试打开
大意是说有八个灯,初始全为关闭,须将其全部开启才能得到flag
输入n(1-8)会改变序号为(n-1),n,(n+1)的灯的状态
试了一下,依次输入12345678就能得到flag
flagzsctf{T9is_tOpic_1s_v5ry_int7resting_b6t_others_are_n0t}
好了这题解完了
法一 静态分析
32位无壳 IDA32 打开,搜索main函数
1 | int __cdecl main_0(int argc, const char **argv, const char **envp) |
根据题目意思,很容易定位到最后一个if语句即为判断八个灯是否全开,
从之前调用了’CLS’指令清屏也能大致猜测出来。
那么flag应该就在sub_457AB4
这个函数里面,跟进一下
1 | int sub_45E940() |
简单的v2和v5异或,再异或0x13
脚本如下
1 | v5=[18,64,98,5,2,4,6,3,6,48,49,65,32,12,48,65,31,78,62,32,49,32,1,57,96,3,21,9,4,62,3,5,4,1,2,3,44,65,78,32,16,97,54,16,44,52,32,64,89,45,32,65,15,34,18,16,0] |
运行结果zsctf{T9is_tOpic_1s_v5ry_int7resting_b6t_others_are_n0t}
法二 广度优先搜索BFS
题目可以转化成相当于一个初始全为0的长度为8的数组,每次操作选一个位置,将其与其相邻共3个数取反(首尾相邻,可看做一个环),一直操作到全部为1。求操作方案。可以采用BFS算法。求出操作方法,然后在程序中依次输入即可。
1 |
|
最后求出结果是12345678
法三 动态调试
看了别人的wp才知道也可以动调解决,学了一下。
用X32dbg打开
右键-搜索-所有用户模块-字符串
定位到关键指令’CLS’(因为之前分析过’CLS’后有个关键判断),双击点进去
接下来由八个jne指令
这里讲一下jne,je指令
- je或jz若相等则跳(机器码74或0F84)
- jne或jnz若不相等则跳(机器码75或0F85)
则这八个jne指令就是判断灯是否打开
我们只需修改这里的指令即可
将jne指令改成jz或直接nop掉
选中后空格修改两种选一种修改就行
之后直接F9运行,随便输入数字,就会出现flag