图书目录

目录

第1章  一切都在计划之中——真正理解计算机的工作原理   1

1.1  完美的周六计划   1

1.1.1  步骤和测试   2

1.1.2  决定总是具有二元性   3

1.1.3  计算机像我们一样思考   4

1.2  如果这是真的   4

1.3  将汇编语言编程比作方块舞   4

1.4  将汇编语言编程比作棋盘游戏   5

1.4.1  代码和数据   6

1.4.2  地址   7

1.4.3  总结   7

第2章  外星人基地——理解二进制和十六进制   9

2.1  新数学怪兽的回归   9

2.1.1  使用火星文计数   10

2.1.2  剖析火星数字   12

2.1.3  数基的本质   13

2.2  八进制:鬼精灵如何偷走8和9   13

2.3  十六进制:解决数字短缺问题   17

2.4  从十六进制到十进制以及从十进制到十六进制   20

2.4.1  从十六进制到十进制   21

2.4.2  从十进制到十六进制   22

2.5  练习!练习!练习!   23

2.6  十六进制算术   24

2.6.1  列和进位   26

2.6.2  减法和借位   27

2.6.3  跨多列借位   28

2.6.4  重点是什么?   29

2.7  二进制   29

2.7.1  二进制值   31

2.7.2  为什么是二进制   33

2.8  十六进制作为二进制的简写   33

2.9  准备计算   35

第3章  揭开面纱——了解计算机的真实面貌   36

3.1  RAX寄存器,我们几乎不了解   36

3.2  开关、晶体管和内存   38

3.2.1  如果敌方陆路来袭,则点亮一盏灯   38

3.2.2  晶体管开关   39

3.2.3  令人难以置信的比特缩小现象   41

3.2.4  随机访问   42

3.2.5  内存访问时间   44

3.2.6  字节、字、双字和四字   44

3.2.7  排成一排的精美芯片   45

3.3  CPU和装配线   48

3.3.1  与内存对话   48

3.3.2  搭乘数据总线   49

3.3.3  寄存器   50

3.3.4  装配线   51

3.4  遵循计划的盒子   51

3.4.1  获取并执行   52

3.4.2  CPU的内部结构   53

3.4.3  改变路线   55

3.5  什么与如何:架构和微架构   56

3.5.1  不断演变的架构   56

3.5.2  地下室的秘密机器   57

3.6  工厂经理   58

3.6.1  操作系统:转角办公室   59

3.6.2  BIOS:软件不“软”   59

3.6.3  多任务魔法   60

3.6.4  提升至内核   61

3.6.5  内核爆炸   62

3.6.6  计划   63

第4章  寻址、寻址、寻址——寄存器、内存寻址及了解数据的位置   64

4.1  内存模型的乐趣   64

4.1.1  16位能“买到”64KB   65

4.1.2  兆字节(MB)的本质   68

4.1.3  向后兼容和虚拟86模式   69

4.1.4  16位的视野限制   70

4.2  分段的本质   71

4.2.1  一条地平线,而不是一个具体位置   74

4.2.2  使用16位寄存器生成20位地址   74

4.3  分段寄存器   76

4.3.1  分段寄存器和x64   77

4.3.2  通用寄存器   77

4.3.3  寄存器的高位和低位   79

4.3.4  指令指针   81

4.3.5  标志寄存器   82

4.3.6  数学协处理器及其寄存器   82

4.4  四种主要的汇编编程模型   83

4.4.1  实模式平面模型   83

4.4.2  实模式分段模型   85

4.4.3  32位保护模式平面模型   87

4.4.4  64位长模式编程模型   89

第5章  汇编的正确方式——汇编语言程序的开发过程   91

5.1  编程的96种方法   91

5.2  文件及其内容   92

5.2.1  二进制文件与文本文件   92

5.2.2  使用GHex十六进制编辑器查看二进制文件内部   94

5.2.3  解释原始数据   97

5.2.4  字节顺序   98

5.3  输入文本,输出代码   102

5.3.1  汇编语言   102

5.3.2  注释   104

5.3.3  当心“只写源代码”!   105

5.3.4  目标代码、链接器和库   106

5.3.5  可重定位性   108

5.4  汇编语言的开发过程   108

5.4.1  工作目录的规则   110

5.4.2  编辑源代码文件   110

