老码识途第1章笔记

on under 二进制
1 minute read

欲向码途问大道,锵锵bit是吾刀

1.c语言中嵌入的汇编能够识别高级语言中的符号,如变量address

int i,gi;
void * address{
    _asm {
    mov address,offset _lb1
    jmp address
    }
    i=2;
_lb1:
    gi=12;
    
}


                            jmp address
004113C8 ff 25 cc 74 41 00  jmp dword ptr ds:[004174cch]

2.指针的类型信息(dword ptr,word ptr,byte ptr…)决定了赋值/读取时写/读多少字节.读/写多少字节的信息不是存放在指针 变量中,而是放到了与该地址相关的赋值指令中,如mov dword ptr [eax],0ch中的dword指明了这个信息.不同类型指针,访问字 节数不同,int *访问4字节,short *访问2字节,这样就方便我们操控一个地址,否则如果只有地址信息,每次访问它还要附加说 明访问的字节数.这时,我们也能理解指针加/减1不是加/减1字节,而是加/减长度为该指针指向类型的长度的字节数,我们也能理解 ,void *类型的指针为什么无法进行加减运算,因为它只是汇编语言中的地址,没有类型信息,加减的时候不知道加减多少字节

3.强制类型转换中有2点:

a)不能将一个short *指针变量赋值给int *变量,因为生成赋值语句的指令时,必然生成move dword指令,即写4字节,而short *的变量只有2字节`

b)指针强制类型转换的影响不是在转换的时候发生,在转换时除了赋值地址没有多余动作,起效的时是在用转换后的身份去访问内 存的时候,编译器会按转换后的指针类型产生相应代码,如下打印的结果是0xcccc000c而不是0c.

int *pi;
short si=12;
pi=(int *)&si;
printf("%x",*pi);

4.计算负数值方法:取反加1

5.call指令的偏移量计算方法:偏移量=跳转到的地址-call指令后一条指令的起始地址

00413766    e8 7a da ff ff      call 00411e5
0041376b    ...                 ...

e8代表call,7a da ff ff代表偏移量0xffffda7a,对应负数-0x2586,0x0041376b+(-0x2586)=0x00411e5

6.call指令相当于以下2条指令,在调用call之前要先push函数参数(x86),如push 1,push2,call 0x0402848,所以可以理解:在当前 函数帧栈中,第1个参数的位置在ebp+8处,因为ebp+4处的内容是call之前push的返回地址.在使用了ebp寻址的函数中,ebp+偏移量 是参数的地址,ebp-偏移量是局部变量的地址

push 返回地址
jmp 函数入口

7._cdecl是调用方清理栈,_pascal,_fastcall和_stdcall是被调用方清理栈.c语言缺省调用惯例是_cdecl,_stdcall通常用于 win32 API.更多

8.函数指针赋值的原则是:只能将与指针原型匹配的函数的入口地址赋值给它,因为不同的函数的调用约定不一定相同,如上面的7 中所示

9.数组的第1个元素的索引从0开始的原因

索引从1开始:

第1个元素的地址=首地址a
第2个元素的地址=首地址a+1x元素大小
第3个元素的地址=首地址a+2x元素大小
第i个元素的地址=首地址a+(i-1)x元素大小

索引从0开始:

第0个元素的地址=首地址a
第1个元素的地址=首地址a+1x元素大小
第2个元素的地址=首地址a+2x元素大小
第i个元素的地址=首地址a+(i)x元素大小

10.从上面9中也理解了c语言为什么会发生数组越界错误,因为它只是拿到首部地址然后加偏移量,如果索引值超出范围,那么求得 的元素地址也就超过了范围

11.一般情况下,网络字节顺序是大端,主机字节顺序是小端

12.内存中字节对齐的规律

首先选定一个盒子,然后依序将字段往盒子中放,当盒子放不下后,又用下一个盒子存放,直至所有字段都存放完毕.

相关限制条件:
a)盒子长度=min{max{sizeof(成员变量)},对齐长度}
b)字段放入盒子的可放置位置如下:
    离盒子头部偏移字节数=nxsizeof(成员变量)     (n=0,1,2,...)

13.为了让结构体更整齐以便于在不同字节序程序间传递,我们就尽量调整结构体的字段顺序,在没办法的情况下,可考虑主动添加 一些字段,防止计算机为了对齐而填充

14.一般情况下,switch比if-else快

15.ga,ge,gb…中ab无符号,gl有符号

16.在cpu保护模式下,每个执行进程(程序的一个实例)都拥有自己独立的线性地址空间,这种机制叫虚存系统

17.完成加载程序并将eip寄存器的值指向main()入口的程序称为加载器

18.重定位与模块绑定理解

relocAddr=actualBase+a
* relocAddr=*relocAddr+actualBase-expectedAddr
eg.在exe中,一般expectedAddr为0x400000(4M),dll为0x1000000(16M),在一个有重定位段的pe中,有多个a,如a=0x100,a=0x502,
表示在pe的偏移0x100,0x502处的4字节值需要修改为原来的值+(实际加载基址与期望加载基址的差)

一般elf的expectedAddr为0x8048000(约128M),linux共享库的expectedAddr为0x40000000(1G)

上面的expectedAddr可通过ida加载一个elf,查看ida中的进度条最开始位置的地址,这个地址即是elf期望的加载基址,如下图期望 加载基址为0x8048000

或者通过linux下的工具readelf -l elffile查看,如下图

20.vtune是一个收费$899的性能分析工具(可下载破解版),python下免费的其他工具可参考这里1,这里2,这里3

21.学习汇编的方法:用汇编实现开发环境所带的运行时库中的函数,如c语言中的strlen,strcpy等.然后,分析系统库实现的这些函 数.因为它们调用频繁,所以要求有很高的效率,基本都用汇编撰写.好好阅读能学到很多高级技巧,(能从高手的经典中汲取到很多营 养),再做性能实验,测试自己版本与系统版本的差异,并分析修改(这一过程可用vtune在优化方面深入实战),最后分析不同库实现 的异同和好坏,如vc,c++ builder,delphi,gcc

22.一个技巧:判断一个数能否被4整除可通过test xxx,3 je ...实现

23.对于_declspec(naked) int strlen2(char *str),_declspec(naked)属于vc的特殊关键字,表示该函数的左右括号不会生 成任何代码,此时strlen2()的代码就是汇编本身

老码识途
home
github
archive
category