吾爱汇编

 找回密码
 立即注册

QQ登录

绑定QQ避免忘记帐号

查看: 5567|回复: 102

[原创逆向图文] 芒果教你写壳之代码虚拟化(二)

  [复制链接]
ssjjdns 发表于 2021-8-27 22:28 | 显示全部楼层 |阅读模式

本帖最后由 ssjjdns 于 2021-8-27 22:46 编辑

大家好我是芒果
芒果验证好久没更新了
壳子好久没更新了,因为以前基本啥也不会,所以写的东西也没有什么技术含量
现在和某些老师学了一点东西
现在准备重新写一下这个代码虚拟化系列

好进入正题了那就!

众所周知,这个代码虚拟化呢,就是一个壳的手段。将一段汇编代码移交至壳内部的虚拟机中执行,防止逆向分析的手段。
那么我们考虑对一段汇编代码进行虚拟化
我的帖子(一)解答了进入虚拟机前和退出虚拟机时后要做什么
现在帖子(二)将展示对一段汇编指令进行虚拟处理时,必要的操作

0x1将一段汇编代码 移至虚拟机执行 存在的问题</br>【我们把一段要移植到虚拟机内部执行的指令,称之为块】
我们期望,一种完美的情况就是,要被VM的汇编指令,不存在任何跳转。这样这一段指令我们就可以舒心的让虚拟机从头执行到尾。
但是,被VM的汇编指令中存在跳转,这些跳转,有可能跳到被VM的代码块之外的。这时候我们必须要退出虚拟机,然后去执行块外代码。
但是,又有一些跳转,是跳转到被VM的代码块内部的。
1.这些跳转来自于被vm的指令块内,即块内跳到块内。
2.也有可能是来自于被VM的指令块外部,即块外部跳到块内


对于情况1我们可以让虚拟机内部逻辑处理就好了
但是对于情况2
情况有点棘手比如下面这样
[如果你不知道下面在说什么,可以去看一下我的虚拟化(一)]
原来代码:
401000 push 12345678
40100A push xxxxxxxxx
40100C push xxxx
40100D call xxxx
............
401244 jmp 40100C or call 40100C
VM后:
401000 JMP xxxxxxxx  //进入虚拟机
40100A xxxxxxxx //一些死代码,本来就是随机的字节,没有意义
40100C xxxxxxxx//一些死代码,本来就是随机的字节,没有意义
40100D xxxxxxxx//一些死代码,本来就是随机的字节,没有意义

.......
401244 jmp 40100C or call 40100C
比如401244处要求jmp或call到40100C处,但是40100C已经被我们处理成了死代码。后面执行的话cpu取的指令没有任何意义,软件就会卡死,而401244处的调用又恰好不属于被VM的指令集合里
换句话就是说,是外部代码需要跳转到虚拟机内部,但原来指令位置已经被替换成死代码了。

0x2 问题解决方案:利用编译原理-基本块划分(或控制流分析)来解决
[基本块的性质或定义:一个代码块,只可能从第一条执行到最后一条,中间不会有任何跳转,而跳转只可能出现在最后一条指令上]
既然我们已经明白了问题所在,那么解决方法也呼之欲出了,我曾为这个问题困扰了一整天。
然后上b站上翻了一下国防科技大学的编译原理 里面控制流分析学了一下,然后解决了
下面给出问题解决的算法
直接看图吧,不多bb了
图片共是两张,论坛可能有时候图片加载失败不知道啥原因,多刷新试试吧
QQ图片20210827221039.png
QQ图片20210827221055.png
用我自己的话说:
1.欲被划分的块的第一条指令能由条件转移语句(jnz je jl.....) 无条件转移语句(jmp)转移到的语句紧跟在条件转移后面的语句
这三类指令都可以作为一个基本块的入口语句
2.对于以上每个入口语句,确定其所属的基本块,它是由该入口语句到下一入口语句(不包括该入口语句),或到一转移语句(包括该转移语句)之间的语句序列组成的

比如一条jnz 那么这个jnz的目标地址,就是一个块的开始
然后jnz指令下面的指令,就是另一个块的开始

