如何将数组内容定位到堆中

我有了一个简单的字节数组,我用一个 x86 程序填充了它。我需要在运行时执行它。

def produce_simple_callable_procedure():
    """
    Produces a simple callable procedure which returns a constant.
    """
    from array import array

    simple = array('B')

    # mov rax, 0x10
    simple.extend((0x81, 0xc0, 0x10, 0x0, 0x0, 0x0))
    # ret
    simple.append(0xc3)

    return simple

def try_to_execute_simple_callable_procedure():
    """
    Attempts to execute a simple callable procedure which returns a constant.
    """
    from ctypes import CFUNCTYPE, c_int

    procedure = CFUNCTYPE(c_int)(program.address)
    print("result correct: %r" % (procedure() == 0x10))
    print("result: %r" % procedure())

    # Alters the first instruction
    program[2] = 15

    print("result correct: %r" % (procedure() == 15))
    print("result: %r" % procedure())

现在,为了让它运行,我需要将其卸载到我的进程中具有 PROT_EXEC 标志的内存区域中。还需要知道该内存区域的地址,以便我可以调用它。我该怎么做?

2. 解决方案

我通过创建一个库来解决这个问题。这是一个小型的包装库,围绕 Linux mmap 命令。Python 提供的 mmap 模块不够用。我无法从对象中获取地址。相反,我必须提供我自己的模块来执行此操作。

# -*- coding: utf-8 -*-
from ctypes import (
    pythonapi, c_void_p, c_size_t, c_int, c_uint64,
    c_byte, cast, POINTER, memmove, string_at,
)
import errno

mmap = pythonapi.mmap
mmap.restype = c_void_p
mmap.argtypes = [c_void_p, c_size_t, c_int, c_int, c_int, c_uint64]

munmap = pythonapi.munmap
munmap.restype = c_int
munmap.argtypes = [c_void_p, c_size_t]

errno_location = pythonapi.__errno_location
errno_location.restype = POINTER(c_int)

errormessage = lambda: errno.errorcode[errno_location()[0]]

PROT_NONE = 0
PROT_READ = 1
PROT_WRITE = 2
PROT_EXEC = 4

MAP_SHARED = 1
MAP_PRIVATE = 2
MAP_ANONYMOUS = 0x20

class RawData(object):
    "Allocated with mmap -call, no file handles."
    def __init__(self, length, prot):
        flags = MAP_PRIVATE | MAP_ANONYMOUS
        self.address = mmap(None, length, prot, flags, -1, 0)
        if 0 == self.address:
            raise Exception(errormessage())
        self.__length = length
        self.__accessor = cast(self.address, POINTER(c_byte))

    def __len__(self):
        return self.__length

    def __getitem__(self, key):
        assert key < len(self)
        return self.__accessor[key]

    def __setitem__(self, key, value):
        assert key < len(self)
        self.__accessor[key] = value

    def close(self):
        "the mapped memory must be freed manually"
        if 0 != munmap(self.address, len(self)):
            raise Exception(errormessage())

    def poke(self, offset, data):
        "poke data (from a tuple) into requested offset"
        for i, byte in enumerate(data):
            self[offset+i] = byte

    def upload(self, data, offset=0):
        "upload the data from a string"
        data = data.tostring()
        assert offset+len(data) <= len(self)
        memmove(self.address+offset, data, len(data))

    def tostring(self):
        return string_at(self.address, len(self))

__all__ = [
    'PROT_NONE',
    'PROT_READ',
    'PROT_WRITE',
    'PROT_EXEC',
    'RawData',
]

我还编写了一个小端整数的实用库,它补充了工具链:

# -*- coding: utf-8 -*-

TWOPOWER32 = 1 << 32
TWOPOWER64 = 1 << 64

TWOPOWER31 = TWOPOWER32 >> 1
TWOPOWER63 = TWOPOWER64 >> 1

def uint32(value):
    assert 0 <= value < TWOPOWER32
    return (
        value >> 0 & 255,
        value >> 8 & 255,
        value >> 16 & 255,
        value >> 24 & 255
    )

def uint64(value):
    assert 0 <= value < TWOPOWER64
    return (
        value >> 0 & 255,
        value >> 8 & 255,
        value >> 16 & 255,
        value >> 24 & 255,
        value >> 32 & 255,
        value >> 40 & 255,
        value >> 48 & 255,
        value >> 56 & 255
    )

def int32(value):
    assert -TWOPOWER31 <= value < TWOPOWER31
    return uint32((TWOPOWER32 + value) & (TWOPOWER32-1))

def int64(value):
    assert -TWOPOWER63 <= value < TWOPOWER63
    return uint64((TWOPOWER64 + value) & (TWOPOWER64-1))

__all__ = ['uint32', 'int32', 'uint64', 'int64']

这是一个简单的例子。以下是使用示例:

from ctypes import CFUNCTYPE, c_int
from array import array
#... bunch of imports

simple = array('B')

