• XSS.stack #1 – первый литературный журнал от юзеров форума

Многопоточное шифрование файлов

Пожалуйста, обратите внимание, что пользователь заблокирован
Да вот вам статья, чтоб не ссорились
Сорян, я сам ковырялся в железе начиная от ZX80 аж в 1991г и знаю все опкоды и номера назначаемых биосом портов и адресов наизусть в том числе новейших . Вам лучше не ругаться друг на друга, тем более на паблике по пустякам себя позорить.
читайте маны, к примеру
 
Последнее редактирование:
NtCreateIoCompletion
NtCreateEvent,
1. включите фантазию используя доки, приложеные мной в виде файла ниже
2. не хочется вступать в полемику, имхо Вы оба мало изучали доки и не смотрели в дизассемблерах и отладчиках, куда ведут OVERLAPPED (IOPS, как Вы их называете функции).
1) Не IOPS (i\o per second) а IOCP (i\o control port), это разные вещи, хотя пишется очень похоже, но я думаю ты мог просто опечататься и имел ввиду IOCP
2) Понятное дело что под капотом работают эвенты, только вот взгляни на прототип функции NtReadFile, там есть в параметрах эвент, и функция поддерживает асинхрон на основе этого механизма, а в функции NtMapViewOfSection нет параметров эвентов, она не поддерживает асинхронную работу на эвентах, поэтому ты не сможешь с ней работать через стандартный IOCP системы.
1670352867239.png

https://xss.pro/attachments/47474/?hash=20de5133fb68c49b301309238740e35b

Или мы снова друг друга не поняли ?
Да вот вам статья, чтоб не ссорились
Я не пойму для чего мне статья, если я собственноручно писал софт на IOCP, который прекрасно работает, и быстро и надежно выполняет поставленные задачи.
Одно дело, если бы я рассуждал поверхностно витая в облаках, не имея рабочего кода и опыта, но наверное я разбираюсь, раз я написал софт, отладил его, и он работает как надо, без единой заминки. Конечно ты можешь предположить, что это произошло по какой-то счастливой случайности, и я, ламер по твоему мнению, по неведомым причинам, чудом написал высоконагруженное приложение, которое работает как надо. Но каков шанс такого события ? Спойлер - в контексте моего 8-ми летнего опыта написания системного софта, с вероятностью 99.9% я написал этот софт успешно, потому что очень долго изучал доку вдоль и поперек и знаю как работают системные механизмы изнутри и снаружи, в фас и в профиль.

Скажи ты сам делал реализацию с портами вывода IOCP + NtCreateSection + NtMapVievOfSection ? Если нет, то зачем спорить, а если да, то обьясни мне тогда как это реализуется, возможно я ошибаюсь, и чего-то не знаю.
Но насколько я сейчас осведомлен, вот вырезки из kernekbase, вот тут уход в ntdll
Ты же видишь где есть связь с OVERLAPPED и эвентами, а где ее нет ?
1670355294862.png

1670355460562.png

1670355573167.png

===============================
1670355690809.png

1670355757801.png



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

++++++++++++++++++
UPD
, возможно я частично понял о чем ты говоришь, так что жду какую-то более конкретную наводку от тебя, если я не ошибаюсь сейчас, то ты предлагаешь делать так

Скрытый контент для пользователей: .

Ты это имел ввиду ?
 

Вложения

  • 1670352994055.png
    1670352994055.png
    14.8 КБ · Просмотры: 509
Последнее редактирование:
Пожалуйста, обратите внимание, что пользователь заблокирован
Скажи ты сам делал реализацию с портами вывода IOCP + NtCreateSection + NtMapVievOfSection ?
Да делал и очень удачно, там правда буткит еще был в комплекте, но это не важно раз разговор про натив.

