zoukankan      html  css  js  c++  java
  • 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

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

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

      

       反汇编代码

       

  • 相关阅读:
    cf C. Vasya and Robot
    zoj 3805 Machine
    cf B. Vasya and Public Transport
    cf D. Queue
    cf C. Find Maximum
    cf B. Two Heaps
    cf C. Jeff and Rounding
    cf B. Jeff and Periods
    cf A. Jeff and Digits
    I Think I Need a Houseboat
  • 原文地址:https://www.cnblogs.com/onetrainee/p/12576764.html
Copyright © 2011-2022 走看看