SMC加密

1.概述

1.SMC(Self-Modifying Code):

可以在一段代码执行之前先对它进行修改

2.实现背景:

SMC技术在DOS时代非常流行,因为DOS时代更接近系统底层,大部分SMC以汇编来实现

3.实现原理:

代码以加密形式保存在可执行文件中,然后在程序执行时再动态解密,这样可以有效地对付静态分析

4.解决思路:

动态跟踪或者分析出解密函数的位置编写程序来解密这些代码

2.示例运用(这里采用汇编代码来执行,但不是唯一执行方式)

1.大体结构

  1. invoke ShowMessage
  2. 修改ShowMessage proc
  3. invoke ShowMessage

2.声明

对于Ring3程序:

.code段默认是不可写,所以MASM里通过链接改变其属性:

ml /c /coff %1.asm
link /subsystem:windows /section:.text,RWE %1.obj

/section:.text,RWE 这句指定了代码段(.text)的属性是RWE

R(ReadAble),W(WriteAble),E(ExecuteAble)

3.引用大佬的汇编代码实现

浅谈SMC技术,值得路过_逆向smc原理-CSDN博客

.386
.model flat, stdcall
option casemap:none

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib

ShowMessage proto
ReplaceMent proto

.data
szMsg1 db "这是未执行SMC之前的代码!", 0
szMsg2 db "SMC已经执行!", 0
szCaption db " SMC demo by LC, 2002", 0
Replace_Len dd 0

.code
main:
                      ;第1次执行子程序ShowMessage,此时还没执行SMC操作
invoke ShowMessage

lea eax, ReplaceMentEnd                                       ;标号ReplaceMent的结束
lea edx, ReplaceMentStart                                         ;标号ReplaceMent的开始
sub eax, edx                                                         ;标号ReplaceMent的长度
mov Replace_Len, eax                                           ;把长度储存起来

                          ;关键代码!!!!!!!!!
lea esi, ReplaceMentStart                                       ;标号ReplaceMent的开始
lea edi, ShowMessageStart                                         ;原程序ShowMessage的标号的开始
mov ecx, Replace_Len                                               ;标号ReplaceMent的长度
rep movsb                                                             ;这里是最关键的语句!!!执行 SMC操作!
                                                                          ;rep 表示循环执行后面的语句,同时ECX-1,直到为0。

                                                                          ;第2次执行子程序ShowMessage,此时已经执行了SMC操作。
                                                                          ;换句话说,ShowMessage的内容已经不是第一次运行时的内容了:
invoke ShowMessage

invoke ExitProcess, 0

                                        下面的都是定义
ShowMessage proc
                                                                              ;这里用“::”的话,就能够使标号成为全局性的
ShowMessageStart::
invoke MessageBox, NULL, addr szMsg1, addr szCaption, MB_OK
ShowMessageEnd::
                                                                            ;用nop来预留空间,以便后面的SMC能够成功执行;
                                                                            ;否则如果空间不够,将有可能产生不可预测的错误:
nop
nop
nop
nop
nop
nop
nop
nop

ret
ShowMessage endp

ReplaceMent proc
                                          ;将要用来SMC的代码:
ReplaceMentStart::
                                        其实现面的一段纯汇编码实现的意义等同于
                                              WIN32ASM中 invoke MessageBox, NULL, addr szMsg2, addr szCaption,
                                              MB_OK or MB_ICONINFORMATION
push MB_OK or MB_ICONINFORMATION
lea eax, szCaption
push eax
lea eax, szMsg2
push eax
push NULL
lea eax, MessageBox
call eax
ReplaceMentEnd::

ret
ReplaceMent endp

3.逆向处理SMC

1.ida代开练习使用程序

在这里插入图片描述

2.查看主函数部分

1.主函数第一部分

主要的功能是按要求输入一串字符,并判断字符串长度是否等于28,如果不等于则输出”Try again……”,等于28则入下一模块

lea     eax, [ebp+Str]   
push   eax
push   offset a100s   ; "%100s"
call   _scanf
add     esp, 8
lea     ecx, [ebp+Str]
push   ecx             ; Str
call   _strlen
add     esp, 4
mov     [ebp+var_4], eax
cmp     [ebp+var_4], 1Ch (28)
jz     short loc_40107F
在这里插入图片描述

2.主函数第二部分

接下来是判断字符串的最后一位(除\n)是否为7Dh(”}”),如果不是则将eax置零并返回,之后跳转到最终的栈销毁操作。是则进入下一模块

在这里插入图片描述

3.主函数第三部分

or循环,条件为对byte_414C3C数组的前67个元素循环

