четверг, 13 января 2011 г.

Чапай должен уметь думать, а не просто шашкой махать

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

предлагается задачка: программа не корректно работает с образами .iso размером более 2Г

для far имеется плагин, который позволяет смотреть в .iso и извлекать файлы, проблема в том что если извлекается файл из .iso за пределами 2Г, он оказывается состоящим из одних нолей.

не долго думая ищем SetFilePointer (не долго, потому что именно через эту функцию двигается указатель смещения в файле), аналогично тому как искали вызов LoadKeyboardLayout в сообщении не надо при старте переключаться на русскую раскладку,
или можно совсем по-простому через F7-F7:


назовем найденную коротенькую функцию, вызывающую SetFilePointer как seek - клавиша Shift-F12 на первом байте функции (здесь начало определяется элементарно, адрес .10001010)



замечаем повыше много nop, которые наверняка не используются (выравнивание после пустой функции по адресу .10001000), и вместо которых можно будет положить патчик если понадобится.

(в скобках замечу что в файле далее есть еще один вызов SetFilePointer, но он к проблеме 2Г не имеет отношения)

для удобства распишем параметры с помощью клавиши точка-с-запятой - курсор поставить на строку с push и нажать ;


все расписанные параметры функции SetFilePointer:


понятен и протопип: DWORD seek( long lDistanceToMove /*позиция файла*/, DWORD dwMoveMethod /*стартовая точка*/ )

сразу виден корень проблемы: параметр lpDistanceToMoveHigh в NULL, lDistanceToMove считается знаковым и не может быть более 2Г, а хочется до 4Г

опять через F7-F7 поищем ссылки на функцию, названную seek


нашли раз:


нашли два:


нашли три:


отмотаем наверх несколько экранов чтобы увидеть что лежит в регистре ebx



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


если думать будет лень, очень красивое решение будет опубликовано через неделю, не потому что раньше никто не догадается, потому что такие блоги читаются полтора раза в неделю.

6 комментариев:

  1. Проще всего заменить lpDistanceToMoveHigh с нуля на указатель на ноль. В этом случае SetFilePointer будет рассматривать пару 0:lDistanceToMove, как int64, то есть lDistanceToMove станет уже ULONG. Ближайший ноль, имеющися под боком - это только что впихнутый в стек dwMoveMethod (он нулевой во всех трех обращениях к функции seek).

    Таким образом, seek преобразуется вот в это:
    mov eax, [esp][8]
    mov edx, [esp][4]
    push eax
    mov eax, [ecx][8c]
    push esp
    nop
    push edx
    push eax
    call SetFilePointer

    Чтобы не копировать код туда-сюда после push esp добавлен nop.

    Вот как-то так. На живом коде не проверял :)

    ОтветитьУдалить
  2. браво! красивое решение с полным разъяснением, но малость повредничаю - до *очень* красивого не хватает чуть-чуть совсем....

    ОтветитьУдалить
  3. У меня есть вопрос.
    Откуда в прототипе функции seek появились типы long и DWORD ?

    ОтветитьУдалить
  4. из прототипа DWORD SetFilePointer( HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod )

    переданные значения не менялись никак внутри seek

    ОтветитьУдалить
  5. Итак, ответ:
    решение становиться очень красивым без лишнего NOP, т.е. команда PUSH ESP будет длиной 2 байта: FF F4

    ABel получает приз - бесплатную дополнительную версию уже после окончания срока обновлений

    ОтветитьУдалить
  6. Я понимаю, что "это не наш метод", но Far вроде уже давно Open Source :) http://www.farmanager.com/opensource.php?l=ru

    ОтветитьУдалить