Статья [reversing] Загадка о файле-невидимке

Dragokas

Very kind Developer
Команда форума
Супер-Модератор
Разработчик
Клуб переводчиков
Сообщения
5,962
Симпатии
5,783
Баллы
588
#1
proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2Faa1%2F48c%2F35b%2Faa148c35b21f619c41d20e4b48806d7d.png&hash=77e92098d57c09de066d0c826fb12d5a


Не так давно, закончив работу над очередной статьёй для Хабра, я решил оптравить её на ревью своему знакомому. Сохранив HTML-страницу со всем окружением (картинки, стили etc), я запаковал её в ZIP-архив и отправил адресату. Уже через пять минут я получил фидбек, который, вопреки моим ожиданиям, был связан вовсе не с самой статьёй, а с тем, что архив был абсолютно пустой. Почесав голову и решив, что я затупил с архивированием, я повторил процедуру, убедившись, что выделил все необходимые для запаковывания файлы. Спустя несколько минут знакомый снова разразился удивлённым «Ты что, шутишь?», в то время как я совсем не шутил.

Я начал собирать воедино все элементы паззла. Во-первых, я выяснил, чем он пытается открыть архив. Вдруг, в качестве viewer'а он использует какую-нибудь третьесортную фигню не пойми от какого разработчика? Однако им оказался дефолтный explorer.exe. Я же пользовался Total Commander'ом как для запаковки, так и для просмотра получившегося архива, и в моём случае он вовсе не был пустым:

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2F8e3%2Fb87%2Fc8d%2F8e3b87c8de14277f4bf4b77a8393579d.png&hash=e0476b5a18e8f689084d610be1ecfe1a


Что, неужели это сборочка xxxWindowsUltimateEditionxxx подкачала? Я попытался открыть тот же самый архив на моём компьютере при помощи explorer.exe и наконец поверил своему знакомому — архив действительно выглядел пустым:

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2F359%2Fc2d%2F8ec%2F359c2d8ec209e21b1bba35fcf17f59f8.png&hash=f9e6490ddc97ed84b9b548f56e0d82f7


Кто же виноват в таком поведении? Давайте разберёмся.

Как протекал процесс, и что из этого вышло, читайте под катом (осторожно, много скриншотов). Перед прочтением данной статьи также настоятельно рекомендую ознакомиться с предыдущими.
7-Zip в качестве архиватора содержимое получившегося архива спокойно отображается в explorer.exe. Проверка «проблемного» архива на ошибки встроенными средствами 7-Zip также ничего не выявила:

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2Fc53%2F08a%2F480%2Fc5308a480ca1a9b9cd5e509ea953abcb.png&hash=2698b37599e9ff499eb308b7bff99a73


Кстати, Вы обратили внимание, что вместо символов '«' и '»' в оригинальном архиве Total Commander показывает underscore character ('_')? 7-Zip File Manager, в свою очередь, заменил '«' на символ '<':

proxy.php?image=https%3A%2F%2Fhsto.org%2Ffiles%2Feb1%2F013%2F07d%2Feb101307d1134b7b8db654eea1fc17f9.PNG&hash=e7a8984239309df20467cf2ca22be312


Да что же такое? Давайте не будем гадать и посмотрим, чем между собой различаются ZIP-архивы в случае, когда один был запакован встроенным архиватором Total Commander'а, а второй — 7-Zip'ом.

Для начала создаём минимальный пример, воспроизводящий проблемную ситуацию — я остановился на пустом файле "«some_file.txt" (конечный архив будет называться "«some_file.zip"). Далее архивируем его обоими способами без сжатия и берём в руки XVI32, в котором открываем оба получившихся архива:

Проблемный архив
proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2F55d%2F787%2F580%2F55d787580ff86178565881a7d20b7e5b.png&hash=39bacb0a67644eb531d013d5685defe5


Нормальный архив
proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2F305%2Fb5f%2Ff22%2F305b5ff228211ce95c68ba820472f37d.png&hash=d044526de52dba2d09488c161a993b87


