用c语言编写花的代码,c语言编程星号
概念
插花是隐藏不想逆向工程的代码块(或其他函数)的一种方式。它将垃圾代码插入到实际代码中,同时确保原始程序正确运行。但节目反编译不好,节目内容难懂,视听效果混乱。
简单来说就是在代码中混入垃圾数据,阻碍静态分析。
花卉说明书分类
命令大致可以分为两种:可执行命令和不可执行命令。
可执行花卉指令
可执行flower指令是指在程序的正常运行中执行flower指令代码的一部分。
然而,运行这些代码是没有意义的。
寄存器的值在执行前后不会改变(当然eip之类的除外)。
反汇编程序也能成功识别一些代码。
目的
首先,花指令的首要目的还是增加静态分析的难度,让代码的真实意图难以识别。
然后,这个flower命令破坏了反编译分析,堆栈指针可以在反编译引擎中抛出异常。(当然我们知道栈指针其实没有问题,只是反编译引擎需要改进。)
不可执行flower指令
不可执行flower指令是指在程序正常执行时,不执行flower指令代码的一部分。
不可执行的花指令利用了反汇编线性扫描算法的缺陷,静态分析可以看到错误码。
最常见的花命令
如图,这是ctf主题中最常见最简单的flower指令之一,是典型的不可执行flower指令。
小路
首先,将0x00401D92代码转换为数据。
然后,将0x00401D94的数据转换为代码,并将0x00401D94和0x00401D94的数据进行nop处理。
我们知道E9是jmp指令对应的机器码。反汇编器读取E9时,下一次作为跳转目标的偏移量读取4个字节的数据,所以我们会看到错误的汇编代码。
写作方法
如果写程序时嵌入了_asm _emit 0E9,反编译器会把下面的指令当作地址数据:下一条指令的实际4个字节与地址数据或操作指令无关。
_ _组件{
_emit 075h #jmp $ 4
_发射2h
_emit 0E9h
_emit 0EDh
}
嵌入上述4个字节的数据会导致反编译和反编译错误。请注意,这里的75是jnz的机器码,所以执行到这里停止时,Zflag=0。
具体例子
无花指令源程序
我在这里写了tea加密算法。用msvc编译生成pdb文件,在ida中易于分析和打开。
#包括
#包括
voidencrypt(uint32_t*v,uint32_t* k ) }
uint32_t v0=v[0],v1=v[1],sum=0,I;/*设置*/
uint 32 _ t delta=0x9e 3779 b 9;/*关键规划常数*/
uint32_t k0=k[0],k1=k[1],k2=k[2],k3=k[3];/*缓存键*/
for(I=0;i 32I ()/*basiccyclestart ) /
苏美三角洲;
v 0=(v 14)k0)v1sum)^)v15)k1);
v1=(v04)k2)v0sum)^)v05)k3);
(/)结束循环)/
v[0]=v 0;v[1]=v1;
}
int main()).
int a=1;
uint 32 _ tflag [ ]={ 1234,5678 };
uint 32 _ tkey [ ]={ 9,9,9 };
加密(标志、密钥);
printf(%d,%d ,flag[0],flag[1]);
0;
}
首先你可以看到主函数和源代码差不多。
您可以看到加密函数与源代码大致相同。
添加花朵命令
接下来,添加两个花命令
第一个flower命令是我们上面在main函数中提供的最简单的写flower命令的方法。
第二个flower命令是可以在encrypt函数中执行flower的命令。这里详细分析一下flower命令及其对应的移除方法。
#
包括
#包括
#定义垃圾代码__asm{
__asm jmp junk1
__asm __emit0x12
__asm junk2:
__asm ret
__asm __emit0x34
__asm junk1:
__asm调用junk2
}
void encrypt(uint32_t* v,uint32_t* k) {
uint32_t v0=v[0],v1=v[1],sum=0,I;/*设置*/
uint 32 _ t delta=0x9e 3779 b 9;/*一个关键调度常量*/
uint32_t k0=k[0],k1=k[1],k2=k[2],k3=k[3];/*缓存键*/
for(I=0;i 32i ) { /*基本循环开始*/
垃圾代码
总和=delta
v0=((v1 4) k0) ^ (v1总和)^((v1 5)k1);
v1=((v 0 4)k2)^(v 0 sum)^((v 0 5)k3);
} /*结束循环*/
v[0]=v 0;v[1]=v1;
}
int main() {
int a=1;
uint32_t flag[]={ 1234,5678 };
uint32_t key[]={ 9,9,9,9 };
_ _组件{
_emit 075h
_发射2h
_emit 0E9h
_emit 0EDh
}
加密(标志、密钥);
printf(%d,%d ,flag[0],flag[1]);
返回0;
}
第一部花卉说明书
Ida首先打开查看主功能:
虽然反编译成功了,但是我们可以看到内容是完全错误的。我们来看一下主函数的汇编代码,这是我们添加的第一个flower指令。
你可以看到这里出现的红色是我们的第一个花指令,贴片方法同上。我们主要看二花指令。
第二部花卉说明书
F5反编译直接报告错误
可以发现花指令的混淆效果还是比较明显的,所以还是继续跟进花指令的反汇编代码吧。
这里框中的说明是我们添加的花说明。逻辑其实很清晰,就是先跳转到junk1,再调用junk2。调用junk2时,地址0x004118D3会被堆栈,然后进入Junk2执行retn指令,会将地址0x00418d3弹出到eip,然后程序继续正常执行。
移除方法
这种去掉连续可执行指令的方法很简单,把整个nop掉就行了。
但是现实复杂的程序中存在大量这样的华丽指令,人工nop耗时且容易出错,所以我们真正要掌握的是自动化的方法,就是编写脚本匹配华丽指令模板来去除。
花朵移除说明
这是我们想要删除的flower指令模板。
#定义垃圾代码__asm{
__asm jmp junk1
__asm __emit0x12
__asm junk2:
__asm ret
__asm __emit0x34
__asm junk1:
__asm调用junk2
}
下面是idapython写的ida匹配模板移除指令脚本。
def nop(addr,endaddr):
while addr endaddr:
PatchByte(地址,0x90)
addr=1
def undefine(addr,endaddr):
while addr endaddr:
MakeUnkn(地址,0)
addr=1
def dejunkcode(addr,endaddr):
while addr endaddr:
制造商代码(地址)
#匹配模板
如果GetMnem(addr)==jmp 且GetOperandValue(addr,0)==addr 5且Byte(addr 2)==0x12:
next=地址10
nop(地址,下一个)
addr=下一个
继续
addr=ItemSize(addr)
去抖动码(0x00411820、0x00411957)
未定义(0x00411820、0x00411957)
MakeFunction(0x00411820,-1)
重要功能分析
Make (ea) #分析代码区,相当于ida快捷键c。
Itize (EA) #获取指令或数据长度
GetMnem(ea) #获取地址的操作码
GetOperandValue(ea,n) #返回指令操作数的解析值。
PatchByte(ea,value) #修改程序字节
Byte(ea) #将地址解释为字节
MakeUnkn(ea,0) #MakeCode的逆过程相当于ida快捷键u。
MakeFunction(ea,end) #将begin to end的指令转换成函数。如果end被指定为psdhb(-1),IDA将通过定位函数的返回指令来尝试自动确定函数的结束地址。
python提供了很多函数。如果不太理解这里的函数,可以在ida的Python命令行中自己尝试一下。通过比较ida汇编窗口的变化和函数的返回值,可以快速掌握函数的用法。
运行脚本
我们在ida中运行脚本,然后可以发现flower指令已经成功的丢弃了nop。按f5键进行反编译:
您可以看到encrypt函数已经被成功反编译。
对于比较复杂的程序来说,编写模板匹配脚本去除花指令是非常重要的,既能准确,又能节省大量时间。
特殊花卉说明
还有一种特殊的花指令,不会影响反汇编和反编译,只是单纯的迷惑人。
比如我们的程序需要推送某个值(这里假设是0x12),正常的操作应该是:
按下0x12
添加了flower指令后,这个操作可以变成这样:
按下0x26
xor双字指针ss:[esp],0x34
我们很容易看出两种写法是等价的。当我们要堆叠的数据是一些明显的特征值时,这个flower指令可以保护我们的特征值,防止算法特征被快速识别。
当然这只是一个简单的例子,这种花指令的复杂性会大大提高分析的难度。
摘要
还有一些其他的使用花卉说明书的方法,包括一些非常特殊和罕见的。本文介绍的是我在学习过程中经常遇到的比较常见的花指令。
往后的路还很长…慢慢学。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。