Выжимаем логи до суха, или как Криптофунтик найдет все что пахнет крипто-трюфелем, находим крипту в неожиданных местах

  • Автор темы Admin

Admin

#1
Администратор
Регистрация
31.12.2019
Сообщения
7,248
Реакции
34
*Криптофунтик это такой мелкий свин который будет искать для вас вкусное, именно так зовется этот авторский парсер.

*Предполагается что читатель усвоил статью - "Базовые навыки работы с ключами и адресами, или как не просрать крипту которая была так рядом."

*Очень желательно что бы читатель так же ознакомился с крипто-верстачком, статья - Бездонная бочка приватных ключей, крипто-верстачок, вперед на поиски кладов!


На кого ориентирована статья?
На тех кто собирает логи на предмет крипты, и тех парсит логи с тем же интересом.


Что в статье.
Общее описание.
Примеры из практики.
Эффект биг даты.
Рабочее пространство для парсинга.
Разбор программы, ключи, результаты.
Раздел для желающих тюнить и допиливать софт.
Послесловие.


Откуда растут ноги.
Эта статья основана в основном на моем опыте сбора и анализа логов по теме криптовалют. Моей основной задачей был сбор информации на стороне клиента, это был парсинг дисков, шар, насов, дасов, браузерного трафика, памяти, до парсинга фтп не дело не дошло.


Что, как и почему, ищем.
Файлы кошельков и это банально, адреса крипты, приватные ключи, неявные пары, интересные данные - скрытую информацию, наводящую информацию.
Интересные данные и с крытая информация звучит как то что требует объяснения. Скрытая информация это например информация накрытая base64 причем хитрецы удаляют = в конце, и я такое находил не только в трафе но и в файлах, как догадался? что натолкнуло глянуть внутрь? - "масса интереса".
Термин "масса интереса" здесь будет означать что по объекту сработали события каждое из которых добавило объекту некий коофициент массы, и этот коофициент перевалил за пороговое значение и парсер прислал объект(файл, фрагмент трафа, фрагмент памяти) на мой холодный стол, так появился алгоритм заглядывающий внутрь того что опознается как бейз64.
Давайте рассмотрим применяемый мной подход в общих чертах, исключительно по его смыслу.
Работа с парсера схожа с работой антивируса, в объекте идет поиск разного и когда что то находится то выставляется событие, по окончании изучения объекта мы имеем по нему самое интересное событие и вес всех событий в совокупности, именно по этим критериям мы определяем куда направить объект, важно отметить что самое интересное событие важнее общей массы, то есть нахождение одного приватного ключа считается более важным чем нахождение любого количества адресов, и такой объект уходит в раздел приватные ключи.
Недавно по форуму пробегал код поиска сид фраз в файлах, и код даже пользовался спросом у форумчан, но - слова фразы могут быть на разных языках это раз(там вроде бы поддерживались 3 языка), а еще разные кошели имеют разные наборы слов и разные способы считать контрольную сумму по этим наборам, и написать универсальный алгоритм практически нереально. Что в данном конкретном случае может предложить подход через события и их вес? - представим что мы в объекте нашли фразу "safe place" это добавило вес, нашли слово "coin" и это добавило вес, нашли адрес крипто-валюты и это тоже добавило вес - наверное такой файл имеет смысл осмотреть в ручном режиме. Так же добавляют веса имена файлов. Кстати кроме крипты я еще и доступы искал...это я к тому что грамотно составленные маски поиска и эвенты, а так же настрой коофициентов подход универсальный. Например если адрес сам по себе имеет невысокий уровень интереса но например тот факт что он был скрыт в бейз64 этот уровень ему повышает.
Что такое неявные пары? - допустим мы нашли 1HZwkjkeaoZfTSaJxDw6aKkxp45agDiEzN и e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 так вот этот хекс ряд подойдет как приватный ключ для адреса выше, даже если парсеру встретится b5bd079c4d57cc7fc28ecf8213a6b791625b8183 и e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 он определит что это пара адрес-приват, насколько все это все важно? - а это зависит от материала, если вы ищите там где уже поискали до вас то на такое неявное и весь расчет. Искать связи между хекс\бин последовательностями меня научил анализ трафа и памяти и я право слово удивился когда эти связи стали находится в файлах, а когда такое находится в файлах это явно повод повнимательнее отнестись к клиенту в целом, кстати масса набранная клиентом хороший показатель, например на клиенте не нашлось ни приватов ни кошелей но куча адресов....обратить на него внимание не самый плохой способ потратить время.