PIO_STATUS_BLOCK разыщите в нативе вот и поймете как работает.
Все сводится к IOCTL который передается драйверу через ZwDeviceIoControlFile (в уже ядре)
Мне лень, вот лишь некоторые прямые сисколы, через натив в юзермоде:
NTSTATUS NtCreateFile (PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES,
+ PIO_STATUS_BLOCK, PLARGE_INTEGER, ULONG, ULONG, ULONG,
+ ULONG, PVOID, ULONG);
NTSTATUS NTAPI NtFsControlFile (HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID,
- PIO_STATUS_BLOCK, ULONG, PVOID, ULONG,
- PVOID, ULONG);
NTSTATUS NTAPI NtFlushBuffersFile (HANDLE, PIO_STATUS_BLOCK);
NTSTATUS NTAPI NtLockFile (HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID,
- PIO_STATUS_BLOCK, PLARGE_INTEGER, PLARGE_INTEGER,
- ULONG, BOOLEAN, BOOLEAN);
Я спорить не собираюсь, просто не хочу чтоб Вы ругались из за хрени, вроде умные и знающие люди...
 
Да делал и очень удачно, там правда буткит еще был в комплекте, но это не важно раз разговор про натив.

PIO_STATUS_BLOCK разыщите в нативе вот и поймете как работает.
Все сводится к IOCTL который передается драйверу через ZwDeviceIoControlFile (в уже ядре)
Мне лень, вот лишь некоторые прямые сисколы, через натив в юзермоде:
NTSTATUS NtCreateFile (PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES,
+ PIO_STATUS_BLOCK, PLARGE_INTEGER, ULONG, ULONG, ULONG,
+ ULONG, PVOID, ULONG);
NTSTATUS NTAPI NtFsControlFile (HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID,
- PIO_STATUS_BLOCK, ULONG, PVOID, ULONG,
- PVOID, ULONG);
NTSTATUS NTAPI NtFlushBuffersFile (HANDLE, PIO_STATUS_BLOCK);
NTSTATUS NTAPI NtLockFile (HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID,
- PIO_STATUS_BLOCK, PLARGE_INTEGER, PLARGE_INTEGER,
- ULONG, BOOLEAN, BOOLEAN);
Я спорить не собираюсь, просто не хочу чтоб Вы ругались из за хрени, вроде умные и знающие люди...
Чекни апдейт, я внизу предыдущего сообщения для тебя добавил хайд, отреагируй че как
И странность есть еще одна, в параметрах функции ZwMapViewOfSection нет PIO_STATUS_BLOCK, она получается полностью синхронно работает, асинхронности нет.
 
Последнее редактирование:
Пожалуйста, обратите внимание, что пользователь заблокирован
Это доказывает что IRP Бро может быть только один в одно время драйверу
Если процедура диспетчеризации драйвера не завершает IRP со значением состояния ошибки, драйвер самого нижнего уровня в цепочке часто устанавливает блок состояния ввода-вывода IRP на значения, которые будут возвращены исходному запросчику операции ввода-вывода. Подпрограммы IoCompletion драйверов более высокого уровня обычно проверяют блок состояния ввода-вывода в IRP, завершенных драйверами более низкого уровня. Блок состояния ввода/вывода в IRP является единственной информацией, передаваемой обратно от базового драйвера устройства к подпрограммам IoCompletion всех драйверов более высокого уровня. Операционная система реализует подпрограммы поддержки, которые записывают значения IO_STATUS_BLOCK в буферы вывода, предоставленные вызывающей стороной. Например, см. ZwOpenFile или NtOpenFile. Эти подпрограммы возвращают коды состояния, которые могут не совпадать с кодами состояния в структурах IO_STATUS_BLOCK. Если одна из этих подпрограмм возвращает STATUS_PENDING, вызывающая сторона должна дождаться завершения операции ввода-вывода, а затем проверить код состояния в структуре IO_STATUS_BLOCK, чтобы определить окончательный статус операции. Если подпрограмма возвращает код состояния, отличный от STATUS_PENDING, вызывающая сторона должна полагаться на этот код состояния, а не на код состояния в структуре IO_STATUS_BLOCK.
Регистр назначенный на прерывание в качестве буфера ОДИН
 
