逆向学习笔记2

一 动态调试

1.exe与elf的区别

可执行文件指的是可以由操作系统进行加载执行的文件

可执行文件的文件格式常见有:exe 和 dll(Windows 系列)、elf(Linux 系列)

EXE(Executable)是Windows操作系统中的可执行文件格式,它是一种二进制格式,包含了程序的机器代码和相关的资源。EXE文件可以直接在Windows上运行,它通常以.exe作为文件扩展名。EXE文件可以包含多个可执行程序和库文件,它们可以通过链接器和加载器来进行连接和加载。

ELF(Executable and Linkable Format)是一种通用的可执行文件格式,主要用于UNIX和类UNIX操作系统,如Linux。ELF文件也是一种二进制格式,包含了程序的机器代码、数据和相关的元数据。ELF文件可以通过链接器和加载器来进行连接和加载,它支持动态链接和共享库的使用。

总的来说,EXE和ELF是不同操作系统中的可执行文件格式,它们在文件结构和使用方式上有一些区别。

文件结构上的区别:

  1. EXE文件通常包含一个程序的完整可执行代码,以及相关的资源和数据。它可以有多个节(section)来存储不同类型的数据。
  2. ELF文件也包含程序的可执行代码,但它使用节(section)和段(segment)的概念来组织数据。节用于存储不同类型的数据,而段用于定义内存映射的相关信息。

使用方式上的区别:

  1. EXE文件主要用于Windows操作系统,可以直接在Windows上运行。它可以通过Windows的API和资源管理器来访问和执行。
  2. ELF文件主要用于UNIX和类UNIX操作系统,如Linux。它可以通过命令行或者调用系统调用来执行。ELF文件还支持动态链接和共享库的使用,可以在运行时加载和链接所需的库。

此外,EXE和ELF还有一些其他细微的差异,比如文件头部的结构和字段的定义,以及特定操作系统的特定功能和限制。

2.IDA中F7和F8区别
F8 Step Over 表示跳到下一步
F7 Step Into 表示进入到代码

一句话:1、想要点进方法里面就按F7
2、想要跳转到下一行就按F8

3.调试sub.elf

首先将IDA的linux_server64放在虚拟机中,再在虚拟机命令行里用管理员(sudo linux_server64)打开,再在命令行运行要调试的sub.elf,在回主机打开IDA,在debugger里找到Attach,再找到remote linux debugger,输入虚拟机的ip(用ifconfig -a命令查找),确认完毕后,即可对sub.elf进行调试(包括F2下断点,F7和F8的步入和步过)

注:如果要调试安卓的文件等,要先运行android_x64_server等文件(注意版本问题),其他步骤类似同上。

二. 位运算

1.位运算概述

从现代计算机中所有的数据二进制的形式存储在设备中。即 0、1 两种状态,计算机对二进制数据进行的运算(+、-、*、/)都是叫位运算,即将符号位共同参与运算的运算。

2.位运算概览:
符号 描述 运算规则
& 两个位都为1时,结果才为1
| 两个位都为0时,结果才为0
^ 异或 两个位相同为0,相异为1
~ 取反 0变1,1变0
<< 左移 各二进位全部左移若干位,高位丢弃,低位补0
>> 右移 各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移)

注意:

逻辑运算符:位与(&),位或(|),位异或(^),位非(~)。

移位运算符:左移(<<),右移(>>),无符号右移(>>>)。

3.按位与运算符(&):

定义:参加运算的两个数据,按二进制位进行”与”运算。

运算规则:

1
0&0=0  0&1=0  1&0=0  1&1=1

总结:两位同时为1,结果才为1,否则结果为0。

例如:3&5 即 0000 0011& 0000 0101 = 0000 0001,因此 3&5 的值得1。

注意:负数按补码形式参加按位与运算。

4.与运算的用途:

1)清零

如果想将一个单元清零,即使其全部二进制位为0,只要与一个各位都为零的数值相与,结果为零。

2)取一个数的指定位

比如取数 X=1010 1110 的低4位,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行按位与运算(X&Y=0000 1110)即可得到X的指定位。

3)判断奇偶

只要根据最未位是0还是1来决定,为0就是偶数,为1就是奇数。因此可以用if ((a & 1) == 0)代替if (a % 2 == 0)来判断a是不是偶数。

5.按位或运算符(|):

定义:参加运算的两个对象,按二进制位进行”或”运算。

1
0|0=0  0|1=1  1|0=1  1|1=1

总结:参加运算的两个对象只要有一个为1,其值为1。

例如:3|5即 0000 0011| 0000 0101 = 0000 0111,因此,3|5的值得7。 

注意:负数按补码形式参加按位或运算。

6.或运算的用途:

常用来对一个数据的某些位设置为1

比如将数 X=1010 1110 的低4位设置为1,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行按位或运算(X|Y=1010 1111)即可得到。

7.异或运算符(^):

定义:参加运算的两个数据,按二进制位进行”异或”运算。

运算规则:

1
0^0=0  0^1=1  1^0=1  1^1=0

总结:参加运算的两个对象,如果两个相应位相同为0,相异为1。

8.异或运算的用途:

1)翻转指定位

比如将数 X=1010 1110 的低4位进行翻转,只需要另找一个数Y,令Y的低4位为1,其余位为0,即Y=0000 1111,然后将X与Y进行异或运算(X^Y=1010 0001)即可得到。

