通过替换mono内opcode保护应用程序的方法
本帖最后由 艾莉希雅 于 2019-7-4 18:27 编辑最近有人询问我关于mono的保护,刚刚好最近学期结束在找实习然后又找不到有点空。
就简单的写一系列的文章来简单的介绍一下mono下如何保护自己的软件不被人艹翻天。
简单的介绍一下替换mono内opcode来保护软件的思路。
能在mono上用的东西,自然也就能在unity上用咯!
众所皆知mono下跑的东西,随随便便动一下就能直接报废根本就不能好好保护。
能做的也就混淆之类的操作容易被还原,而unity游戏在没有启用il2cpp下Assembly-Csharp.dll又是一个超级软柿子。
所幸,mono是开源的玩意,可以让我们看到原始码,甚至自己修改出一份自己专属的mono。
今天给大家介绍的就是大街上偶尔会见到的opcodes替换。
不过opcode替换居然还没烂大街,本人表示震惊。
这个帖子默认大家都会编译mono,如果真的很多人不会的话……再水一贴美滋滋
那么先介绍一下今天的主角opcodes是什么玩意。
所谓的opcodes实际上就是告诉mono,接下来这个操作是要干什么的,因为C#并不是编译型语言,所以必然存在解释器。
接下来我们通过一个简单的例子介绍一下。
typedef enum{
NOP = 0,
ADD = 1,
SUB = 2,
AND = 3,
OR = 4,
XOR = 5,
IN = 6,
NOT = 7,
} CODES;
void vm_exec(VM *vm, int start){
int sp;
int ip;
int a;
int b;
a = b = 0;
int addr;
ip = start;
sp = -1;
int opcode = vm->code;
while(opcode != HALT && ip < vm->code_size){
ip++;
switch(opcode){
case ADD:
b = vm->stack;
a = vm->stack;
vm->stack[++sp] = a + b;
break;
case SUB:
b = vm->stack;
a = vm->stack;
vm->stack[++sp] = a - b;
break;
case MUL:
b = vm->stack;
a = vm->stack;
vm->stack[++sp] = a * b;
break;
case AND:
b = vm->stack;
a = vm->stack;
vm->stack[++sp] = a & b;
break;
case OR:
b = vm->stack;
a = vm->stack;
vm->stack[++sp] = a | b;
break;
case XOR:
b = vm->stack;
a = vm->stack;
vm->stack[++sp] = a ^ b;
break;
case NOT:
a = vm->stack;
vm->stack[++sp] = a ? false : true;
break;
default:
printf("Invalid opcode");
exit(1);
}
opcode = vm->code;
}
}
在这个Virtual-Machine例子中,我们遇见了1,那么所要做的就是找到1也就是add进行我们的操作
而今天的话题就是:如果1与2互换呢?今天我们就要在mono中把opcodes替换掉,来嗨一下。
至于mono的opcode在哪里?为什么不问问我们厉害的搜索功能呢!
mono-master\mono\cil,就是我们要找的目标。
打开定义opcode的文件opcode.def我们可以看到
嗯?第一行就告诉我们不要手改这个文件,要修改cil-opcodes.xml再重新生成。
在这里给大家做一个提醒,如果手改本文件可能会造成编译失败。
本人就是不信邪去手改了它,导致来回折腾然后不得不低头。
那么这个opcode.def要怎么才能生成呢?
我们后面再讲。我们先去看看cil-opcodes.xml里面是什么东西。
一一对应,不是吗……
在本次的例子中我们就把这个call与jmp换一下好的!对换的魔法来了!
如图所示!换了完事!接下来来看看怎么生成opcode.def。
十分简单!只需要一句话make-opcodes-def.pl cil-opcodes.xmlopcode.def
但是这句话没有在百度中找到也没有在谷歌找到,需要建议同学们记一下笔记
其实这句话是跟着代码中的$ARGV分析make-opcodes-def.pl文件整的,居然没有找到资料。
到底其他的开发者是怎么生成那个opcode.def我对此感到奇怪。
替换完opcodes后,重新编译mono即可。
此时mono的opcode中的jmp与call已经被对换。是不能运行正常的C#程序的。
所以接下来我们要修改程序以及其依赖的库,把jmp与call的opcode也对换掉。
这里以这个HelloWorld为例子简单的介绍一下手改的方法。
如图所示有两个call,而我们已经把call换成了jmp。
而jmp是27,所以我们手动将其改完27后保存即可。
修改后保存即可……然后我们在替换了opcode的mono中运行一下
完美!我们再拖去反编译看看怎么样了。
反编译的结果果然是错误的。仅仅对换call与jmp即可达到这个效果。
如果再替换掉其他opcode那事情就会变得更为精彩。
自然的,这个技术在unity游戏中也是可以使用的。
老样子换掉mono,然后再把库的opcode换掉。
如图所示跑的好好的不是吗
编译好的测试程序附件可以拿来玩哦
这里留一个课后作业
思考一下怎么才能不用手改opcode的方法批量替换opcode呢 梦游枪手 发表于 2019-7-4 21:39
学到了,不过感觉在虚拟机解析指令前把调换的opcode映射成真实的opcode更方便点,用dnspy修改opcode太费事 ...
是大佬,我死了,羡慕死了
opcode对换在实际应用中不可能通过手动的方式来换,讲道理一个库内就多少个call与jmp了
只是觉得这种形式便于表达
实际上怎么方便怎么来,换opcode在许多小作坊的游戏中有看见
但是不知道为什么这种简单方便然后能整死一堆人的方法居然没有大范围使用
不是很能理解怎么回事 本帖最后由 小白玩机 于 2019-8-24 03:22 编辑
艾莉希雅 发表于 2019-7-13 11:13
实际上你看着这篇文章动手做就能省下一笔哦
在找实习应该不是很有空就是,可以试试写(笑)
悟空:“快放了我师 傅!不然就别怪我用法宝 了!”蜘蛛精:“切,放马 过来,老娘不惧!”孙悟 空拿出一个4G优盘,蜘蛛精脸色大变 !孙悟空又拿出一个400G硬盘,蜘蛛 精两眼翻白,颤声***:“谁给你的? ”孙悟空:“蜘蛛侠。”蜘蛛精吐血而亡 :“前男友靠不住啊 支持学习~~~ 学习,对c#毫无研究 学到了,不过感觉在虚拟机解析指令前把调换的opcode映射成真实的opcode更方便点,用dnspy修改opcode太费事了。 艾莉希雅 发表于 2019-7-4 23:03
是大佬,我死了,羡慕死了
opcode对换在实际应用中不可能通过手动的方式来换,讲道理一个库内就多少个ca ...
哈哈,可能是嫌麻烦吧,没有现成的保护方案,又不想自己写映射。 sdl wsl
感谢楼主分享 感谢楼主分享,学习了 厉害了,支持。 虽然说看不懂,但是有这个方向会去研究,会回过头来看的