循环内部:
mov     edx, [ebp+var_70]
movsx   eax, byte_414C3C[edx]
xor     eax, 7Dh               //将byte_414C3C数组前67位元素每一位都异或7Dh(加密)
mov     ecx, [ebp+var_70]
mov     byte_414C3C[ecx], al       //异或结果覆盖原来的值
jmp     short loc_401095  
循环结束后:
loc_4010BC:
mov     dword ptr [ebp-6Ch], offset byte_414C3C //将byte_414C3C数组赋给dword ptr [ebp-6Ch]
mov     esi, esp
push   offset unk_414BE0       //将unk414BE0也就是下一层加密代码的地址压入栈
lea     edx, [ebp+Str]
push   edx
call   dword ptr [ebp-6Ch]
add     esp, 8
cmp     esi, esp
call   __chkesp
jmp     short loc_4010E1  

loc_4010E1:
xor     eax, eax     //返回结果eax=0

3.对第一层代码进行解密,双击byte_414C3C查看内容,推测为SMC加密代码

长度为67,起始地址为00414C3C,结束地址为00414C7F

在这里插入图片描述

4.利用ida执行idc脚本

#include <idc.idc>

static xor_setp1(){
  auto addr = 0x00414c3c;   //这里填入要解密字节串的起始地址
  auto i = 0;
  for(i=0;addr+i<0x00414C7F;i++)   //循环结束的条件为字节串的结束地址
  {
      PatchByte(addr+i,Byte(addr+i)^0x7D);   //异或的数字根据情况修改
  }
}
static main()
{
xor_setp1();
}

5.重新打开byte_414C3C,UCP进行重新创造函数分析

在这里插入图片描述

1.重新创造函数分析

在这里插入图片描述

