2020.03.16-2020.03.22

32位软件逆向技术

数据结构

计算机存储、组织数据的方式。确定数据结构后,算法就很容易得到,有时候也根据特定算法来判断数据结构

局部变量

局部变量分配空间时通常会使用栈和寄存器

利用栈存放局部变量

变量分配与清除的形式:

1
2
3
sub esp,n
...用[ebp-xxxx]寻址
add esp,n
1
2
3
add esp,-n
...用[ebp+xxxx]寻址
sub esp,-n
  1. (省空间)
1
2
3
push reg  ;(相当于sub esp,4)
...用[ebp-xxxx]寻址
pop reg

局部变量的起始值是随机的,是其他函数执行后留在栈中的垃圾数据,因此需要进行初始化,一种方法是通过mov指令,另一种是用push,如:“push 5”

利用寄存器存放局部变量

除了栈占用2个寄存器,编译器会利用其他6个通用寄存器尽可能的存放局部变量,如果寄存器不够用会存到栈中

全局变量

全集变量通常位于.data区块的一个固定地址处,访问时一般会用一个固定的硬编码地址直接对内存进行寻址(←使用这种方式比较容易识别出这是一个全局变量。)
一般编译器会将全局变量放到可读写的区块里,如果放到只读区块里就是一个常量

数组

访问一般时通过基址加变址寻址实现的(基址可以是常量也可以是寄存器)

虚函数

虚函数是在程序运行时定义的函数。(c++)
虚函数的地址不能在编译时确定,只能在调用即将进行时确定。所有对虚函数的引用通常放在一个专用数组-虚函数表(virtual table)中,数组中的元素存放的就是类中数表的地址。

虚函数 是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数
基类:当创建一个类时,您不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类。
继承:代表了 is a 关系。例如,哺乳动物是动物,狗是哺乳动物,因此,狗是动物,等等。(runoob.com)

程序通过两次寻址得到虚函数的地址然后执行

控制语句

if-else

通常为:

1
2
cmp a,b
jz/jnz(相当于je/jne) xxxx

整数用cmp指令比较,浮点值用fcom、fcomp等指令比较
许多情况下,编译器用test或or之类的较短的逻辑指令来替换cmp指令

switch-case

编译器会编译出一组由不同关系运算组成的语句

如果编译时设置优化选项为“Maximize Speed”,编译器会用"dec eax"代替cmp指令。如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
push ecx
lea eax,[esp]
push eax
push 0040804c
call 004010A1 ;scanf
mov eax,[esp+8] ;将输入的值传给eax
add esp,00000008

dec dax ;检查eax是否为1h
je 00401055
;zf记录相关指令执行后结果是否为0,如果为0那么ZF=1,如果不为0那么ZF=0
;je:等于则转移(检测zf=1)

(同理也可使用sub eax,xxxxxx判断是否为其他值)
....

转移指令机器码的计算

短转移:无条件和有条件的机器码均为2字节,范围为-128~127(2^8)
长转移:无条件的机器码为5字节,条件转移为6字节。因为无条件用一个字节(jmp)表示转移类型;条件转移用2个字节(如je、jns),用其他四个字节表示偏移量
子程序调用指令:调用的参数不涉及寄存器、栈等值的类似于长转移;涉及的例如“call dword ptr [eax+2]” 比较复杂,不表(…)
条件转移指令的范围是16位遗留下的。
cpu开发人员只给目的地址分配了1字节(8位),将跳转的长度限制在225字节之内

无条件短转移的机器码形式为:“EBxx”。B00H~EB4F是向后转移,EB80~EBFFH是向前转移

转移指令的机器码形式为:
位移量=目的地址-起始地址-跳转指令本身长度
转移指令机器码=转移类别机器码(如前文中的EB)+位移量

https://brubbish.github.io/34199.html#根据位移进行转移的意义

jmp 401005对应机器码EB03

python爬虫学习

记录的有点乱,还没整理好
学习的原因是 看了某个论坛上一个学了几周爬虫 后用其爬的结果回答的答主 遂感牛逼,再加上有时候想学点别的作为调节(🤦‍)
https://brubbish.github.io/710f8e5f.html