Уже невооружённым взглядом заметно, что их содержимое отличается друг от друга. Впрочем, это не должно пока что вызывать никаких конкретных эмоций, ведь архиваторы вполне могут писать информацию о себе любимых в какие-нибудь «дополнительные» поля. Чтобы убедиться, в чём именно заключается разница между этими файлами, давайте заглянем в спецификацию и на стороннее описание формата ZIP и «разобьём» наши байты на составные части:

Проблемный архив
Local file header

50 4B 03 04 -- signature
14 00 -- PKZip version needed to extract
02 00 -- General purpose bit flag
00 00 -- Compression
83 55 -- Mod. time
DC 46 -- Mod. date
00 00 00 00 -- CRC-32 checksum
00 00 00 00 -- Compressed size
00 00 00 00 -- Uncompressed size
0E 00 -- File name len
00 00 -- Extra field len
3C 73 6F 6D 65 5F 66 69 6C 65 2E 74 78 74 -- File name ("<some_file.txt")

Central directory file header

50 4B 01 02 -- Signature
14 00 -- Version
14 00 -- PRZip version needed to extract
02 00 -- Flags
00 00 -- Compression
83 55 -- Mod. time
DC 46 -- Mod. date
00 00 00 00 -- CRC-32 checksum
00 00 00 00 -- Compressed size
00 00 00 00 -- Uncompressed size
0E 00 -- File name len
00 00 -- Extra field len
00 00 -- File comm. len
00 00 -- Number of the disk on which this file exists
00 00 -- Internal attr.
20 00 00 00 -- External attr.
00 00 00 00 -- Offset of local header
3C 73 6F 6D 65 5F 66 69 6C 65 2E 74 78 74 -- File name ("<some_file.txt")

End of central directory record

50 4B 05 06 -- Signature
00 00 -- Number of this disk
00 00 -- Number of the disk on which the central directory starts
01 00 -- Number of central directory entries on this disk
01 00 -- Total number of entries in the central directory
3C 00 00 00 -- Central directory size
2C 00 00 00 -- Offset of cd wrt to starting disk
00 00 -- Comment len



Нормальный архив
Local file header

50 4B 03 04 -- signature
0A 00 -- PKZip version needed to extract
00 08 -- General purpose bit flag
00 00 -- Compression
84 55 -- Mod. time
DC 46 -- Mod. date
00 00 00 00 -- CRC-32 checksum
00 00 00 00 -- Compressed size
00 00 00 00 -- Uncompressed size
0F 00 -- File name len
00 00 -- Extra field len
C2 AB 73 6F 6D 65 5F 66 69 6C 65 2E 74 78 74 -- File name ("B«some_file.txt")

Central directory file header

50 4B 01 02 -- Signature
3F 00 -- Version
0A 00 -- PRZip version needed to extract
00 08 -- Flags
00 00 -- Compression
84 55 -- Mod. time
DC 46 -- Mod. date
00 00 00 00 -- CRC-32 checksum
00 00 00 00 -- Compressed size
00 00 00 00 -- Uncompressed size
0F 00 -- File name len
24 00 -- Extra field len
00 00 -- File comm. len
00 00 -- Number of the disk on which this file exists
00 00 -- Internal attr.
20 00 00 00 -- External attr.
00 00 00 00 -- Offset of local header
C2 AB 73 6F 6D 65 5F 66 69 6C 65 2E 74 78 74 -- File name ("B«some_file.txt")
0A 00 20 00 00 00 00 00 01 00 18 00 F0 88 3F D4 6D B1 D0 01 F0 88 3F D4 6D B1 D0 01 F0 88 3F D4 6D B1 D0 01 -- Extra field

End of central directory record

50 4B 05 06 -- Signature
00 00 -- Number of this disk
00 00 -- Number of the disk on which the central directory starts
01 00 -- Number of central directory entries on this disk
01 00 -- Total number of entries in the central directory
61 00 00 00 -- Central directory size
2D 00 00 00 -- Offset of cd wrt to starting disk
00 00 -- Comment len



