Использование¶
Работа с файлами БД¶
Для чтения БД используется классс DatabaseReader
. При инициализации класса считывается основная информация о
БД (версия формата, язык и т.д) и список таблиц. Каждая таблица представляет собой объект класса Table
.
Обращение к строкам таблицы может выполняться путем итерирования объекта таблицы, либо путем обращения по индексу.
Каждая строка таблицы БД представляет собой объект класса Row
. Методы работы со строками аналогичны методам
работы с таблицами БД.
Стоит обратить внимание на то, что преобразование значений полей из внутреннего формата 1С происходит при обращении к
полю. В дальнейшем значение кэшируется внутри объект. Таким образом, чтобы не снижать скорость работы, не рекоммендуется
применять методы Row.as_dict
и Row.as_list
если не требуются значения всех полей.
Значения полей неограниченной длины представлены объектами класса Blob
. Значение поля может быть считано в
память целиком путем обращения к свойству Blob.value
. Если объект слишком большой, чтобы поместиться в памяти
(размер можно получить через len(Blob)
), то он может быть считан частями по 256 байт путем итерирования.
Следующий пример демонстрирует чтение данных о пользователях (а так же расшифровку хэшей паролей) из таблицы V8USERS файловой БД.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | import binascii
import re
import base64
import argparse
import onec_dtools
def extract_hashes(text):
"""
Получает SHA1 хэши паролей пользователей из расшифрованных данных поля DATA
:param text: расшифрованное поле DATA
:return: кортеж хэшей: (SHA1(pwd), SHA1(TO_UPPER(pwd))
"""
result = re.search('\d+,\d+,"(\S+)","(\S+)",\d+,\d+', text)
if result is None:
return
return tuple([''.join('{:02x}'.format(byte) for byte in base64.decodebytes(x.encode())) for x in result.groups()])
def decode_data_fld(buffer):
"""
Декодирование поля DATA таблицы V8USERS
:param buffer: зашифрованные данные
:return:
"""
# Первый байт содержит длину маски шифрования
# Далее каждая порция байт соответствующей длины поксорена на маску
mask_length = int(buffer[0])
j = 1
decoded = []
for i in buffer[mask_length + 1:]:
decoded.append('{:02X}'.format(int(buffer[j] ^ int(i))))
j += 1
if j > mask_length:
j = 1
decoded_hex_str = ''.join(decoded)
decoded_bin_str = binascii.unhexlify(decoded_hex_str)
return decoded_bin_str.decode("utf-8-sig")
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('path_to_1CD', type=str)
args = parser.parse_args()
with open(args.path_to_1CD, 'rb') as f:
db = onec_dtools.DatabaseReader(f)
print("+{}+{}+{}+{}+".format(6*'-', 50*'-', 42*'-', 42*'-'))
print("|{:6}|{:50}|{:42}|{:42}|".format('Админ', 'Имя пользователя', 'SHA1', 'SHA1'))
print("+{}+{}+{}+{}+".format(6*'-', 50*'-', 42*'-', 42*'-'))
for row in db.tables['V8USERS']:
if row.is_empty:
continue
hashes = extract_hashes(decode_data_fld(row['DATA'].value))
if hashes is None:
continue
print("|{0[ADMROLE]!r:6}|{0[NAME]:50}|{1[0]:42}|{1[1]:42}|".format(row, hashes))
print("+{}+{}+{}+{}+".format(6*'-', 50*'-', 42*'-', 42*'-'))
|
Результатом на примере демо базы конфигурации Управляемое приложение будет следующая таблица:
1 2 3 4 5 6 7 8 | +------+--------------------------------------------------+------------------------------------------+------------------------------------------+
|Админ |Имя пользователя |SHA1 |SHA1 |
+------+--------------------------------------------------+------------------------------------------+------------------------------------------+
|True |Администратор |da39a3ee5e6b4b0d3255bfef95601890afd80709 |da39a3ee5e6b4b0d3255bfef95601890afd80709 |
|False |Менеджер по закупкам |da39a3ee5e6b4b0d3255bfef95601890afd80709 |da39a3ee5e6b4b0d3255bfef95601890afd80709 |
|False |Менеджер по продажам |da39a3ee5e6b4b0d3255bfef95601890afd80709 |da39a3ee5e6b4b0d3255bfef95601890afd80709 |
|False |Продавец |da39a3ee5e6b4b0d3255bfef95601890afd80709 |da39a3ee5e6b4b0d3255bfef95601890afd80709 |
+------+--------------------------------------------------+------------------------------------------+------------------------------------------+
|
Работа с контейнерами¶
Работать с контейнерами можно как используя классы ContainerReader
и ContainerWriter
для распаковки/упаковки
контейнеров соответственно, так и применяя синтаксический сахар в виде функций parse
и build
.
Следующий код реализует возможности распаковки и обратной сборки контейнеров по аналогии с тем, как это делает C++ версия v8unpack:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | import argparse
import sys
import onec_dtools
def main():
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-P', '--parse', nargs=2, metavar=('in_filename', 'out_dir_name'))
group.add_argument('-B', '--build', nargs=2, metavar=('in_dir_name', 'out_filename'))
if len(sys.argv) == 1:
parser.print_help()
return 1
args = parser.parse_args()
if args.parse is not None:
onec_dtools.extract(*args.parse)
if args.build is not None:
onec_dtools.build(*args.build)
if __name__ == '__main__':
sys.exit(main())
|