转载自
095K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3u0G2L8%4c8C8K9i4c8*7i4K6u0W2j5$3!0E0i4K6u0r3i4K6y4r3M7q4)9K6c8o6V1K6i4K6t1$3L8X3u0K6M7q4)9K6b7W2)9J5y4X3&6T1M7%4m8Q4x3@1u0Q4c8e0k6Q4z5e0k6Q4z5o6N6Q4c8e0N6Q4b7f1u0Q4b7e0m8Q4c8e0S2Q4b7U0m8Q4z5o6S2Q4c8e0N6Q4z5f1q4Q4z5o6c8Q4c8e0g2Q4b7V1g2Q4z5o6S2Q4c8e0g2Q4b7e0g2Q4b7V1b7`.
使用C/Delphi编写ShellCode
这里说的ShellCode指的是一段x86机器码,无需重定位,在内存任何位置都可以执行的代码.
使用C/Delphi编写代码也是由其编译器特性决定的。
一说起ShellCode,一般人都想到MASM/TASM/FASM/NASM这类汇编语言编译器,但是,汇编语言写ShellCode维护起来总是很麻烦,肯定不如高级语言维护起来方便,尤其是大型的工程,例如PE Virus/Packer这样的工程。
ShellCode可以在内存中任何一个位置独立运行,他不需要外界对其进行任何操作,不需要装载器对其进行装载、重定位、填充IAT这样操作。
因此很多工作都需要自己去做。所以,在编写时需要一定的前置知识:
1.编译器的实现原理.高级语言源代码是怎么变成Opcode的,如何处理局部/全局变量,文件作用域内代码是如何排布的
2.PE文件结构的知识.尤其是EAT方面的,因为在 ShellCode执行过程中调用的API都需要你自己获取到,如何获取?自己写算法实现就好了~
3.操作系统的一些知识~
下面来说一下如何使用C/Delphi来编写 ShellCode
1.Kernel32.dll基址的获取.这个必须用汇编实现,这是最重要的,因为Kernel32.dll提供的LoadLibraryA和 GetProcAddress函数保证了ShellCode中其他功能的实现。
2.API的动态获取.在获取了 Kernel32.dll基址以后,就可以参照PE结构解析导出表获取相关函数的地址了。
3.固定代码的自定位.使用高级语言编写出的代码,里面肯定有类似mov eax,[0040xxxx]这样的代码,类似这样的访问固定地址的代码在ShellCode的执行过程中会造成各种异
常,如何减少这样的问题?内联汇编获取EIP,然后修正即可。选取一个好的数据结构可以很好的解决这个问题。
4.代码的编写。这里讨论的是使用高级语言编写ShellCode,因此我们谈论的主题将是高级语言。虽然高级语言编译器为我们提供了内联汇编的特性,但是这不是本文重点,只有在需要使用内联汇编的时候才使用,如获取Kernel32.dll基址与代码的自定位。编写时需要注意以下几点:
(1)ShellCode函数内部禁止引用全局变量/常量
(2)代码编写要紧凑,并且实现功能的代码的作用域仅限于本文件中,即函数从开始到结束的代码都在一个文件中,不要试图调用其他文件中的函数,这样会造成很严重的后果
只要保证上述几点都能做到,那么写出来的ShellCode将没有任何问题。
使用高级语言编写ShellCode的优点:易于理解,易于调试,易于维护,可以实现复杂的功能。在调用ShellCode前后堆栈、寄存器是平衡的~
使用高级语言编写ShellCode的缺点:编写出的 ShellCode体积较大,不易于优化,在对长度要求较高的场合不适用。
最后着重提一下使用Delphi编写 ShellCode,使用Delphi编写ShellCode的好处就是Delphi语法中的with … do结构可以很好的减少长代码的产生,使代码更易读,不像C语言在调用函数时需要lpShellCodeCrl->fMessageBoxA(…);这么罗嗦了。
再有一个好处就是可以编写涵盖32位~64位平台的ShellCode,Win32~Native环境的ShellCode,覆盖很全面。
由于现有的LCC/VS编译器的64位编译器中不允许使用内联汇编,使得使用C语言编写的ShellCode只能停留在32位下。
新推出的Delphi XE2提供了64位编译器支持的同时,还支持64位内联汇编,这就完美了。
有什么欠缺或不当的地方,还请指出。本文完。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课