CC++反汇编中几个比较重要的知识点

CC++反汇编中几个比较重要的知识点

1. C++的虚函数表

2. C++中的引用

3. C中针对switch的优化

 

1. C++的虚函数表

#include <Windows.h>
#include <iostream>
#include <stdio.h>

using namespace std;

class animal{
    virtual void speak()=0;
    virtual void name()=0;
};

class cat: public animal {
public:
    void speak() {
        cout << "miaomiaomaio~" << endl;
    }
    void name() {
        cout << "I am a cat" << endl;
    }
};

int main() {
    cat c;
    c.speak();
}

  如上代码,其对于cat类,存在两个虚函数。

  虚函数存储在虚函数表中,因此该成员的第一位是一个表的地址,里面存储了其对应的虚函数地址。

  

  我们在IDA中查看其虚函数表所在位置

  

2. C++中的引用

#include <Windows.h>
#include <iostream>
#include <stdio.h>

using namespace std;

void func(int& x) {
    cout << x << endl;
}

int main() {
    int x = 2;
    func(x);
}

  引用的本质是指针,但是又对指针做了限制,无法对指针本身指向内容进行修改。

  查看下面的反汇编代码就很好理解。

  我们查看其反汇编代码:

  func(x);
  005E20B9  lea         eax,[ebp-0Ch]   // 传入变量x的地址
  005E20BC  push        eax  
  005E20BD  call        005E145B

  cout << x << endl;

  005E1941  mov         eax,dword ptr [ebp+8]  
  005E1944  mov         ecx,dword ptr [eax]  
  005E1946  push        ecx          // 从指针中取出值来进行操作

3. C中针对switch的优化

1)常规形式

  switch被翻译成 if..else..结构

2)大表索引

#include <stdio.h>

int main(int argc, char* argv[])
{
    int s = 5;
    switch (s) {
    case 101:
        printf("101
");
        break;
    case 102:
        printf("102
");
        break;
    case 103:
        printf("103
");
        break;
    case 104:
        printf("104
");
        break;
    default:
        printf("error
");
        break;
    }
    return 0;
}

  我们查看其反汇编代码:

   switch (s) {
  00AF183F  mov         eax,dword ptr [ebp-8]  
  00AF1842  mov         dword ptr [ebp+FFFFFF30h],eax  
  00AF1848  mov         ecx,dword ptr [ebp+FFFFFF30h]  
  00AF184E  sub         ecx,65h  
  00AF1851  mov         dword ptr [ebp+FFFFFF30h],ecx  
  00AF1857  cmp         dword ptr [ebp+FFFFFF30h],3  
  00AF185E  ja          00AF18A9  
  00AF1860  mov         edx,dword ptr [ebp+FFFFFF30h]  
  00AF1866  jmp         dword ptr [edx*4+00AF18CCh] 

  其将变量S-101h,获取索引(0,1,2,3),然后判断如果大于3,直接跳到default域中,否则根据索引去 00AF18CCh 这张表中获取跳转地址。

  我们查看这张表中的内容:

  

  因此这样可以极大的加快查找效率

3)大表+小表索引

  上面大表索引有一个缺点,就是当出现断层时,中间很大一块要使用default的地址填补,比如 1,2,3,4,101,102,103,104;

  此时就是采用大表+小表的索引形式,构建两张大表。

  我们按例子中的思路再来构建 1,2,3,4,101,102,103,104 这种情况

    switch (s) {
  0088504F  mov         eax,dword ptr [ebp-8]  
  00885052  mov         dword ptr [ebp+FFFFFF30h],eax  
  00885058  mov         ecx,dword ptr [ebp+FFFFFF30h]  
  0088505E  sub         ecx,1  
  00885061  mov         dword ptr [ebp+FFFFFF30h],ecx  
  00885067  cmp         dword ptr [ebp+FFFFFF30h],67h  
  0088506E  ja          008850FE  
  00885074  mov         edx,dword ptr [ebp+FFFFFF30h]  
  0088507A  movzx       eax,byte ptr [edx+00885138h]  
  00885081  jmp         dword ptr [eax*4+00885114h]

  可以看到索引值从00885138h这张表中获取,一个字节,拿到该索引值后又从00885114h这张表中获取。

      

   这样本来需要四个字节存储的空白只需要一个字节就够了。

4)放弃大表+小表,重新回归大表。

  上面那种仍然有很多空白,我们继续设想,如果更加极端的情况呢?

  现在我们假设 1,2,3,4,101,102,103,104,10001,10002,10003,10004

  在这种情况下,我们继续观察,发现其放弃采用两张表的形式,又回归到一张表中了。

  其根据大小按索引重新排序,回归到一张表中。

  

   反汇编代码

   

原文地址:https://www.cnblogs.com/onetrainee/p/12576764.html