Пожалуйста, обратите внимание, что пользователь заблокирован
я не знаю архитектуру где 2 регистра или более или хз несколько, ну в манах же сказано - не читаешь - не пишешь или то или то.
 
Это доказывает что IRP Бро может быть только один в одно время драйверу

Регистр назначенный на прерывание в качестве буфера ОДИН
Так ты на вопросы так и не ответил вот эти
1)
UPD, возможно я частично понял о чем ты говоришь, так что жду какую-то более конкретную наводку от тебя, если я не ошибаюсь сейчас, то ты предлагаешь делать так
Скрытый контент для пользователей: .

Ты это имел ввиду ?

2) И странность есть еще одна, в параметрах функции ZwMapViewOfSection нет PIO_STATUS_BLOCK, она получается полностью синхронно работает, асинхронности нет.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
+ там юзается NtCreateThread + уведомление сервера подсистемы, в общем учите матчасть
ZwMapViewOfSection - в том то и хрен, я уже задолбался объяснять что буфер и прерывание на аппаратном уровне одно, а то что выше - параллелится уже ядром
один регистр на буфер в контроллере прерываний, харэ уже... эта технология уже давно, иначе винт бы просто с катушек съехал, да UDMA есть - но там аппаратное разграничение, туда залезьт - это я хз биос перепрошивать у контроллера. На уровне софта как низкоуровневого, так и высокоуровнего - буфер 1, хоть обосрись но нельзя одновременно железку читать и писать.
 
Последнее редактирование:
+ там юзается NtCreateThread + уведомление сервера подсистемы, в общем учите матчасть
ZwMapViewOfSection - в том то и хрен, я уже задолбался объяснять что буфер и прерывание на аппаратном уровне одно, а то что выше - параллелится уже ядром
один регистр на буфер в контроллере прерываний, харэ уже...
Про потоки это и ежу понятно, пул потоков сам по себе не появится из воздуха, у меня есть точно такая же реализация (алгоритмически схоже), только не через секции, а через обычные NtCreateFile + NtReadFile\NtWriteFile
Теперь я вкурил, как можно секции прикрутить сюда
Все пазл сложился🥳
 
Пожалуйста, обратите внимание, что пользователь заблокирован
;)
Ребят всё же мы откинулись на натив, хотя ТС хотел другого :)
аж даже до ядра дошли ))
 
Последнее редактирование:
;)
Ребят всё же мы откинулись на натив, хотя ТС хотел другого :)
Да рано или поздно он придет к нативу, так что все равно полезно будет читать
 
Спасибо, наводка на IOCP очень полезна. Как раз читал сегодня статью про реверс локбит, он тоже юзает порт. На расте, чувствую, будет проблематично делать что-то такое. Выкачу сюда сорец, если осилю)
 
This is not very good advice, if you map more files, 30-40 GB each, firstly, they will not fit entirely into the RAM, and secondly, if the paging file is enabled, additional time will be spent resetting the virtual RAM that does not fit into the real physical RAM process memory to disk. It is better to read large files sequentially, in parts, while it is not at all necessary to encrypt the entire huge file as a whole, it is enough to distribute the "stripes" evenly over this file, and encrypt only within these "zebra" bands. Encryption speed with this method takes off very much on large files. Small ones, of course, can be encrypted entirely.


The general algorithm is
N = <number of processor cores>
1) A pool of threads is created, the number of threads is 2 * N, while the pool must be configured so that only N threads work at the same time, the rest are sleeping, this is necessary so that during the switching of the thread queue in the pool, there are no downtime, some threads can complete their work, but the pool will not have time to immediately issue a new task to it, so it will take the thread that has been waiting for a task for a long time (read about IOCP on msdn)
2) A global queue of files is created, the main thread bypasses the files in the directories, and throws the files into the queue, it also makes sure that the queue does not overflow, and it will definitely overflow, since read / encryption / write operations take much longer than directory traversal, even taking into account the fact that traversal works in one thread, and there are N worker threads processing files
3) The working pool of threads takes files from the queue, each thread reads the file, encrypts it, writes it back, so you have a continuous supply of files to the queue, which the worker threads parse and work with the files.

