reversing逆向分析技术总结-(2)

64位软件逆向技术

(x64通常是指AMD64, Intel64的合称

1 寄存器

RAX, RBX, RCX, RDX, R8 ~ R15, 扩充8个128位寄存器XMM(一般用来优化代码)
寄存器是向下兼容的, 用后缀DWORD, WORD, BYTE, 例如R8(64位), R8D(32位), R8W(16位), R8B
在这里插入图片描述

2 函数
  • (1) 栈平衡
  • (2) 启动函数 一般反汇编成start, 可在start中找到main函数的调用
    编译器设定多线程DLL(/MD)时, IDA会显示main符号, 可直接定位
    设置为/MT多线程时, 不会显示main, 需要通过特征定位main, call cs:exit上的第一个call是main
    在这里插入图片描述
    • (3) 调用约定
      x64程序只有一种调用约定, 快速调用约定. 6个参数保存在寄存器里, 其余参数从右往左入栈, 调用方负责平衡栈空间. RDI, RSI, RDX, RCX, R8, R9, 大于8或不是1,2,4,8字节的参数由引用(地址)传递. XMM用于传递浮点数参数. 如果子函数需要调用RCX, RDX, R8, R9则调用者提前申请32字节栈空间通过栈来传参.(注意这32字节是默认的预留栈空间, 但是需要调用者申请使用)
      在这里插入图片描述
      在这里插入图片描述
    • (4) 参数传递
      • 多参数传参
        在这里插入图片描述
      • 结构体的传参
        • 不超过8字节
          结构体直接存进寄存器, 用高低32位访问成员, 逆向分析时可以借此特征判断结构体数据
        • 超过8字节
          先复制结构体到栈, 再通过结构体地址当成函数参数(通过寄存器比如rcx, 不够的时候是放到栈里)传递给函数(引用传递), 被调用的函数通过结构体地址+偏移取结构体内容
          不过需要注意, 预留栈空间和局部变量在栈中是有差异的, 调用一个函数, 返回地址之前的是预留栈空间, 之后的是开辟出来存局部变量的空间, 并且局部变量中也有隐含的预留空间, 即4个参数的存储空间, 比如
          在这里插入图片描述对应的栈结构大概如下, rsp0是main函数调用fun函数之前的栈顶
          在这里插入图片描述
    • thiscall传递
      C++成员函数调用约定, 调用时会隐含传递一个this指针
    • 函数返回值
      8字节返回值在RAX, 浮点数在MMX0, 大于8字节, 返回栈空间地址, 作为引用参数返回
3 数据结构
  • 局部变量
    局部变量保存在栈空间中, 注意与预留栈空间的空间关系, 预留栈空间在低地址, 局部变量在高地址
  • 全局变量
    编译时就会被固定, 常用固定地址访问; 另外注意, 全局变量也是先定义的在低地址, 后定义的在高地址, 据此可还原变量定义顺序
  • 数组
    数组中数据是从低到高地址排列(结合写C程序的经验, 是常理之中)
    访问数组通常用数组访问公式, 数组下标已知会被优化成直接计算偏移量, 未知则继续用寻址公式
    特征
    + [数组首地址 + n]
    + [数组首地址 + 寄存器 * n]
    遇到以上特征就可大致判断为数组访问, 优化过后也可能有XMM寄存器的使用
4 控制语句
  • if 语句
    通常编译器会对表达式的结果取反操作, 比如 > 取成 <= 即jle
  • if else 语句
    特征: jxx跳转, 跳转目的有jmp指令
  • if else if else
    特征: jxx跳转目的有jmp指令, else if跳转目的有jmp指令, else 代码结尾没有jmp指令, 并且所有jmp目的地址相同
  • switch-case
    分支数 < 6 时编译器用if else实现, 当分支数 >= 6时, 编译器会优化
    优化后用一维数组表示case表, 用下表寻址代码块, 而且当case的间隔较小时, 会完整的列出1-n表格, 其中缺失的数字会用default地址替代
    在这里插入图片描述在这里插入图片描述 当case用if来实现时, 如果判断次数较多, 会优化成判定树形式
    在这里插入图片描述在这里插入图片描述
  • 转移指令机器码计算
    (1) call / jmp direct (与x86类似, 用位移量寻址)
    位移量 = 目的地址 - 起始地址 - 跳转指令长度
    (2) call /jmp memory direct
    32位系统, 跳转指令机器码 + 绝对地址
    64位系统, 跳转指令机器码 + 相对地址
5 循环语句
  • do while 循环, 有一个向上跳转的jxx
  • while 循环, 有jmp向上跳转, jxx向下跳转 (效率低于do while, 所以通常被优化为do)
  • for 循环, 开始有一个jmp向下, 最后有一个jmp向上, 中间有个jxx取反向下跳出循环(通常被优化为if + do循环)
6 数字运算符
  • 加减法, 同32位用lea加速程序; 另外常量折叠指编译器在编译时就计算了只涉及常量的加减乘法, 执行时直接拿结果计算即可
  • 乘法, 同32位用lea加速计算
  • 除法, 优化成等价的移位运算或乘法运算
    • 无符号除法移位优化公式
      x ≥ 0 , x 2 n = x > > n x < 0 , x 2 n = ( x + ( 2 n − 1 ) ) > > n x \ge 0, \frac{x}{2^n} = x >> n \\ x < 0, \frac{x}{2^n} = (x + (2^n - 1)) >> n x0,2nx=x>>nx<0,2nx=(x+(2n1))>>n
      实际实现时, 用到了有符号扩展+and指令的技巧, 同时概括了正负两种情况, 实现以上两个公式, 正的时候+0, 负的时候+ 2 n − 1 2^n - 1 2n1

在这里插入图片描述

  • 有符号除法
    相当于在无符号除法基础上加一个求补操作
    neg eax

  • 有符号除法, 除数正非 2 n 2^n 2n

    • 优化公式1
      x o = x ∗ c > > 64 ( 32 ) > > n , x ≥ 0 x o = ( x ∗ c > > 64 ( 32 ) > > n ) + 1 , x < 0 c > 0 , n 可 以 为 0 \frac{x}{o} = x * c >> 64(32) >> n, x \ge 0\\ \frac{x}{o} = (x * c >> 64(32) >> n) + 1, x < 0\\ c > 0, n可以为0 ox=xc>>64(32)>>n,x0ox=(xc>>64(32)>>n)+1,x<0c>0,n0
    • 优化公式2
      x o = ( x ∗ c > > 64 ( 32 ) ) + x > > n , x ≥ 0 x o = ( ( x ∗ c > > 64 ( 32 ) ) + x > > n ) + 1 , x < 0 c < 0 \frac{x}{o} = (x * c >> 64(32)) + x >> n, x \ge 0\\ \frac{x}{o} = ((x * c >> 64(32)) + x >> n) + 1, x < 0\\ c < 0 ox=(xc>>64(32))+x>>n,x0ox=((xc>>64(32))+x>>n)+1,x<0c<0
      n为右移总次数, o是除数, o = 2 n c o = \frac{2^n}{c} o=c2n, c是编译器为优化除法转成乘法计算出来的magic_num, 根据c的正负知道是采用的哪一个优化公式
  • 有符号除法, 除以负非 2 n 2^n 2n, 区别在与c取相反符号, 即优化公式1中的c是负的, 优化公式2中的c是正的, 这样就是除以负的非 2 n 2^n 2n, 此外还有一处细微差别

    • 优化公式1
      x o = x ∗ c > > 64 ( 32 ) > > n , x ≥ 0 x o = ( x ∗ c > > 64 ( 32 ) > > n ) + 1 , x < 0 c < 0 , n 可 以 为 0 \frac{x}{o} = x * c >> 64(32) >> n, x \ge 0\\ \frac{x}{o} = (x * c >> 64(32) >> n) + 1, x < 0\\ c < 0, n可以为0 ox=xc>>64(32)>>n,x0ox=(xc>>64(32)>>n)+1,x<0c<0,n0
    • 优化公式2
      x o = ( x ∗ c > > 64 ( 32 ) ) − x > > n , x ≥ 0 x o = ( ( x ∗ c > > 64 ( 32 ) ) − x > > n ) + 1 , x < 0 c > 0 \frac{x}{o} = (x * c >> 64(32)) - x >> n, x \ge 0\\ \frac{x}{o} = ((x * c >> 64(32)) - x >> n) + 1, x < 0\\ c > 0 ox=(xc>>64(32))x>>n,x0ox=((xc>>64(32))x>>n)+1,x<0c>0
      n为右移总次数, 除数o计算公式
      ∣ o ∣ = 2 n 2 64 ( 32 ) − c |o| = \frac{2^n}{2^{64(32)} - c} o=264(32)c2n
  • 无符号除法, 除以 2 n 2^n 2n
    无须判断符号, shr替代除法

  • 无符号除法, 除以非 2 n 2^n 2n
    类似有符号除法优化公式1, 不过不用判断符号, 只考虑正数情况
    注意有符号与无符号的重要区别在于, 无符号用mul, 有符号用imul
    另一种优化公式
    x o = ( x − ( x ∗ c > > 32 ( 64 ) ) > > n 1 ) + ( x ∗ c > > 32 ( 64 ) ) > > n 2 o = 2 32 ( 64 ) + n 2 32 ( 64 ) + c \frac{x}{o} = (x - (x *c >> 32(64)) >> n_1) + (x * c >> 32(64)) >> n_2 \\ o = \frac{2^{32(64) + n}}{2^{32(64) + c}} ox=(x(xc>>32(64))>>n1)+(xc>>32(64))>>n2o=232(64)+c232(64)+n

  • 取模
    通常优化为位运算和除法运算, 再经由除法运算优化

    • 除数为 2 n 2^n 2n
      优化公式1
      x % 2 n = x & ( 2 n − 1 ) , x ≥ 0 x % 2 n = ( x & ( 2 n − 1 ) ) − 1 ∣ ( ∼ ( 2 n − 1 ) ) + 1 , x < 0 x \% 2^n = x \& (2^n - 1), x \ge 0\\ x \% 2^n = (x \& (2^n - 1)) - 1 | (\sim(2^n - 1)) + 1, x < 0 x%2n=x&(2n1),x0x%2n=(x&(2n1))1((2n1))+1,x<0
      优化公式2
      x % 2 n = x & ( 2 n − 1 ) , x ≥ 0 x % 2 n = ( ( x + ( 2 n − 1 ) ) & ( 2 n − 1 ) ) − ( 2 n − 1 ) , x < 0 x \% 2^n = x \& (2^n - 1), x \ge 0\\ x \% 2^n = ((x + (2^n - 1)) \& (2^n - 1)) - (2^n - 1), x < 0 x%2n=x&(2n1),x0x%2n=((x+(2n1))&(2n1))(2n1),x<0
    • 除数为非 2 n 2^n 2n
      x % c = x − x / c ∗ c x \% c = x - x / c * c x%c=xx/cc, 余数 = 被除数 - 除数 * 商
7 虚函数

每个类生成一个虚表(假如存在虚函数)
封装, 继承, 多态相关细节放到C++逆向总结文章中


以上
之后要继续撰写关于windows和linux逆向相关的总结文章, 这只是开始, to be continued …

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值