如此划分基本块,我们的程序的控制流可以用一张有向图来表示 【不明白有向图是啥的,百度一下,一两句说不清,知识来源:离散数学 图论 QQ截图20210827223220.png
QQ截图20210827223323.png
然后,我们只需要在基本块的头部开始进入虚拟机,在尾部退出虚拟机即可,中间的指令会依次顺序执行,不用再考虑跳转问题
至此,问题得到完美解决!!!!!!!!!!!!!!!
[至于在基本块尾部的跳转指令如何处理,要么原始执行,要么特殊处理【代码乱序】,【代码乱序】技术我会再开帖子讲解!!!]
下面附上代码[本来是想用易语言的,因为考虑到性能问题,直接C++写]代码一共206行 下拉鼠标可以看完整的
[C++] 纯文本查看 复制代码
include<bits/stdc++.h>
#define MAX_ZL_CNT 10000007
#define MAX_VM_CNT 10007 
using namespace std;
string s[MAX_ZL_CNT],b[MAX_VM_CNT],e[MAX_VM_CNT];//S存指令,b存要vm的地址开头,e存要vm的地址结尾 
map<string,int>h; //h存放string下标 
int cnt,vm_cnt;//指令总数 被v的指令总数 
struct rec{
        int isJmp;
        int isBegin;
        int jmpFrom;
        int jmpTo; 
        int div_id;
}; 
rec g[MAX_ZL_CNT];//g存放指令的性质
string ItoS(int x){
        string tmp;
        while(x){
                tmp=char(x%10+48)+tmp;
                x/=10;
        }
        return tmp;
} 
string bu0(string x){
        int l=8-x.length();
        for(int i=1;i<=l;i++){
                x="0"+x;
        }
        return x;
} 
string toD(string x){
        for(int i=0;i<x.size();i++){
                if(int(x[i])>=97 && int(x[i])<=122){
                        x[i]=char(65+int(x[i])-97);
                }
        }
        return x;
} 
int toI(string x){
        int base=1;
        int ret=0;
        for(int i=x.size()-1;i>=0;i--){
                ret+=(int(x[i])-48)*base;
                base*=10;
        }
        return ret;
} 

string GetJmpType(string x){
        int l=x.find("j");
        int r=0;
        for(int i=l;i<x.length();i++){
                if(x[i]==' '){
                        r=i;
                        break;
                }
        }
        return x.substr(l,r-l);
} 
string GetJmpAddr(string x){
        int l=x.find("0x");
        return x.substr(l+2,x.length()-l-2);
} 