Как видите, в случае проблемного архива символ '«' действительно по какой-то причине «превратился» в '<' (0x3C), в то время как в нормальном архиве он продолжает оставаться самим собой (0xC2 0xAB — именно так он представлен в UTF-8).

А что будет, если мы просто заменим '<' в проблемном архиве на '«', разумеется, попутно изменив значения остальных байт, на которые мы могли повлиять таким образом? Заменяем 0x3C на 0xC2 0xAB (обратите внимание, что сделать это необходимо сразу в двух местах), 0x0E 0x00 (File name len) на 0x0F 0x00 (это также необходимо сделать в двух местах), 0x3C 0x00 0x00 0x00 (Central directory size) на 0x3D 0x00 0x00 0x00 (т.к. мы предыдущими нашими действиями увеличили размер Central directory) и 0x2C 0x00 0x00 0x00 (Offset of cd wrt to strating disk) на 0x2D 0x00 0x00 0x00. В результате должно получиться следующее:

proxy.php?image=https%3A%2F%2Fhsto.org%2Ffiles%2Fea2%2Fe52%2Fe32%2Fea2e52e3213d49a2afada401eafd2015.PNG&hash=73a3798e0286f6318dc9632156553336


Открываем получившийся архив в explorer.exe и видим:

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2F073%2Fc6f%2F4c4%2F073c6f4c4671acf5f42a88d0c8e4659f.png&hash=9f61d74cdf55aaa0fe685d6115a68633


Да, файл теперь виден, но с его названием явно что-то не так. Рыскаем по спецификации в поисках слова «unicode» и встречаем следующее:

APPENDIX D — Language Encoding (EFS)

D.1 The ZIP format has historically supported only the original IBM PC character
encoding set, commonly referred to as IBM Code Page 437. This limits storing
file name characters to only those within the original MS-DOS range of values
and does not properly support file names in other character encodings, or
languages. To address this limitation, this specification will support the
following change.

D.2 If general purpose bit 11 is unset, the file name and comment should conform
to the original ZIP character encoding. If general purpose bit 11 is set, the
filename and comment must support The Unicode Standard, Version 4.1.0 or
greater using the character encoding form defined by the UTF-8 storage
specification. The Unicode Standard is published by the The Unicode
Consortium (www.unicode.org). UTF-8 encoded data stored within ZIP files
is expected to not include a byte order mark (BOM)
Посмотрим, выставлен ли 11 бит поля флагов в наших случаях:

Проблемный архив
proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2F600%2F53a%2Fae8%2F60053aae863d1a1aba1f127c76a904b8.png&hash=e238f8b4bbeadfecd09baef861658c00


Нормальный архив
proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2F404%2Fdac%2F51e%2F404dac51e014b9a3ac9fbccb43923a91.png&hash=ef59a0076094314148b5991b0fe3eeb9


Давайте выставим этот флаг в случае проблемного файла (Tools -> Bit manipulation)

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2F176%2F9fc%2Fb33%2F1769fcb33fc713877b37de1419172a32.png&hash=cb2e897d505918d5d891f232d2cc25e3


и попробуем ещё раз открыть наш архив в explorer.exe:

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2F073%2Fc6f%2F4c4%2F073c6f4c4671acf5f42a88d0c8e4659f.png&hash=9f61d74cdf55aaa0fe685d6115a68633


Точно, мы забыли, что поля флагов на самом деле два:

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2F3a3%2Fe3b%2F716%2F3a3e3b71659b15407baa4a194f438dd6.png&hash=5f887172801dd00911e64d9033d694c4


Выставляем необходимый бит и в этом поле и снова открываем наш архив:

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2F594%2F1a2%2Fe73%2F5941a2e736e13d4a335feeff76a991b1.png&hash=0046d0c24befcd6b2eeaf2656a7bb8b7


