我理解Polymorphic 和Metamorphic 代码的概念,但我最近阅读了两者的维基百科页面(因为我以前没有这样做过!)。现在我真的想为自己编写一些变形代码。
我是一个没有语言的大师,很多人。我知道一些PHP,MySQL,c / c ++,Java,Bash脚本,Visual Basic 6,VBScripting,Perl,JavaScript。
任何人都可以在任何这些语言中提供变形代码的示例。我希望看到一个有效的例子,即使程序的输出只是“Hello World”,也要通过例子来理解这是如何发生的(我正在努力理解这些技术如何通过心理思维来实现)。任何语言都可以做到,这些只是首选语言。
此外,在互联网上搜索只返回了c / c ++中的有限数量的例子(甚至不是完整的工作示例,更多部分代码片段),因为我所建议的其他语言的水平不够低是否具备制作变形代码所需的功能/灵活性?
答案 0 :(得分:47)
以下是我认为将其归类为用C语言编写的变形代码的示例。我担心我在编写可移植C代码方面没有太多经验,因此可能需要对其进行一些修改。在其他平台上编译(我在Windows上使用旧版本的Borland)。此外,它依赖于目标平台是x86,因为它涉及一些机器代码生成。从理论上讲,它应该在任何x86操作系统上编译。
工作原理
每次运行程序时,它都会生成一个随机修改的自身副本,文件名不同。它还打印出一个已修改的偏移列表,以便您可以看到它实际上在做什么。
修改过程非常简单。源代码只是用汇编指令序列来解释,它们实际上什么都不做。当程序运行时,它会找到这些序列,然后用不同的代码随机替换它们(显然也没有任何代码)。
对于其他人需要能够编译的内容,硬编码偏移列表显然不现实,因此序列的生成方式使得在搜索目标代码时很容易识别,希望没有匹配任何误报。
每个序列首先对某个寄存器执行push操作,一组修改该寄存器的指令,然后执行pop操作以将寄存器恢复为其初始值。为简单起见,在原始资源中,所有序列仅为PUSH EAX
,八个NOP
和POP EAX
。然而,在应用程序的所有后续版本中,序列将完全随机。
解释代码
我已将代码分成多个部分,因此我可以尝试逐步解释它。如果你想自己编译,你只需要将所有部分加在一起。
首先,一些相当标准的包括:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
接下来,我们定义了各种x86操作码。这些通常与其他值组合以生成完整指令。例如,PUSH
define(0x50
)本身就是PUSH EAX
,但您可以通过添加0到7范围内的偏移量来推导其他寄存器的值。{ {1}}和POP
。
MOV
最后六个是几个双字节操作码的前缀字节。第二个字节对操作数进行编码,稍后将对其进行更详细的解释。
#define PUSH 0x50
#define POP 0x58
#define MOV 0xB8
#define NOP 0x90
#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 };
是一个宏,可以在代码中的任何位置插入我们的垃圾操作序列。正如我之前解释的那样,它最初只写出JUNK
,PUSH EAX
和NOP
。 POP EAX
是该序列中JUNKLEN
的数量 - 而不是序列的全长。
如果你不知道,NOP
是一个伪函数,它将文字值直接注入到目标代码中。我怀疑如果您使用不同的编译器,可能需要移植它。
__emit__
我们的代码将被加载的一些全局变量。全局变量很糟糕,但我不是一个特别好的编码器。
#define JUNK __emit__(PUSH,NOP,NOP,NOP,NOP,NOP,NOP,NOP,NOP,POP)
#define JUNKLEN 8
接下来我们有一个简单的函数,它将我们的目标代码读入内存。我从不释放那段记忆,因为我不在乎。
请注意随机点插入的 unsigned char *code;
int codelen;
宏调用。您将在整个代码中看到更多这些内容。您几乎可以在任何地方插入它们,但如果您使用真正的C编译器(而不是C ++),如果您尝试将它们放在变量声明之前或之间,它就会抱怨。
JUNK
另一个简单的功能是在修改后再次编写应用程序。对于新文件名,我们只需用每次递增的数字替换原始文件名的最后一个字符。不会尝试检查文件是否已存在以及我们是否覆盖了操作系统的关键部分。
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;
}
这个下一个函数为我们的垃圾序列写出一个随机指令。 reg 参数表示我们正在使用的寄存器 - 将在序列的任一端推送和弹出的内容。 偏移是代码中将写入指令的偏移量。 space 给出了我们在序列中留下的字节数。
根据我们有多少空间,我们可能会限制我们可以写出哪些说明,否则我们会随意选择是{sa 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);
}
,NOP
还是其中一个。 MOV
只是一个字节。 MOV是五个字节:我们的MOV操作码(添加了 reg 参数),以及4个随机字节,表示移入寄存器的数字。
对于两个字节序列,第一个只是随机选择的前缀之一。第二个是NOP
到0xC0
范围内的一个字节,其中最低有效3位代表主寄存器 - 即必须设置为 0xFF
的值参数。
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;
}
}
和reg
操作中识别出 PUSH
,则此函数可以尝试验证是否给定 POP
的指令是我们的垃圾操作之一,主寄存器与给定的 offset
参数匹配。
如果找到有效匹配,则返回指令长度,否则返回零。
reg
下一个函数是搜索和替换垃圾序列的主循环。首先是在8个字节之后(或者 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;
}
设置为),在同一个寄存器上查找PUSH
操作码后跟POP
操作码。
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;
,我们可以安全地跳过它,因为我们永远不会在生成的代码中使用ESP
(ESP
上的堆栈操作需要特别考虑这不值得努力。
ESP
一旦我们匹配了可能正在寻找的PUSH和 if (reg == 4) continue; /* register 4 is ESP */
组合,我们就会尝试阅读其间的说明。如果我们成功匹配了我们期望的字节长度,我们会考虑可以替换的匹配。
POP
然后我们随机选择7个寄存器中的一个(如前所述,我们不考虑 j = 0; JUNK;
while (inc = readinstruction(reg,i+1+j)) j += inc;
if (j != JUNKLEN) continue; JUNK;
),并在该寄存器中写出ESP
和PUSH
操作序列的任何一端。
POP
然后我们需要做的就是使用我们的 reg = rand()%7; JUNK;
reg += (reg >= 4);
code[i] = PUSH+reg; JUNK;
code[i+JUNKLEN+1] = POP+reg; JUNK;
函数填写中间的空格。
writeinstruction
在这里我们显示刚修补的偏移量。
space = JUNKLEN;
j = 0; JUNK;
while (space) {
inc = writeinstruction(reg,i+1+j,space); JUNK;
j += inc;
space -= inc; JUNK;
}
最后我们有主要功能。这只是调用前面描述的功能。我们读入代码,替换垃圾,然后再写出来。 printf("%d\n",i); JUNK;
}
}
参数包含应用程序文件名。
argv[0]
这就是它的全部内容。
最后一些说明
运行此代码时,显然您需要确保用户具有相应的权限,以便在与原始代码相同的位置写出文件。然后,一旦生成了新文件,如果您在文件扩展名很重要的系统上,则通常需要重命名它,或者如果需要,则设置其执行属性。
最后,我怀疑您可能希望通过调试器运行生成的代码,而不是直接执行它并希望最好。我发现如果我将生成的文件复制到原始可执行文件上,调试器很高兴让我在查看原始源代码的同时逐步完成它。然后,无论何时到达代码 JUNK 的某个点,您都可以进入程序集视图并查看已生成的代码。
无论如何,我希望我的解释相当清楚,这就是你要找的那种例子。如果您有任何疑问,请随时在评论中提问。
奖励更新
作为奖励,我认为我还在脚本语言中包含了变形代码的示例。这与C示例完全不同,因为在这种情况下我们需要改变源代码,而不是二进制可执行文件,我认为这更容易。
对于这个例子,我已经广泛使用了php的int main(int argc, char* argv[]) {
readcode(argv[0]); JUNK;
replacejunk(); JUNK;
writecode(argv[0]); JUNK;
return 0;
}
功能。每行以标签开头,以goto
结尾,指向下一行的标签。这样每条线基本上都是自包含的,我们可以愉快地将它们洗牌,并且仍然可以像以前一样完成程序。
条件和循环结构稍微复杂一点,但它们只需要以跳转到两个不同标签之一的条件的形式重写。我在代码中包含了注释标记,循环将尝试使其更容易理解。
所有代码都是回显自身的混乱副本,因此您可以通过将输出剪切并粘贴回源字段并再次运行来轻松地在ideone上进行测试。
如果你想让它变异更多,那么每次运行代码时用一组不同的随机字符串替换所有标签和变量会很容易。但我认为最好尽量保持简单。这些例子只是为了证明这个概念 - 我们实际上并没有试图避免检测。 :)
答案 1 :(得分:26)
公开提供的变形代码示例受到以下几个因素的限制:
1)专业知识:变形编码是计算机编程中非常先进的技术。能够编码适合采样的连贯和干净的变形代码的程序员数量非常少。
2)财务激励:变形编码在商业应用中的用途有限。因此,拥有足够技能来创建变形代码的程序员的数量没有专业的曝光/激励来创建/学习变形编码技术。
3)合法性:变形编码在强大的病毒创建中有很大的应用。因此,任何创建变形代码的负责任的专业人员都会有自由分发样本的道德问题,因为黑客可能能够使用该代码来增强恶意攻击。相反,任何有足够能力创建变形代码的黑客都没有动机宣传他的技能,如果发现他的一次攻击,那么他就会根据能力在一个非常短的嫌疑人名单上。
4)保密:最后,可能是最现实主义的变形代码难以找到的原因是因为任何程序员都表现出变形编程的能力,并且没有被当局逮捕网络犯罪很可能是由政府安全机构,私人保安公司或反病毒公司招募的,然后程序员随后的研究/知识将受到保密协议的约束,以保持竞争优势。
< / LI> 为什么只有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;
}
?>
警告:创建zombie process,知道在运行代码之前如何杀死僵尸进程
信息查找技巧:
This article包含更多特定信息,然后是维基百科。但是,本文不包含真正的源代码。如果您需要我的建议,虽然找到示例源代码的可能性极小,但您可以找到足够的学术文档来创建自己的变形代码。考虑这一点开始(google scholar):
在阅读学术论文/论文时,请务必查看文档末尾的来源,因为这些来源我也有宝贵的信息。
祝你好运!
答案 2 :(得分:2)
这个答案还没有结束,我将继续对其进行扩展,直到这个问题的答案完成为止
脚本示例 - PHP
我已经制作了我自己的PHP脚本James Holderness提供的副本,因此我可以通过演示如何看待变形脚本是如何工作的。完整的代码写在这里; http://null.53bits.co.uk/index.php?page=php-goto-replicator
简单地说,在最初执行脚本之后,它将自身复制到一个具有随机文件名的新文件,其中的代码行以新的随机顺序排列,然后它会执行一个新进程,该进程正在执行脚本文件的新副本并且原始副本退出。现在有一个正在运行的脚本的新副本,它是原始文件的副本,但具有随机文件名,并且代码行的顺序不同。这是一个永久的过程;重新排序和复制,然后执行一个新实例(进程)杀死前一个实例。
我的目的是将James Holderness的PHP答案扩展到一个有效的自我复制和变形代码示例。
这是我提出的原始PHP代码;
<?php goto a01;
a01: $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; goto a02;
a02: $randomString = __DIR__."/"; goto a03;
a03: $i = 0; goto a04;
a04: if ($i < 10) goto a05; else goto a07;
a05: $randomString .= $characters[rand(0, strlen($characters) - 1)]; goto a06;
a06: $i++; goto a04;
a07: $randomString .= ".php"; goto a08;
a08: $ARGS=Array("-f",$randomString); goto a09;
a09: $handle_out = fopen("$randomString", "w"); goto l01;
l01: $filename = __FILE__; goto l02;
l02: $contents = file_get_contents($filename); goto l03;
l03: $lines = explode("\n",$contents); goto l04;
l04: $collection = array(); goto l05;
l05: $pattern = '%^[^:]+:.*goto [^;]+;$%'; goto l06;
l06: $i = 0; goto l07;
l07: if ($i < count($lines)-1) goto l08; else goto l23;
l08: $line = $lines[$i]; goto l09;
l09: $line = trim($line); goto l10;
l10: if (substr($line,0,2) != '//') goto l11; else goto l22;
l11: if (preg_match($pattern, $line) === 1) goto l12; else goto l13;
l12: $collection[] = $line; goto l22;
l13: shuffle($collection); goto l14;
l14: $j = 0; goto l15;
l15: if ($j < count($collection)) goto l16; else goto l19;
l16: echo $collection[$j]."\n"; goto l17;
l17: fwrite($handle_out, $collection[$j]."\n"); goto l18;
l18: $j++; goto l15;
l19: $collection = array(); goto l20;
l20: fwrite($handle_out, $line."\n"); goto l21;
l21: echo $line."\n"; goto l22;
l22: $i++; goto l07;
l23: fclose($handle_out); goto f01;
f01: $pid = pcntl_fork(); goto f02;
f02: if ($pid == -1) goto f03; else goto f04;
f03: die("Could not fork a new child\n"); goto f03;
f04: if ($pid) goto f05; else goto f06;
f05: exit(0); goto f05;
f06: $sid = posix_setsid(); goto f07;
f07: if ($sid < 0) goto f08; else goto f09;
f08: die("Child posix_setsid error\n"); goto f08;
f09: sleep(10); goto f10;
f10: pcntl_exec(PHP_BINARY, $ARGS);
l24: exit(0); goto l24;
?>
答案 3 :(得分:0)
根据my comment,2013年9月29日:
在接下来的几周中,我将看一下C示例并再次进行更新。我使用Linux,并且代码无法在Linux上运行,所以我的目标是针对Linux进行调整,然后将其发布回去。
大约7年后的一个月(照常按时!),由于全球大流行,我终于找到了时间将@ james-holderness提供的答案移植到Linux(并修改为64位)符合Intel)。
可以在以下位置获得完整的文章,并附有关于同一主题的其他说明和其他变体:https://github.com/jwbensley/metamorphic_polymorphic/tree/master/meta_001_c
代码的完整副本也复制到下面:
#include <ctype.h> // isdigit()
#include <inttypes.h> // intN_t, PRIuN, uintN_t
#include <sys/stat.h> // chmod()
#include <stdio.h> // fp, fclose(), fopen(), fprintf(), fread(), fseek(),
// ftell(), fwrite(), printf(),
// SEEK_END, SEEK_SET, stderr
#include <stdlib.h> // EXIT_FAILURE, EXIT_SUCCESS, free(), malloc()
#include <string.h> // strchr(), strcmp(), strdup(), strlen()
#define B_PUSH_RAX ".byte 0x50\n\t" // push rax
#define B_PUSH_RBX ".byte 0x53\n\t" // push rbx
#define B_POP_RAX ".byte 0x58\n\t" // pop rax
#define B_POP_RBX ".byte 0x5b\n\t" // pop rbx
//#define B_NOP ".byte 0x90\n\t" // nop
/*
REX.W + 0x90+r64 == XCHG RAX, r64
"xchg rax, rax" is NOP (0x90)
Hackaround by using REX.W flag + opcode 0x87 + r64 to avoid the use of 0x90
*/
#define B_NOP ".byte 0x48,0x87,0xc0\n\t" // REX.W xchg rax,rax
#define H_PUSH 0x50 // push + reg
#define H_POP 0x58 // pop + reg
//#define H_NOP 0x90 // nop
#define H_NOP_0 0x48 // --------------------
#define H_NOP_1 0x87 // REX.W xchg rax,rax |
#define H_NOP_2 0xC0 // --------------------
#define JUNK_ASM __asm__ __volatile__ (B_PUSH_RBX B_PUSH_RAX B_NOP B_NOP B_POP_RAX B_POP_RBX)
#define JUNKLEN 10
int local_rand () {
/*
This program has such a short lifetime, srand(time(0)) simply isn't random
enough within a single execution of the program
*/
int digit;
FILE *fp;
fp = fopen("/dev/urandom", "r");
fread(&digit, 1, 1, fp);
fclose(fp);
return digit;
}
void insert_junk(uint8_t *file_data, uint64_t junk_start) {
JUNK_ASM;
/*
This is the matrix of source and destination register opcodes for Intel.
For example;
0xB8 == "mov"
0xB8 + 0xC0 == 0x178 "mov eax, eax"
0xB8 + 0xC8 == 0x180 "mov eax, ebx"
EAX ECX EDX EBX ESP EBP ESI EDI
EAX C0 C8 D0 D8 E0 E8 F0 F8
ECX C1 C9 D1 D9 E1 E9 F1 F9
EDX C2 CA D2 DA E2 EA F2 FA
EBX C3 CB D3 DB E3 EB F3 FB
ESP C4 CC D4 DC E4 EC F4 FC
EBP C5 CD D5 DD E5 ED F5 FD
ESI C6 CE D6 DE E6 EE F6 FE
EDI C7 CF D7 DF E7 EF F7 FF
*/
/*
The junk assembly instructions use the following pattern so that they can be identified:
r1 = random register from RAX, RBX, RCX or RDX
r2 = a different random register from RAX, RBX, RCX, RDX
push r1
push r2
xchg r1, r2
xchg r1, r2
pop r2
pop r1
*/
uint8_t reg_1 = (local_rand()%4);
uint8_t reg_2 = (local_rand()%4);
while(reg_2 == reg_1) {
reg_2 = (local_rand()%4);
}
uint8_t push_r1 = 0x50 + reg_1;
uint8_t push_r2 = 0x50 + reg_2;
uint8_t pop_r1 = 0x58 + reg_1;
uint8_t pop_r2 = 0x58 + reg_2;
uint8_t nop[3] = {0x48,0x87,0xC0};
nop[2] += reg_1;
nop[2] += (reg_2 * 8);
file_data[junk_start] = push_r1;
file_data[junk_start + 1] = push_r2;
file_data[junk_start + 2] = nop[0];
file_data[junk_start + 3] = nop[1];
file_data[junk_start + 4] = nop[2];
file_data[junk_start + 5] = nop[0];
file_data[junk_start + 6] = nop[1];
file_data[junk_start + 7] = nop[2];
file_data[junk_start + 8] = pop_r2;
file_data[junk_start + 9] = pop_r1;
}
int32_t load_file(uint8_t **file_data, uint32_t *file_len, const char *filename) {
JUNK_ASM;
FILE *fp = fopen(filename, "rb");
if (fp == NULL) {
fprintf(stderr, "Error opening file %s for reading\n", filename);
return(EXIT_FAILURE);
}
fseek(fp, 0L, SEEK_END);
if (ftell(fp) < 1) {
fprintf(stderr, "File %s 0 bytes in length\n", filename);
} else {
*file_len = ftell(fp);
}
*file_data = malloc(*file_len);
if (*file_data == NULL) {
fprintf(stderr, "Failed to malloc space for file\n");
return(EXIT_FAILURE);
}
fseek(fp, 0L, SEEK_SET);
if (fread((void*)*file_data, *file_len, 1, fp) != 1) {
fprintf(stderr, "Failed to fread() file %s\n", filename);
free(file_data);
return EXIT_FAILURE;
}
fclose(fp);
printf("Loaded %" PRIu32 " bytes from %s\n\n", *file_len, filename);
return EXIT_SUCCESS;
}
void replace_junk(uint8_t *file_data, uint64_t file_len)
{
JUNK_ASM;
for (uint64_t i = 0; i < file_len; i += 1) {
// Start of the junk ASM
if (file_data[i] >= H_PUSH && file_data[i] <= (H_PUSH + 3)) {
if (file_data[i + 1] >= H_PUSH && file_data[i + 1] <= (H_PUSH + 3)) {
if (file_data[i + 2 == H_NOP_0]) {
if (file_data[i + 3] == H_NOP_1) {
printf(
"Junk found, from offset 0x%" PRIX64 " (%" PRIu64 ") to "
"0x%" PRIX64 " (%" PRIu64 ")\n",
i, i, (i + JUNKLEN), (i + JUNKLEN)
);
printf("Replaced: ");
for (uint64_t j = i; j < (i + JUNKLEN); j += 1) {
printf("0x%" PRIX32 " ", file_data[j]);
}
printf("\n");
insert_junk(file_data, i);
printf("With: ");
for (uint64_t j = i; j < (i + JUNKLEN); j += 1) {
printf("0x%" PRIX32 " ", file_data[j]);
}
printf("\n");
printf("\n");
}
}
}
}
}
printf("All existing junk sequences have been replaced\n\n");
}
int32_t write_file(uint8_t *file_data, uint32_t file_len, const char *filename) {
JUNK_ASM;
FILE *fp;
int lastoffset = strlen(filename)-1;
char lastchar = filename[lastoffset];
char *newfilename = strdup(filename);
lastchar = '0'+(isdigit(lastchar)?(lastchar-'0'+1)%10:0);
newfilename[lastoffset] = lastchar;
fp = fopen(newfilename, "wb");
if (fp == NULL){
fprintf(stderr, "Error opening file %s for writing\n", filename);
free(newfilename);
return(EXIT_FAILURE);
}
fwrite(file_data, file_len, 1, fp);
if (ferror (fp))
fprintf(stderr, "Unable to write to new file %s\n", filename);
fclose(fp);
if (chmod(newfilename, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) != 0) {
fprintf(stderr, "Unable to chmod file %s\n", newfilename);
return EXIT_FAILURE;
}
printf("Wrote modified binary data to %s\n\n", newfilename);
free(newfilename);
return EXIT_SUCCESS;
}
int main(int argc, char* argv[]) {
JUNK_ASM;
// Load this file into memory
uint8_t *file_data = NULL;
uint32_t file_len;
if (load_file(&file_data, &file_len, argv[0]) != EXIT_SUCCESS)
return EXIT_FAILURE;
// Replace the existing junk ASM sequences with new ones
replace_junk(file_data, file_len);
// Write to disk as new file
if (write_file(file_data, file_len, argv[0]) != EXIT_SUCCESS) {
free(file_data);
return EXIT_FAILURE;
}
free(file_data);
return EXIT_SUCCESS;
}