5.4.3  汇编源代码文件   111

5.4.4  汇编器错误   111

5.4.5  回到编辑器   113

5.4.6  编译器警告   113

5.5  链接目标代码文件   114

5.5.1  链接器错误   115

5.5.2  测试EXE文件   115

5.5.3  错误与缺陷   116

5.5.4  调试器和调试   117

5.6  走进汇编语言的世界   117

5.6.1  安装软件   118

5.6.2  步骤1:在编辑器中编辑程序   120

5.6.3  步骤2:使用NASM汇编程序   121

5.6.4  步骤3:使用ld链接程序   123

5.6.5  步骤4:测试可执行文件   124

5.6.6  步骤5:在调试器中观察它的运行   124

第6章  一个可使用工具的立足之地——Linux和塑造你的工作方式的工具   126

6.1  集成开发环境(IDE)   126

6.2  SASM简介   128

6.2.1  配置SASM   129

6.2.2  SASM的字体   130

6.2.3  使用编译器链接   130

6.2.4  SASM速览   132

6.2.5  SASM的编辑器   134

6.2.6  SASM对你的代码有何要求   134

6.3  Linux和终端   135

6.3.1  Linux控制台   136

6.3.2  Konsole中的字符编码   136

6.3.3  三个标准UNIX文件   138

6.3.4  I/O重定向   139

6.3.5  简单文本过滤器   141

6.3.6  使用SASM内部的标准输入和标准输出   142

6.3.7  使用转义序列进行终端控制   142

6.3.8  为什么不使用GUI应用程序   143

6.4  使用Linux Make   145

6.4.1  依赖   145

6.4.2  当文件是最新的   147

6.4.3  依赖链   148

6.4.4  调用Make   149

6.4.5  为Make创建自定义按键绑定   150

6.4.6  使用touch强制构建   151

6.5  使用SASM进行调试   152

第7章  遵循你的指令——近距离观察机器指令   154

7.1  构建自己的沙箱   154

7.2  指令及其操作数   157

7.3  源操作数和目标操作数   157

7.3.1  即时数据   158

7.3.2  寄存器数据   160

7.3.3  内存数据和有效地址   162

7.3.4  混淆数据及其地址   162

7.3.5  内存数据的大小   163

7.3.6  糟糕的旧时光   164

7.4  团结在“标志”周围   164

7.4.1  标志礼仪   167

7.4.2  在SASM中观察标志   167

7.4.3  使用INC和DEC进行加1和减1操作   167

7.4.4  标志如何改变程序的执行   169

7.4.5  如何检查SASM中的变量   170

7.5  有符号值和无符号值   172

7.5.1  二进制补码和NEG   172

7.5.2  符号扩展和MOVSX   174

7.6  隐式操作数和MUL   176

7.6.1  MUL和进位标志   177

7.6.2  使用DIV进行无符号除法   178

7.6.3  MUL和DIV速度很慢   179

7.7  阅读和使用汇编语言参考   180

7.7.1  复杂记忆的备忘录   180

7.7.2  初学者的汇编语言参考   181

7.7.3  标志   181

7.8  NEG取反(二进制补码,即乘以-1)   182

7.8.1  受影响的标志   182

7.8.2  有效形式   182

7.8.3  示例   182

7.8.4  备注   182

7.8.5  有效形式   183

7.8.6  操作数符号   183

7.8.7  示例   184

7.8.8  备注   184

7.8.9  这里没有提到的内容……   185

第8章  我们崇高的目标——创建有效的程序   186

8.1  汇编语言程序的骨架   186

8.1.1  初始注释块   188

8.1.2  .data部分   188

8.1.3  .bss部分   189

8.1.4  .text部分   189

8.1.5  标签   190

8.1.6  初始化数据的变量   191

8.1.7  字符串变量   191

8.1.8  使用EQU和$推导字符串长度   193

8.2  通过栈后进先出   194

8.2.1  每小时500个盘子   195

8.2.2  倒置堆叠   196

8.2.3  压入指令   197

8.2.4  POP指令登场   198

8.2.5  PUSHA和POPA已停用   199

8.2.6  压入和弹出的详细信息   200

8.2.7  短期存储   201

8.3  通过SYSCALL使用Linux内核服务   202

