Shellcode:x86优化(二)

Shellcode:x86优化(二)_第1张图片

条件跳转/控制流

这部分会涉及到高级语言中FOR,WHLIE 和 DO/WHILE 语句。

NOP 指令只是填充材料,用它来取代一些有用的地方。

通常如果 ECX 空闲的话我会用它来配合 LOOP 指令只用,这都得看情况。

循环 2 次


Shellcode:x86优化(二)_第2张图片

另一种循环两次的方法是如果你所有的寄存器都用完了,你可以使用进位标志。我们首先清空它(如果还没有的话),将该标志保存到栈中,执行我们的代码,重新保存标志符,填充,然后再循环。


Shellcode:x86优化(二)_第3张图片

循环 3 次

对于PF,将一个寄存器设置为0,并inc 直到PF=1.

对于SF,将一个寄存器设置为0,并增加 43 到 63 的一个数,直到SF=1。

对于ZF,将一个寄存器设置为0,并增加 85 知道ZF=1.

Shellcode:x86优化(二)_第4张图片

你可能会说,我们也可以简单的讲 EAX 设置为3,然后 dec 直到0,这也是正确的。

Shellcode:x86优化(二)_第5张图片

如果可以用ECX,我们可以用LOOP

Shellcode:x86优化(二)_第6张图片

当 ECX 空闲,就用 LOOP。

如果我们不能用 ECX,将 AL 设置为 -1 然后用 sub,相比于第 2 个例子,这样能够为我们节省一个字节。如果你喜欢用加法,你也可以将 AL 设置为 1。


Shellcode:x86优化(二)_第7张图片

循环 4 次

这个会比较简单,因为对于ZF,256能被4整除,对于SF,256能被128整除。


Shellcode:x86优化(二)_第8张图片

你可以继续这样做,但是从上面的例子中你应该学会自己写。

关于实现涉及条件跳转的控制流的最后一部分是使用相对偏移量。比如说,现在情况是这样,有一个其他函数的返回值,你想伪造一个函数来混淆代码,让分析代码变得更困难。这是很基础的。

虽然这里只测试了TRUE或FALSE,但如果要测试-1或小于0的有符号值也很简单啊。

Shellcode:x86优化(二)_第9张图片

字符转换

在很多情况下你会想要把一串小写字母转换为大写以及相反操作,或者实现unicode到ansi的转换。这里只是展示如何转换拉丁字母表。

大/小写转换

转换大小写不过是开关切换键的事。观察下面的字母以及它们的二进制值。

a = 01100001 A = 01000001

b = 01100010 B = 01000010

c = 01100011 C = 01000011

对于小写,第五位是1(我是从零开始算的)。

如果你想要一串字符串全是小写或全是大写,用XOR。

// flip the case    "\x34\x20"            /* xor al, 0x20            */

如果你想将字符串都变成小写但又不想每一位都比较,用OR。

// convert to lowercase    "\x0c\x20"            /* or al, 0x20              */

至于要转换成大写,将它和0xDF进行 and,这样第五位就会变为0. 这里有个问题是,如果是数字或者该字母没有对应的小写字母的话,这个方法将失效。

// convert to uppercase    "\x24\xdf"            /* and al, 0xdf            */

当然你也可以用BTS把它设置为小写,但是这需要占用更多字节。

// set to lowercase    "\x0f\xba\xe8\x05"    /* bts eax, 0x5            */

或者如果你只想改变那个字母,用BTR。

// flip case    "\x0f\xba\xf0\x05"    /* btr eax, 0x5            */

如果你的那串字符串里有数字或者其他字符会发生什么呢?有一次我在shellcode里用了转化小写而不是转换为大写,因为大写需要条件跳转而当时我并不想跳转。

拿metasploit的代码 block_api.asm 来举个例子。

将字符串转换为小写,你可以用下面的代码。记住,这里是从InLoadOrderLinks  读取DLL的相关信息,跟Metasploit 读的不一样。

Shellcode:x86优化(二)_第10张图片


Shellcode:x86优化(二)_第11张图片

你肯定不能直接把这段代码插入到原来的metasploit代码中去,因为生成的hash值完全不一样。这只是给你介绍一直方法。

如果你用OR来设置数字0~9的第五位,它们不会有任何改变,因为它们的第五位本来就是1 啊。

同样的,对于用来做分开模块名和后缀的“.”也一样不会改变。比如,用OR可以将KERNEL32.DLL 变成 kernel32.dll  而不需要任何条件跳转。

但是如果是用SUB指令来讲其转换为大写,你需要条件跳转。

Ansi 和 Unicode

好吧,这并不完全是unicode转换因为我们只用到拉丁字母表。Unicode字符串以两个null字节作为结束标志,所以一旦遇到null应该停止。

Shellcode:x86优化(二)_第12张图片

致谢

有很多人通过分享他们的知识和想法帮我间接完成了这篇博文。文章中的代码借鉴了这些人的想法:

drizz,

r!sc, d0ris, jb, Z0MBiE, WiteG, Vecna, Mental Driller, GriYo, JPanic,

Qkumba/Peter Ferrie, Jacky Qwerty, Super, hh86, benny 还有一些我忘了的。

本文由 看雪翻译小组 lumou 编译,来源 modexp

如果你喜欢的话,不要忘记点个赞哦!

你可能感兴趣的:(Shellcode:x86优化(二))