2

背景/我想要完成的事情的解释

我目前正在从事一个小型恶意软件分析项目,并正在尝试实现我使用Unicorn编写的字符串解密器。为了精简内容并使代码更易于查看,我在下面从我的较大代码库中制作了一个较小的示例。

我正在做的是提取代表小字符串解密例程的 x86 片段。有一系列 mov 指令最终被异或产生一个明文字符串。我已经注释掉了应该产生的字符串值。在以下示例中,模拟了未注释的 X86_CODE64 指令,但仅在hpe.com我从堆栈地址中读取时才会产生结果。(提示:要查看输出,请在 上运行字符串asdf.txt)我希望看到apple.comhpe.com

问题

根据下面的代码,我做错了什么/根本没有做会导致以下代码片段无法正确解密字符串?

免责声明:这是我第一次使用独角兽,所以如果我表达不清楚或解释有困难,我提前道歉!

#!/usr/bin/python

from __future__ import print_function
from unicorn import *
from unicorn.x86_const import *

# code to be emulated
# Strings should include apple.com and hpe.com
X86_CODE64 = b'\xc7D$<\xa9GY\x01\xc7D$@\xa2XQ/\x8bD$<\x8aD$8\x84\xc0u\x19H\x8b\xcb\x8bD\x8c<5\xc17</\x89D\x8c<H\xff\xc1H\x83\xf9\x02r\xeaE3\xc0H\x8dT$<H\x8b\xcf\xe8<\xd2\xfe\xff\x88]\xa4\xc7E\xa8\x86/\x00v\xc7E\xac\x82q\x13u\xc7E\xb0\x8a_p\x1a\x8bE\xa8\x8aE\xa4\x84\xc0u\x19H\x8b\xcb\x8bD\x8d\xa85\xe7_p\x1a'

# Strings should be svchost.exe
#X86_CODE64 = b"\xba\xe7_p\x1a\xc7D$|\x94)\x13r\xc7E\x80\x88,\x044\xc7E\x84\x82'\x15:\x89U\x88\x8bD$|\x8aD$x\x84\xc0u\x16H\x8b\xcf\x8bD\x8c|3\xc2"

# Strings should be apple.com
#X86_CODE64 = b'\xc7E\xa8\x86/\x00v\xc7E\xac\x82q\x13u\xc7E\xb0\x8a_p\x1a\x8bE\xa8\x8aE\xa4\x84\xc0u\x19H\x8b\xcb\x8bD\x8d\xa85\xe7_p\x1a'


# Set up Unicorn
ADDRESS = 0x10000000
STACK_ADDRESS = 0x90000
mu = Uc(UC_ARCH_X86, UC_MODE_64)
mu.mem_map(ADDRESS, 4 * 1024 * 1024)
mu.mem_map(STACK_ADDRESS, 4096*10)

# Write code to memory
mu.mem_write(ADDRESS, X86_CODE64)
# Initialize Stack for functions
mu.reg_write(UC_X86_REG_ESP, STACK_ADDRESS + 4096)
mu.reg_write(UC_X86_REG_EDX, 0x0000)

# Run the code
try:
    mu.emu_start(ADDRESS, ADDRESS + len(X86_CODE64), timeout=10000)
except UcError as e:
    pass

#a = mu.mem_read(ADDRESS, 4 * 1024 * 1024)
#print(a)
b = mu.mem_read(STACK_ADDRESS, 4096*10)

with open('asdf.txt', 'ab') as fp:
    fp.write(b)

4

1 回答 1

2

这段代码几乎没有问题。

pass首先,您可能永远不想通过except至少在顶层写入来吞下所有异常。至少为了知道是否发生了任何意外情况而将它们写入控制台会很好。如果你这样做,你会注意到独角兽Invalid memory fetch (UC_ERR_FETCH_UNMAPPED)在代码执行期间抛出了一个。

如果您分析字节,您会注意到在第一个代码中间有一个奇怪的调用

40: e8 3c d2 fe ff          call   0xfffffffffffed281

这个调用是在解密之后hpe.com并且 unicorn 停止执行代码并且永远不会到达代码的第二部分。在 unicorn 中可能有更好的方法来处理这个问题,但现在让我们只nop调用(用 5x 替换 5 个字节\x90)。这仍然不会产生预期的apple.com字符串,因为此代码有更多问题。第二部分(通话后)没有使用RSP,但RBP您没有在代码中设置它。

所以我们需要补充一点:

mu.reg_write(UC_X86_REG_EBP, STACK_ADDRESS + 4096)

这是另一个问题。您正在为 64 位设置独角兽,但您初始化了 32 位寄存器 - ESP, EDX. 这是故意的吗?在您的情况下,这可能不是问题,但您可能应该初始化 64 位 regs。

添加RBP设置为某个堆栈地址后,您仍然不会看到第二个字符串,因为代码有点太早了。最后的指令是read & xor

6a: 8b 44 8d a8             mov    eax,DWORD PTR [ebp+ecx*4-0x58]
6e: 35 e7 5f 70 1a          xor    eax,0x1a705fe7

但是没有存储,没有增量到下一部分,也没有循环。

也许你复制的字节太少。如果我们这样添加那些缺失的字节:89448da8for store ( mov DWORD PTR [rbp+rcx*4-0x58],eax)、48ffc1for inc rcx4883f903forcmp rcx, 0x3和最后72eafor jb -0x16

所以总的来说,你的第一个代码错过了以下字节 89448da848ffc14883f90372ea(+ nop the call),并且

❯ python3 program.py
❯ 字符串 asdf.txt
apple.com
hpe.com

你得到了预期的结果。

简要检查了第二个和第三个代码,它似乎没有call,但它们也缺少商店、公司和循环部分。

于 2022-02-26T09:13:20.990 回答