首页
社区
课程
招聘
[原创]SMC技术浅析
发表于: 2020-11-27 15:47 7427

[原创]SMC技术浅析

2020-11-27 15:47
7427

之前弄了一道逆向题,在研究的时候发现它使用了一项我没有见过的技术,现在学习了一波,写一下心得

在上一篇的WP里,谈及了一项技术,SMC,即Self Modifying Code,动态代码加密技术,指通过修改代码或数据,阻止别人直接静态分析,然后在动态运行程序时对代码进行解密,达到程序正常运行的效果,而计算机病毒通常也会采用SMC技术动态修改内存中的可执行代码来达到变形或对代码加密的目的,从而躲过杀毒软件的查杀或者迷惑反病毒工作者对代码进行分析。通常来说,SMC使用汇编去写会比较好,因为它涉及更改机器码,但SMC也可以直接通过C、C++来实现。

注意,如果使用VS20XX来实现SMC,需要将禁用优化,开启固定基址,关闭随机基址,关闭数据保护,并且选择release X64去编译

以下的伪代码演示了一种SMC技术的典型应用:

在程序中使用SMC最简单的方法就是修改(或加密)整个数据段或代码段,而想要修改段,我们就要了解PE文件结构, Microsoft为它的32位Windows系统设计了一种全新的可执行文件格式,被成为“Portable Executable”,也就是PE格式

位于文件最开始部位的是一个MS-DOS头部和一段DOS stub代码,在PE文件中保留这一部分是为了DOS和Windows系统共存那一段时期设计的,当程序运行在DOS系统时,DOS系统按照DOS可执行文件的格式调用DOS stub代码,一个典型的DOS stub代码就是在控制台上输出一行提示:“This program cannot be run in MS-DOS mode”,当然不同的编译器产生的DOS stub代码也各不相同。曾经有一段时间很流行一种既可以在DOS系统上运行,又可以在Windows上运行的程序,其原理就是人为地替换这段DOS stub代码。紧跟在DOS stub代码之后的就是PE文件的内容了,首先是一个PE文件标志,这个标志有4个字节,也就是“PE/0/0”。这之后紧接着PE文件头(PE Header)和可选头部(Optional Header,也可以理解为这个PE文件的一些选项和参数),这两个头结构存放PE文件的很多重要信息,比如文件包含的段(Sections)数、时间戳、装入基址和程序入口点等信息。这些之后是所有的段头部,段头部之后跟随着所有的段实体。PE文件的尾部还可能包含其它一些混杂的信息,包括重分配信息、调试符号表信息、行号信息等等,这些信息并不是一个PE文件必须的部分,比如正常发布的Release版本的程序就没有调试符号表信息和行号信息。

(看得眼疼)

PE文件的段,是可以新增的,我们可以通过如下代码去新增一个段:

值得注意的是,段必须要设置成可读写的情况,这样我们才能去修改它

在新增了一个段后,我们要想加密它,就得先找到它,即寻址,网上告诉了我们很多寻址操作,但最简单的莫过于直接指针赋值

直接指针赋值可以找到它的地址,然后我这里的加密选择的是最简单无脑的异或,当我们异或后再跳转到该函数是,会发生报错

我们可以查看一下为什么会有异常,分别将异或前和异或后的机器码输出出来看看,我们会发现:

机器码发生了变化,这直接导致代码被改变,无法被识别而报错

明白了这个,我们就可以去写一个简单的SMC了

直接上代码:

这个代码编译出来的程序,是还未经加密的,所以直接解密无法运行,我们要手改一下他的机器码,把他的机器码加密一下

打开StudyPE+,查看段地址

然后在winhex中寻找到相应的地址,将机器码改掉,我这里选择的是与0x2D异或,这样,0x2D就是我的key

修改完数据后保存,打开程序,输入key,发现程序正常运行了


整挺好

 
 
IF .运行条件满足
  CALL DecryptProc (Address of MyProc);对某个函数代码解密
  ........
  CALL MyProc                           ;调用这个函数
  ........
  CALL EncryptProc (Address of MyProc);再对代码进行加密,防止程序被Dump
IF .运行条件满足
  CALL DecryptProc (Address of MyProc);对某个函数代码解密
  ........
  CALL MyProc                           ;调用这个函数
  ........
  CALL EncryptProc (Address of MyProc);再对代码进行加密,防止程序被Dump
 
 
 
#pragma code_seg(".ddd")
void abc()
{
    cout << "WIN";
}
void d()
{
    ;
}
#pragma code_seg()
#pragma comment(linker, "/SECTION:.ddd,ERW")
#pragma code_seg(".ddd")
void abc()
{
    cout << "WIN";
}
void d()
{
    ;
}
#pragma code_seg()
#pragma comment(linker, "/SECTION:.ddd,ERW")
 
char* b1 = (char*)abc;
char* c1 = (char*)d;
int i = 0;
for (; b1 < c1; b1++)
{
    i++;
}
void* a1 = (char*)abc;
for (int i = 0; i < 32; i++)
{
    *((BYTE*)a1 + i) ^= key;
}
char* b1 = (char*)abc;
char* c1 = (char*)d;
int i = 0;
for (; b1 < c1; b1++)
{
    i++;
}
void* a1 = (char*)abc;

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2020-11-27 15:48 被Ddjsq_2333编辑 ,原因:
收藏
免费 2
支持
分享
最新回复 (2)
雪    币: 341
活跃值: (1166)
能力值: ( LV3,RANK:24 )
在线值:
发帖
回帖
粉丝
2
这个东东,我记得十几年前的Themida就有了,好像叫Code Replace还是啥的,当要执行该段代码的时候,才会在地址中出现,执行完又给你抹掉了
2020-11-27 17:06
0
雪    币: 47
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
这个手动修改真的能实现吗
2022-11-14 16:49
0
游客
登录 | 注册 方可回帖
返回
//