Здорово! Вот только почему Total Commander «превращает» символ '«' в '<'? Для того, чтобы понять это, возьмём в руки OllyDbg и запустим в нём исследуемый нами файловый менеджер. Хотя, подождите, давайте проверим, включена ли технология ASLR для totalcmd.exe. Загружаем его в PE Tools при помощи Alt-1, нажимаем на кнопку «Optional Header» и видим, что база меняться не будет (для более подробного описания данного процесса рекомендую посмотреть предыдущую статью):

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2F093%2Fdb2%2Fd47%2F093db2d47726c0663d40def10110139a.png&hash=1695ab2bf54fd5a45aec29b30574f056


Очевидно, что для создания архива TC в начале должен воспользоваться WinAPI-функцией CreateFile, так что поставим бряки на её вызовах (вероятнее всего, в нашем случае он должен использовать юникодовую версию данной функции):

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2F723%2F928%2F100%2F723928100728d207a7ca924f5ae1bd70.png&hash=8032b88a7227448894224365ff5887a5


Убираем бряки, которые срабатывают на каждое ненужное нам действие (например, на событие получения окном TC фокуса — по адресу 0x00567264):

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2Fe8e%2F218%2F6dc%2Fe8e2186dc54b76769bb9bb96126e8a95.png&hash=f796bb087c225ca4bce1de8d852a2924