8.3.1  通过SYSCALL指令使用x64内核服务   202

8.3.2  ABI与API的区别   203

8.3.3  ABI的寄存器参数方案   203

8.3.4  通过SYSCALL退出程序   204

8.3.5  被SYSCALL破坏的寄存器   205

8.4  设计一个不平凡的程序   205

8.4.1  定义问题   206

8.4.2  从伪代码开始   207

8.4.3  持续改进   207

8.4.4  那些不可避免的惊讶时刻   211

8.4.5  扫描缓冲区   212

8.4.6  差一错误   214

8.4.7  从伪代码到汇编代码   215

8.4.8  SASM输出窗口的陷阱   218

8.4.9  进一步学习   218

第9章  位、标志、分支和表——逐步驶入汇编编程的主航道   219

9.1  位就是位(字节也是位)   219

9.1.1  位编号   220

9.1.2  最合乎逻辑的做法   220

9.1.3  AND指令   221

9.1.4  掩码位   221

9.1.5  OR指令   223

9.1.6  XOR(异或)指令   223

9.1.7  NOT指令   224

9.1.8  分段寄存器没有逻辑   225

9.2  移位   225

9.2.1  通过什么移位   226

9.2.2  移位的工作原理   226

9.2.3  将位放入进位标志   227

9.2.4  旋转指令   227

9.2.5  通过进位标志旋转位   228

9.2.6  将已知值设置到进位标志中   228

9.3  位操作实战   229

9.3.1  将一字节拆分成两个“半字节”   232

9.3.2  将高半字节移入低半字节   232

9.3.3  使用查找表   233

9.3.4  通过移位和加法进行乘法   234

9.4  标志、测试和分支   237

9.4.1  无条件跳转   237

9.4.2  有条件跳转   238

9.4.3  在缺少条件的情况下跳转   239

9.4.4  标志   240

9.4.5  使用CMP进行比较   240

9.4.6  跳转指令的“丛林”   241

9.4.7  “大于”与“高于”   241

9.4.8  使用TEST查找1位   243

9.4.9  使用BT寻找0位   244

9.5  x64长模式内存寻址详解   244

9.5.1  有效地址计算   246

9.5.2  位移   247

9.5.3  x64位移大小问题   247

9.5.4  基址寻址   248

9.5.5  基址 + 位移寻址   248

9.5.6  基址 + 索引寻址   248

9.5.7  索引×比例+位移寻址   249

9.5.8  其他寻址方案   251

9.5.9  LEA:绝密数学机器   253

9.6  字符表转换   254

9.6.1  转换表   254

9.6.2  使用MOV或XLAT进行转换   256

9.7  用表代替计算   261

第10章  分而治之——使用过程和宏来应对程序复杂性   262

10.1  层层嵌套   263

10.2  调用和返回   272

10.2.1  调用中的调用   274

10.2.2  意外递归的危险   275

10.2.3  需要警惕的标志相关错误   276

10.2.4  过程及其所需的数据   278

10.2.5  保存调用者的寄存器   278

10.2.6  在Linux系统调用中保存寄存器   280

10.2.7  PUSHAD和POPAD已废弃   281

10.2.8  本地数据   283

10.2.9  在过程定义中放置常量数据   283

10.2.10  更多表技巧   285

10.3  本地标签和跳转的长度   287

10.3.1  强制本地标签访问   290

10.3.2  短、近和远跳转   290

10.4  构建外部过程库   291

10.4.1  当工具达到极限时   292

10.4.2  在SASM中使用包含文件   292

10.4.3  SASM包含文件的存储位置   299

10.4.4  创建包含文件库的最佳方法   300

10.4.5  独立汇编和模块   301

10.4.6  全局和外部声明   301

10.4.7  全局变量和外部变量的机制   303

10.4.8  将库链接到程序中   313

10.4.9  太多过程和库会造成危险   313

10.5  制作过程的艺术   314

10.5.1  可维护性和重用性   314

10.5.2  决定什么应该是一个过程   315

10.5.3  使用注释标题   316

10.6  Linux控制台中的简单光标控制   317

10.7  创建和使用宏   325

10.7.1  宏定义的机制   326

10.7.2  定义带参数的宏   332

10.7.3  调用宏的机制   333

10.7.4  宏内的本地标签   334

