38

我了解多态代码和变形代码的概念,但我最近阅读了这两种代码的 Wikipedia 页面(出于某种原因,我以前没有这样做过!)。现在我真的很想为自己写一些变质的代码。

我是一个不会语言的大师,只是涉足许多语言。我知道一些 PHP、MySQL、c/c++、Java、Bash 脚本、Visual Basic 6、VBScripting、Perl、JavaScript。

任何人都可以提供任何这些语言的变形代码示例。我希望看到一个工作示例,即使程序的输出只是“Hello World”,以通过示例了解这是如何发生的(我正在努力理论化如何仅通过心理思考来实现这些技术)。任何语言都可以,这些只是首选语言。

此外,在互联网上搜索仅返回有限数量的 c/c++ 示例(甚至没有完整的工作示例,更多部分代码片段),是因为我建议的其他语言级别不够低,无法拥有强大的功能/制作变形代码所需的灵活性?

4

4 回答 4

53

下面是一个我认为会被归类为用 C 编写的变形代码的示例。恐怕我没有太多编写可移植 C 代码的经验,因此可能需要进行一些修改才能在其他平台上编译(I' m 在 Windows 上使用旧版本的 Borland)。此外,它依赖于 x86 的目标平台,因为它涉及一些机器代码生成。理论上它应该可以在任何 x86 操作系统上编译。

这个怎么运作

每次程序运行时,它都会生成一个随机修改的自身副本,文件名不同。它还打印出已修改的偏移量列表,以便您可以看到它实际在做什么。

修改过程非常简单。源代码只是用汇编指令序列解释,实际上什么都不做。当程序运行时,它会找到这些序列并随机用不同的代码替换它们(这显然也不做任何事情)。

对于其他人需要能够编译的东西,硬编码偏移列表显然是不现实的,因此序列的生成方式使得它们在通过目标代码的搜索中易于识别,希望不会匹配任何误报.

每个序列都以对某个寄存器的推送操作开始,一组修改该寄存器的指令,然后是弹出操作以将寄存器恢复为其初始值。为简单起见,在原始源中,所有序列都只是PUSH EAX、八个NOPs 和POP EAX。然而,在应用程序的所有后续版本中,序列将完全是随机的。

解释代码

我已将代码分成多个部分,因此我可以尝试逐步解释它。如果你想自己编译它,你只需要将所有部分连接在一起。

首先一些相当标准的包括:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

接下来我们定义了各种 x86 操作码。这些通常会与其他值组合以生成完整的指令。例如,PUSHdefine( 0x50) 本身是PUSH EAX,但您可以通过添加 0 到 7 范围内的偏移量来导出其他寄存器的值。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,NOPPOP EAX. JUNKLEN是该NOP序列中 s 的数量 - 不是序列的全长。

如果您不知道,__emit__这是一个将文字值直接注入目标代码的伪函数。如果您使用不同的编译器,我怀疑它可能是您需要移植的东西。

#define JUNK __emit__(PUSH,NOP,NOP,NOP,NOP,NOP,NOP,NOP,NOP,POP)
#define JUNKLEN 8

将加载我们的代码的一些全局变量。全局变量不好,但我不是一个特别好的编码器。

unsigned char *code;
int codelen;

接下来我们有一个简单的函数,它将我们的目标代码读入内存。我从不释放那段记忆,因为我不在乎。

注意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;
}

另一个简单的函数,用于在修改后再次写出应用程序。对于新文件名,我们只需将原始文件名的最后一个字符替换为每次递增的数字。没有尝试检查文件是否已经存在以及我们没有覆盖操作系统的关键部分。

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);
}

下一个函数为我们的垃圾序列写出一条随机指令。reg参数表示我们正在使用的寄存器 - 将在序列的任一端推送和弹出的内容。偏移量是代码中将写入指令的偏移量。空间给出了我们在序列中剩下的字节数。

根据我们有多少空间,我们可能会限制我们可以写出哪些指令,否则我们会随机选择它是 aNOP还是其他指令MOV之一。NOP只是一个字节。MOV 是 5 个字节:我们的 MOV 操作码(添加了reg参数)和 4 个随机字节,表示移入寄存器的数字。

对于两个字节序列,第一个只是我们随机选择的一个前缀。第二个字节是最低有效 3 位表示主寄存器的范围内的字节0xC0-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;
  }
}

现在我们有了读回这些指令之一的等效函数。假设我们已经在序列的任一端识别出regfrom PUSHandPOP操作,该函数可以尝试验证给定指令是否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;
}

下一个函数是搜索和替换垃圾序列的主循环。它首先在八个字节后(或任何设置的)上查找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需要特殊考虑,不值得努力)。

    if (reg == 4) continue; /* register 4 is ESP */

