代码隐藏到数据之中的“骚”操作

函数指针,可以为一个函数重命名,代替其进行函数调用,而我们也用函数指针做过简单的C语言面向对象编程:函数指针与C的面向对象 。书上说(很多人这样说):函数指针是为了便于分层设计、利于系统抽象、降低耦合度以及使接口与实现分开。这从正向的角度来说依旧是总结性的,而不是本质性的,函数指针并非是针对于面向对象的,从本质来说它更是用来做数据与代码转换的,怎么说?
如果我们设计了个功能函数比较重要,也不想其他人轻易通过反汇编获取自己的函数设计思想,或者是为了给整个软件重要部分“加密保护”。有一种方式就是将对应函数的代码应藏到数据段中去,而调用函数时使用函数指针将该段数据解析成指令直接调用。我们以一个简单的例子来做分析:

#include "stdafx.h"

int add(int x, int y)
{
    return (x+y);
}
int main(int argc, char* argv[])
{
    printf("%d\n",add(10,20));
    return 0;
}

假设这里的add()函数是一个需要进行保护/保密的函数,我们普通调用是通过函数名加参数直接调用,但是经过隐藏代码到数据段的方式就需要使用函数指针来调用了,如何操作?我们先来看该函数的反汇编代码和硬编码:

;add:
55                   push        ebp
8B EC                mov         ebp,esp
83 EC 40             sub         esp,40h
53                   push        ebx
56                   push        esi
57                   push        edi
8D 7D C0             lea         edi,[ebp-40h]
B9 10 00 00 00       mov         ecx,10h
B8 CC CC CC CC       mov         eax,0CCCCCCCCh
F3 AB                rep stos    dword ptr [edi]
8B 45 08             mov         eax,dword ptr [ebp+8]
03 45 0C             add         eax,dword ptr [ebp+0Ch]
5F                   pop         edi
5E                   pop         esi
5B                   pop         ebx
8B E5                mov         esp,ebp
5D                   pop         ebp
C3                   ret

由于汇编指令与硬编码对应,所以我们将硬编码提取出来存放到一个数组中去,然后在调用时用函数指针读取该数组,就相当于读取一条条指令(而这里是直接读取机器指令),其功能是完全相同的。如下所示,add数组看起来只是普通的数据:

#include "stdafx.h"

char add[] = {
    0X55,             
    0X8B, 0XEC,          
    0X83, 0XEC, 0X40,       
    0X53,             
    0X56,             
    0X57,             
    0X8D, 0X7D, 0XC0,       
    0XB9, 0X10, 0X00, 0X00, 0X00, 
    0XB8, 0XCC, 0XCC, 0XCC, 0XCC, 
    0XF3, 0XAB,          
    0X8B, 0X45, 0X08,       
    0X03, 0X45, 0X0C,       
    0X5F,             
    0X5E,             
    0X5B,             
    0X8B, 0XE5,          
    0X5D,             
    0XC3,             
};
int main(int argc, char* argv[])
{
    int (*pAdd)(int,int);
    pAdd = (int (__cdecl *)(int,int))&add;//将数据以指令的方式解析
    //pAdd = (int (*)(int,int))&add; 
    //__cdecl属于默认,可以省略
    printf("%d\n",pAdd(10,21));
    return 0;
}

而在这种经过代码隐藏的程序中,其反汇编中就不能直接找到add()函数了,而必须要经过全局数组的地址来找硬编码,再经过硬编码转换至汇编进行分析,在数据量非常庞大的情况下这种方式就可以很有效地进行简单的代码隐藏(因为从本质来讲数据和代码并无两样)。

;main:
;...

;从数据区读取指令数组
;将数据区对应指令地址存放到函数栈中[ebp-4],然后修改eip为[ebp-4]进行调用
00401038 C7 45 FC 18 73 42 00 mov         dword ptr [ebp-4],offset add (00427318)
0040103F 8B F4                mov         esi,esp
00401041 6A 15                push        15h
00401043 6A 0A                push        0Ah
00401045 FF 55 FC             call        dword ptr [ebp-4]
00401048 83 C4 08             add         esp,8

0040104B 3B F4                cmp         esi,esp
0040104D E8 DE 00 00 00       call        __chkesp (00401130)
00401052 50                   push        eax
00401053 68 1C 20 42 00       push        offset string "%d\n" (0042201c)
00401058 E8 53 00 00 00       call        printf (004010b0)
0040105D 83 C4 08             add         esp,8

;...
00401072 C3                   ret

;add()对应在数据段的指令
00427318  55 8B EC 83 EC 40  U嬱冹@
0042731E  53 56 57 8D 7D C0  SVW峿.
00427324  B9 10 00 00 00 B8  ......
0042732A  CC CC CC CC F3 AB  烫烫螳
00427330  8B 45 08 03 45 0C  婨..E.
00427336  5F 5E 5B 8B E5 5D  _^[嬪]
0042733C  C3 00 00 00 00 00  ......

测试结果:

这里写图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值