Люди странные.
Вот как то раз я решил собрать с клиентов все последовательности 32 байтных хексов, сделать из них ключи и поискать соответствия на них в логах, когда я нашел такие соответствия я начал искть а где же были эти хекс последовательности, и увидел что кто то их просто хранил в файле(может сервис или кошель выдал это челу и велел сохранить), но дальше веселее я нашел файл с sha256 музыкальных композиций и хитрый меломан сделал из них приват ключи и даже двигал там крипту, отсюда растут ноги у раздела про поиски кладов, и поиска соответствий в самом прасере. А чем больше эвентов тем чудесатей, и опять же пища для ума =)


Эффект биг даты.
Вернемся к адресу 1HZwkjkeaoZfTSaJxDw6aKkxp45agDiEzN он интересный по нескольким причинам, нашелся он у одного клиента а приватный ключ к нему совсем у другого. Парсер так устроен что что сначала обрабатывает каждого клиента по отдельности и когда обработает всех клиентов он формирует общую базу и ищет уже пары ключ-адрес между клиентами, и иногда находит =)
Но веренмся к данной находке,
так выглядит находка в логе парсера
"b5bd079c4d57cc7fc28ecf8213a6b791625b8183": { # это тело от 1HZwkjkeaoZfTSaJxDw6aKkxp45agDiEzN
"btc_type_prv_hex": [
"
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" # это тело приватного ключа от которого можно сформировать данный адрес
],
"eth_type_prv_hex": [],
"20p_base_xx_found": [
"1HZwkjkeaoZfTSaJxDw6aKkxp45agDiEzN" # здесь мы видим в какой форме был найден адрес
],
"20p_bech32_found": [],
"20p_bitcoincash_found": [],
"32p_bitcoincash_found": [],
"32p_base_xx_found": [],
"32p_bech32_found": [],
"32p_found_in_hex": [
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" # а вот здесь мы видим в какой форме был найден приват ключ
],
"20p_found_in_hex": [],
"20p_found_in_trash": [],
"20p_found_in_trash_src": []
}

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

в логе от клиента А - видим в кеше браузера
search 1HT7xU2Ngenf7D4yocz2SAcnNLW7rK8d4E
search 1HZwkjkeaoZfTSaJxDw6aKkxp45agDiEzN
search 1J35L45RYDXjvtADKgRn5G9uS7iHgQbaaE
search 1JThDFJLAJz5vsg3WpE56qiidUvpAHVeRC

в логе от клиента Б - видим в кеше браузера
ud%5Bem%5D bb6626242fcdb098df32715fef8d39ed6f9892057293fcac8177b2b5984f6422
ud%5Bfn%5D 1630036277d45c5e12c8e17915141b25c603240211e148f3c79ad3415238fa35
ud%5Bln%5D 581b935960d26c5a14cf05ae05cfcd7cf64b8449e3a98f7913f0891bdaff0ca2
ud%5Bph%5D 2e6a44fa51f86fb88997b6473bfc0b497406d12374dfd42f85ce9bd12dc1bf50
ud[em] bb6626242fcdb098df32715fef8d39ed6f9892057293fcac8177b2b5984f6422
ud[fn] 1630036277d45c5e12c8e17915141b25c603240211e148f3c79ad3415238fa35
ud[fn] e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
ud[ln] 581b935960d26c5a14cf05ae05cfcd7cf64b8449e3a98f7913f0891bdaff0ca2
ud[ln] e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
ud[ph] 2e6a44fa51f86fb88997b6473bfc0b497406d12374dfd42f85ce9bd12dc1bf50
ud[ph] e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

