re--SMC

参考:http://t.csdnimg.cn/g7fUY

参考:http://t.csdnimg.cn/qi3q5

简介

SMC,即Self Modifying Code,动态代码加密技术,指通过修改代码或数据,阻止别人直接静态分析,然后在动态运行程序时对代码进行解密,达到程序正常运行的效果。

SMC的实现方式有很多种,可以通过修改PE文件的Section Header、使用API Hook实现代码加密和解密、使用VMProtect等第三方加密工具等。

所谓SMC技术,就是一种将可执行文件中的代码或数据进行加密,防止别人使用逆向工程工具(比如一些常见的反汇编工具)对程序进行静态分析的方法,只有程序运行时才对代码和数据进行解密,从而正常运行程序和访问数据。计算机病毒通常也会采用SMC技术动态修改内存中的可执行代码来达到变形或对代码加密的目的,从而躲过杀毒软件的查杀或者迷惑反病毒工作者对代码进行分析。现在,很多加密软件(或者称为“壳”程序)为了防止Cracker(破解者)跟踪自己的代码,也采用了动态代码修改技术对自身代码进行保护。

  在自己的软件中使用SMC(代码自修改)技术可以极大地提高软件的安全性,保护私有数据和关键功能代码,对防止软件破解也可以起到很好的作用。但是,SMC技术需要直接读写对内存中的机器码,需要对汇编语言和机器码有相当的了解,具体的实现一般都是采用汇编语言。从理论上讲,只要支持指针变量和内存直接访问,像C/C++这样的高级语言一样可以使用SMC技术,利用C/C++语言的一些特性,比如函数地址和变量地址直接访问等特性,实现了几种对运行中的代码和数据进行动态加密和解密的方法。首先是利用Windows可执行文件的结构特性,实现了一种对整个代码段进行动态加密解密的方法;接着又利用C/C++语言中函数名称就是函数地址的特性,实现了一种对函数整体进行加密解密的方法;最后采用在代码中插入特征代码序列,通过查找匹配特征代码序列定位代码的方式,实现了一种对任意代码片断进行解密解密的方法。

SMC一般有俩种破解方法

第一种是找到对代码或数据加密的函数后通过idapython写解密脚本。

第二种是动态调试到SMC解密结束的地方dump出来。

计算机中,dump的中文意思为“转储”,一般指将数据导出、转存成文件或静态形式,即将动态(易失)的数据,保存为静态的数据(持久数据)。

SMC的实现是需要对目标内存进行修改的,.text一般是没有写权限的。那么就需要拥有修改目标内存的权限:

在linux系统中,可以通过mprotect函数修改目标内存的权限
在Windows系统中,VirtualProtect函数实现内存权限的修改
因此也可以观察是否有这俩个函数来判断是否进行了SMC。 

[网鼎杯 2020 青龙组]jocker

32位打开

看到有virtualprotect可见应该有SMC

4030C0里是数据

大概就是输入24位的数据然后经过wrong函数得到OMG里的数据

写个脚本

#include<stdio.h>
int main()
{
   
    int data[] =  {0x66,0x6B,0x63,0x64,0x7F,0x61,0x67,0x64,0x3B,0x56,0x6B,0x61,0x7B,0x26,0x3B,0x50,0x63,0x5F,0x4D,0x5A,0x71,0x0C,0x37,0x66};
  
	char ch;
    for(int i=0;i<24;i++)
    {
        if ( (i & 1) != 0 )
      		data[i] += i;
   		else
     	 	data[i] ^= i;
		ch=(char)data[i];
		printf("%c",ch);
    }
//flag{fak3_alw35_sp_me!!}    	
    return 0;
}

假的

继续分析

参考:http://t.csdnimg.cn/tgQeX

ida- option- general 打开堆栈指针

栈祯不平衡

Alt k把call处改为0

反编译

2.函数脱壳

回到main函数,在if ( encrypt(Destination) )这里下断点,(下断点的位置还有深入学习一下)

开始动态调试,输入上面的假flag即可通过omg判断

在main中点击encrypt函数,仍然不能反汇编,我们找到encrypt函数定义处对应的汇编代码

从定义头一直到endp,还有下方这一块区域,全部选中之后按u(undefined)取消定义,

再在定义头的位置按p即可识别为函数

值得一提的是动态调试结束后encrypt函数又会识别错误,这是因为每次运行程序会执行脱壳代码,而回到静态调试时会回到脱壳前的状态,所以导致识别错误

#include<stdio.h>
int main()
{
   
    unsigned char Buffer[]="hahahaha_do_you_find_me?";
	char flag[50];
	int v2[]={0x0E,0x0D,0x09,0x06,0x13,0x05,0x58,0x56,0x3E,0x06,0x0C,
	0x3C,0x1F,0x57,0x14,0x6B,0x57,0x59,0x0D};
	
    for (int  i = 0; i <= 18; ++i )
 	{
    	flag[i] = v2[i] ^ Buffer[i];
  	}
	puts(flag);
 	//flag{d07abccf8a410c
    return 0;
}