string GetZL(string x){
        int ccnt=0;
        int cur=0;
        for(int i=1;i<x.length();i++){
                if(x[i-1]==' ' && x[i]!=' ')ccnt++;
                if(ccnt==2){
                        cur=i;
                        break;
                } 
        }
        return x.substr(cur,x.length()-cur);
} 
string GetBinCode(string x){
        int cur1=0;
        int cur2=0; 
        for(int i=1;i<x.length();i++){
                if(x[i-1]==' ' && x[i]!=' ')cur1=i;
                if(x[i-1]!=' ' && x[i]==' ' && cur1!=0){
                        cur2=i;
                        break; 
                }
        }
        return x.substr(cur1,cur2-cur1);
} 
void DivIt(string x,string y){
        
        int l=h[x];
        int r=h[y];
        int jc=r-l+1;
        int kuai_cnt=1; 
        int f=0;//防止块重复做的标记 
        //下面扫一遍统计每条指令所属块 
        for(int i=0;i<jc;i++){                
                if(g[l+i].isBegin==0 && g[l+i].isJmp==0){//既不是块开头,也不是跳转语句
                        g[l+i].div_id=kuai_cnt;//标记属于那个块 
                        continue;
                }
                //块开头优先判断 
                if(g[l+i].isBegin==1){
                        if(f!=1){
                                kuai_cnt++; 
                        } 
                        f=0;
                        g[l+i].div_id=kuai_cnt;//标记属于那个块         
                        continue;
                }
                //其次判断是否是跳转指令 
                if(g[l+i].isJmp==1){
                        g[l+i].div_id=kuai_cnt;//标记属于那个块 
                        kuai_cnt++; 
                        f=1;
                        continue;
                }
        } 
        
        int j=0;
        int tmp1=g[l-1].div_id;
        g[l-1].div_id=-1;
        int jout_cnt=0;
        int outjin_cnt=0; 
        string jout[MAX_VM_CNT];//由块内部跳转到块外部的指令 
        string outjin[MAX_VM_CNT];//由外部跳转到块内部的指令 
        cout<<"ProtectBegin"<<endl; 
        for(int i=l;i<=r;i++){
                if(g[i].div_id!=g[i-1].div_id)cout<<"kuai"<<++j<<":"<<endl;
                
                //检测是否由块外部跳转进入块
                if(g[i].jmpFrom!=0){
                        if(g[i].jmpFrom<l || g[i].jmpFrom >r){//如果是来自块外部的跳转的话 
                                outjin_cnt++;
                                outjin[outjin_cnt]="<t><"+s[i].substr(0,8)+">"+"@"+ItoS(g[i].div_id)+"@<t>"; 
                        }
                } 
                //如果是跳转指令,检测是跳转到块内部还是块的外部 
                if(g[i].isJmp==1){
                        if(g[i].jmpTo<l || g[i].jmpTo>r){//如果跳转到块的外部 
                                jout_cnt++;
                                jout[jout_cnt]="<1>"+s[i].substr(0,8)+"<2>"+GetJmpType(s[i])+"<3>"+GetJmpAddr(s[i])+"<4>"+ItoS(g[i].div_id)+"<5>"+ItoS(i)+"<6>";
                                cout<<GetJmpType(s[i])+"|"+GetJmpAddr(s[i])+"@"<<ItoS(jout_cnt)<<"_"<<endl;
                        }
                        else cout<<"."<<GetJmpType(s[i])<<" kuai"<<g[g[i].jmpTo].div_id<<"_"<<endl;
                }
                else{
                        cout<<"."<<GetZL(s[i])<<"<"<<GetBinCode(s[i])<<">"<<ItoS(g[i].div_id)<<"_"<<endl;                                
                }
        
        } 
        g[l-1].div_id=tmp1;
        cout<<"ProtectEnd"<<endl; 
        //输出跳转到块外部的指令
        cout<<"{";
        cout<<"jout__________________"<<endl;
        for(int i=1;i<=jout_cnt;i++)cout<<jout[i]<<endl;
        cout<<"outjin________________"<<endl;
        for(int i=1;i<=outjin_cnt;i++)cout<<outjin[i]<<endl; 
        cout<<"}";
        cout<<endl<<"==========================================================="<<endl;
} 
int main(){
//        cout<<ItoS(250); 
        freopen("111.txt","r",stdin);
        freopen("out.txt","w",stdout);
    //--------------预处理所有跳转-------------------------------
    int f=0; 
        while(getline(cin,s[++cnt])){                        
                g[cnt].isJmp=0;
                g[cnt].isBegin=0;
                h.insert(make_pair(s[cnt].substr(0,8),cnt));
        }
        cnt--;//由于getline多读入一次才会结束的,修正 cnt值 
        vm_cnt=toI(s[cnt]); 
        for(int i=1;i<=cnt-vm_cnt-1;i++){
                string a=s[i];
                if(f==1){
                        g[i].isBegin=1;//前一句指令是跳转语句         
                        f=0; 
                }
                int cur=a.find("j");
                if(cur!=a.npos){  //找到跳转指令的 操作数 
                        if(a.find("0x")==a.npos)continue;//如果有jxx 寄存器这种形式的指令,直接跳过 
                        f=1;
                        cur=a.find("0x");
                        a=a.substr(cur+2,a.length()-cur-1);
                        a=bu0(a);//补够8位 
                        a=toD(a);//转大写 
                        g[h[a]].isBegin=1;
                        g[i].jmpTo=h[a];
                        g[i].isJmp=1;
                        g[h[a]].jmpFrom=i;
                        //cout<<a<<endl;                 
                }
                
        }
   
        //cout<<vm_cnt<<endl;
        for(int i=1;i<=vm_cnt;i++){
                b[i]=s[cnt-i].substr(0,8);
                e[i]=s[cnt-i].substr(8,8);
                DivIt(b[i],e[i]);
        }
        return 0;
} 




评分