я вбил в поиск этот адрес и нашел упоминания, например здесь =)
https://medium.com/analytics-vidhya...a-and-visualizing-the-same-using-e9a5d35ca64c
вообще у этого приват ключа есть секретики и думаю форумчане их быстро раскроют =)
начинать работу с подобным найденным хексом следует в крипто-верстачке, вот так
>b:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
получаем
...
bc1qngw83fg8dz0k749cg7k3emc7v98wy0c74dlrkd - bech32:legacy:bech32 9a1c78a507689f6f54b847ad1cef1e614ee23f1e compressed by default
BTC:private:mainnet
5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss - WIF:base58check 80 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 not compressed 5c5bbb26
1HZwkjkeaoZfTSaJxDw6aKkxp45agDiEzN - p2pkh:legacy:base58check 00 b5bd079c4d57cc7fc28ecf8213a6b791625b8183 compressed unknown 2f20e59b
3DMrVbb4GkPDYpPoGUVFetsfjEFVf9Hd3Q - p2psh:p2sh-segwit:base58check 05 8001bbf9081d5f8e0e6d96548ed0db53f2b0277c compressed unknown 3a53855b
LTC:private:mainnet
TAgaTiX4btdMhNY6eSU5N5jvc71o6hXKdhoeBzEk31AHykGDou8i - WIF:base58check b0 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 01 0e41541f
LZGpRyQPybaDjbRGoB87YH2ebFnmKYmRui - p2pkh:legacy:base58check 30 9a1c78a507689f6f54b847ad1cef1e614ee23f1e compressed unknown 4052c9e1
....

далее вводим чек
>c
и видим такое
address body checked 5, found 1, time elapsed 0.0002538999979151413
или такое
address body checked 5, found 0, time elapsed 3.0899999998723615e-05
если первый вариант значит на каком то из адресов есть баланс, если второй то ищем вкусное где то еще, через данный адрес прошло немало вкусного кстати.
А именно
Address
1HZwkjkeaoZfTSaJxDw6aKkxp45agDiEzN
Format
Base58 (P2PKH)
Transactions
683

Total Received
59.08944864 BTC
Total Sent
59.08944864 BTC
Final Balance

0.00000000 BTC


последняя транзакция
Hash
e46465f6754e5b9bb8193ff3a3bc66e1fbca64022178887577d5283c88a457d1
2021-04-05 00:59
1HZwkjkeaoZfTSaJxDw6aKkxp45agDiEzN
0.00002378 BTC
16fDy6r9EkoMENXsDiWGEptfSdGEYJDSg2
0.00001189 BTC
Fee
0.00001189 BTC(5.308 sat/B - 1.327 sat/WU - 224 bytes)


Попробуйте проверить эфир для этого привата =)
Приведенный пример не без лукавства, думаю кто то догадается в чем именно прикол именно с этим ключом.

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


Рабочая среда.
Для обработки большого количества данных, очень рекомендую делать все это на RAM диске, так же желательно иметь несколько гиг свободного озу, все зависит от того какие объемы вы парсите, ну и ядра - их много не бывает, и да пожалейте свой старенький двух-ядерный ноут.


Настройки парсера из коробки.
Парсер настроен на "частый гребень", то есть он настроен ничего не пропустить, и это вам выдаст много информации и ясно что там будет много ненужного, но информация помечена коофициентами и сопровождается пояснениями и вы сможете сделать несколько этапов обработки информации.


Эксплуатация.
Потребуется установка питон библиотек
https://pypi.org/project/bitcoinlib
https://pypi.org/project/pycryptodome/
https://pypi.org/project/ecdsa/


Параметры командной строки:
-fi folder input
указывает на папку где будет проходить парсинг, предполагается что эта папка содержит папки клиентов, выглядеть это может вот так

