SMC技术浅析
2021-02-04 18:58:00 Author: mp.weixin.qq.com(查看原文) 阅读量:134 收藏

本文为看雪论坛优秀文章

看雪论坛作者ID:Ddjsq_2333

简 介

在上一篇的WP里,谈及了一项技术,SMC,即Self Modifying Code,动态代码加密技术,指通过修改代码或数据,阻止别人直接静态分析,然后在动态运行程序时对代码进行解密,达到程序正常运行的效果,而计算机病毒通常也会采用SMC技术动态修改内存中的可执行代码来达到变形或对代码加密的目的,从而躲过杀毒软件的查杀或者迷惑反病毒工作者对代码进行分析。
通常来说,SMC使用汇编去写会比较好,因为它涉及更改机器码,但SMC也可以直接通过C、C++来实现。
 
注意,如果使用VS20XX来实现SMC,需要将禁用优化,开启固定基址,关闭随机基址,关闭数据保护,并且选择release X64去编译。
 
以下的伪代码演示了一种SMC技术的典型应用:
IF .运行条件满足  CALL DecryptProc (Address of MyProc);对某个函数代码解密  ........  CALL MyProc                           ;调用这个函数  ........  CALL EncryptProc (Address of MyProc);再对代码进行加密,防止程序被Dump

PE文件

在程序中使用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文件的段,是可以新增的,我们可以通过如下代码去新增一个段:
#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;}

直接指针赋值可以找到它的地址,然后我这里的加密选择的是最简单无脑的异或,当我们异或后再跳转到该函数是,会发生报错。
我们可以查看一下为什么会有异常,分别将异或前和异或后的机器码输出出来看看,我们会发现:
机器码发生了变化,这直接导致代码被改变,无法被识别而报错。
 
明白了这个,我们就可以去写一个简单的SMC了。

代码

#include<iostream>#include<Windows.h> using namespace std; #pragma code_seg(".ddd")void abc(){    cout << "WIN";}void d(){    ;}#pragma code_seg()#pragma comment(linker, "/SECTION:.ddd,ERW")  int main(){    int key;    cout << "input you key:" << endl;    cin >> key;    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;    }    abc();    system("PAUSE");}

这个代码编译出来的程序,是还未经加密的,所以直接解密无法运行,我们要手改一下他的机器码,把他的机器码加密一下。
 
打开StudyPE+,查看段地址:
 
然后在winhex中寻找到相应的地址,将机器码改掉,我这里选择的是与0x2D异或,这样,0x2D就是我的key
 
修改完数据后保存,打开程序,输入key,发现程序正常运行了。 

整挺好。

- End -

看雪ID:Ddjsq_2333

https://bbs.pediy.com/user-home-910802.htm

  *本文由看雪论坛 Ddjsq_2333 原创,转载请注明来自看雪社区。

# 往期推荐

公众号ID:ikanxue
官方微博:看雪安全
商务合作:[email protected]

球分享

球点赞

球在看

点击“阅读原文”,了解更多!


文章来源: http://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458380938&idx=1&sn=dfbc9a378b16e51b4459e4d7b16ab1c8&chksm=b180da0086f7531666ac8f03435e78048ae696c03111bb54f102e63fadabccdf25e903fc3b26#rd
如有侵权请联系:admin#unsafe.sh