参与人数 41威望 +1 HB +140 THX +23 收起 理由
再来壹瓶 + 1 [吾爱汇编论坛52HB.COM]-学破解防破解,知进攻懂防守!
花盗睡鼠 + 2 + 1 [吾爱汇编论坛52HB.COM]-学破解防破解,知进攻懂防守!
虚心学习 + 1 [吾爱汇编论坛52HB.COM]-吃水不忘打井人,给个评分懂感恩!
创客者V2.0 + 1
459121520 + 1
爱学习的老冯头 + 1 [吾爱汇编论坛52HB.COM]-感谢楼主热心分享,小小评分不成敬意!
冷亦飞 + 1
l278785481 + 1
小严在河南 + 1 + 1
仙之初 + 1 + 1 [吾爱汇编论坛52HB.COM]-软件反汇编逆向分析,软件安全必不可少!
河图 + 1 + 1 [吾爱汇编论坛52HB.COM]-学破解防破解,知进攻懂防守!
temp + 1 + 1
playboy + 2
我是好人 + 1 [吾爱汇编论坛52HB.COM]-吃水不忘打井人,给个评分懂感恩!
ldljlzw + 1
山野屌丝 + 1
PDWORD + 1
消逝的过去 + 1 [吾爱汇编论坛52HB.COM]-吃水不忘打井人,给个评分懂感恩!
拿着雪糕 + 1
jsntsjg + 1
wangcongha + 1 [吾爱汇编论坛52HB.COM]-学破解防破解,知进攻懂防守!
baky1223 + 1 [吾爱汇编论坛52HB.COM]-吃水不忘打井人,给个评分懂感恩!
bnjzzheng + 1 [吾爱汇编论坛52HB.COM]-吃水不忘打井人,给个评分懂感恩!
大彩笔 + 1 + 1
SmallEXpel + 1 [吾爱汇编论坛52HB.COM]-吃水不忘打井人,给个评分懂感恩!
ZSSR2009525 + 1
king51999 + 1 [快捷评语]--积极评分,从我做起。感谢分享!
87481067 + 1 [快捷评语]--2021年,我们爱0爱1
mengzhisuoliu1 + 1
janford + 1
beamstyle + 1
hnymsh + 1 + 1 [快捷评语]--积极评分,从我做起。感谢分享!
小G一小只 + 1 求助主题超时无人接单,强制删除,撤销主题退还H钻。
blood1116 + 1
白云点缀的蓝 + 6 + 1
GDjx + 1 + 1
hysmy17 + 1 + 1
洋葱、 + 2 + 1
橄榄绿 + 1 + 1
小小沫涵 + 2 + 1
Shark恒 + 1 + 100 + 1 赞!高质量内容!

查看全部评分

吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
alei666 发表于 2021-8-27 23:17 | 显示全部楼层
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
小小沫涵 发表于 2021-8-27 23:31 | 显示全部楼层

感谢分享教程!
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
橄榄绿 发表于 2021-8-28 10:10 | 显示全部楼层
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
hkh314306 发表于 2021-8-28 10:47 | 显示全部楼层

很不错嗷
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
blood1116 发表于 2021-9-9 14:07 | 显示全部楼层
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
逆光尘埃 发表于 2021-11-29 10:36 | 显示全部楼层

感谢分享教程!
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
beamstyle 发表于 2021-12-7 10:41 | 显示全部楼层

感谢分享教程!!
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
保安 发表于 2021-12-9 12:37 | 显示全部楼层

插个眼 大牛更新回复一下 谢谢
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
mengzhisuoliu1 发表于 2021-12-9 14:24 | 显示全部楼层

谢谢啦~~~~~~~
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

警告:本站严惩灌水回复,尊重自己从尊重他人开始!

1层
2层
3层
4层
5层
6层
7层
8层
9层
10层

免责声明

吾爱汇编(www.52hb.com)所讨论的技术及相关工具仅限用于研究学习,皆在提高软件产品的安全性,严禁用于不良动机。任何个人、团体、组织不得将其用于非法目的,否则,一切后果自行承担。吾爱汇编不承担任何因为技术滥用所产生的连带责任。吾爱汇编内容源于网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除。如有侵权请邮件或微信与我们联系处理。

站长邮箱:SharkHeng@sina.com
站长QQ:1140549900


QQ|RSS|手机版|小黑屋|帮助|吾爱汇编 ( 京公网安备11011502005403号 , 京ICP备20003498号-6 )|网站地图

Powered by Discuz!

吾爱汇编 www.52hb.com

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