All this is done on asynchronous options for working with files, so that there would be no delays and downtime, for highly loaded software, every tick of the processor is important, while a read / write operation is in progress, you can encrypt an already read buffer, and so on. You can also get a little speedup if files are opened with buffering disabled, then read / write operations will be faster, due to the lack of filling temporary alignment buffers in the file system drivers, but here it is important to independently align the buffer along the boundary of the disk sector (physical sector, many confusion with logical, these are different things), otherwise nothing will be read / written.
this. no matter how many threads you have doing the encryption->overwrite, there's a set limit on how fast the needle can physically move on a hard drive. you'll always have to pause/wait for thread or unique_lock mutex while a single file is being written to.

the speed will come in the way you are reading the files and piping them to a queue that does the encryption and writing on disk. that can be broken into multiple threads. as well as the way you encrypt, such as only encrypting the first 5mb of a file and skipping every other byte instead of running the entire 5mb straight through. if you think about it, you can breakdown the major processes into threads, such as 1) scanning for files, 2) encrypting file, 3) overwriting the file. just remember that if not done properly, it will overlap and rescan already touched files and end up wasting speed. it is not just a simple "search all folders recursively and encrypt all files". this will just be slow and get caught in heuristics for moving in an obvious and straight way through the disk. it will reach a few folders before it is stopped by any AV.

other than that, multithreading isn't the only thing to focus on. different encryption methods could also very much increase/slow down the speed and make multithreading pointless. for example, choosing encryption types such as salsa20 in comparison to rsa2048. going even further into it, phrase bit length is also a major factor. 256 bit key phrases will be just as effective as 2048 or 4096, and will keep the speed and still be efficient. on that, it matters most on your cryptographic seed and how you generated the randomness for it, because the only way your encryption will be defeated is hundreds of cloud computing cracking it like the recent Zeppelin Ransomware as they used 128bit, or the more likely scenario that you're cryptographic process was insecure and could be exploited and the seed recreated. there are other reason too like improperly freeing the key from memory (ie using memset() instead of securezeromemory()... etc), but that's a coding problem and not a development schema problem.
 
Последнее редактирование:
you'll always have to pause thread or mutex while a single file is being encrypted.
Нет это не так, шифроваться одновременно могут несколько буферов, это делается параллельно в разных потоках, так же вызывать асинхронные NtReadFile и NtWriteFile можно одновременно из разных потоков, это не помешает производительности, системный вызов отработает, далее уже диспетчеризацией задач IO будет заниматься драйвер файловой системы, поэтому не нужно ничего блокировать на момент обработки файла, если ты будешь использовать мутексы, и ждать пока обработается один файл, можешь делать однопоточное приложение, количество потоков в таком случае тебя не спасет, но в таком случае скорость у тебя будет как у улитки или черепахи.
there are other reason too like improperly freeing the key from memory (i.e using memset() instead of securezeromemory()... etc), but that's a coding problem and not a development schema problem.
Выделяешь виртуальную память, и помечаешь ее как несбрасываемую на диск, все, она никогда не окажется в файле подкачки, и потом форензики не смогут достать ключи при анализе. Перед освобождением памяти она затирается нулями через самописные функии. Все просто.

such as salsa20 in comparison to rsa2048
Как ты вообще можешь сравнивать сальсу и RSA, первое это поточный шифр, при этом симметричный, а второе это ассиметричная система, это совершенно разные вещи используемые для разных задач. Сальсу\чачу используют для шифрования самих файлов, а RSA используют для того что бы зашифровать ключи шифрования сальсы\чачи
 
