安全矩阵

 找回密码
 立即注册
搜索
查看: 5763|回复: 0

SMC技术浅析

[复制链接]

991

主题

1063

帖子

4315

积分

论坛元老

Rank: 8Rank: 8

积分
4315
发表于 2021-2-4 23:05:55 | 显示全部楼层 |阅读模式
原文链接:SMC技术浅析

一、简介

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


通常来说,SMC使用汇编去写会比较好,因为它涉及更改机器码,但SMC也可以直接通过C、C++来实现。

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

以下的伪代码演示了一种SMC技术的典型应用:
  1. IF .运行条件满足
  2.   CALL DecryptProc (Address of MyProc);对某个函数代码解密
  3.   ........
  4.   CALL MyProc                           ;调用这个函数
  5.   ........
  6.   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版本的程序就没有调试符号表信息和行号信息。

2、简单实现

PE文件的段,是可以新增的,我们可以通过如下代码去新增一个段:
  1. #pragma code_seg(".ddd")
  2. void abc()
  3. {
  4.     cout << "WIN";
  5. }
  6. void d()
  7. {
  8.     ;
  9. }
  10. #pragma code_seg()
  11. #pragma comment(linker, "/SECTION:.ddd,ERW")
复制代码

值得注意的是,段必须要设置成可读写的情况,这样我们才能去修改它 在新增了一个段后,我们要想加密它,就得先找到它,即寻址,网上告诉了我们很多寻址操作,但最简单的莫过于直接指针赋值。
  1. char* b1 = (char*)abc;
  2. char* c1 = (char*)d;
  3. int i = 0;
  4. for (; b1 < c1; b1++)
  5. {
  6.     i++;
  7. }
  8. void* a1 = (char*)abc;
  9. for (int i = 0; i < 32; i++)
  10. {
  11.     *((BYTE*)a1 + i) ^= key;
  12. }
复制代码

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


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

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

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

代码
  1. #include<iostream>
  2. #include<Windows.h>

  3. using namespace std;

  4. #pragma code_seg(".ddd")
  5. void abc()
  6. {
  7.     cout << "WIN";
  8. }
  9. void d()
  10. {
  11.     ;
  12. }
  13. #pragma code_seg()
  14. #pragma comment(linker, "/SECTION:.ddd,ERW")


  15. int main()
  16. {
  17.     int key;
  18.     cout << "input you key:" << endl;
  19.     cin >> key;
  20.     char* b1 = (char*)abc;
  21.     char* c1 = (char*)d;
  22.     int i = 0;
  23.     for (; b1 < c1; b1++)
  24.     {
  25.         i++;
  26.     }
  27.     void* a1 = (char*)abc;
  28.     for (int i = 0; i < 32; i++)
  29.     {
  30.         *((BYTE*)a1 + i) ^= key;
  31.     }
  32.     abc();
  33.     system("PAUSE");
  34. }
复制代码

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


打开StudyPE+,查看段地址:


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



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

整挺好。



























回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-9-21 00:34 , Processed in 0.015612 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表