10.7.5  宏库作为包含文件   335

10.7.6  宏与过程:优点和缺点   335

第11章  字符串及其他——那些令人惊叹的字符串指令   337

11.1  汇编语言字符串的概念   338

11.1.1  彻底改变你的“字符串感”   338

11.1.2  源字符串和目标字符串   339

11.1.3  文本显示虚拟屏幕   339

11.2  REP STOSB:软件机关枪   347

11.2.1  机关枪式操作虚拟显示   348

11.2.2  执行STOSB指令   348

11.2.3  STOSB和方向标志DF   349

11.2.4  定义显示缓冲区中的行   350

11.2.5  将缓冲区发送到Linux控制台   351

11.3  半自动武器:没有REP的STOSB   351

11.3.1  谁减少了RCX?   352

11.3.2  LOOP指令   352

11.3.3  在屏幕上显示标尺   353

11.3.4  MUL不是IMUL   354

11.3.5  Ruler的教训   355

11.3.6  STOS的四种大小   355

11.3.7  再见,BCD算术   356

11.4  MOVSB:快速块复制   356

11.4.1  DF和重叠阻挡移动   357

11.4.2  单步REP字符串指令   360

11.5  将数据存储到不连续的字符串中   361

11.5.1  显示一个ASCII表   361

11.5.2  嵌套指令循环   366

11.5.3  当RCX变为0时跳转   367

11.5.4  关闭内循环   367

11.5.5  关闭外循环   368

11.5.6  回顾showchar   369

11.6  命令行参数、字符串搜索和Linux栈   369

11.6.1  显示SASM的命令行参数   370

11.6.2  使用SCASB进行字符串搜索   373

11.6.3  REPNE与REPE   374

11.6.4  无法将命令行参数传递给SASM中的程序   375

11.7  栈及其结构和使用方法   375

11.7.1  直接访问栈   377

11.7.2  程序序言和结语   380

11.7.3  栈上的寻址数据   381

11.7.4  不要弹出   382

第12章  转向C——调用C语言编写的外部函数   383

12.1  GNU   384

12.1.1  瑞士军刀编译器   385

12.1.2  以GNU方式构建代码   385

12.1.3  SASM使用GCC   387

12.1.4  如何在汇编工作中使用gcc   387

12.1.5  为什么不使用gas   388

12.2  链接标准C库   389

12.2.1  C调用约定   390

12.2.2  调用者、被调用者和破坏者   390

12.2.3  设置栈帧   392

12.2.4  在结语中销毁栈帧   393

12.2.5  栈对齐   393

12.2.6  通过puts()输出字符   395

12.3  使用printf()格式化文本输出   397

12.3.1  将参数传递给printf()   398

12.3.2  printf()需要在RAX中加上前置0   399

12.3.3  你需要使用-no-pie选项   400

12.4  使用fgets()和scanf()输入数据   400

12.5  成为Linux时间领主   406

12.5.1  C库的时间机器   406

12.5.2  从系统时钟获取time_t值   407

12.5.3  将time_t值转换为格式化字符串   408

12.5.4  生成单独的本地时间值   409

12.5.5  使用MOVSD复制glibc的tm结构   409

12.6  理解AT&T指令助记符   413

12.6.1  AT&T助记符约定   413

12.6.2  AT&T内存引用语法   415

12.7  生成随机数   416

12.7.1  使用srand()为生成器设定种子   416

12.7.2  生成伪随机数   417

12.7.3  相比之下有些比特更随机   423

12.7.4  调用寄存器中的地址   425

12.7.5  使用puts()将一个裸换行符发送到控制台   426

12.7.6  如何向libc函数传递六个以上的参数   426

12.8  C语言如何处理命令行参数   427

12.9  简单文件 I/O   430

12.9.1  使用sscanf()将字符串转换为数字   431

12.9.2  创建和打开文件   432

12.9.3  使用fgets()从文件读取文本   434

12.9.4  使用fprintf()将文本写入文件   436

12.9.5  将过程收集到库中的注意事项   437

12.10  永远学习,永远不要停下来   444

12.10.1  何去何从   445

12.10.2  走出原点   446

(以下内容可扫描封底二维码下载)

附录A  Insight调试器的回归   447

附录B  部分x64指令参考   454

附录C  字符集图表   509