打印在它上的文本看起来非常类似于objdump -d shellcode.o命令的输出。
如果您有机会编写自己的shellcode,您将立即识别此JMP,呼叫,流行模式。纯表单如下所示(NASM语法):
JMP短_END _RUN:POP ESI;现在ESI寄存器包含数据标签的地址;实际的Shellcode指令在这里_end:呼叫_Run数据:DB'一些数据'
当我们在真实系统上使用shellcode时,我们不知道我们的shellcode的内存地址将被加载。有时这些信息可以非常有用。当我们的shellcode不仅包含代码而且数据而且呼叫可以在相对地址上运行,因此允许我们写入位置独立代码(图片),数据访问指令(MOVS)需要绝对地址。
注意:最后一句在x86_64架构上不再是真实的,因为它引入了一个名为" RIP相对地址&#34的新寻址模式;
当我们在相对地址模式中使用JMP和呼叫指令时,我们实际上使用偏移相对于JMP或Call opcode.so相对跳转JMP短0(再次NASM语法),只会跳转到下一个指令和JMP短-2将创建一个无限循环(假设整个JMP指令需要两个字节)。
呼叫偏移指令更有趣,因为它不仅跳转到偏移量,而且还将推动堆栈上以下指令的地址(所谓的返回地址)。
现在我们可以了解JMP,呼叫,流行模式Works.First我们需要在数据之前定位呼叫指令,我们想要获得地址。然后我们做一个相对跳转到呼叫。调用将在堆栈上放置下一个指令(在这种情况下我们的数据)的地址,并将再次执行相对跳转到指定的偏移量。现在我们有我们在堆栈上的数据的地址,因此我们可能只是将其流程为我们选择的注册
当我们再次看看T恤时,我们可能会注意到印刷的实际偏移有问题。 JMP 0x2B实际上应该是JMP 0x2A,因为呼叫指令的地址是0x2F = 0x05 + 0x2a。另一方面,呼叫指令应该跳转到POP ESI指令,因此偏移量应为0x2F(调用Addr)+ 0x05(呼叫指令的长度)+ Offset = 0x05,或-0x2f(使用2'补充这个值可以表示为0xFFFFFFD1)。
MOV DWORD PTR [ESI + 0x8],ESI MOV字节PTR [ESI + 0x7],0x0 mov DWORD PTR [ESI + 0xC],0x0
我们现在知道ESI在我们的最后一个shellcode指令之后指向该区域。我们可以将此内存区域说明为:
执行所有这些移动指令(在我们这里拥有的英特尔语法中,它始终是mov ist,src)我们的内存区域将如下所示:
现在这很有意思。看起来我们有一个七个字符串终止为零,然后指向该字符串的指针和空值。
🤔七字符串,七字符串......当它是关于shellcodes它必须是/ bin / sh:d,所以它看起来像t恤上的shellcode被截断,最后两个指令应该如下所示:
既然我们知道丢失的字节是什么,我们可能希望我们的shellcode正在调用其中一个执行函数。在unistd.h中声明了cexerme.h是:
它需要三个参数,应该知道每个C程序员。argv和envp阵列包含指向字符串的指针,必须终止包含null的条目。以下是我们如何在C中使用EXECVE:
int main(int argc,char ** argv){char * args [] = {" / bin / sh",null}; char * env [] = {null};执行(args [0],args,Env);}
实际上,当Env为空时,我们可能会一点压缩此代码(通过重用已在args数组中存在的NULL):
int main(int argc,char ** argv){char * args [] = {" / bin / sh",null};执行(args [0],& args [0],& args [1]);}
mov eax,0xb;执行(文件名,argv,envp)movebx,ESI Lea ECX,[ESI + 0x8] LEA EDX,[ESI + 0xC] INT 0x80
INT 0x80指令是调用从32位代码(64位代码时下通常使用系统调用指令)。当我们称一个系统函数,我们通过在EBX函数参数,ECX EDX,ESI Linux内核的标准方法,EDI和EBP以此顺序注册。 EAX寄存器用于选择功能本身。我们可能会在此处查看所有可用功能的列表。
例如要呼叫退出(0),首先需要检查分配给退出功能(0x01)的值并将其放在EAX寄存器中的值。退出(0)采取一个争论。我们必须将该参数值放在EBX寄存器中(后续参数将进入ECX,然后在EDX中进入EDX等)。最后我们可以使用INT 0x80软件中断调用内核:
EXECVE函数分配给0x0B。当我们再次查看T恤时,在那里,在一块MOV之后,我们可以看到这函数恰好被视为:
mov eax,0xb;执行(文件名,argv,envp)movebx,ESI Lea ECX,[ESI + 0x8] LEA EDX,[ESI + 0xC] INT 0x80
Lea指令用于将操作数的地址加载到指定的寄存器。由于我们使用间接存储器寻址,因此lea ECX,[ESI + 0x8]相当于C中的ECX = ESI + 0x08。
在所有这些MOV和租赁之后,我们在EBX中拥有/ bin / sh字符串的地址,args数组的地址(指针到/ bin / sh,后跟null)在ecx中,最后在edx中的null的null的地址。在我们的代码中的其他单词中相当于我们之前看到的C代码:
int main(int argc,char ** argv){char * args [] = {" / bin / sh",null};执行(args [0],& args [0],& args [1]);}
跟随呼叫执行,是退出(0)的呼叫。这是Shellcode中使用的标准技术,只需退出程序而不崩溃。这样我们就不会留下我们的代码痕迹(毫无疑问)。
JMP短_sh_last _sh_start:pop esi mov dword [esi + 0x8],esi mov byte [esi + 0x7],0x0 mov dword [esi + 0xc],0x0 mov eax,0xb;执行(文件名,ARGV,ENVP)MOV EBX,ESI LEA ECX,[ESI + 0x8] lea EDX,[ESI + 0xC] INT 0x80 MOV EAX,0x1;退出(0)MOV EBX,0x0 int 0x80 _sh_last:呼叫_sh_start db' / bin / sh'
现在这个故事的寓意:始终把一个工作的shellcode放在T恤上,以避免像这样的帖子进一步尴尬;)
奖金:此repo包含一个Makefile,它将构建shellcode,并准备包含shellcode字节的C头文件。还有一个包装程序程序,将证明shellcode确实有效。您需要的唯一是32位Linux。
uname -alinux 4.15.0-133-通用#137〜16.04.1-Ubuntu SMP Fri 1月15日02:55:05 UTC 2021 I686 I686 I686 GNU / Linux
要编译汇编代码,您将需要nasm。您可以使用apt-get安装它。