Нажимаем Alt-F5 (комбинация клавиш для архивирования файлов в Total Commander'е), жмём на «OK» и оказываемся тут:

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2F732%2F6a0%2F5dd%2F7326a05ddd6a11d4097db143f0653bdd.png&hash=147ab42381868ffabfdbd7ca40087f57


Давайте попробуем понять, выполнил ли уже TC преобразование символа '«' в '<'. Для этого откроем окно «Memory» при помощи Alt-M -> left-click по первой строке -> Ctrl-B -> вводим "<some_file.txt" в поле «ASCII»:

proxy.php?image=https%3A%2F%2Fhsto.org%2Ffiles%2F205%2Fe2b%2Fd89%2F205e2bd89b69449895ffcc9cdf737d54.PNG&hash=a91ad94950267d8e619b94dc5fd80df0


Нажимаем на кнопку «OK» и видим, что уже на данный момент приложение осуществило своё преобразование:

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2Ffd3%2F9e9%2F4a5%2Ffd39e94a5216401bfc71fe5657014432.png&hash=a2c26765507bf3c4a46fd3d74faa0fcd


Нажимаем Alt-K, чтобы посмотреть на Call Stack:

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2Fa4a%2F7ac%2F61e%2Fa4a7ac61eed769dc419926bd1e7f2d1e.png&hash=aa3c347b5d9833280feaec119406d96e


Ставим бряки на начало каждой из процедур в списке, нажимаем F9, удаляем получившийся архив и убеждаемся, что в памяти процесса больше нет строки "<some_file.txt". После этого снова запускаем процесс архивирования и останавливаемся в начале первой процедуры из показанного ранее Call Stack'а:

proxy.php?image=https%3A%2F%2Fhsto.org%2Ffiles%2Fc98%2Ffc6%2Fd90%2Fc98fc6d90d0a49d8987def9435ee60b3.PNG&hash=37e1f1fa04db584ae335c681c329ef3f


Снова ищем ту же самую строку во всей памяти процесса и… Находим её:

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2Fa92%2F005%2F18c%2Fa9200518cd70dec1f715edd40dff946a.png&hash=b13f3953eeba3643057313a1358f0674


Что ж, последний логичный на данный момент вариант — это поставить бряк на начало последней процедуры в Call Stack'е, из которой, собственно, нас сюда и позвали:

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2F041%2Fc87%2F567%2F041c87567b389a29b0cbb1184b957d87.png&hash=805a9248d2c5f73d9ce1653f95dec0ee


Прыгаем на вызов (right-click по строке с адресом текущей процедуры в окне Call Stack'а -> Show Call), бежим вверх до подсказанного OllyDbg начала процедуры и ставим на него бряк:

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2F4ca%2F5b2%2F6db%2F4ca5b26db4cf73b5a568123b1fe936e5.png&hash=56774e786ab2e5b12e28e0bfca5a0acd


Проделываем те же действия, что и раньше (усиленно нажимаем F9, удаляем архив, проверяем память процесса на отсутствие строки "<some_file.txt", нажимаем Alt-F5 и кнопку «OK») и останавливаемся на только что поставленном бряке. Ищем ту же строку и… Снова находим её:

proxy.php?image=https%3A%2F%2Fhsto.org%2Ffiles%2F0ae%2Faca%2F94c%2F0aeaca94c2714a73834a1e06056a234b.PNG&hash=4847ecd8760ea8db635404c2fce88e48


Учитывая, что Call Stack на текущий момент пустой, можно предположить, что мы выполняемся в отличном от main thread'а потоке или же попали сюда в результате условного или безусловного перехода. Нажимаем Ctrl-R и видим:

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2F0fe%2Ff5f%2Fe1b%2F0fef5fe1b6856f3c63742959629c5955.png&hash=f37a49ff4489bff675b7f46ff00e86a5


Прыгаем на единственную ссылку при помощи нажатия клавиши Enter:

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2Fd48%2F217%2Fde9%2Fd48217de9aee39c43172f3fc76019883.png&hash=a028c3fe234848b035832414acd76f35


Смотрим, кто в свою очередь ссылается на эту строчку:

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2F152%2F176%2Fa7b%2F152176a7b37e8e7e5830ef49ca0604ff.png&hash=9e81ac1b4ba16c12382ce4510218642b


Прыгаем туда:

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2F4ab%2F296%2F677%2F4ab296677a6ba5b3b0ac5103988915c3.png&hash=4b0b880069b194929e0af7f8a7e89b74


Заходим внутрь нескольких процедур и видим вызов WinAPI-функции CreateThread:

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2F356%2F2d6%2F646%2F3562d6646cfeab05e11c6fb4e0b8959a.png&hash=2662b2b86e916b88ddcb1791467ca958


В принципе, убедиться в этом можно было бы и другим способом — для этого достаточно посмотреть на заголовок окна CPU, который в моём случае сообщал, что ID потока равен 0x000013B8:

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2F4cc%2F6e0%2Fa41%2F4cc6e0a41fc30d2b1dd85c55235c4aca.png&hash=8dd308a019bb0e6b6053a2363bc9cb74


При этом в окне «Log», открываемом по нажатию Alt-L, видно, что ID main thread'а равен 0x00001E30:

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2F050%2Fd6b%2F173%2F050d6b173e4c0a40da2233b58ccee3a0.png&hash=78d162e1def137b7d47485cda7e2ae8b


Нажимаем Alt-F5, ставим бряки на вызовах CreateThread

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2F5e8%2Fb5a%2Ff42%2F5e8b5af4225a13014b80961c1a6b31da.png&hash=446e91b3ed582578b64e84cf058ddd31


, нажимаем на кнопку «OK» и останавливаемся на уже знакомом нам месте:

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2Fafd%2F8c8%2F89b%2Fafd8c889b57018a0050fd89d63fe2379.png&hash=bc9dd105383b72a540d931d7b0208fce


Смотрим на Call Stack и методом «двоичного поиска» (делим кол-во входных параметров пополам и смотрим на результат) раскручиваем цепочку вызовов различных процедур до состояния, когда становится известно, после вызова какой именно из них в памяти процесса появляется строка "<some_file.txt" — ею является процедура, находящаяся по адресу 0x00491780. Посмотрев внимательно на то, что происходит внутри неё, мы можем обнаружить вызов WinAPI-функции CharToOem:

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2Fd0e%2Ffb7%2F1b4%2Fd0efb71b414de70036551a07116cc55a.png&hash=60001c32130ef817299ca1912a959054


Согласно официальной документации, данная функция переводит переданную строку в OEM-defined character set, причём, если используется ANSI-версия, мы можем выполнить т.н. «in place translation» (src и dest могут указывать на один и тот же адрес, что избавит от необходимости создавать отдельный буфер для конечной строки), что и происходит в случае TC:

lpszDst [out]
Type: LPSTR
The destination buffer, which receives the translated string. If the CharToOem function is being used as an ANSI function, the string can be translated in place by setting the lpszDst parameter to the same address as the lpszSrc parameter. This cannot be done if CharToOem is being used as a wide-character function
Да, после её вызова переданный ей буфер уже действительно содержит символ '<' вместо '«':

proxy.php?image=https%3A%2F%2Fhsto.org%2Ffiles%2F119%2Fcdd%2F2b4%2F119cdd2b4f104777b8bf87b1585ffa5c.PNG&hash=e3615f060c3a6418ed446efb42c0d554


Что, будем патчить? А давайте сначала посмотрим на опции архивирования в TC:

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2Fa43%2F9fa%2Fd4a%2Fa439fad4abe6460f9314ac9ab5ceb07a.png&hash=eebe2257155832758337bb25ce2eb934


По дефолту опция «Pack Unicode names» установлена в «Ask every time a Unicode name is encountered». Следовательно, TC не посчитал, что встреченное имя является юникодовым. А если попробовать заархивировать файл, например, с китайскими иероглифами?

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2F166%2F4a4%2Ff37%2F1664a4f3753df4fa048f9da0989df3c6.png&hash=829669500d17346b2881323d097b15f1


Как видите, в таком случае TC отображает окно с сообщением о встреченном имени файла, который содержит символы, отличные от используемой кодовой страницы. В случае с символом '«' такого сообщения не было, вероятнее всего, потому, что многие code page'и (в моём случае, видимо, CP1251) содержат данный символ в отведённых им дополнительных «ячейках». А вот если выставить данную опцию в «All as UTF-8 if at least one contains characters>127», то мы увидим, что наш файл "«some_file.txt" корректно запаковывается и впоследствии отображается в explorer.exe.

Вы можете спросить «Почему, даже если имя файла сменилось с „«some_file.txt“ на „<some_file.txt“, explorer.exe не смог отобразить его хотя бы с изменившимся именем»? Дело в том, что '<' является одним из символов, которые запрещены для использования в названиях директорий и файлов в случае NTFS:

The following reserved characters:

< (less than)
> (greater than)
: (colon)
" (double quote)
/ (forward slash)
\ (backslash)
| (vertical bar or pipe)
? (question mark)
* (asterisk)
Более того, разархивировать такой архив встроенными средствами Windows также не получится. Во-первых, из-за символа '«' в названии самого архива:

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2Fe7c%2F3ca%2F9c2%2Fe7c3ca9c297c4121212e4a145c9d02f0.png&hash=6274fe8e6bc4e974bbfcc2332161d35a


Во-вторых, из-за его содержимого:

proxy.php?image=https%3A%2F%2Fhabrastorage.org%2Fgetpro%2Fhabr%2Fpost_images%2Fa3d%2Fa7c%2Ff92%2Fa3da7cf92aa5977e1d5565822dd3360c.png&hash=acd3226d35e5236d62c9bdd007a93e17


Послесловие

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

Спасибо за внимание, и снова надеюсь, что статья оказалась кому-нибудь полезной.

Автор: @NikitaTrophimov
Источник
 

Паразит

Пользователь
Сообщения
61
Симпатии
2
Баллы
18
#2
Dragokas, прекрасная статья.
А временной штамп шестнадцатиричный, правильно?
Как по нему получить понятную для человека дату?
 

Dragokas

Very kind Developer
Команда форума
Супер-Модератор
Разработчик
Клуб переводчиков
Сообщения
5,962
Симпатии
5,783
Баллы
588
#3
Паразит, а где там что про время говорит автор?
В любом случае, для перекодировки можешь воспользоваться этим скриптом: Registry Time Decoder
 

Паразит

Пользователь
Сообщения
61
Симпатии
2
Баллы
18
#4
11 - 12 байты - Время модификации.
13 - 14 байты - Дата модификации.
 
Сверху Снизу