2009-10-14 30 views
3

Я попытался построить очень минималистичную библиотеку чтения памяти, чтобы прочитать ее из unsigned int. Тем не менее, я нахожу сообщение об ошибке «HEAP CORRUPTION DETECTED», когда метод ReadUnsignedInt хочет вернуться.Как исправить кучевое повреждение

ОБОСНОВАННАЯ КОРРУПЦИЯ HEAP. CRT обнаружил, что приложение записано в память после окончания буфера.

Как я уже говорил, это может быть причиной при попытке удвоить что-то удалить. Это может быть вызвано некорректным использованием std::tr1::shared_ptr, но я не могу определить, что я делаю с ними неправильно. Код выглядит следующим образом (обработка ошибок опущена):

unsigned int Memory::ReadUnsignedInt (unsigned int address) const { 
    std::tr1::shared_ptr<byte> bytes = 
     this->ReadBytes(address, sizeof(unsigned int)); 
    return *((int*)bytes.get()); 
    // correct value (how to improve this ugly piece of code?) 
} 

std::tr1::shared_ptr<byte> Memory::ReadBytes (
    unsigned int address, int numberOfBytes) const 
{ 
    std::tr1::shared_ptr<byte> pBuffer(new byte(numberOfBytes)); 
    ReadProcessMemory(m_hProcess.get(), (LPCVOID)address, 
     pBuffer.get(), numberOfBytes * sizeof(byte), NULL)) 
    return pBuffer; 
} 

ответ

4

Майкл и Навин оба нашли тот же самый главный недостаток вашего кода, но не единственный недостаток.

shared_ptr будет delete заостренный объект, когда его счетчик ссылок будет равен нулю.

Это означает, что вы можете только дать ему объекты, выделенные new - not new[].

Вместо этого вы можете использовать shared_ptr<vector<byte> > или boost::shared_array<byte>.

+1

Отличная уловка - хотя это было бы тихой ошибкой на большинстве платформ, так как байт не имеет деструктора, который нужно назвать, правильным ответом здесь будет использование вектора вместо shared_ptr. – Michael

+1

... или shared_array для этого вопроса (я думаю). – peterchen

+0

Обновлено. Я изучал, как добавить пользовательский деструктор в 'shared_ptr', но это больше усилий, чем того стоит. – ephemient

2

Вопрос заключается в:

new byte(numberOfBytes) 

Это выделяет один байт со значением NumberOfBytes.

Вы хотите сделать:

new byte[numberOfBytes]  

Что выделяет массив байтов NumberOfBytes долго.

Но так как вы знаете, что читаете только 4 байта, зачем вообще выделять память? Просто передайте адрес unsigned int в стеке.

0

Я считаю, new byte(numberOfBytes) должно быть new byte[numberOfBytes]. В противном случае вы выделили бы только один байт. Чтобы закончить ответ, поскольку @ephemient указал, что вы не можете использовать shared_ptr здесь, так как он будет делать delete, где вы должны делать delete[]. Если этого не сделать, поведение будет неопределенным.

2

Основные проблемы с вашим кодом уже указаны. Глядя на это, мне все равно интересно, почему вы вообще используете shared_ptr.Если бы я делал это, я бы, вероятно, использовать что-то вроде этого:

unsigned Memory::ReadUnsignedInt(unsigned address) { 
    unsigned ret; 
    ReadProcessMemory(m_hProcess.get(), (void *)address, &ret, sizeof(ret), NULL); 
    return ret; 
} 

std::vector<char> Memory::ReadBytes(unsigned address, int num) { 
    std::vector<char> ret(num); 
    ReadProcessMemory(m_hProcess.get(), (void *)address, &ret[0], num, NULL); 
    return ret; 
} 

Затем снова, вместо ReadUnsignedInt, я был бы соблазн использовать шаблон:

template <class T> 
T Memory::Read(unsigned address) { 
    T ret; 
    ReadProcessMemory(m_hProcess.get(), (void*)address, &ret, sizeof(ret), NULL); 
    return ret; 
} 

Поскольку вы не» т передать параметр, от которого он может вывести тип параметра шаблона, вы всегда должны явно указать:

int x = Read<int>(wherever); 
char a = Read<char>(wherever); 

Альтернативой было бы передать назначение в качестве параметра:

template <class T> 
Memory::Read(unsigned address, T &t) { 
    ReadProcessMemory(my_hProcess.get(), (void *)address, &t, sizeof(t), NULL); 
}; 

, который вы будете использовать, как:

Read(wherever, some_int); 
Read(somewhere, some_long); 

и так далее.

Если вы обеспокоены неэффективностью возврата вектора char, вы, вероятно, не должны - VC++ (как и большинство других разумно текущих компиляторов) имеет так называемую «оптимизацию значений именных ссылок», что означает ситуация подобна этому, она передает скрытую ссылку на вектор, которому вы назначаете результат, и ReadBytes будет использовать это, чтобы поместить данные непосредственно там, где это все равно закончится. В этом отношении, при любой разумной оптимизации, включенной вообще, ReadBytes почти наверняка окажется в виде встроенной функции, поэтому ничто из этого не приведет к действительно «пройденному» или «возвращенному».

С другой стороны, этот код не будет работать особенно хорошо со старыми компиляторами - и с достаточно старыми компиляторами версии с использованием функций шаблонов членов, вероятно, даже не будут компилироваться. Тем не менее, если вы используете достаточно современный компилятор, жизнь должна быть хорошей.

Смежные вопросы