# x86 and x64 machine code (MOV eax, 0x10; RET)
simple.extend((0x81, 0xc0) + int32(0x10))
simple.append(0xc3)

program = RawData(len(simple), PROT_READ|PROT_WRITE|PROT_EXEC)
program.upload(simple)

procedure = CFUNCTYPE(c_int)(program.address)
print("result:", procedure())

# alters the first instruction
program.poke(2, int32(123))
print("result:", procedure())

# transforms that first instruction into (NOP,NOP,NOP,NOP,NOP,NOP)
program.poke(0, [0x90]*6)
print("result:", procedure())
<think>嗯,用户问的是Java中数组内容已写入但从未被读取的问题。首先,我得确定这种情况可能的原因和影响。可能的情况包括代码中存在未使用的数组元素,或者整个数组未被后续使用。这种情况会不会导致问题呢? 首先,Java中声明并初始化数组后,如果写入数据但没有读取,编译器会不会给出警告?比如像Eclipse或IntelliJ这样的IDE可能会有“未使用的变量”提示。例如,如果数组是局部变量并且没有被使用,编译器可能会警告。但如果是成员变量或者被其他方式引用,可能不会。 然后,内存和性能方面的影响。数组未被读取,但数据已经被写入,这会占用内存空间。如果数组很大,可能会导致内存浪费,特别是在循环中频繁创建这样的数组,可能会影响性能。不过,Java的垃圾回收机制会在数组不再被引用时回收内存,所以如果是局部变量,在方法执行完后就会被回收。 代码规范和可维护性方面,未使用的数组会让代码变得难以理解,增加维护成本。其他开发者可能会困惑这些数组的作用,甚至可能误删或修改相关代码,导致潜在的错误。 可能的场景比如临时调试时添加的数组,之后忘记移除;或者逻辑错误导致本该使用的数组未被正确引用。比如,在循环中填充数组,但后续处理错误地使用了另一个数组,导致当前数组未被读取。 如何检测和解决呢?静态代码分析工具如SonarQube可以检测未使用的变量。IDE的代码检查功能也能提示。解决方案包括移除未使用的数组,或者检查是否有逻辑错误导致未使用。如果是遗留代码,可能需要进一步确认是否真的不需要使用。 总结的话,这种情况虽然不会直接导致运行时错误,但会影响内存和代码质量。需要根据具体情况处理,确保代码的简洁和高效。</think>在Java中,如果数组内容被写入但从未被读取,这种情况可能涉及以下几个层面的分析: --- ### 1. **编译器层面的处理** - **无编译错误**:Java编译器不会因此报错,因为语法上是合法的。 - **IDE警告**:现代IDE(如IntelliJ、Eclipse)可能提示 _"Unused assignment"_ 或 _"Value is never used"_,表明写入的数据未被后续代码使用。 - **示例**: ```java int[] arr = new int[3]; arr[0] = 10; // 写入但未读取 ``` --- ### 2. **运行时内存影响** - **内存占用**:数组会占用内存空间,直到对象不可达后被垃圾回收(GC)。 - **潜在问题**:如果数组很大或频繁创建未使用的数组,可能导致内存压力。 - **示例**: ```java void createUnusedArray() { int[] data = new int[1000000]; // 占用约4MB内存(int为4字节) for (int i = 0; i < data.length; i++) { data[i] = i; // 写入但未读取 } } // 方法结束后,data变为不可达,GC会回收内存 ``` --- ### 3. **代码逻辑问题** - **可能原因**: - **调试残留**:临时写入数据用于测试,但忘记移除。 - **逻辑错误**:本应使用数组的代码未被正确调用。 - **设计缺陷**:过度预分配资源,但未实际使用。 - **示例**: ```java public void processData() { int[] buffer = new int[100]; fillBuffer(buffer); // 填充数据 // 忘记调用 readBuffer(buffer); } ``` --- ### 4. **性能优化建议** - **静态代码分析**:使用工具(如SonarQube、FindBugs)检测未使用的变量。 - **IDE辅助**:利用IDE的 _"Code Inspection"_ 功能定位问题代码。 - **重构策略**: 1. 直接删除未使用的数组。 2. 检查是否因逻辑错误导致数组未被读取。 3. 若为预分配优化,需评估实际必要性。 --- ### 5. **特殊情况分析** - **多线程环境**:若数组被写入但未被当前线程读取(可能被其他线程读取),需确保同步机制正确。 - **反射/字节码操作**:通过反射间接访问数组时,编译器无法检测实际使用情况。 --- ### 总结 | 问题类型 | 影响 | 解决方案 | |----------------|--------------------|------------------------------| | 代码冗余 | 增加维护成本 | 删除未使用代码 | | 内存浪费 | 短期内存占用 | 优化数据结构或缩小数组规模 | | 潜在逻辑错误 | 功能缺失 | 检查调用链和业务逻辑 | 建议通过代码审查和工具检测,确保数组的写入和读取符合预期逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值