Дело о призрачном потоке

*Почти закрытое дело.*

---

Файл слишком долго лежал у меня на столе. `.pub`, 19 мегабайт, вид вполне приличный. Но что-то было не так: под Windows он отказывался открываться. Без объяснений, без предупреждения. Просто закрытая дверь — а под Publisher 2003 и того хуже: полная тишина. Та самая тишина, которая говорит о многом.

Подозреваемый уже был на примете: **временный путь**, спрятанный в потоке `Contents`, с шатким алиби вроде *«я был в `C:\users\v3l1g4\Temp\`, честное слово»*. Пахло разорванной ссылкой за версту. Его допросили, даже заставили признаться на копии — подменили алиби на настоящий путь, `V:\RESEARCH\windows_regenerated.pub`, всё чисто и аккуратно.

Исправленный файл не прошёл даже дальше порога.

Подозреваемый номер два: пропавший **маркер `CompObj`**, жалкие четыре байта (`FF FF FF FF`), которым полагалось быть на месте, но их не было. Дошли до того, что разобрали структуру OLE голыми руками, пересчитали секторы, переписали запись каталога точно в нужном месте — ювелирная работа. Приклеили обратно на копию.

Дверь всё так же закрыта.

Вот тут мы сделали то, что делает любой хороший детектив, когда все версии рушатся: **усомнились в себе самих**. Пересмотрели собственные патчи и обнаружили, что они, скорее всего, сами внесли свою порчу, сдвинув внутренние смещения, которых мы толком ещё не понимали. Подозреваемый, которого мы обвиняли, возможно, был невиновен с самого начала — а отпечатки пальцев на месте преступления оставили мы сами.

Тогда мы сменили метод. Хватит обвинений вслепую. **Отрицательный контроль.** Файл-свидетель, `linux.pub`, у которого были абсолютно те же видимые недостатки — временный путь, пропавший маркер — и который прекрасно открывался под Windows. Оправдан, дважды. Настоящий виновник всё ещё разгуливал на свободе, и у него было железное алиби: он выглядел точь-в-точь как все остальные.

Пришлось вызвать экспертов. **`libmspub`**, собранный в отладочном режиме, по полной программе — настоящая криминалистическая лаборатория, запущенная прямо на нашей машине. Вердикт экспертизы: внутренняя структура была **безупречна**. Ни ошибок, ни подозрительных смещений, все внутренние указатели пересчитывались правильно сами по себе. Дело номер три — встроенные объекты OLE, тот самый подозреваемый, которого мы вроде бы уже держали за руку — развалилось под тяжестью собственной экспертизы.

Мы уже начали думать, что этому файлу просто нечего скрывать. Что мы искали виновного там, где было только недоразумение.

И тут — почти случайно, в очередной раз просматривая список всех имён всех потоков, ничего особенного не ожидая — мы это увидели.

`EscherDelayStm`.

Только не совсем. В конце было на один символ больше. Невидимый глазу, невидимый даже для `olefile`, который прочитал его не моргнув, невидимый для самого Wine, который его и записывает, и читает обратно, ни разу не пожаловавшись.

**Один лишний нулевой байт. Всего один.** `EscherDelayStm\x00` вместо `EscherDelayStm`.

Мотив? Этот поток никогда не создавал проблем, пока был пустым — идеальное алиби для половины наших подозреваемых (level1, крохотная визитка, никогда его не выдавала, потому что скрывать внутри было нечего). Но стоило появиться настоящим изображениям для загрузки — мегабайтам улик — как этот лишний байт мешал Windows найти правильное имя. Поиск по точному имени, тихий провал, и Publisher остаётся искать данные, которые никогда не найдёт.

Мы проверили: один лишний нулевой байт в имени структурно не может ничего больше сдвинуть — вердикт подтверждён фактами, запись каталога фиксированного размера, ничего никуда не протекает. Чистое, локализованное, почти изящное преступление.

Исправление? Два байта на замену. Поле «Name Length», уменьшенное с 32 до 30. Ни строчки больше. Из тех решений, которые, стоит их найти, заставляют просто покачать головой.

**Проверено. Подтверждено. Файл открылся.**

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

*Дело закрыто. Исправление опубликовано. Отчёт подан.*
*Баг №60088, для архива.*


Рецензии