一旦我们匹配了一个看起来很可能的 PUSH 和POP组合,我们就会尝试阅读其间的说明。如果我们成功匹配了我们期望的字节长度,我们认为这是一个可以替换的匹配。

    j = 0;                                           JUNK;
    while (inc = readinstruction(reg,i+1+j)) j += inc;
    if (j != JUNKLEN) continue;                      JUNK;

然后,我们随机选择 7 个寄存器中的一个(如在我们不考虑之前解释的那样ESP),并在序列的任一端写出该寄存器的PUSHandPOP操作。

    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]参数包含应用程序文件名。

int main(int argc, char* argv[]) {

  readcode(argv[0]);     JUNK;
  replacejunk();         JUNK;
  writecode(argv[0]);    JUNK;

  return 0;
}

这就是它的全部。

一些最后的笔记

运行此代码时,显然您需要确保用户具有在与原始代码相同的位置写出文件的适当权限。然后,一旦生成了新文件,如果您在文件扩展名很重要的系统上,通常需要重命名它,或者如果需要,设置其执行属性。

最后,我怀疑您可能希望通过调试器运行生成的代码,而不是直接执行它并希望获得最好的结果。我发现如果我将生成的文件复制到原始可执行文件上,调试器很乐意让我在查看原始源代码的同时单步执行它。然后,每当您到达代码中的某个点JUNK时,您可以弹出到程序集视图并查看已生成的代码。

无论如何,我希望我的解释已经相当清楚,这就是你正在寻找的那种例子。如果您有任何问题,请随时在评论中提问。

奖金更新

作为奖励,我想我还会在脚本语言中包含一个变形代码的示例。这与 C 示例完全不同,因为在这种情况下,我们需要修改源代码,而不是二进制可执行文件,我认为这更容易一些。

在这个例子中,我大量使用了 php 的goto函数。每行都以一个标签开始,并以goto指向下一行的标签结束。这样每一行基本上都是独立的,我们可以愉快地打乱它们,并且仍然让程序像以前一样工作。

条件和循环结构稍微复杂一些,但它们只需要以跳转到两个不同标签之一的条件的形式重写。我在代码中包含了注释标记,循环将尝试使其更容易遵循。

ideone.com 上的示例代码

代码所做的只是回显其自身的洗牌副本,因此您只需将输出剪切并粘贴回源字段并再次运行即可轻松地在 ideone 上对其进行测试。

如果您希望它发生更多变异,那么每次运行代码时都可以很容易地使用一组不同的随机字符串替换所有标签和变量。但我认为最好尽量让事情尽可能简单。这些例子只是为了演示这个概念——我们实际上并不是在试图避免被发现。:)

于 2013-05-04T23:43:47.883 回答
26

公开可用的变形代码示例受到以下几个因素的限制:

  • 1) 专长:变形编码是计算机编程中极为先进的技术。能够编写适合采样的连贯和干净的变形代码的程序员数量非常少。

  • 2)财务激励:变形编码在商业应用中的用途有限。正因为如此,拥有足够技能来创建变形代码的程序员的数量没有专业的接触/激励来创建/学习变形编码技术。

  • 3) 合法性:变形编码在强大的病毒创建中有很大的应用。因此,任何创建变形代码的负责任的专业人员在自由分发样本时都会遇到道德问题,因为 ametuer 黑客可能能够使用该代码来增强恶意攻击。相反,任何有能力创建变形代码的黑客都没有动力宣传他的技能,如果他的一次攻击被发现,因为他将根据能力被列入一个非常短的嫌疑人名单。

  • 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;
}

?>

警告:创建一个僵尸进程,知道如何在运行代码之前杀死一个僵尸进程

信息查找技术:

这篇文章包含比维基百科更具体的信息。但是,本文不包含真正的源代码。如果您愿意接受我的建议,尽管找到示例源代码的可能性很小,但您可能能够找到足够的学术文档来创建自己的变形代码。考虑这个开始(谷歌学者):

在阅读学术文章/论文时,请务必查看文档末尾的来源,因为这些来源也包含有价值的信息。

祝您在求知过程中好运!

于 2012-08-05T05:03:11.183 回答
2

这个答案未完待续,我会随着时间的推移继续展开,直到这个问题的答案完成

脚本示例 - PHP

我自己制作了 James Holderness 提供的 PHP 脚本的副本,这样我就可以通过演示变质脚本如何工作来亲眼看看。完整的代码写在这里;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;
?>
于 2013-09-29T14:55:58.077 回答
0

根据在 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;

}
于 2020-08-24T16:53:32.810 回答