还少一半分析finally函数

同理,先u 然后在头部 p,再反汇编

这个非常迷惑,分析上面得到的反汇编代码无处下手,因为判断条件中有个随机数

三分逆向七分猜hhhh

"%tp&:"这个字符串是最后一段,flag的最后一个字符是'}'根据题目描述的异或操作,可能是这段字符使用同一个值异或,

所以只要由':' ^ n = '}'得到这个用于异或的值即可求出其他字符(flag最后一位是花括号的右边)

 #include<stdio.h>
 #include<string.h> 
 int main()
 {
 	char v3[8];
 	char flag[8];
 	strcpy(v3, "%tp&:");
 	char ch;
 	ch=':'^'}';
 	printf("%c\n",ch);
 	int n;
 	n=(int)ch;
 	printf("%d\n",n);
 	for(int i=0;i<5;i++)
 	{
 		flag[i]=v3[i]^n;
	 }
  puts(flag);
  //b37a}
  return 0;
 }
 
 
  
  
  
  
  
  
  
  
  
  

所以flag为         flag{d07abccf8a410cb37a}

[GWCTF 2019]re3

64位

输入长度为32位的字符串,利用mprotect函数将dword_400000处的0xF000长度地址修改成了可读可写可执行,sub_402219()里的数据进行了异或处理。

看到mprotect,所以可能有SMC

没法f5,估计是jz的作用影响了ida的正常运行

可以edit -plugins -findcrypton

可能有AES,base64,MD5加密

可以看到sub_402219()函数既作为数据参与异或,又作为函数来引用,而且这个函数打不开
我们猜测异或完后,这个函数才是真正的函数。所以先将sub_402219()里的数据还原看看,
直接在伪代码部分点sub_402219跳转不到它的数据段,得在汇编代码里找

找到汇编

按D转换成data型

快捷键shift+f2,写IDC的脚本

参考:IDA 中的IDC脚本编写笔记 - lyshark - 博客园 (cnblogs.com)

参考:http://t.csdnimg.cn/jy29h

#include <idc.idc>

static main()
{
    auto addr = 0x402219;
    auto i = 0;
    for(i=0;i<224;i++)
    {
        PatchByte(addr+i,Byte(addr+i)^0x99);
    }
}

执行完成后,右键要修改的所有数据->Analyze selected area->force ,再在函数开头按P,变为函数。快捷键C转化为代码

jumpout

函数边界识别错误, 或者是
由于编译器某些原因会把某些代码分出来,不存放在连续区域,而是在其他位置,这些块有时ida能识别出来,被称为chunk,有些不能识别,此时就得自己手动设置。
如果是边界识别错误,那就alt+p,找到正确的末尾;
如果是后者,那就通过append_func_tail来将目标区域添加到本函数中,另外,得先undefine目标区域。

u p  c d反反复复,八百遍才成功。

看unk-603120

中间几行没什么用处

findcrypt发现401CF9是MD5加密

动态调试

可以动调一下获得加密后的a1

在汇编找到main找到IDA先在sub_40207B()函数执行后一条下断点

学习过程处处是问题..........

溜了溜了

上海省赛-今天天气怎么样

32位

#include<stdio.h>
int main()
{
	int data[]={0x66,0x6B,0x63,0x64,0x7F,0x63,0x69,0x70,0x57,0x60,0x79,0x54,0x78,0x5B,
	0x6B,0x50,0x67,0x54,0x73,0x61,0x7C,0x50,0x64,0x48,0x6C,0x56,0x7E,0x46,0x65,0x60};
	char flag[30];
	int i;
	for(i=0;i<30;i++)
	{
		if ( (i & 1) != 0 )
     	flag[i] =data[i]+ i;
    	else
      	flag[i] =data[i]^ i;
	}
	puts(flag);
	return 0;
} 
//flag{how_is_the_weather_today}

然后找到myfunction的汇编,发现栈祯不平衡

下断点动调,先输入上面那个假的flag

然后找到关键函数

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
unsigned char v2[] = {0x66, 0x6B, 0x63, 0x64, 0x7F,
                     0x63, 0x69, 0x70, 0x57, 0x60,
                     0x79, 0x54, 0x78, 0x5B, 0x6B,
                     0x50, 0x67, 0x54, 0x73, 0x61,
                     0x7C, 0x50, 0x64, 0x48, 0x6C,
                     0x56, 0x7E, 0x46, 0x65, 0x60};

