Следующий код вылетает в x64 bit, но не в x86 build - PullRequest
0 голосов
/ 29 декабря 2018

Я всегда использовал класс шифрования xor для своих 32-битных приложений, но недавно я начал работать с 64-битным и столкнулся со следующим сбоем: https://i.stack.imgur.com/jCBlJ.png

Вот класс xor I 'm используя:

// xor.h
#pragma once

template <int XORSTART, int BUFLEN, int XREFKILLER>
class XorStr
{
private:
    XorStr();
public:
    char s[BUFLEN];

    XorStr(const char* xs);
    ~XorStr()
    {
        for (int i = 0; i < BUFLEN; i++) s[i] = 0;
    }
};

template <int XORSTART, int BUFLEN, int XREFKILLER>
XorStr<XORSTART, BUFLEN, XREFKILLER>::XorStr(const char* xs)
{
    int xvalue = XORSTART;
    int i = 0;
    for (; i < (BUFLEN - 1); i++)
    {
        s[i] = xs[i - XREFKILLER] ^ xvalue;
        xvalue += 1;
        xvalue %= 256;
    }
    s[BUFLEN - 1] = (2 * 2 - 3) - 1;
}

Сбой происходит, когда я пытаюсь использовать запутанную строку, но не обязательно происходит в 100% случаев (однако, никогда не происходит на 32-битной).Вот небольшой пример 64-битного приложения, которое будет зависать на второй запутанной строке:

#include <iostream>
#include "xor.h"

int main()
{
    // no crash
    printf(/*123456789*/XorStr<0xDE, 10, 0x017A5298>("\xEF\xED\xD3\xD5\xD7\xD5\xD3\xDD\xDF" + 0x017A5298).s);

    // crash
    printf(/*123456*/XorStr<0xE3, 7, 0x87E64A05>("\xD2\xD6\xD6\xD2\xD2\xDE" + 0x87E64A05).s);

    return 0;
}

Это же приложение будет прекрасно работать, если встроено в 32-битное.

Вот скрипт HTMLдля генерации запутанных строк: https://pastebin.com/QsZxRYSH

Мне нужно настроить этот класс для работы на 64-битной, потому что у меня есть много строк, которые я уже зашифровал, которые мне нужно импортировать из 32-битного проекта втот, над которым я сейчас работаю, 64-битный.Любая помощь приветствуется!

1 Ответ

0 голосов
/ 29 декабря 2018

Нарушение доступа вызвано тем, что 0x87E64A05 больше, чем наибольшее значение, которое может содержать 32-разрядное целое число со знаком (которое составляет 0x7FFFFFFF).

Поскольку int, скорее всего, 32-разрядное, то XREFKILLER не можетудерживайте 0x87E64A05, и поэтому его значение будет определяться реализацией.

Это значение затем используется позже для повторного вычитания из xs после того, как переданный указатель был искусственно продвинут литерал 0x87E64A05, который будет интерпретироваться как long или long long для подгонки значения в зависимости от того, является ли long 32-битным или большим, и поэтому не будет сужаться до значения, определенного реализацией.

Поэтому выфактически остаются с некоторым случайным указателем в xs[i - XREFKILLER], и это может привести к неопределенному поведению, например, к нарушению доступа.

Если скомпилировано для 32-битного x86, вероятно, так получится, что int и указатели имеют один и тот же бит-size и что поведение, определяемое реализацией, чрезмерное / недостаточное и сужающее поведение оказываются такими, что сложение и вычитание отменяют корркак и ожидалось.Однако, если тип указателя больше 32 бит, он не может работать.


Нет никакого смысла в XREFKILLER.Он просто выполняет одно вычисление, которое немедленно возвращается (если нет переполнения / недополнения).


Обратите внимание, что тот факт, что компилятор вообще принимает сужение в аргументе шаблона, является ошибкой.Ваша программа некорректна, и компилятор должен выдать вам сообщение об ошибке.Например, в GCC эта ошибка сохраняется до версии 8.2, но была исправлена ​​в текущей транке (то есть в версии 9).


У вас будут похожие проблемы с XORSTART, если char окажется signed на вашей платформе, потому что тогда указанные вами значения не будут вписываться в нее.Но в этом случае вы должны будете включить предупреждения, потому что это не будет преобразованием, делающим программу плохо сформированной.Также поведение ^ может быть не таким, как вы ожидаете, если char равно signed в вашей системе.


Не ясно, какой смысл

s[BUFLEN - 1] = (2 * 2 - 3) - 1;

является.Это должно быть:

s[BUFLEN - 1] = '\0';

Передача результирующей строки в printf в качестве первого аргумента приведет к ложному неопределенному поведению, если полученная строка будет содержать %, что будет интерпретировано как введениев спецификатор формата.Используйте std::cout.


Если вы хотите использовать printf, вам нужно написать std::printf и #include<cstdio>, чтобы гарантировать его доступность.Однако, поскольку это C ++, вы все равно должны использовать std::cout.


Более существенно, что ваша выходная строка может содержать 0, отличную от завершающей после преобразования.Это будет интерпретироваться как конец строки в стиле C.Это кажется серьезным недостатком дизайна, и вы, вероятно, захотите использовать std::string вместо этого по этой причине (и потому что это лучший стиль).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...