我理解多态和变形代码的概念,但我最近阅读了两者的维基百科页面(因为我之前没有做过这个原因!).现在我真的想为自己编写一些变形代码.
我是一个没有语言的大师,很多人.我知道一些PHP,MySQL,c/c ++,Java,Bash脚本,Visual Basic 6,VBScripting,Perl,JavaScript.
任何人都可以提供任何这些语言的变形代码示例.我希望看到一个有效的例子,即使程序的输出只是"Hello World",也要通过例子来理解这是如何发生的(我正在努力理解这些技术如何通过心理思维来实现).任何语言都可以做到,这些只是首选语言.
此外,搜索互联网只返回了c/c ++中的有限数量的示例(甚至不是完整的工作示例,更多的部分代码片段),因为我所建议的其他语言不够低,没有足够的功能/变形代码所需的灵活性?
Jam*_*ess 50
下面是我认为将其归类为用C语言编写的变形代码的示例.我担心我在编写可移植C代码方面没有太多经验,因此可能需要进行一些修改才能在其他平台上编译(我'在Windows上使用旧版本的Borland).此外,它依赖于目标平台是x86,因为它涉及一些机器代码生成.理论上它应该在任何x86操作系统上编译.
这个怎么运作
每次运行程序时,它都会生成一个随机修改的自身副本,文件名不同.它还打印出已修改的偏移列表,以便您可以看到它实际上正在执行某些操作.
修改过程非常简单.源代码只是用汇编指令序列来解释,它们实际上什么都不做.当程序运行时,它会找到这些序列并随机地用不同的代码替换它们(显然也什么都不做).
硬编码偏移列表显然对于其他人需要能够编译的东西是不现实的,因此序列的生成方式使得在搜索目标代码时很容易识别,希望不会出现任何误报. .
每个序列以某个寄存器上的推送操作开始,一组修改该寄存器的指令,然后是弹出操作以将寄存器恢复为其初始值.为了简单起见,在原始资源中,所有序列都只是PUSH EAX,8 NOP秒,和POP EAX.然而,在应用程序的所有后续代中,序列将完全是随机的.
解释代码
我已将代码分成多个部分,因此我可以尝试逐步解释它.如果你想自己编译,你只需要将所有部分连接在一起.
首先,一些相当标准包括:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
Run Code Online (Sandbox Code Playgroud)
接下来我们定义了各种x86操作码.这些通常与其他值组合以生成完整指令.例如,PUSHdefine(0x50)本身就是PUSH EAX,但你可以通过添加0到7范围内的偏移量来推导其他寄存器的值.POP和的相同的东西MOV.
#define PUSH 0x50
#define POP 0x58
#define MOV 0xB8
#define NOP 0x90
Run Code Online (Sandbox Code Playgroud)
最后六个是几个双字节操作码的前缀字节.第二个字节对操作数进行编码,稍后将对其进行更详细的说明.
#define ADD 0x01
#define AND 0x21
#define XOR 0x31
#define OR 0x09
#define SBB 0x19
#define SUB 0x29
const unsigned char prefixes[] = { ADD,AND,XOR,OR,SBB,SUB,0 };
Run Code Online (Sandbox Code Playgroud)
JUNK是一个宏,它在代码中的任何地方插入我们的垃圾操作序列.正如我之前解释的,它最初只是写出PUSH EAX,NOP和POP EAX.JUNKLEN是该NOP序列中s 的数量- 而不是序列的全长.
如果你不知道,__emit__是一个伪函数,它将文字值直接注入到目标代码中.我怀疑如果你使用不同的编译器,它可能是你需要移植的东西.
#define JUNK __emit__(PUSH,NOP,NOP,NOP,NOP,NOP,NOP,NOP,NOP,POP)
#define JUNKLEN 8
Run Code Online (Sandbox Code Playgroud)
一些全局变量,我们的代码将被加载.全局变量很糟糕,但我不是一个特别好的编码器.
unsigned char *code;
int codelen;
Run Code Online (Sandbox Code Playgroud)
接下来我们有一个简单的函数,它将我们的目标代码读入内存.我从不释放那段记忆,因为我只是不在乎.
注意JUNK在随机点插入的宏调用.您将在整个代码中看到更多这些内容.您几乎可以在任何地方插入它们,但是如果您使用的是真正的C编译器(而不是C++),那么如果您尝试将它们放在变量声明之前或之间,它就会抱怨.
void readcode(const char *filename) {
FILE *fp = fopen(filename, "rb"); JUNK;
fseek(fp, 0L, SEEK_END); JUNK;
codelen = ftell(fp);
code = malloc(codelen); JUNK;
fseek(fp, 0L, SEEK_SET);
fread(code, codelen, 1, fp); JUNK;
}
Run Code Online (Sandbox Code Playgroud)
另一个简单的功能是在修改后再次编写应用程序.对于新文件名,我们只需用每次递增的数字替换原始文件名的最后一个字符.不会尝试检查文件是否已存在,以及我们是否覆盖了操作系统的关键部分.
void writecode(const char *filename) {
FILE *fp;
int lastoffset = strlen(filename)-1;
char lastchar = filename[lastoffset];
char *newfilename = strdup(filename); JUNK;
lastchar = '0'+(isdigit(lastchar)?(lastchar-'0'+1)%10:0);
newfilename[lastoffset] = lastchar;
fp = fopen(newfilename, "wb"); JUNK;
fwrite(code, codelen, 1, fp); JUNK;
fclose(fp);
free(newfilename);
}
Run Code Online (Sandbox Code Playgroud)
下一个函数为我们的垃圾序列写出一个随机指令.该REG参数表示,我们正在使用的寄存器-什么会被推并在序列两端弹出.该偏移是在该指令将被写入代码的偏移量.而space表示我们在序列中留下的字节数.
这取决于我们有多大的空间,我们可能会受限于哪些指令就可以写出来,否则我们随机选择它是否是一个NOP,MOV或者其他人之一.NOP只是一个字节.MOV是五个字节:我们的MOV操作码(添加了reg参数),以及4个随机字节,表示移入寄存器的数字.
对于两个字节序列,第一个只是随机选择的前缀之一.第二个是在范围中的字节0xC0,以0xFF其中至少显著3位代表了主要寄存器-即必须设置为我们的值reg参数.
int writeinstruction(unsigned reg, int offset, int space) {
if (space < 2) {
code[offset] = NOP; JUNK;
return 1;
}
else if (space < 5 || rand()%2 == 0) {
code[offset] = prefixes[rand()%6]; JUNK;
code[offset+1] = 0xC0 + rand()%8*8 + reg; JUNK;
return 2;
}
else {
code[offset] = MOV+reg; JUNK;
*(short*)(code+offset+1) = rand();
*(short*)(code+offset+3) = rand(); JUNK;
return 5;
}
}
Run Code Online (Sandbox Code Playgroud)
现在我们有相同的功能来回读这些指令之一.假设我们已经在序列的任何一端识别出reg来自PUSH和POP操作,该函数可以尝试验证给定的指令是否offset是我们的垃圾操作之一,并且主寄存器是否与给定reg参数匹配.
如果找到有效匹配,则返回指令长度,否则返回零.
int readinstruction(unsigned reg, int offset) {
unsigned c1 = code[offset];
if (c1 == NOP)
return 1; JUNK;
if (c1 == MOV+reg)
return 5; JUNK;
if (strchr(prefixes,c1)) {
unsigned c2 = code[offset+1]; JUNK;
if (c2 >= 0xC0 && c2 <= 0xFF && (c2&7) == reg)
return 2; JUNK;
} JUNK;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
下一个函数是搜索和替换垃圾序列的主循环.它首先寻找一个PUSH操作码,然后POP在8个字节后的同一个寄存器上查找操作码(或者JUNKLEN设置为的任何内容).
void replacejunk(void) {
int i, j, inc, space;
srand(time(NULL)); JUNK;
for (i = 0; i < codelen-JUNKLEN-2; i++) {
unsigned start = code[i];
unsigned end = code[i+JUNKLEN+1];
unsigned reg = start-PUSH;
if (start < PUSH || start >= PUSH+8) continue; JUNK;
if (end != POP+reg) continue; JUNK;
Run Code Online (Sandbox Code Playgroud)
如果寄存器结果是ESP,我们可以安全地跳过它,因为我们永远不会ESP在我们生成的代码中使用(堆栈操作ESP需要特别考虑,不值得付出努力).
if (reg == 4) continue; /* register 4 is ESP */
Run Code Online (Sandbox Code Playgroud)
一旦我们匹配了可能正在寻找的PUSH和POP组合,我们就会尝试阅读其间的说明.如果我们成功匹配了我们期望的字节长度,我们会考虑可以替换的匹配.
j = 0; JUNK;
while (inc = readinstruction(reg,i+1+j)) j += inc;
if (j != JUNKLEN) continue; JUNK;
Run Code Online (Sandbox Code Playgroud)
然后我们随机选择7个寄存器中的一个(如前所述ESP),并在序列的任一端写出该寄存器的操作PUSH和POP操作.
reg = rand()%7; JUNK;
reg += (reg >= 4);
code[i] = PUSH+reg; JUNK;
code[i+JUNKLEN+1] = POP+reg; JUNK;
Run Code Online (Sandbox Code Playgroud)
然后我们需要做的就是使用我们的writeinstruction函数填充其间的空间.
space = JUNKLEN;
j = 0; JUNK;
while (space) {
inc = writeinstruction(reg,i+1+j,space); JUNK;
j += inc;
space -= inc; JUNK;
}
Run Code Online (Sandbox Code Playgroud)
这里是我们显示刚修补的偏移量的地方.
printf("%d\n",i); JUNK;
}
}
Run Code Online (Sandbox Code Playgroud)
最后我们有主要功能.这只是调用前面描述的功能.我们读入代码,替换垃圾,然后再写出来.该argv[0]参数包含应用程序的文件名.
int main(int argc, char* argv[]) {
readcode(argv[0]); JUNK;
replacejunk(); JUNK;
writecode(argv[0]); JUNK;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这就是它的全部内容.
一些最后的笔记
运行此代码时,显然您需要确保用户具有相应的权限,以便在与原始代码相同的位置写出文件.然后,一旦生成了新文件,如果您在文件扩展名很重要的系统上,通常需要重命名它,或者如果需要,则设置其执行属性.
最后,我怀疑您可能希望通过调试器运行生成的代码,而不是直接执行它并希望最好.我发现如果我将生成的文件复制到原始可执行文件上,调试器很高兴让我在查看原始源代码的同时逐步完成它.然后,无论何时到达JUNK代码中的某个点,您都可以进入程序集视图并查看已生成的代码.
无论如何,我希望我的解释已经相当清楚,这就是你要找的那种例子.如果您有任何疑问,请随时在评论中提问.
奖金更新
作为奖励,我想我还会在脚本语言中包含一个变形代码示例.这与C示例完全不同,因为在这种情况下我们需要改变源代码,而不是二进制可执行文件,我认为这更容易一些.
对于这个例子,我已经广泛使用了php的goto功能.每一行都以标签开头,并以goto指向下一行标签的方式结束.这样每条线基本上都是自包含的,我们可以愉快地将它们洗牌,并且仍然可以像以前一样完成程序.
条件和循环结构稍微复杂一点,但它们只需要以跳转到两个不同标签之一的条件的形式重写.我在代码中包含了注释标记,循环将尝试使其更容易理解.
所有代码都是回显自身的混乱副本,因此只需将输出剪切并粘贴回源字段并再次运行,就可以轻松地在ideone上进行测试.
如果你想让它变异更多,那么每次运行代码时,用一组不同的随机字符串替换所有标签和变量会很容易.但我认为最好尽量保持简单.这些例子只是为了证明这个概念 - 我们实际上并没有试图避免检测.:)
rec*_*nja 26
公开可用的变形代码示例受到以下几个因素的限制:
1)专业知识:变形编码是计算机编程中非常先进的技术.能够编码适合于采样的相干且干净的变形代码的程序员的数量是非常少的.
2)财务激励:变形编码在商业应用中的使用有限.因此,具有足够技能来创建变形代码的程序员的数量没有专业曝光/激励来创建/学习变形编码技术.
3)合法性:变形编码在强有力的病毒创建中有很大的应用.因此,任何创建变形代码的负责任的专业人员都会自由地分发样本的道德问题,因为黑客可能能够使用该代码来增强恶意攻击.相反,任何有足够能力创建变形代码的黑客都没有动机宣传他的技能,如果发现他的一次攻击,那么他就会根据能力在一个非常短的嫌疑人名单上.
4)保密:最后,也许最难以找到变形代码的现实主义原因是因为任何表现出变形编程能力并且没有被当局逮捕进行网络犯罪的程序员很可能会被政府安全机构招募,私人保安公司或反病毒公司和程序员随后的研究/知识然后受到保密协议的约束,以保持竞争优势.
为什么只有C/C++例子?
您提到仅查找C/C++多边形/变形编程的代码示例,并推断只有靠近硬件的语言可以是多边形/变形的.这对于多边形/变形代码的最严格定义是正确的.解释语言可以具有多边形/变形行为,但依赖于静态编译的解释器来执行,因此大部分"运行时签名"不可变.只有编译过的低级语言才能提供计算灵活性,以便具有高度可变的"运行时签名".
这是我写的一些'多态'PHP代码.PHP是一种解释型语言而不是编译语言,因此无法实现真正的多态性.
PHP代码:
<?php
// Programs functional Execution Section
system("echo Hello World!!\\n");
// mutable malicious payload goes here (if you were devious)
// Programs Polymorphic Engine Section
recombinate();
?>
<?php
function recombinate() {
$file = __FILE__; //assigns file path to $file using magic constant
$contents = file_get_contents($file); //gets file contents as string
$fileLines = explode("\n", $contents); //splits into file lines as string array
$varLine = $fileLines[2]; //extracts third file line as string
$charArr = str_split($varLine); //splits third line into char array
$augStr = augmentStr($charArr); //recursively augments char array
$newLine = implode("",$augStr); //rebuilds char array into string
$fileLines[2] = $newLine; //inserts string back into array
$newContents = implode("\n",$fileLines); //rebuilds array into single string
file_put_contents($file,$newContents); //writes out augmented file
sleep(1); //let the CPU rest
$pid = pcntl_fork(); //forks process
if($pid) { //if in parent:
exit(0); //exit parent process
} //WARNING: creates 'Zombie' child process
else { //else in child process
system("nohup php -f " .$file . " 2> /dev/null"); //executes augmented file
exit(0); //exits exit child process
}
}
function augmentStr($inArr) {
if (mt_rand(0,6) < 5) { //determines mutability
/*$startIndex & $endIndex define mutable parts of file line as Xs
* system("echo XXXXX ... XXXXX\\n");
* 01234567890123 -7654321
*/
$startIndex = 13;
$endIndex = count($inArr)-7;
$targetIndex = mt_rand($startIndex,$endIndex); //choose mutable index
$inArr[$targetIndex] = getSafeChar(mt_rand(0,62)); //mutate index
$inArr = augmentStr($inArr); //recurse
}
return $inArr;
}
function getSafeChar($inNum) { //cannot use escaped characters
$outChar; //must be a standard PHP char
if ($inNum >= 0 && $inNum <= 9 ) { $outChar = chr($inNum + 48); }
else if ($inNum >= 10 && $inNum <= 35) { $outChar = chr($inNum + 55); }
else if ($inNum >= 36 && $inNum <= 61) { $outChar = chr($inNum + 61); }
else if ($inNum == 62) { $outChar = " "; }
else { $outChar = " "; }
return $outChar;
}
?>
Run Code Online (Sandbox Code Playgroud)
警告:创建一个僵尸进程,知道在运行代码之前如何杀死僵尸进程
信息查找技术:
本文包含比Wikipedia更具体的信息.但是,本文不包含真正的源代码.如果您需要我的建议,虽然找到示例源代码的可能性很小,但您可以找到足够的学术文档来创建自己的变形代码.考虑这个开始(谷歌学者):
在阅读学术论文/论文时,请务必查看文档末尾的来源,因为这些来源我也有宝贵的信息.
祝你在寻求知识方面好运!