unsigned char v3[] = {0x4D, 0xD8, 0x76, 0x2D, 0x0C, 0x26, 0x0C, 0x53, 0xDA, 0xC0,
                     0x17, 0x37, 0x8C, 0xD7, 0xF3, 0xD9, 0xD0, 0x46, 0x2B, 0x15,
                     0x98, 0x67, 0xF1, 0xAD, 0xA6, 0x0E, 0x7C, 0x66, 0x90, 0x7F};

unsigned char box[] = {0x30, 0x72, 0x99, 0xA0, 0x47, 0xA3, 0x6C, 0xC8, 0x96, 0xBB,
                      0x4E, 0x97, 0x5A, 0x07, 0xA7, 0x26, 0x78, 0x12, 0x84, 0xD8,
                      0x90, 0x09, 0xD2, 0xF9, 0x3E, 0x34, 0x40, 0x49, 0x6D, 0x1D,
                      0x42, 0x7D, 0xAF, 0x77, 0xD0, 0x2F, 0xC1, 0x8A, 0x15, 0x9F,
                      0x57, 0xF1, 0x28, 0xDA, 0x5C, 0xEA, 0x3B, 0x5B, 0xB4, 0x63,
                      0xF3, 0x79, 0xF8, 0x94, 0x02, 0x37, 0x5F, 0xDC, 0xB7, 0xB2,
                      0x52, 0x00, 0x62, 0x0C, 0x45, 0x24, 0x11, 0xED, 0xFC, 0xA5,
                      0x0E, 0x1F, 0x44, 0x71, 0x08, 0xFE, 0x43, 0x25, 0x41, 0xB1,
                      0x69, 0x2A, 0xE3, 0x81, 0x5D, 0xD1, 0xDB, 0x93, 0xFB, 0x0A,
                      0x53, 0xEE, 0xE1, 0x46, 0x1C, 0xFD, 0xA8, 0xEC, 0x65, 0x59,
                      0x3D, 0xC9, 0x21, 0xBF, 0xD9, 0x80, 0x88, 0x17, 0xD3, 0xE7,
                      0xF7, 0x91, 0x92, 0x70, 0x06, 0x50, 0xB8, 0xC5, 0xEF, 0xFF,
                      0x2B, 0x86, 0x6F, 0x61, 0x39, 0x51, 0x67, 0x0B, 0x6A, 0xAC,
                      0x1A, 0x01, 0x27, 0xE2, 0xE6, 0xD7, 0x9E, 0x35, 0xF4, 0x32,
                      0xE0, 0x23, 0x04, 0x98, 0x48, 0xA4, 0x73, 0x3A, 0xA1, 0x7E,
                      0x64, 0x75, 0x9A, 0x2E, 0xAE, 0x4F, 0xDF, 0xD6, 0x89, 0xBA,
                      0x05, 0x18, 0xBE, 0x16, 0x31, 0xEB, 0xC3, 0xA2, 0xB3, 0x3C,
                      0x2C, 0xCB, 0xE9, 0xF5, 0x36, 0x4A, 0x87, 0x9C, 0x8F, 0xCC,
                      0xF6, 0x8C, 0x10, 0x7C, 0xC2, 0xAB, 0x14, 0x58, 0x4C, 0xB5,
                      0xD4, 0x4D, 0x0F, 0xBC, 0x03, 0xA6, 0xCA, 0x8E, 0xB0, 0x1E,
                      0x1B, 0xCE, 0xC0, 0xD5, 0xF0, 0xDD, 0x85, 0xBD, 0x54, 0x60,
                      0x83, 0x6E, 0xAA, 0x8D, 0xC4, 0xDE, 0x3F, 0xF2, 0x0D, 0x2D,
                      0x6B, 0x9B, 0x33, 0xC6, 0x7F, 0x5E, 0x29, 0x22, 0x95, 0xCD,
                      0xB9, 0xB6, 0x38, 0xFA, 0x9D, 0x8B, 0xAD, 0x7B, 0x20, 0xE8,
                      0xC7, 0x74, 0x68, 0x4B, 0x19, 0x7A, 0x66, 0xCF, 0xE5, 0x76,
                      0xE4, 0xA9, 0x82, 0x13, 0x56, 0x55};

void decrypt(unsigned char *a1, unsigned char *a2, int len) {
    int v7 = 0, v6 = 0;
    for (int i = 0; i < len; i++) {
        v7 = (v7 + 1) % 256;
        v6 = (v6 + a1[v7]) % 256;
        unsigned char v4 = a1[v7];
        a1[v7] = a1[v6];
        a1[v6] = v4;
        a2[i] ^= a1[(a1[v7] + a1[v6]) % 256];
    }
}

int main() {
    decrypt(box, v3, 30);
    printf("%s\n", v3);
//flag{This_is_a_beautiful_day!}
    return 0;
}

  • 20
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值