2.发现其逻辑为判断前五个字符是否为”flag{“,不是则跳转到loc_414C7B,返回eax=0,是则进入loc_414C64

xor     byte ptr [ecx+edx], 43h      //将byte数组前90位逐位异或43h
inc     ecx //循环变量自增
cmp     ecx, 90               //循环条件:byte数组前90位
jl     short loc_414C64             // 即jump less指令,ecx小于90则跳转,do-while循环
add     eax, 5                
push   offset unk_414A84           //将偏移量unk_414A84压入栈,该地址对应的是第三层SMC函数地址
push   eax
call   edx
pop     ecx
pop     ecx

3.回到第一层main函数内给出的第二层自加密第二层代码

loc_4010BC:
mov     dword ptr [ebp-6Ch], offset sub_414C3C
mov     esi, esp
push   offset unk_414BE0       //这里unk_414BE0嵌套在sub_414C3C,分析知为第二层的地址
lea     edx, [ebp+Str]
push   edx
call   dword ptr [ebp-6Ch]
add     esp, 8
cmp     esi, esp
call   __chkesp
jmp     short loc_4010E1

1.进入unk_414BE0函数查看

在这里插入图片描述

根据代码格式推测为SMC,字符串起始位置的地址为00414BE0,结束地址为00414C3B

对unk_414BE0进行解密,使用idc脚本

#include <idc.idc>
static xor_setp2(){
auto addr = 0x00414be0; //这里填入要解密字节串的起始地址
auto i = 0;
for(i=0;addr+i<0x00414C3A;i++) //循环结束的条件为字节串的结束地址
{
PatchByte(addr+i,Byte(addr+i)^0x43); //异或的数字根据情况修改
}
}
static main()
{
xor_setp2();
}

解密后

在这里插入图片描述

汇编转换UCP

在这里插入图片描述

创建函数进行分析

.data:00414BE0 sub_414BE0      proc near               ; DATA XREF: _main_0+B5↑o
.data:00414BE0
.data:00414BE0 var_8 = dword ptr -8
.data:00414BE0 var_4 = byte ptr -4
.data:00414BE0 arg_0 = dword ptr 8
.data:00414BE0 arg_4 = dword ptr 0Ch
.data:00414BE0
.data:00414BE0 push ebp
.data:00414BE1 mov ebp, esp
.data:00414BE3 push ecx
.data:00414BE4 push ecx
.data:00414BE5 push ebx
.data:00414BE6 mov ebx, [ebp+arg_0]
.data:00414BE9 lea eax, [ebp+var_8]
.data:00414BEC push esi
.data:00414BED push edi
.data:00414BEE xor edx, edx //edx变量置0
.data:00414BF0 mov [ebp+var_8], 93A9A498h //[ebp+var_8]设
.data:00414BF7 mov edi, ebx //为-1817598824
.data:00414BF9 mov [ebp+var_4], dl
.data:00414BFC mov esi, edx
.data:00414BFE sub edi, eax //edi减去eax结果存入edi
.data:00414C00
.data:00414C00 loc_414C00: ; CODE XREF: sub_414BE0+32↓j
.data:00414C00 lea ecx, [ebp+var_8]
.data:00414C03 add ecx, esi //将ecx,esi相加
.data:00414C05 mov al, [edi+ecx] //将edi和ecx对应的数赋给al寄存器
.data:00414C08 xor al, 0CCh //al 异或0CCh
.data:00414C0A cmp al, [ecx] //判断al的值是否和
.data:00414C0C jnz short loc_414C31 //这里为while循环的标志,如果不相等跳到loc_414C31进行栈销毁,否则进入循环内部
.data:00414C0E inc esi //esi自增
.data:00414C0F cmp esi, 4 //判断esi是否和4相等
.data:00414C12 jl short loc_414C00 //如果esi小于4则进行下一层,否则进入if语句内部
.data:00414C14 mov ecx, [ebp+arg_4]
.data:00414C17
.data:00414C17 loc_414C17: ; CODE XREF: sub_414BE0+42↓j
.data:00414C17 xor byte ptr [edx+ecx], 55h //将edx和ecx相加的结果与55h异或
.data:00414C1B inc edx //edx自增
.data:00414C1C cmp edx, 15Bh //循环变量edx和15BH比较
.data:00414C22 jl short loc_414C17 //do-while循环入口,如果edx小于15BH则进入循环,跳转到loc_414C17,否则跳出循环
.data:00414C24 lea eax, [ebx+4] //ebx参数和4相加存入eax
.data:00414C27 push offset unk_414A30 //将地址unk_414A30压入栈传入下面的ecx
.data:00414C2C push eax
.data:00414C2D call ecx //调用ecx函数
.data:00414C2F pop ecx
.data:00414C30 pop ecx
.data:00414C31
.data:00414C31 loc_414C31: ; CODE XREF: sub_414BE0+2C↑j
.data:00414C31 pop edi
.data:00414C32 pop esi
.data:00414C33 xor eax, eax
.data:00414C35 pop ebx
.data:00414C36 mov esp, ebp
.data:00414C38 pop ebp
.data:00414C39 retn
.data:00414C39 sub_414BE0 endp
.data:00414C39

根据分析可知while代码执行的条件为: *(a1+v3)^0xCC == (&v5+v3), 也就是输入的字符异或0xCC对应与v5中的数0x93A9A498相等,求得字符为ehT, 由于与输入的字符顺序相反,故输入的第6-9个字符为The

2.对传入的地址unk_414A84进行分析

双击进入unk_414A84

在这里插入图片描述

分析代码逻辑为SMC自加密,同理使用idc脚本

#include <idc.idc>

static xor_setp3(){
auto addr = 0x00414a84; //这里填入要解密字节串的起始地址
auto i = 0;
for(i=0;addr+i<0x00414a84 + 347;i++) //循环结束的条件为字节串的结束地址
{
PatchByte(addr+i,Byte(addr+i)^0x55); //异或的数字根据情况修改
}
}
static main()
{
xor_setp3();
}

解密得到

在这里插入图片描述

UCP转换

在这里插入图片描述

这里传入了固定字符串”cmVhbEN0Rl8=”推测为flag的第10到17位,接下来的几个loc函数为base64的右移运算部分。在函数的末尾,注意到在这里插入图片描述

dl和固定字符串在while循环内进行了相等判断,同时可以用OD进行动态运行该程序到第三个函数随便输入的字符串加密后的结果仍为固定字符串,因此对字符串base64解密得:realCtF_,如果比较正确后则对sub_414A30函数解密并编译

3.进入unk_414A30分析

在这里插入图片描述

同理分析其为SMC自加密,使用idc脚本解密

#include <idc.idc>

static xor_setp4(){
auto addr = 0x00414a30; //这里填入要解密字节串的起始地址
auto i = 0;
for(i=0;addr+i<0x00414a30 + 83;i++) //循环结束的条件为字节串的结束地址
{
PatchByte(addr+i,Byte(addr+i)^0x4D); //异或的数字根据情况修改
}
}
static main()
{
xor_setp4();
}

解密得到

在这里插入图片描述

UCP转换

在这里插入图片描述

分析逻辑,注意到函数压入了6FF22h,68344360h,7574766Bh,转换为字符串为”kvtu`C4h”o”,在loc_414A55函数中

edx自增后,将byte ptr [ebp+edx+var_C]和bl(之前存入得三串字符串)判断是否相等,并且如果不相等则跳转到本身

分析为while循环,在循环内部,edi减去了esi,而esi为参数地址,edi为字符串

而在loc_414A68中又进行了dec ecx即ecx自减

逻辑:

将参数地址所有的字符减1和字符串比较,所以输入的字符串应该为”just_B3g!n”

6.总结得到flag

flag{The_realCtF_just_B3g!n}

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