C++中hack private权限

  众所周知,面向对象的语言都会有类似于private,protected,public之类的权限控制。其实如前一篇blog所讲(http://blog.csdn.net/cracker_zhou/article/details/70956249) :在C++中,权限只是为了在代码的运行期阻止获得变量或者某个函数的地址而达到不能调用的作用。言外之意就是只要能搞到内存地址,什么都好说。下面提供几种hack私有成员变量地址的方法。
  
  1. 计算成员变量内存偏移
  我相信对C++内存分配懂一点的小伙伴都知道这种方法。

class B
{
private:
    int b;
    std::string x;
    char* ch;
public:
    int getint(){ return b; }
    std::string getstr(){ return x; }
    char* getch(){ return ch; }
};

int main() 
{
    B b;
    *(int*)&b = 20;
    std::string* str = (std::string*)((long)&b + sizeof(int));
    str->append("zhou");
    char** ch = (char**)((long)str+sizeof(std::string));
    *ch = new char('a');
    std::cout << "int:" << b.getint() << std::endl   // int:20
        << "str:" << b.getstr().c_str() << std::endl // str:"zhou"
        << "char*:" << *b.getch() << std::endl;   // char*:'a'
    delete *ch; 
    return 0;
}

  2. 类的等效替换
  上面通过计算成员变量的偏移确实可以hack内存地址,但是弊端也很明显成员变量越多,后面就越需要计算偏移,而且二重指针也很容易搞出事情。那么怎么办呢?我们只需要自定义一个拥有一样内存分布的数据结构即可。

class B
{
private:
    int b;
    std::string x;
    char* ch;
public:
    int getint(){ return b; }
    std::string getstr(){ return x; }
    char* getch(){ return ch; }
};

// 在此模拟一个与class B拥有相同内存分布的struct,但权限都是开放的
typedef struct
{
public:
    int b;
    std::string x;
    char* ch;
}Proof;

int main() 
{
    B b;
    Proof* proof = (Proof*)&b;
    proof->b = 10;
    proof->x.append("zhou");
    proof->ch = new char('a');
    std::cout << "int:" << b.getint() << std::endl  // int:10
        << "str:" << b.getstr().c_str() << std::endl  // str:"zhou"
        << "char*:" << *b.getch() << std::endl;  // char*:'a'
    delete proof->ch; 
    return 0;
}

 3. 元编程(meta programming)
 之前一直以为元编程只有python,js这种动态语言中有,但是没想到C++的元编程也很强大。概念是一样的,元编程(C++操作template,python操作type)的存在都是为了动态生成类。但是我发现一个问题:C++的模板类中传递参数时并不会校验权限问题。(比如上面中类B,当我们在运行时使用&B::b获得偏移时会直接报权限错误,但是在模板中传参并不会这样!)

    // 这是一个简单的类模板传值的例子
    template<typename T,typename T V>
    typename T f(T)
    {
        return V;
    }

    int main() 
    {
        std::cout << f<int,10>(int());  // 10
        return 0;
    }
那么如何把类成员地址传出去呢?原理是相同的。
    struct A {
    private:
        int a;
    };

    // tag used to access A::a
    struct A_f {
        using type = int A::*;
    };

    template<typename Tag, typename Tag::type M>
    struct Rob {
        friend typename Tag::type get(Tag) {
            return M;
        }
    };
    template struct Rob<A_f, &A::a>;

    int main() 
    {
        A a;
        a.*get(A_f()) = 20;  // 传入A_f()纯属为了把类型传入,满足template的语法要求
        std::cout << a.*get(A_f());  // 得到地址后可以进行get和set操作
        return 0;
    }

  第三种方案实在强大,版权归外国某大神所有( http://stackoverflow.com/users/34509/johannes-schaub-litb ),它在stackoverflow原回答也贴出来(http://stackoverflow.com/questions/424104/can-i-access-private-members-from-outside-the-class-without-using-friends#3173080)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值