-fi "R:\Temp\src"
содержимое папки как правило выглядит так
R:\Temp\src
CA[1E......
CA[5C......
каждым клиентом будет заниматься отдельный тред, так что это важно что бы папки были организованы верным образом.

-fo folder output
указывает на папку куда будет укладываться результат, например

-fo "R:\Temp\dst"
это должна быть либо пустая папка, либо ее вообще не должно быть, в последнем случае программа ее создаст сама

-th num threads. ignoerd for single client mode (one client, one thread)
не стоит указывать больше тредов чем умеет ваш процессор

sc single client mode. interpret folder in as single client folder, otherwise is interpret as container of client folders.
этот ключ вам вряд ли понадобится.

fipt find pairs for total clients
по окончании работы над клиентскими папками идет поиск пар ключ-адрес для всего что было собрано со всех клиентов, тот самый эффект биг даты.

fip find pairs for each client separate (use 'fip' and 'fipt' have sense if 'icd' used)
искать пары внутри каждого клиента отдельно, это не мешает потом еще искать для всех вместе

icd copy interested client folders. not affected for 'sc' mode
клиенты которые были признаны интересными будут скопированы в специальную папку в папке назначения, это удобно что бы отфильтровать только тех где что то было про крипту, скопированные клиенты будут сопровождаться пояснениями что послужило причиной их копирования. Папки с данными клиентов вам очень пригодятся когда вы найдете BIP38 ключи или запароленные кошельки.

icf copy interested files
файлы признанные интересными будут скопированы в специальную папку в папке результатов, это удобно что бы не скакать по папками сверяясь с логом - а где же лежит тот самый файл что бы посмотреть его в редакторе, скопированные файлы будут сопровождаться пояснениями что послужило причиной их копирования.

delsrc delete source folder after scan
удалять отработанные исходные папки клиентов, это очень удобно при включенном копировании клиентов и папок, так как ваш диск скорее всего будет ограничен размером, но об этом позже

noclistats no save client reasons (__reasons_for_save__) work with "icd" key
не сопровождать скопированных клиентов описанием причин, может быть нужно если у вас просто режим первичной фильтрации клиентов

wchk check words
искать интересные слова, и этот ключ отсылает к данной структуре в исходние

Код:
# this checks only if file not interested by previous checks
# look at min_file_weight_for_interest for know what points need for mark file to be interested
interested_words = {
EVENT__interested_words: [
(re.compile(rb"([sS]eed)|([wW]allet)|([cC]oin)|([sS]secret)"), min_file_weight_for_interest - 1), # (min_file_weight_for_interest - 1) == one of this word and any another + event
(re.compile(rb"(safe place)"), min_file_weight_for_interest)] # keep it in safe place =)
}
структура простая - маска, придаваемый нахождением вес. Хочу заметить что питоновская библиотека re это не те же самые рег-экспы что и скажем у awk, у меня не было времени изучать этот модуль и я просто добавил простейшие маски, тем самым сформировав для вас шаблон который вы сможете дополнять, и да по человечески - во внешних файлах конфигурации все это размещать тоже времени не было.

b64chk check in base64
делать поиск в том что могло бы быть base64

nocolors b\w mode
черно белый режим

nologs no logs mode.
не вести лог

я делаю так
py -3 main.py -fi "R:\Temp\src" -fo "R:\Temp\dst" -th 20 fip fipt icd icf icw wchk b64chk delsrc

Ориентир на статиску.
stats_files_total = 61983
stats_files_processed = 57895
stats_folders_total = 27346
stats_folders_processed = 27346
stats_bytes_processed = 9965399967
stats_data_convert_times = 3235
stats_skip_files = 4088
stats_files_by_interested_words = 1581
stats_checked_20p_b58_total = 608
stats_low_interested_total = 4
stats_wallet_files = 54
stats_handy_check_files = 96
stats_handy_check_folders = 15
stats_events_in_files_total = 241226

total clients processed 3075
time elapsed 3164.98 seconds.



Результаты работы.
Все результаты по работе попадают в папку выхода - вспоминаем ключ -fo
За прогрессом работы можно следить заглядывая в файл
temp_log_worker_0
там видно сколько сделано и сколько осталось, это файл лога менеджера, он не занимается прарсингом клиентов, он раздает остальным потокам задачи, отмечает статистику и подбивает итоги.

статистику можно будет посмотреть в stats.txt

db_prv_variants.json
этот файл в выхлопе, один из самых важных, здесь собрано все что интерпретировалось как приватные ключи и этот файл можно проверить верстачком на балансы, смотрите в верстачке про команду chkdb, он как правило очень громоздкий выходит и для ручной обработки не подходит.

файлы temp_log___il... это логи по событиям, после il идет уровень интересности события, а далее описание про что это.

файлы pairs_btc_like.json, pairs_btc_like_hex.json - это найденные пары для биткоин подобных валют, тот который _hex это пары где часть данных была не в привычном кодированном виде а в хексе, для эфира pairs_eth_like.json.

выглядеть содержимое папки результата может так



2ad4b1793dabbcbeb4777.png


Да без файлового менеджера типа фар, со всем этим работать будет крайне неудобно, впрочем для тех кто не умеет использовать фар то и нормально =)

заглянем в interested_word_files


786ee21a3b214f588bc29.png


первая цифра в именах это уровень самого интересного события по файлу, вторая это какой вес он набрал, третья это crc32 что помогает избежать дублирований.
к каждому файлу прилагается описание .src.txt которое объясняет почему он был выбран, посмотрим внутрь main.db.src.txt
R:\Temp\1\AA--------------------------------------------------------------------------------------------\main.db - это путь окуда взят было файл
interested words(interested_words): b'safe place', - это событие которое в нем произошло, в скобочках имя эвента.
interest file by his weight(file_weight_interest): weight is 30 - а это причина почему он был признан интересным в данном случае набрал критический вес - вторая цифра в имени.

Подобное же относится и ко всем остальным папкам и файлам, для наглядности стоит заглянуть в wallet


5389541c0f371990cbd0d.png


обратите внимание что некоторые файлы набрали вес больше других, это как правило означает что там было найдено много адресов а так же то что их активно использовали, так что начинайте смотреть материал по самому интересному событию и самому большому весу, это самые перспективные объекты.
заглянем во внутрь 31_306_3900267033_wallet.dat.src.txt
R:\Temp\..................................................................\Coins\BitcoinBitcoinQT\wallet.dat
check it file handy(check_it_file_handy): mask on file/folder name: R:\Temp\..................................................................\Coins\BitcoinBitcoinQT\wallet.dat
wallet file(wallet): mask on file/folder extension: R:\Temp\..................................................................\Coins\BitcoinBitcoinQT\wallet.dat
wallet file(wallet): wallet found by format 12:16 = 62310500
base58 20+ checked(base58_20p_checked): b'16cAVR3...........................'
base58 20+ checked(base58_20p_checked): b'1JaPNw............................'
base58 20+ checked(base58_20p_checked): b'33KF8.............................'
....
base58 20+ checked(base58_20p_checked): b'16Q...............................'
interest file by his weight(file_weight_interest): weight is 306

собственно понятно почему это кошель набрал свой вес в 306 баллов.

Повторюсь времени на точный тюниг парсера у меня не было и именно поэтому будут явно лишние файлы в выборке, но все они в одной папке и легко выпиливаются\репарсятся это - раз, а два - вес файла это очень хороший ориентир на то с чего стоит начинать просмотр а что отодвинуть на самую последнюю очередь.

В общих чертах все, скачайте паблик логов, попробуйте парсер на них.


По поводу избыточности данных.
Обо всех этих созданных джейсонах, все ли они необходимы, и куда их применять.
Я как правило стараюсь собирать как можно больше данных, иногда идеи как их использовать приходят спустя значительное количество времени, именно поэтому продукт сохраняет много оперативных баз которые могут пригодится разве что кодерам с богатой фантазией и знанием предмета. То есть db_hex_... файлы за пределами работы не нужны, я их использую для проверок того правильно ли отработал код, поиска артрефактов(странностей в работе софта), и проверки всяких безумных теорий.


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


Модификация Кода.
Часть для тех кто будет, исправлять, модифицировать, дорабатывать код.

Важные для понимания и тюнига участки:
для начала смотрим переменные класса
class Ripper:
обращаем внимание на флаги и структуры
читаем эвенты, в основном весь парсинг построен на эвентах

Код:
# events
EVENT__pair = "pair"
EVENT__hidden_content = "hidden_content"
EVENT__base58_32p_checked = "base58_32p_checked"
...
тысячи веток писать в таких задачах точно не вариант, поэтому таблицы и коллбеки
# event config records
internal_event_types = {
EVENT__pair: { # pair found - private key and addr
# pair check after all of files parsed already, so file of pair source in that moment unknown
"log": "pairs", # log file for save info about that event
"log_legend": "pair", # legend why it interested
"log_interest_level": 50, # level of interest log
"client_interest_level": 50, # level of interest client where it found
"counters_up": {"stats_pairs": 1, "stats_client_weight": 100},
},

EVENT__hidden_content: { # inside in base64 or binary data
"log": "hidden",
"log_legend": "hidden content",
"log_interest_level": 40,
"file_interest_level": 40,
"save_file_folder": "interested_files_hidden",
"client_interest_level": 40,
"counters_up": {"stats_hidden_content_in_file": 1, "stats_hidden_content_total": 1,
"stats_file_weight": 100, "stats_client_weight": 100},
},

EVENT__file_in: {
"handler": __internal_event__file_in,
"set_attributes": {"fl_in_base64": False, "fl_in_bin": False, "fl_current_file_interest_level": 0,
"fl_skip_current_file": False, "fl_wallet_file": False, "fl_handy_check_file": False,
"interested_file_reasons_log": "", "fl_current_file_save_folder": ""},
"counters_up": {"stats_files_total": 1, "stats_files_in_folder_and_subfolders": 1},
"counters_set": {"stats_file_weight": 0},
},
....
# about file/folder names and extensions (before check file names converted to lower index)
file_name_check = {
"extensions": {
EVENT__archive_file: [re.compile(r".*\.(rar$)|(tar$)|(gz$)|(7z$)|(zip$)")],
EVENT__skip_file: [re.compile(r".*\.(jpg$)(jpeg$)|(gif$)|(waw$)|(avi$)|(mpg$)|(pdf$)")],
EVENT__wallet: [re.compile(r".*\.(dat$)|(seco$)|(vault$)")], # да все dat файлы считать кошелями тоже так себе идея
},
"names": {
EVENT__check_it_file_handy: [re.compile(r"(electrum)|(wallet)|(coin)|(passphrase)")]
}
}

folder_name_check = {
"extensions": {
},
"names": {
EVENT__mark_it_folder_to_handy_check: [re.compile(r"(electrum)|(wallet)|(coin)")]
}
}

# this checks only if file not interested by previous checks
# look at min_file_weight_for_interest for know what points need for mark file to be interested
interested_words = {
EVENT__interested_words: [
(re.compile(rb"([sS]eed)|([wW]allet)|([cC]oin)|([sS]secret)"), min_file_weight_for_interest - 1), # (min_file_weight_for_interest - 1) == one of this word and any another + event
(re.compile(rb"(safe place)"), min_file_weight_for_interest)] # keep it in safe place =)
}
....

весь парсинг содержимого файлов происходит здесь
def process_buffer(self, data: bytes) -> None:


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


Самое важное чего не хватает парсеру.
Экстракцию ключей из wallet.dat в db_prv_variants.json я сделать просто не успел, а их было бы очень быстро и удобно проверить верстачком. Возможно эту функцию добавите в фунтик именно вы =)


Послесловие.
Период в месяц это очень жесткое условие. У меня практически не было наработок именно на питоне, да и с самим питоном я пока на вы, поэтому качество кода так себе и реализовано меньше задуманного, а еще плохо оформлено и пишу я эти строки днем 20го числа, на рефакторинги просто не было времени. Можно сказать едва успел. Это я к тому что если на конкурс выделять чуть больше времени то и продукты будут качественнее, и возможно их будет больше, кто знает сколько кодеров отказались от участия потому что поняли что не успеют с кодом. А без кода такой материал, для большинства - ну в лучшем случае любопытен, а хочется ведь что то годное к применению вот просто сразу, взял и запустил.

Софт
crypto-funtik - парсер логов: https://vk.cc/c292c5

Автор: CryptoBoy
 

Members, viewing this thread

Сейчас на форуме нет ни одного пользователя.