Нет это не так, шифроваться одновременно могут несколько буферов, это делается параллельно в разных потоках, так же вызывать асинхронные NtReadFile и NtWriteFile можно одновременно из разных потоков, это не помешает производительности, системный вызов отработает, далее уже диспетчеризацией задач IO будет заниматься драйвер файловой системы, поэтому не нужно ничего блокировать на момент обработки файла, если ты будешь использовать мутексы, и ждать пока обработается один файл, можешь делать однопоточное приложение, количество потоков в таком случае тебя не спасет, но в таком случае скорость у тебя будет как у улитки или черепахи.

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


Как ты вообще можешь сравнивать сальсу и RSA, первое это поточный шифр, при этом симметричный, а второе это ассиметричная система, это совершенно разные вещи используемые для разных задач. Сальсу\чачу используют для шифрования самих файлов, а RSA используют для того что бы зашифровать ключи шифрования сальсы\чачи
thank you for the awesome response!

in the end when it comes down to the physical level, i believe only one file can be written at a time. you can have as many threads as you want writing to a disk but they will all have to wait on each other one way or another. just like it if you have threads ntwrite()ing, one will get a code of status_pending until i/o completion.

plenty of times the keys have been been properly removed from ram or initial clues that could lead to recreation of the cryptographic seed, because ram wasn't wiped in a secure way or specific data was left readable in memory. that is the reason there is such functions like securezeromemory when you want to verify that the compiler will force wipe any memory that it could leave floating around which it not deem important to securely free.

and yes, the las part was just an example to describe the importance of choosing a good encryption for the job. just like no one would think to encrypt entire db in rsa2048 and rather as most, like you described. there have been wares that have encrypted all files in rsa instead of a practical cipher. i'm not saying they were good, but the fact they exist merits my saying so.
 
one will get a code of status_pending until i/o completion
да он получит статус ожидания\выполнения задачи, но этот поток может продолжать потом другую работу, шифровать, делать другие задачи, пока ядро пишет в девайс, нет смысла делать блокировки мутексами
that is the reason there is such functions like securezeromemory when you want to verify that the compiler will force wipe any memory
Это обычная апи функция, открой IDA PRO или отладчик, посмотри что находтся под капотом этой функции, но я не использую ее, у меня есть собственные самописные аналоги, надежные, проверенные
1670454976517.png
1670455002168.png
 
yes, it will receive the status of waiting / executing the task, but this thread can then continue other work, encrypt, do other tasks while the kernel writes to the device, there is no point in doing locks with mutexes

This is a regular api function, open IDA PRO or a debugger, see what is under the hood of this function, but I do not use it, I have my own self-written analogues, reliable, proven
Посмотреть вложение 47538
Посмотреть вложение 47539
my point about using a mutex was a metaphor. i probably didn't explain it well, but it was meant to describe the bottleneck situation of multiple threads competing to write to disk and how they all still had to wait for one at a time to finish.

and yes, i know it's just a function with memset under the hood, but there's a reason it exists and isn't just a basic #define. while memset can some times be ignored by the compiler if it deems it is best optimized to ignore it. securezeromemory will never be optimized by compiler and will always wipe, no matter what. which is important in cryptography.

regardless, good talk!
 
Пожалуйста, обратите внимание, что пользователь заблокирован
securezeromemory - это изврат компилятора, если Вы не будете OpenMP, Intel Compiler юзать...
в общем не суть - любой вызов ядерного NtCreateMutant и прочих сисколов - ведут к потере производительности априори, так как вызывают прерывание в ядре, если контроллер имеет достаточный буфер и отдельное прерывание (для чего и придумали DMA/UDMA и т.д.) он работает отдельно, но не факт что он не остановит чтение/запись, так как регистры ограничены на шине.
Вообще почитайте старика Рихтера https://wm-help.net/books-online/book/59464.html
Любое обращение к ядерным функциям, типа мьютекса (мутанта), потока (трэда), и т.д. несёт огромные затраты тактов проца, а вот умные технологии типа ПДП (DMA) и призваны от этого отгородить.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх