02. Я уже даже не вижу код. Я вижу блондинку, брюнетку и рыжую
Структуры — кирпичики
Предыдущие статьи о реверсе данных автомобильных навигаторов Siemens VDO Dayton CARMiN
Блоки данных carindb состоят из однотипных кирпичиков, структур данных, несущих определенную информацию. Значительную часть из них я уже описал в предыдущих статьях, напомню:
PTR
. Размер 16 бит (точнее, размер данных в бинарнике, которые описывает структура, размер самой структуры больше). Относительное от начала блока смещение в ushort ptr. Часть структуры, не учитывающаяся в размере — byte руку, располагающийся в hex по значению смещения структуры, визуально показывает голубым фоном, куда PTR ссылается. В окне hex PTR отображается синими символами на голубом фоне. По определению, не может быть больше размера блока, к которому относится.LIST
. Размер 32 бита. Последовательно расположенные: PTR ptr и ushort cnt, где ptr — указатель на адрес внутри блока, а cnt — количество элементов массива, начинающегося с этого адреса. Тип данных массива определяется тем, где в блоке расположен LIST. Абсолютный адрес начала может быть запрошен у структуры через offset. Светло-голубой фон, ptr синими символами, cnt — тёмно-фиолетовым.BL_ADDR
. Размер 32 бита. Первые 24-бита — addr — абсолютное смещение от начала файла в страницах размером 0×800 байт, т.е. для абсолютного смещения в байтах умножаем на 0×800 или побитово сдвигаем влево на 7 позиций. Последние 8 бит — size — размер блока в 0×800 байтных страницах. Обращаясь к «виртуальному» элементу offset, получается значение абсолютного смещения. По определению, по данному смещению должно располагаться значение, равное значению этого BL_ADDR. Если не так, то данные, которые пытаемся описать как BL_ADDR, к этому типу не относятся. Структура отслеживает ситуацию и сигнализирует об этом, окрашивая фон красным и выводя сообщение о несоответствии в лог. Валидный BL_ADDR в окне hex — на оранжевом фоне, желтые — символы addr и темно-голубые (cDkAqua) — size.BL_HEAD
. Размер 64 бита. Первые 32 — BL_ADDR, затем байт zero_aligment всегда равный нулю, затем байт type, определяющий тип блока (enum en_BL_TYPE), и 2 байта is_compressed и uncompressed_size, в русском carindb всегда равные нулю. Это заголовок блока, поэтому его BL_ADDR обязан быть не только валидным, но и указывать именно на тот адрес, по которому расположен BL_HEAD. В структуре добавлен утилитарный байт here_last_byte, броско выделенный (bgcolor=cPurple, fgcolor=cAqua) в hex, отмечающий последний байт блока, в размере BL_HEAD не учитывается. Расцветка BL_HEAD в окне hex — оранжевый фон, байт type cDkPurple, остальные незначащие байты — серые.
В прошлой статье выяснили, что блока типа 0xB «внешние» «ссылки» ведут в 0xA. Из блока типа 0xD ссылки ведут в 0xC. Из 0×0F в 0×0E. Из 0×11 в 0×10.
Если 0xB — это блок с начальными буквами стран, то 0xA, куда ведут следы, должен содержать информацию о странах.
На файл carindb_bnl, официальную карту Бенилюкса (удобный вариант, всего три страны — Бельгия, Нидерланды и Люксембург) применяю результаты прошлой статьи: размечаю блок 0xB вызовом block(FindBlockByType(0x0B));
.
Коллаж для наглядности: на скрин результатов функции добавил уменьшенный скриншот hex с адреса 0×2000, начала блока 0xA, идущего сразу после 0xB, и часть этого же 0xA тоже увеличена.
В блоке 0xB три main_data, они содержат буквы B, N и L и ссылаются соответственно на абсолютные адреса 0×2030, 0×2038, 0×2040 — синие точечные стрелки, о факте голубеньким маякуют соответствующие PTR.here в main_data.
На увеличенной части выделил желтым фоном заголовок блока: — '00000401 000A0000' это BL_HEAD, — '00300003' — LIST на массив из 3х элементов, на которые идут ссылки из 0xB, что сразу и дает размер каждого элемента. — '00480003' — еще один LIST, указывает на данные сразу после данных первого. — uint zero — '00f00017' LIST на массив из 17h элементов, ptr F0h позволит рассчитать размер элемента предыдущего LIST, (f0 — 30) /3 =0×40 — Шесть uint zero — вероятно, всегда равны нолю.
На уменьшенной картинке скрина содержимое блока 0xA поместилось целиком, в самом низу, с адреса 0×2210 три zero-ended строковых значения: belgië, luxembourg, nederland. БИНГО!
Значение, на которое указывает элемент main_data из блока 0xB, содержащий символ «B» — 0210h, смещение строки «belgië», «L» — «luxembourg», «N» — «nederland»
Последние 2 байта элементов данных блока 0xA (LIST '00300003'), (на которые ведут ссылки из 0xB по значениям) — ссылки на (LIST '00480003')
Новые кирпичики: CONST_B, CONST_S, CONST_I, PSTR
Добавляю в vdo_inc.bt новые типы.
CONST_B
,CONST_S
,CONST_I
— константы, структуры, требующие инициализации ожидаемым значением при объявлении.Размер соответственно 8, 16 и 32 бита. В hex белые буквы на темно-сером фоне, но если значение под константой не равно значению инициализации, фон красный, запись в лог.PSTR
. Размер 16 бит. Аналог PTR с типом содержимого по ссылке — оканчивающаяся на ноль строка, структура сохраняет в свой элемент str эту строку, заодно раскрашивая её зеленым фоном и темно-оранжевыми символами. Цвет в hex — голубой фон, как у PTR, но символы темно-зеленые, а не синие.
//}CONST_B
typedef struct(uchar val){
if(ReadUByte(FTell()) != val){
SetBackColor(cRed);
Printf("0f:%04X const ubyte(%02Xh) != %Xh\n",
FTell(), val, ReadUByte(FTell()));
}
ubyte value;
}CONST_B ;
//}CONST_S
typedef struct(ushort val){
if(ReadUShort(FTell()) != val){
SetBackColor(cRed);
Printf("0f:%04X const ushort (%04Xh) != %Xh\n",
FTell(), val, ReadUShort(FTell()));
}
ushort value;
}CONST_S;
//}CONST_I
typedef struct(uint val){
if(ReadUInt(FTell()) != val){
SetBackColor(cRed);
Printf("0f:%04X const ushort (%04Xh) != %Xh\n",
FTell(), val, ReadUInt(FTell()));
}
uint value;
}CONST_I;
//}PSTR
typedef struct{
local uint offset ; // absolute block offset for next PTR ptr
if( exists(parentof(this).offset) ){
offset = parentof(this).offset; // start block offset
}
PTR ptr ; // pointer to string/0
if(ptr.ptr){ // theoreticaly may==0, in this case no need calculate any vars
offset = ptr.ptr + offset; // set own absolute offset
if(offset){ // shoulb be not zero
local DWORD curr_position = FTell(); // remember ret
FSeek(offset); // jmp to offset
string str ; // str as real
FSeek(curr_position); //ret
}else{
local string str; // str as local
SPrintf(str, "No offset %04X", ptr.ptr);
}
}
}PSTR ;
string Read_PSTR(PSTR &a){
local string s;
SPrintf(s, "%X: %s", a.offset, a.str);
return s;
}
//}PSTR
Блок 0×0A. Информация о странах
В инклюде @inc_block.bt» в функцию block () в swith выбора структуры добавляю раздел с типом структуры BT_0×0A для соответствующего значения типа.
// reading block type
local uchar type = ReadUByte(start_offset + 5);
FSeek(start_offset);
switch (type){ // choice template for every known type
case 0x0B:
BT_0x0B_0x0D_0x0F_0x11 block_0x0B ;
break;
case 0x0A:
BT_0x0A block_0x0A ;
break;
В основном темплейте описываю typedef с новой структурой BT_0×0A.
typedef struct{
BL_HEAD head; // заголовок
local ushort size = head.addr.size * 0x800; // size of this block
local uint offset < hidden=true> = head.addr.offset; // absolute block offset
LIST pl_data; // <----------------need more analitic
LIST pl_moreinfo;// <----------------need more analitic
CONST_I zero(0); // uint must be == 0
LIST pl_addinfo; // <----------------need more analitic
// six uints == 0
CONST_I zero(0); CONST_I zero(0); CONST_I zero(0);
CONST_I zero(0); CONST_I zero(0); CONST_I zero(0);
struct{
local ushort size = head.addr.size * 0x800; // size of this block
local uint offset < hidden=true> = head.addr.offset; // absolute block offset
PSTR pstr_name; // ptr 2 zero-ended str
CONST_B zero(0); // ubyte allways 0
ubyte unk;
CONST_S zero(0); // ushort allways 0
PTR p_moreinfo; // ptr to item of LIST pl_moreinfo;
Printf("%s \n", pstr_name.str);
}try_data[3];
byte after_parsed_block_info ;
}BT_0x0A;
block(FindBlockByType(0x0B)); // 0B 1 BT_0x0B_0x0D_0x0F_0x11
block(FindBlockByType(0x0A)); // 0A 1 BT_0x0A
И применяю его на файлах carindb_bnl, carindb_ee и carindb_rus.
На скрине выделен стрелкой единственный случай, когда байт CONST_B zero (0), идущий после PSTR pstr_name равен 1, а не ожидаемому 0. Эта запись соответствует Австрии — единственной стране, которая есть в списке дважды в 2х вариантах написания — с умляутами и без (в консоли верхняя часть ascii выводится как черный ромбик-вопросик, в теле файла написано Österreich), и на одном из этих вариантов значение единица. Признак синонима? Переименовываю zero (0) в is_synonym (0).
Подсвеченный желтым ubyte unk гораздо интереснее, о нем следующий раздел.
Language codes en_LANG
В вывод в консоль названия страны Printf("%s \n", pstr_name.str);
добавляю вывод значения ubyte unk: Printf("%02X\t(%i)\t%s \n", unk, unk, pstr_name.str);
Результаты стоят цитирования
carindb_bnl
01 (1) belgi�
03 (3) luxembourg
01 (1) nederland
carindb_ee
FF (255) balgaria
13 (19) ceska republika
FF (255) eesti
1A (26) hrvatska
1B (27) latvija
18 (24) lietuva
FF (255) magyarorszag
12 (18) polska
FF (255) rom�nia
17 (23) slovenija
14 (20) slovenska republika
carindb_rus
0B (11) andorra
FF (255) azarbaycan
01 (1) belgie
FF (255) bosna i hercegovina
FF (255) bulgaria
FF (255) byelarus
FF (255) ceska republika
0A (10) danmark
04 (4) deutschland
FF (255) eesti vabariik
02 (2) �ire
FF (255) ellas
06 (6) espa�a
03 (3) france
FF (255) hrvatska
05 (5) italia
FF (255) kibris
FF (255) latvija
FF (255) lietuva
FF (255) luxembourg
FF (255) lyoveldio island
FF (255) magyarorszag
FF (255) makedonija
FF (255) moldova
01 (1) nederland
0E (14) norge
0f:2102 const ubyte(00h) != 1h
04 (4) oesterreich
04 (4) �sterreich
FF (255) polska
0F (15) portugal
FF (255) romania
FF (255) rossiya
FF (255) sak'art'velo
04 (4) schweiz
FF (255) shqiperia
FF (255) slovenija
FF (255) slovensko
FF (255) srbija i crna gora
0C (12) suomen tasavalta
07 (7) sverige
FF (255) turkiye
FF (255) ukrayina
02 (2) united kingdom
Очевидно: для стран с одинаковыми значениями unk общее — официальный язык, значение FF — для отсутствующего в списке языков. То, что в rus карте для стран, имеющих валидные коды в официальных картах, равны FF, можно объяснить свежестью официальных карт. Видимо, эти языки были добавлены позже. Список значений взят из какой-то стандартов?
Но ни в одном из найденных вариантов кодирования языков сочетаний найденных конкретных чисел конкретным странам не встречалось. ISO_3166 трёхсимвольный, CLDR — нет, IETF — нет, country codes — нет.
Нет, нет и нет. Единственное, что смог найти с этими значениями кодов языка у стран, — учебный пример в книге Relational Database Programming: A Set-Oriented Approach, Таблица 5–2 содержит начало составленного по вышеприведенным данным перечисления кодов языков, сводной информации по трём carindb enum en_LANG. Его добавляю в inc_common.bt, и unk заменяю типом этого перечисления en_LANG en_lang
.
//en_LANG
typedef enum {
typedef enum {
_Nederlands = 1, //01 (1) nederland, belgie - Dutch
_English = 2, //02 (2) united kingdom, eire
_French = 3, //03 (3) france, luxembourg
_Deutch = 4, //04 (4) deutschland oesterreich osterreich schweiz
_Italian = 5, //05 (5) italia
_Hispain = 6, //06 (6) espana
_Svedian = 7, //07 (7) sverige
__unk_08 = 8,
__unk_09 = 9,
_Danian = 0xA, //0A (10) danmark
_Catalan = 0xB, //0B (11) andorra
_Finnish = 0xC, //0C (12) suomen tasavalta
__unk_0d = 0xd,
_Norvegian = 0xE, //0E (14) norge
_Portugal = 0xF, //0F (15) portugal
__unk_10 = 0x10,
__eng_TOO = 0x11,
_Polish = 0x12, //12 (18) polska
_Czech = 0x13, //13 (19) ceska republika
_Slovak = 0x14, //14 (20) slovenska republika
_Croatian = 0x1A, //1A (26) hrvatska
_Latvian = 0x1B, //1B (27) latvija
_Lithuanian = 0x18, //18 (24) lietuva
_Slovene = 0x17, //17 (23) slovenija
_UNK_lng = 0xFF
}en_LANG;
В структуре BL_0xA структуру try_data переименовываю в brief_geo, и смотрю на следующую, на которую указывает LIST pl_moreinfo, и начала которых PTR p_moreinfo из brief_geo уже раскрасили голубеньким фоном, что задаёт размер каждого элемента 0×38 байт.
Значение '00000506' в начале каждого элемента похоже на BL_ADDR
Далее 4 байта, вероятно LISTы, адресующие в BL_ADDR.
Затем 4 константы 32 битная со значениями Bh, 16h, 21h, 2Ch, или если десятичные, 11,22,33,44.
Вероятно LIST, адресующий дальше в блоке что-то.
Константа hex01f4012c / dec32768300 / bin01111101000000000100101100
Константа hex03e801f4 / dec65536500 / bin11111010000000000111110100
Константа 32 битная, равная 1.
Непонятное 16 битное значение, надо ближе посмотреть, что это может быть
Константа 32 битная, равная 0.
Строка 0-ended кода страны по ISO_3166–1
Константа 8 битная, равная 0.
Строка-константа zero-ended, равная '---'
Константа 16 битная, равная 0.
Размер каждого элемента немаленький, но множество констант с непонятной информационной наполненностью. Вот для чего в каждом элементе хранить значения 32768300 или 65536500? Что они могут значить? Что значат 32768 или 65536, это понятно — 2 в 15 и 16 степени. Но 300 и 500, что за этим? Загадка.
Кирпичик FAR_LIST moooooooooooooooooooo
Назову сочетание последовательно идущих BL_ADDR и LIST, как FAR_LIST, ссылка на далёкий, не в этом блоке список-массив. Размер данных 64 бита, расцветка BL_ADDR обычная, а у половины LIST, у cnt фон оранжевый, чуть другого оттенка, чем BL_ADDR, bgcolor=0×22BFFF.
typedef struct{
BL_ADDR far_block; // link to block type 0xd
local ushort size = far_block.size * 0x800;
local uint offset = far_block.offset;
LIST pl_data ; // far pointer list, BL_ADDR+LIST
}FAR_LIST ;
string Read_FAR_LIST (FAR_LIST &a){
local string s;
SPrintf( s, "%04X+%02X=%04X cnt=%i(%02Xh); %s",
a.far_block.offset, a.pl_data.ptr.ptr, a.pl_data.offset,
a.pl_data.cnt, a.pl_data.cnt,
Read_BL_ADDR(a.far_block));
return s;
}
Вернусь к блоку 0xA, описав массив структур по вышеописанным данным.
struct{
local ushort size = head.addr.size * 0x800; // size of this block
local uint offset < hidden=true> = head.addr.offset; // absolute block offset
FAR_LIST idx_ch_cityes;
CONST_I dec_11(0x0B) ;
CONST_I dec_22(0x16) ;
CONST_I dec_33(0x21) ;
CONST_I dec_44(0x2C) ;
LIST pl_addinfo;
CONST_I hex01f4012c(0x01f4012c); // dec 32768300
CONST_I hex03e801f4(0x03e801f4); // dec 65536500
CONST_I const_1(1);
ushort unk_1 ;
CONST_I zero(0);
//https://ru.wikipedia.org/wiki/ISO_3166-1
string alpha_2_ISO3166_1 ;
CONST_B aligment_b(0);
string const_triple_defice ;
CONST_S aligment_s(0);
}more_info[pl_all_moreinfo.cnt] ;
Применяю к carindb_bnl — все отлично, к carindb_ee уже не так здорово — в некоторых местах красные несоответствия константы CONST_I const_1(1), у некоторых записей не 1, а 0.
Темплейт раскрашивает carindb_rus, как граната курятник.
В русском варианте карты элемент more_info меньше, чем в «официальных» на 12 байт. Необходимо отыскать признак, позволяющий однозначно отличить «официальный» вариант от «русского». Не знаю, как навигатор на самом деле определяет, где какой, но байт с абсолютным смещением 0×2e в официальных картах равен 1, а в неофициальной русской — 2. В самом начале инклюд-файла inc_common.bt добавляю определение локальной переменной, область видимости которой при таком расположении будет «везде».
// only here i find different between 0A addinfo fields count
// 1 - new, 2 - old, non-crypted, not-compressed
local ubyte IS_OFICIAL_MAP = ( ReadUShort(0x2e) == 1);
Красными стрелками на скрине флуктуации
CONST_I const_1(1);
. Разбиваю 32 битную константу на 2 CONST_S const_a_0(0) и const_b_0(0). Уже понятно, что это не константа, принимает не только значения 0, но оставляю тип, чтобы попытаться понять — в каких случаях какое значение.Элементы массива more_info[pl_all_moreinfo.cnt] адресуются из предыдущего блока brief_geo (PTR p_moreinfo). Структуру more_info копирую, как typedef структуры MORE_INFO_0xA, и добавляю в brief_geo определение переменной с этим типом. В консоль вывожу переменную unk_1, подсвеченную желтым фоном, строку названия и наименование в enum кода языка.
typedef struct{
local ushort size = head.addr.size * 0x800; // size of this block
local uint offset < hidden=true> = head.addr.offset; // absolute block offset
FAR_LIST idx_ch_cityes;
CONST_I dec_11(0x0B) ;
CONST_I dec_22(0x16) ;
CONST_I dec_33(0x21) ;
CONST_I dec_44(0x2C) ;
LIST pl_addinfo;
CONST_I hex01f4012c(0x01f4012c); // dec 32768300
CONST_I hex03e801f4(0x03e801f4); // dec 65536500
CONST_S const_a_0(0);
CONST_S const_b_0(0);
ushort unk_1 ;
CONST_S zero_0(0);
if(IS_OFICIAL_MAP){
CONST_S zero2(0);
//https://ru.wikipedia.org/wiki/ISO_3166-1
string alpha_2_ISO3166_1 ;
CONST_B aligment_b(0);
string const_triple_defice ;
CONST_S aligment_s(0);
}
}MORE_INFO_0xA;
<.......>
struct{
local ushort size = head.addr.size * 0x800; // size of this block
local uint offset = head.addr.offset; // absolute block offset
PSTR pstr_name; // ptr 2 zero-ended str
CONST_B is_synonym(0);// !!! carindb_rus.0xA.osterreich = 1
en_LANG en_lang ;// language code
CONST_S zero(0); // ushort allways 0
PTR p_moreinfo; // ptr to item of LIST pl_moreinfo;
// jump to MORE_INFO
local ushort return_here = FTell();
FSeek(p_moreinfo.ptr + offset);
MORE_INFO_0xA more_info; // ju
FSeek(return_here);
Printf("%i;\t %s \t ;%s\n",
more_info.unk_1, pstr_name.str, EnumToString(en_lang));
}brief_geo[pl_all_data.cnt] ;
Значения unk_1 и в русской, и в официальных картах равны у соответствующих стран.
Увеличение номера, похоже, связано с алфавитным порядком, если сортировать по алфавиту английские названия стран, а не строки из карт. Осталось только нагуглить — есть ли в природе такой список, где Албания (shqiperia) unk_1 = 2, а Андорра (andorra) unk_1 = 5, Австрия (oesterreich) — 14, Азербайджан — 15. И ведь есть, удовлетворяющий условию: World Countries List, однако Но условия ставить надо чётче: где-то после Боснии и Герцеговины (27) нумерация сбивается, и порядковый номер Болгарии 31, а не 33, как в carindb. Но если ввести поправку -2, то до Латвии страны из carindb соответствуют, затем менять коэффициент на +1, и снова, и снова (у Македонии он аж 33), на скрине выделил желтым. Самая последняя страна, 241 'srbija i crna gora' или 'Serbia and Montenegro' в современном списке nationsonline отсуствует (и с чего бы?).
Очевидно, первоначальный список захардкожен значениями в прошивки. И это явно коды английских наименований, которые, в отличие от местных, на CD отсутствуют. Архитекторы не подумали о том, что география изменится, а потом ради совместимости с ранее выпущенными картами изменить нумерацию стало невозможно.
Жаль, планировал сделать полный enum список стран, в том числе и тех, которые не представлены на моих CD.
Сделаю сокращенный, взяв страны, которые были на анализируемых 3х CD.
//en_ENG_COUNTRY_NAME
typedef enum {
Albania = 2,
Andorra = 5,
Austria = 14,
Azerbaijan = 15,
Belarus = 20,
Belgium = 21,
Bosnia_and_Herzegovina = 27,
Bulgaria = 33,
Croatia = 53,
Cyprus = 55,
Czech_Republic = 56,
Denmark = 57,
Estonia = 67,
Finland = 72,
France = 73,
Georgia = 80,
Germany = 81,
Greece = 84,
Hungary = 97,
Iceland = 98,
Ireland = 103,
Italy = 105,
Latvia = 117,
Lithuania = 123,
Luxembourg = 124,
North_Macedonia = 126,
Moldova_Republic_of = 140,
Netherlands = 150,
Norway = 160,
Poland = 171,
Portugal = 172,
Romania = 176,
Russian_Federation = 177,
Slovakia_Slovak_Republic = 190,
Slovenia = 191,
Spain = 196,
Sweden = 204,
Switzerland = 205,
Turkey = 216,
Ukraine = 221,
United_Kingdom = 223,
Serbia_and_Montenegro = 241
}en_ENG_COUNTRY_NAME ;
Острова и союз
В MORE_INFO_0xA пара CONST_S, которые вовсе не константы: const_a_0(0) и const_b_0(0), собираю все варианты значений, сопоставленные странам, сортирую.
Желтым выделены Ирландия, Кипр и Великобритания, у которых const_a_0(0) становится = 1. Больше островов в списке нет, ushort is_island.
Зеленым выделены страны, у которых const_b_0(0) = 1. Вот нет Великобритании, Швеции, но есть Швейцария… А так был бы [список стран ЕС]https://ru.wikipedia.org/wiki/Государства_—_члены_Европейского_союза). Впрочем, спишу на разногодность карт, и назову переменную ushort is_EU. Если примут значения не 1 и не 0 — лог, красное, как обычно.
Ссылка (и) на города (и сёла) страны
Осталась нераспознанной часть данных между more_info и строковыми значениями. Начало этого блока указывает LIST в заголовке BT_0×0A с именем pl_all_addinfo. На элементы этого раздела ссылается LIST pl_addinfo в структуре MORE_INFO_0xA.
В carindb_rus каждый элемент more_info ссылается на единственный элемент pl_all_addinfo, в LIST pl_all_addinfo.cnt=2Ah, LIST pl_all_moreinfo.cnt=2A. Это видно на скриншоте — через каждые 12 байт — голубое пятнышко следа от more_info.pl_addinfo.
В carindb_bnl значений этой непознанной структуры не 3, как можно было бы ожидать по аналогии с русской картой, а 23. Из Бельгии просят 8 значений, из Люксембурга 7, из Голлании — опять 8.
первые 8 байт неизвестной структуры — FAR_ADDR, 2 байта — непонятно, последние 2 — ссылка на начало строк.
struct{
local string char_list="";
FAR_LIST ch_idx;
ushort unkn ;
PSTR const_str_begin;
Printf("%X \t%i\n", unkn, unkn);
}addinfo[pl_all_addinfo.cnt + 1] ;
В carindb_rus все unkn = 30h. То есть запрашивается единственный список индексов символов.
Между последним элементов pl_all_addinfo и началом строк — «пустота» размером 12 байт, оканчивающаяся PTR на начало строк, как и у остальных элементов, прибавить к массиву addinfo дополнительный элемент.
В carindb_bnl по more_info.pl_addinfo для Бельгии, Люксембурга и Голландии соответственно unkn {14 20 25 26 27 34 35 3a}, {14 20 25 26 27 34 3a } и {14 20 25 26 27 34 35 3a}. В разных элементах — разные FAR_ADDR, могут совпадать адреса блоков, но индексы символов из них запрашиваются с разных смещений и в разном количестве. unkn — размер города? Статус — столица, районный центр? Может, географические места, не только города, но и реки или холмы, места? Названия по-разному для разных системных языков, не, там шесть вариантов. Разные масштабы карты? Необходимы дополнительные исследования — получать тип блоков и список ch, на которые ссылается LIST.
А це пiд помидоры: жадные структуры
В исследовании для максимизации получаемой информации у меня многие структуры излишне жадные. Проверка каждой константы, что она в этом месте константа, для BL_ADDR прочитывается и запоминается рядом лежащий тип, в Value значений выводится не только та информация, которая в данных самой структуры, но и расчетная, и прочитанные связные со стороны. Для боевых решений — совершенно вредный подход. Но для реверса не просто приемлемый, а крайне полезный.
Важно при этом не увлекаться. В прошлой статье в структуре, описывающей блок типа struct BT_0×0B_0×0D_0×0F_0×11, показалась хорошей идея использовать рекурсивную структуру. Это может быть ошибочным решением — на больших файлах, которые появляются глубже, 010Editor ложится, т.к. цепочки получаются чрезмерно глубокими.
И понятно, что реальный автонавигатор оперирует гораздо более простыми структурами данных, хотя по информационному наполнению и последовательности чтения они близки к рассматриваемым.
Рефакторинг BT_0×0B_0×0D_0×0F_0×11
Пара функций
string getChList(LIST &ch_idx)
формирует и возвращает строку из char, расположенных через 12 байт — размер данных BT_0×0B_0×0D_0×0F_0×11 — по начальному адресу и в количестве входного параметра LIST.string getPStrList(uint base_offset, LIST &brif_idx, uchar max_result)
собирает строку из строк, ссылки на которые расположены через 8 байт от начального адреса brif_idx, в количестве brif_idx.cnt, но не более max_result
//parameter - LIST CH_IDX, return - string
string getChList(LIST &ch_idx){
local string s;
local uint i; //cnt
for(i=ch_idx.offset+4; // ch absolute addr
i<(ch_idx.offset + 4 +ch_idx.cnt * 12); // LIST end, 12=sizeof(CH_IDX)
i+= 12){ // 12 = sizeof(CH_IDX)
SPrintf(s,"%s %c", s, ReadByte(i)); //ch = ReadByte(i);
}
return s;
}
//parameter - far block begin, LIST BRIEF_GEO, return - string OF STRINGS
string getPStrList(uint base_offset, LIST &brif_idx, uchar max_result){
local string s, res;
local uint offset_str , tu< format=hex>;
local uint i, max; //cnt
max = (brif_idx.cnt > max_result)? max_result: brif_idx.cnt;
for(i=brif_idx.offset; // ch absolute addr
i<(brif_idx.offset + max * 8); // 8=sizeof(BRIEF_GEO)
i+= 8){
ReadString( ReadUShort(i) + base_offset ) sometimes work strange
offset_str = ReadUShort(i);
offset_str += base_offset;
s = ReadString(offset_str); //
SPrintf(res,"%s, %s", res, s);
}
return res;
}
Добавляю в enum типов блоков новые значения
// Block types enum ------------------------------------------
typedef enum {
ABSTRACT = 12h,
CH_country = 0Bh, //fully parsed
COUNTRY = 0Ah, // -- block0xA.addinfo.unknown rus = 30h
CH_idx_d = 0Dh, //fully parsed
CH_idx_f = 0Fh, //fully parsed
CH_idx_11 = 11h, //fully parsed
Invalid = 0xFF
}en_BL_TYPE ;
Рефакторю темплейт индексов букв BT_0×0B_0×0D_0×0F_0×11 — в блоке только массив CH_IDX, каждый из которых в колонке Value выводит (Read_CH_IDX) строковое значение, полученное вышеописанными новыми функциями — список строк-названий из блока в который идет адресация (с адресом и типом блока-цели), или список символов-детей.
//{BT_0x0B_0x0D_0x0F_0x11;
//{CH_IDX
struct CH_IDX;
typedef struct{
BL_ADDR bl_postaddr;
char ch ; // char
local en_BL_TYPE en_curr_bl_type = head.type; // current bl type
// is_ptr_out - boolean
if(bl_postaddr.type == (en_curr_bl_type-1) ){ // outer link
ubyte is_ptr_out ;
}else{ // innler link
ubyte is_ptr_out ;
}
// next for LIST far_away, have use size and offset - from bl_type_0c
local uint size = bl_postaddr.size *0x800; // size in blocks, * 0x800
local uint offset = bl_postaddr.offset;
LIST pl_postaddr ;
CONST_S align_s(0);
if(align_s.value) // тут ноль не ноль
Printf("Warn, %X align_s = %i( %X )\n",
FTell(), // offset where happened
align_s.value, align_s.value);
//BAD WAY - MAY NOT ENOUGHT MEMORY FOR BIG BLOCKS
if(!is_ptr_out){
// jmp and recursive declare children struct
local uint return_addr = FTell();
FSeek(pl_postaddr.offset);
CH_IDX childs[pl_postaddr.cnt];
FSeek(return_addr);
}
}CH_IDX ;
string Read_CH_IDX(CH_IDX &a){
local string brif_str;
local uchar MAX_CNT_STR_getPStrList = 5;
if(a.is_ptr_out){
// get str list of brif strnames
SPrintf(brif_str, "%c [%i]>%08X %02x(%s)>%s", a.ch,
a.pl_postaddr.cnt, a.bl_postaddr.raw,
a.bl_postaddr.type, EnumToString(a.bl_postaddr.type),
getPStrList(a.bl_postaddr.offset,
a.pl_postaddr, MAX_CNT_STR_getPStrList) );
}else{ //if(!is_ptr_out){
// get chars list
SPrintf(brif_str, "%c [%i]>%s", a.ch,
a.pl_postaddr.cnt, getChList(a.pl_postaddr));
} //if(is_ptr_out)
return brif_str;
}
//}CH_IDX
// BT_0x0B_0x0D_0x0F_0x11 chars - and index to ge names or char set
typedef struct{
BL_HEAD head; // заголовок
local ushort size = head.addr.size * 0x800; // size of this block
local uint offset < hidden=true> = head.addr.offset; // absolute block offset
LIST pl_data;
CH_IDX char_of[pl_data.cnt] ;
byte after_parsed_block_info ;
}BT_0x0B_0x0D_0x0F_0x11;
//}BT_0x0B_0x0D_0x0F_0x11;
Применяю на двух первых попавшихся блоках 0xB и 0xD.
Содержимое блоков индексов букв почтовых адресов в интерфейсе отображает строковые наименования адресов, адресуемое количество, адрес блока и его тип, если ссылка элемента ведёт наружу.
Если элемент адресуется внутрь, на следующий за буквой список букв — то создаётся рекурсивный тип CH_IDX childs, в интерфейсе исследуемый точно так же. Естественно, у него тоже могут быть дети — пока не дойдёт до списка, ведущего «наружу».
Рефакторинг BT_0×0A
Подобным образом реорганизую полученные элементы BT_0×0A. Идеология: блоке есть главный массив информации типа BRIF_0xA, адресуемый самым первым в заголовке LIST pl_all_data.
Описание этой структуры содержит объявления адресуемых структур MORE_INFO_0xA, внутри которых, в свою очередь, адресуются соответствующие им ADDINFO_0xA. Каждая из структур выводит основные значение в интерфейс 010, в Values:
BRIF_0xA: английское и локальное наименование страны, официальный язык.
MORE_INFO_0xA: английское название, двухбуквенный код по ISO3166_1 (если есть), принадлежность к ЕС (EU, если да), остров (если это остров), и количество элементов ADDINFO_0xA
ADDINFO_0xA: значение с неизвестным назначением , количество элементов адресуемого, и, раз известно, что адресуются элементы типа CH_IDX, список символов через функцию getChList. Аттрибутивная функция comment=Comment_ADDINFO_0xA создаёт в колонке Comment вкладки Variables вызов функции block для копипаста в темплейт и возможности исследования
//{BT_0x0A
//ADDINFO_0xA - additional data - part of MORE_INFO_0xA
typedef struct{
FAR_LIST ch_idx;
ushort unkn ;
PSTR const_str_begin;
}ADDINFO_0xA ;
string Read_ADDINFO_0xA(ADDINFO_0xA &a){
local string s;
SPrintf(s, "%X -[%i] %08X t(%02X)> %s\n", a.unkn,
a.ch_idx.pl_data.cnt, a.ch_idx.far_block.raw,
a.ch_idx.far_block.type,
getChList(a.ch_idx.pl_data) );
return s;
}
string Comment_ADDINFO_0xA(ADDINFO_0xA &a){
local string s;
SPrintf(s, "block(0x%04X); // comment ADDINFO_0xA", a.ch_idx.far_block.offset);
return s;
}
//}ADDINFO_0xA
//MORE_INFO_0xA; - call from BRIF_0xA
typedef struct{
local ushort size = head.addr.size * 0x800; // size of this block
local uint offset < hidden=true> = head.addr.offset; // absolute block offset
FAR_LIST idx_ch_cityes;
CONST_I dec_11(0x0B) ;
CONST_I dec_22(0x16) ;
CONST_I dec_33(0x21) ;
CONST_I dec_44(0x2C) ;
LIST pl_addinfo;
CONST_I hex01f4012c(0x01f4012c); // dec 32768300
CONST_I hex03e801f4(0x03e801f4); // dec 65536500
ushort is_island ;
if((is_island & ~1)) { // !=0, !=1
Printf (" is_island = %i\n", is_island); // !=0, !=1
FSeek(FTell()-2); ushort is_island ;
}
ushort is_EU ;
if(is_EU & ~1){ // !=0, !=1
Printf (" is_EU = %i\n", is_EU);
FSeek(FTell()-2); ushort is_EU ;
}
en_ENG_COUNTRY_NAME en_eng_strname;
CONST_S aligment(0);
if(IS_OFICIAL_MAP){
CONST_S zero2(0);
//https://ru.wikipedia.org/wiki/ISO_3166-1
string alpha_2_ISO3166_1 ;
CONST_B aligment_b(0);
string const_triple_defice ;
CONST_S aligment_s(0);
}
// call ADDINFO_0xA
local uint return_here = FTell();
FSeek(pl_addinfo.offset);
ADDINFO_0xA addinfo[pl_addinfo.cnt] ;
FSeek(return_here);
}MORE_INFO_0xA;
string Read_MORE_INFO_0xA(MORE_INFO_0xA &a){
local string s;
SPrintf(s, "%s",EnumToString(a.en_eng_strname));
if(exists(a.alpha_2_ISO3166_1)) SPrintf(s, "%s, '%s'", s, a.alpha_2_ISO3166_1);
if(a.is_EU) SPrintf(s, "%s, EU", s);
if(a.is_island) SPrintf(s, "%s, island", s); /// Iseland in rus map - not iseland))
SPrintf(s,"%s . add_cnt:[%i]", s, a.pl_addinfo.cnt);
return s;
}
//}MORE_INFO_0xA;
//{BRIF_0xA; main data 0xA - BRIF_0xA
typedef struct{
local ushort size = head.addr.size * 0x800; // size of this block
local uint offset = head.addr.offset; // absolute block offset
PSTR pstr_name; // ptr to zero-ended str
CONST_B is_synonym(0);// !!! carindb_rus.0xA.osterreich = 1
en_LANG en_lang;// language code
CONST_S zero(0); // ushort allways 0
PTR p_moreinfo; // ptr to item of LIST pl_all_moreinfo;
// jump to MORE_INFO
local ushort return_here = FTell();
FSeek(p_moreinfo.ptr + offset);
MORE_INFO_0xA more_info;
FSeek(return_here);
}BRIF_0xA;
string Read_BRIF_0xA(BRIF_0xA &a){
local string s;
SPrintf(s, "%s, `%s`. Lang: %s",
EnumToString(a.more_info.en_eng_strname),
a.pstr_name.str, EnumToString(a.en_lang)
);
return s;
}
//}BRIF_0xA;
typedef struct{
BL_HEAD head; // заголовок
local ushort size = head.addr.size * 0x800; // size of this block
local uint offset = head.addr.offset; // absolute block offset
LIST pl_all_data; // brief geo info
LIST pl_all_moreinfo;// more info, ptrs from briefs
CONST_I zero(0);
LIST pl_all_addinfo; // outer links to ch_idx file
// next - six uints == 0
CONST_I zero(0); CONST_I zero(0); CONST_I zero(0);
CONST_I zero(0); CONST_I zero(0); CONST_I zero(0);
BRIF_0xA brief_geo[pl_all_data.cnt] ; // main data
/*
MORE_INFO_0xA more_info[pl_all_moreinfo.cnt] ;
NO NEED MADE THIS ARRAY - ALL ITEMS WILL BE CREATD FROM brief_geo ptrs
FSeek(pl_all_addinfo.offset); // pl_all_addinfo - byte after more_info
ADDINFO_0xA addinfo[pl_all_addinfo.cnt] ;
NO NEED MADE THIS ARRAY - ALL ITEMS WILL BE CREATD FROM brief_geo ptrs
*/
byte after_parsed_block_info ;
}BT_0x0A;
//}BT_0x0A
ADDINFO_0xA отсылает к типу 0×11 (индексы букс POI), хотя мне ожидалось, что это будет 0xD (индексы букв городов), судя по следованию типов в файле и логике.
В русском варианте у MORE_INFO_0xA по одному ADDINFO_0xA, и тип его 0×30
В официальных картах количество MORE_INFO_0xA от 4 до 10.
Получили возможность, описав в темплейте блок из комментария ADDINFO_0xA, и кликнув по MORE_INFO_0xA.FAR_LIST.PTR.here — посмотреть строковые значения, на которые оттуда ссылаются: блок описан, если в окне hex нажать +, вкладка Variables сфокусируется на элементе темплейта CH_IDX char_of.
Для Бельгии:
14 -[7] 0045D708 t(11)> a d g l m t z
— в блоке 0045D708 ссылается на строки:a [1]>004A5508 10()>, atomium, d - d [2]>004A5508 10()>, dierenpark planckendael, domaine des grottes de han ; g [2]>004A5508 10()>, grand hornu, grottes de han; g [2]>004A5508 10()>, grand hornu, grottes de han; m [1]>004A5508 10()>, manneken pis; t [1]>004A5508 10()>, trois bornes; z [1]>004A5508 10()>, zoo antwerpen
Достопримечательности страны.20 -[3] 0045D708 t(11)> c k m
в блоке 0045D708:c [1]>004A5508 10()>, centre historique de bastogne; k [3]>004A5508 10()>, koninklijk legermuseum, koninklijk museum voor midden afrik, koninklijke musea v. schone kunsten; m [9]>004A5508 10()>, musee d'art et d'histoire, musee d'art moderne, musee de l'armee, musee des sciences naturelles, musee royal d'afrique centrale 2
Музеи.25 -[9] 0045D708 t(11)> b c g j k l o p s
в блоке 0045D708:b [4]>004A5508 10()>, basiliek van het heilig hart, basilique du sacre coeur, belfort, butte du lion; c [2]>004A5508 10()>, cathedrale notre dame de tournai, chateau de laeken; g [2]>004A5508 10()>, grand place, grote markt; j [1]>004A5508 10()>, justitiepaleis; k [2]>004A5508 10()>, kasteel van laken, koninklijk paleis; l [2]>004A5508 10()>, l'abbaye de villers la ville, la collegiale sainte gertrude; o [2]>004A5508 10()>, onze lieve vrouwebasiliek, onze lieve vrouwekathedraal; p [2]>004A5508 10()>, palais de justice, palais royal; s [1]>004A5508 10()>, stadhuis leuven
. Исторические, памятники архитектуры.26 -[4] 0045D708 t(11)> b p t w
в блоке 0045D708:b [4]>004A5508 10()>, bellewaerde park, bobbejaanland, boudewijn seapark, bruparck; p [4]>004A5508 10()>, pairi daiza, parc d'aventures scientifiques, plopsa coo, plopsaland; t [1]>004A5508 10()>, technopolis; w [1]>004A5508 10()>, walibi belgium
Парки развлечений.27 -[4] 0045D708 t(11)> h n p s
в блоке 0045D708:h [1]>004A5508 10()>, het zwin; n [1]>004A5508 10()>, nationale plantentuin; p [1]>004A5508 10()>, parc naturel des hautes fagnes; s [1]>004A5508 10()>, signal de botrange
34 -[4] 0045D708 t(11)> a b c l
в блоке 0045D708:a [2]>004A5508 10()>, aeroport charleroi bruxelles sud, aeroport de charleroi gosselies; b [3]>004A5508 10()>, bru, brussel nationaal, brussels airport; c [1]>004A5508 10()>, crl; l [1]>004A5508 10()>, luchthaven brussel
Аэропорты35 -[2] 0045D708 t(11)> o z
в блоке 0045D708:o [1]>004A5508 10()>, oostende ramsgate; z [1]>004A5508 10()>, zeebrugge
Порт (Но Антверпена нет? Паром?3A -[15] 0045D708 t(11)> a b c d e h k m n p q s t v z
в блоке 0045D708:a [7]>004A5508 10()>, abele n38 frankrijk, antwerpen a12 nederland 1, arendonk e34 nederland, arlon a28 france, arlon e411 luxembourg; b [8]>004A5508 10()>, baarle hertog chaamseweg nederland, baarle hertog turnhoutsew nederland, bastogne n84 luxembourg, beauraing n40 france, berneau n627 nederland; c [2]>004A5508 10()>, chimay france, couvin n5 france; d [1]>004A5508 10()>, dinant n96 france; e [5]>004A5508 10()>, erquelinnes n54 france, essen n117 nederland, estaimpuis n511 france, eupen e40 deutschland, eynatten n68 deutschland; h [1]>004A5508 10()>, hoogstraten e19 nederland; k [3]>004A5508 10()>, kelmis n3 deutschland, kinrooi n78 nederland, knokke n376 nederland; m [7]>004A5508 10()>, maaseik n761 nederland, maasmechelen e314 nederland, maldegem n410 nederland, martelange n4 luxembourg, moelingen e25 nederland; n [1]>004A5508 10()>, neerpelt n74 nederland; p [2]>004A5508 10()>, paal n403 nederland, philippeville n40 france; q [1]>004A5508 10()>, quevry le grand n6 france; s [2]>004A5508 10()>, sankt vith e42 deutschland, st. vith e421 luxembourg; t [4]>004A5508 10()>, tongeren n79 nederland, tournai e42 france, turnhout n119 nederland, turnhout n12 nederland; v [4]>004A5508 10()>, veldwezelt n2 nederland, veurne e40 frankrijk, vielsalm n68 luxembourg, virton n87 france; z [1]>004A5508 10()>, zelzate n423 nederland
Пограничные переходы?Заворачиваю свои догадки в enum:
typedef enum {
Sites_of_interest = 0x14,
Museum = 0x20, //musee d'art moderne, musee de l'armee, musee des sciences Naturelles
Architecture = 0x25, //
Fun_ park = 0x26, //boudewijn seapark, bruparck
Nature_park = 0x27, //het zwin, nationale plantentuin
City = 0x30, //russian map
Aeroport = 0x3a, //brussel nationaal, brussels airport; , luchthaven brussel
// 0x35 // oostende ramsgate (tonnel), zeebrugge (port), need mode exmpls
Border_point = 0x34 //
}en_PLACE_CATEGORY
В carindb_bnl пока неясна категория 0×35. Чуть правлю атрибутивный Read_ADDINFO_0xA — добавляю вывод строкового значения нового enum, и добавляю вывод абсолютного адреса начала списка.
На закладке Variables копипащу (35) -[4] 0045D708 of:022EBD7C ty(11)> e h i r
. Перехожу по адресу в hex окне: выделить 022EBD7C, +, клик на hex окне, +
+ — и в окне Variables фокус автоматически переходит на конкретный элемент массива, описанного в темплейте.
e [2]>004A5D08 10()>, eemshaven, europoort hull
h [2]>004A5D08 10()>, hoek van holland harwich, hoek van holland stena line
i [2]>004A5D08 10()>, ijmuiden felison terminal, ijmuiden newcastle
r [1]>004A5D08 10()>, rotterdam europoort
Понятно, значит, категория Seaport = 0×35 в en_PLACE_CATEGORY.
Файл carindb_ee дает пару новых значений категорий: Sport = 0x23, //automotodrom brno, o2 arena, ski areal jasna, o2 arena
И 0×28 с единственным представителем h [2]>015A6408 10()>, hala ludowa, hala stulecia
. Объект — понятен, это «Зал Столетия» или Народный зал (польск. Hala Stulecia, Hala Ludowa) во Вроцлаве. Но какую категорию он представляет?
UNESCO World Heritage Site? Автор назначение характеризовал, как «structure to host «exhibitions, concerts, theatrical and opera performances, and sporting events)»
Резюме
Структуры CONST_B, CONST_S, CONST_I — константы с инициализирующим параметром — ожидаемым значением
Структура PSTR — указатель PTR на строку, оканчивающуюся нолем
Структура FAR_LIST — указатель-счетчик на массив в дальнем блоке
Перечисление en_LANG — коды разговорных языков
Глобальный
local ubyte IS_OFICIAL_MAP = ( ReadUShort(0x2e) == 1);
— вид файла, официальный, или carindb_rusНа кончике пера описано перечисление en_ENG_COUNTRY_NAME
Гипотеза о информации принадлежности страны к Евросоюзу, и признак того, что страна — островная.
Блок описания страны содержит ссылки на категории POI, points of interests, буквенные индексы блоки типа 11h, но не содержат ссылок на города, относящиеся к стране.
На кончике пера — en_PLACE_CATEGORY, типы POI
Рефакторинг BT_0×0B_0×0D_0×0F_0×11 — полностью описаны данные и их назначение
Полностью распарсен, в т.ч. и семантически блок 0×0A — информация о странах. То есть вообще весь, до последнего байта.
Дальнейший анализ — в следующей статье.