2)与0相异或值不变

例如:1010 1110 ^ 0000 0000 = 1010 1110

3)使一个数的最低位为零

使a的最低位为0,可以表示为:a & 1。1的值为 1111 1111 1111 1110,再按”与”运算,最低位一定为0。因为” ~”运算符的优先级比算术运算符、关系运算符、逻辑运算符和其他运算符都高。

9.异或的几条性质:
  • 1、交换律
  • 2、结合律 (a^b)^c == a^(b^c)
  • 3、对于任何数x,都有 x^x=0,x^0=x
  • 4、自反性: a^b^b=a^0=a;
10.取反运算符 (~):

定义:参加运算的一个数据,按二进制进行”取反”运算。

运算规则: 

1
2
~1=0
~0=1

总结:对一个二进制数按位取反,即将0变1,1变0。

11.左移运算符(<<):

定义:将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。

设 a=1010 1110,a = a<< 2 将a的二进制位左移2位、右补0,即得a=1011 1000。

若左移时舍弃的高位不包含1,则每左移一位,相当于该数乘以2。

12.右移运算符(>>):

定义:将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。

例如:a=a>>2 将a的二进制位右移2位,在符号位左补0 或者 左补1得看被移数是正还是负。

操作数每右移一位,相当于该数除以2。

手搓base64代码

1
2
3
4
5
6
7
8
9
10
11
import base64  #导入base64模块
data = 'KRWfD4zPB7bcrdCyVVsxM9Kiw78itVs0' #要解码的字符串
T1 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
#base64原表
T2 = 'QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm7894561230+/'
#题目给的base64变表
result = ''
for ch in data:
result += T1[T2.index(ch)] #在T2中找到data对应字符,再在T1中取出来,连成字符串。
result = bytearray(base64.b64decode(result)) #用python自带的base64模块进行解码
print(result) #打印flag

task题目wp

1.base题目

首先查看有没有upx壳,发现没有后,直接IDA分析,F5看伪代码,然后看到两个字符串”443067337B495F4C3076655F53616B7572616A696D614D61697D“和“0123456789ABCDEF”前者经过分析下面的伪代码,可以看出这就是我们要比较的目标,后者分析发现形式非常像16进制,在分析伪代码发现,代码进行的移位加密非常像Base64,结合想法后可以得出结论:这是一个Base16加密,于是直接用网上在线的base16解码解第一个字符串就得出flag了。

1
D0g3{I_L0ve_SakurajimaMai}

总结:题目本身是非常简单的,难点在于认出这是base16加密,这类题目通常会给出一个标准的密码表,如base16的“0123456789ABCDEF”,这是提示,也是解题的关键,所以对base系列的伪代码移位要足够熟练才能快速认出。

2.xor题目

首先查看有没有壳,发现无壳后IDA分析,发现输入的s[]与s1[]进行了异或操作,又点击sub_916找s1[],发现s1[]与s2[]比较,直接点击s2[]看字符串用lazy_ida将s2[]取出得到:

1
s2[] =[0x56, 0x4E, 0x57, 0x58, 0x51, 0x51, 0x09, 0x46, 0x17, 0x46, 0x54, 0x5A, 0x59, 0x59, 0x1F, 0x48, 0x32, 0x5B, 0x6B, 0x7C, 0x75, 0x6E, 0x7E, 0x6E, 0x2F, 0x77, 0x4F, 0x7A, 0x71, 0x43, 0x2B, 0x26, 0x89, 0xFE,0x00]

此时直接写一个s2与s1异或的脚本会出现如下结果:

1
'/$ 2(}!d''":/m-T<A*$INç

发现不是所求的flag,于是重新看伪代码,对s1进行ctrl+x进行交叉引用,发现有一个sub_84A函数引用了s1,点进去:

1
2
for ( i = 0; i <= 33; ++i )
s1[i] ^= 2 * i + 65;

发现s1又进行了一次异或,所以更改脚本:

1
2
3
4
5
6
7
T1 = 'qasxcytgsasxcvrefghnrfghnjedfgbhn'
T2 = [0x56, 0x4E, 0x57, 0x58, 0x51, 0x51, 0x09, 0x46, 0x17, 0x46, 0x54, 0x5A, 0x59, 0x59, 0x1F, 0x48, 0x32, 0x5B, 0x6B, 0x7C, 0x75, 0x6E, 0x7E, 0x6E, 0x2F, 0x77, 0x4F, 0x7A, 0x71, 0x43, 0x2B, 0x26, 0x89, 0xFE, 0x00]
result = ''

for i in range(33):
result += chr(ord(T1[i])^T2[i]^(2 * i + 65))
print(result)

解出flag

1
flag{c0n5truct0r5_functi0n_in_41f}

总结:本题伪代码非常简单,但是可能会遗漏s1又进行了一次异或这个关键条件,所以,要常常交叉引用一下看这个s1是否在别处进行了引用,不能只看明面上的伪代码,要仔细找到题目中每一个关键条件,只有这样才能解出真正的flag。

第三周总结

第三周整体难度不高,主要是知识上的积累,题目方面变得更加有难度,同时更考验细心程度,这可能就是逆向的解题关键,仔细分析伪代码,认真找题目要点,结合条件编写脚本,这就是我这一周最大的收获了吧。(弱弱问一句,第三周压缩包里是只有base和xor是题目吗,其他都是给我们看一下这些函数内部构成吗?)