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

Интересный метод самоудаления

DildoFagins

TPU unit
Забанен
Регистрация
11.08.2020
Сообщения
4 315
Решения
2
Реакции
5 265
Пожалуйста, обратите внимание, что пользователь заблокирован

Вложения

  • delete-self-poc-main.zip
    3.2 КБ · Просмотры: 57
Пожалуйста, обратите внимание, что пользователь заблокирован
Очень даже интересный метод. Можно при запуске удалять свой файл и работать только из памяти.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Только не совсем понимаю почему поток :$DATA переименовывается именно в :wtfbbq. Что это за значение?
 
Пожалуйста, обратите внимание, что пользователь заблокирован
$DATA переименовывается именно в :wtfbbq. Что это за значение?
Насколько я понимаю, это значение может быть любым не равным $DATA, автор видимо назвал его так для смеха (читается это как "what the fuck barbecue").
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Интересно, а если попробывать так удалить какие-нибудь аверские либы/базы?
Вряд ли какой-либо авер даст открыть свой файл с флагом DELETE.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Вряд ли какой-либо авер даст открыть свой файл с флагом DELETE.
если отсеить эмуль, заинжектиться в траст процесс и оттуда попробовать, то вполне есть шанс...
 
https://habr[.]com/ru/companies/ru_mts/articles/767078/

Ещё один пример кода с самоудалением построенный на той же идеи переименования потока $data
 
Пытаюсь разобраться как работает этот PoC, потому что он у меня не отрабатывает до конца и завершается с ошибкой.

Снимок.JPG


Ошибка про то что при повторной попытке получения хендла нашего файла (из которого работает процесс) прога его не находит на диске. При этом сам экзешник переименовывается в имя потока :wtfbbq". Проводник файла с таким именем не видит. В cmd через dir видим что файл на месте и никуда не делся.

Открываю описание пока с гита и читаю:
Как это работает в данном POC?
1. Откройте HANDLE для текущего запущенного процесса с доступом DELETE. Обратите внимание, что требуется только доступ DELETE.
2. С помощью функции SetFileInformationByHandle переименуйте основной файловый поток, :$DATA, в :wtfbbq.
3. Закройте HANDLE.
4. Откройте HANDLE для текущего процесса и установите флаг DeleteFile класса FileDispositionInfo в TRUE.
и т.д.

Как я понимаю я дохожу только до пункта 4. Притом только до первой его половины. Хендл я второй раз уже не могу открыть. Подозреваю что из-за того, что нам не надо переименовывать файл целиком. Нужно только поток внутри файла переименовать. Предполагаю что именно в операции переименования и кроется вся проблема.

Из описания становится понятно что за переименование у нас отвечает функция SetFileInformationByHandle. И по задумке автора она должна переименовывать не весь файл целиком (что я имею у себя) а только поток :$DATA. Отлично, разбираемся чего такого мы в неё напередовали:

BOOL fRenameOk = SetFileInformationByHandle(hHandle, FileRenameInfo, pfRename, (DWORD)(sizeof(FILE_RENAME_INFO) + sizeof(WCHAR) * wcslen(lpwStream)))

hHandle - дескриптор на наш экзешник;
FileRenameInfo - режим работы с файлом. тут мы говорим что хотим переименовать файл.
pfRename - указатель на структуру типа FILE_RENAME_INFO в которой мы сообжаем как мы хотим переименовывать файл.
(DWORD)(sizeof(FILE_RENAME_INFO) + sizeof(WCHAR) * wcslen(lpwStream)) - это размер структуры pfRename.

Кажется двигаюсь в верном направлении. Начинаю смотреть какие режимы взаимодействия есть с файлом кроме FileRenameInfo. Лёрн мелкомягких говорит что их всего шесть ( https://learn.microsoft[.]com/ru-ru/windows/win32/api/fileapi/nf-fileapi-setfileinformationbyhandle ) Вроде наш самый подходящий. Тогда ковырнём что у нас храниться в самой структуре pfRename.

2.JPG


BOOLEAN ReplaceIfExists; - Флаг, указывающий, следует ли заменять существующий файл с тем же именем. Пробовал оба значения, разницы не увидел.
DWORD Flags; - что это за параметр я узнать не смог. Но он есть!
HANDLE RootDirectory; - лерн сказал тут надо передавать NULL
DWORD FileNameLength; - тут размер нового имени
WCHAR FileName[1]; - сюда передаём имя новое. Потдерживается в том числе и имена потоков, начинающиеся с ":".

Понятно что в PoC мы обходим вниманием поля ReplaceIfExists, Flags и RootDirectory. Пробовал туда принудительно пропысывал рекомендованые значения, результат не поменялся.

В моём понимании при попытке с помощью функции SetFileInformationByHandle переименовать основной файловый поток, :$DATA, в :wtfbbq файл должен оставаться с таким же именем на диске, с небольшими изменениями внутри. Но этого не происходит. Или я что то не так понимаю или всё таки что то с поком не так. Подскажите как запустить эту шарманку.

Исходник вроде не правил сильно.

C:
#pragma comment(lib, "Shlwapi.lib")

#include <Windows.h>
#include <shlwapi.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>

#define DS_STREAM_RENAME L":wtfbbq"
#define DS_DEBUG_LOG(msg) wprintf(L"[LOG] - %s\n", msg)

static
HANDLE
ds_open_handle(
    PWCHAR pwPath
)
{
    return CreateFileW(pwPath, DELETE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);


}

static
void*
ds_rename_handle(
    HANDLE hHandle
)
{
    LPCWSTR lpwStream = DS_STREAM_RENAME;
    PFILE_RENAME_INFO pfRename = (PFILE_RENAME_INFO)malloc(sizeof(FILE_RENAME_INFO) + sizeof(WCHAR) * wcslen(lpwStream)); // FILE_RENAME_INFO contains space for 1 WCHAR without NULL-byte
    if (pfRename == NULL)
    {
        DS_DEBUG_LOG(L"could not allocate memory");
        return NULL;
    }
    RtlSecureZeroMemory(pfRename, sizeof(FILE_RENAME_INFO) + sizeof(WCHAR) * wcslen(lpwStream));

    // set our FileNameLength and FileName to DS_STREAM_RENAME
    pfRename->FileNameLength = (DWORD)(sizeof(WCHAR) * wcslen(lpwStream));
    RtlCopyMemory(pfRename->FileName, lpwStream, sizeof(WCHAR) * (wcslen(lpwStream) + 1));


    BOOL fRenameOk = SetFileInformationByHandle(hHandle, FileRenameInfo, pfRename, (DWORD)(sizeof(FILE_RENAME_INFO) + sizeof(WCHAR) * wcslen(lpwStream)));
    if (!fRenameOk)
    {
        free(pfRename);
        return NULL;
    }
    return pfRename;
}

static
BOOL
ds_deposite_handle(
    HANDLE hHandle
)
{
    // set FILE_DISPOSITION_INFO::DeleteFile to TRUE
    FILE_DISPOSITION_INFO fDelete;
    RtlSecureZeroMemory(&fDelete, sizeof(fDelete));

    fDelete.DeleteFile = TRUE;

    return SetFileInformationByHandle(hHandle, FileDispositionInfo, &fDelete, sizeof(fDelete));
}

int
main(
    int argc,
    char** argv
)
{
    WCHAR wcPath[MAX_PATH + 1];
    RtlSecureZeroMemory(wcPath, sizeof(wcPath));

    // get the path to the current running process ctx
     if (GetModuleFileNameW(NULL, wcPath, MAX_PATH) == 0)
    {
        DS_DEBUG_LOG(L"failed to get the current module handle");
        return 0;
    }

    HANDLE hCurrent = ds_open_handle(wcPath);
    if (hCurrent == INVALID_HANDLE_VALUE)
    {
        DS_DEBUG_LOG(L"failed to acquire handle to current running process");
        return 0;
    }

    // rename the associated HANDLE's file name
    DS_DEBUG_LOG(L"attempting to rename file name");
    void* pfRename = ds_rename_handle(hCurrent);
    if (pfRename == NULL)
    {
        DS_DEBUG_LOG(L"failed to rename to stream");
        return 0;
    }

    DS_DEBUG_LOG(L"successfully renamed file primary :$DATA ADS to specified stream, closing initial handle");
    CloseHandle(hCurrent);
    free(pfRename); // free memory allocated in ds_rename_handle
    pfRename = NULL;

    // open another handle, trigger deletion on close
    hCurrent = ds_open_handle(wcPath);
    if (hCurrent == INVALID_HANDLE_VALUE)
    {
        DS_DEBUG_LOG(L"failed to reopen current module");
        return 0;
    }

    if (!ds_deposite_handle(hCurrent))
    {
        DS_DEBUG_LOG(L"failed to set delete deposition");
        return 0;
    }

    // trigger the deletion deposition on hCurrent
    DS_DEBUG_LOG(L"closing handle to trigger deletion deposition");
    CloseHandle(hCurrent);

    // verify we've been deleted
    if (PathFileExistsW(wcPath))
    {
        DS_DEBUG_LOG(L"failed to delete copy, file still exists");
        return 0;
    }

    DS_DEBUG_LOG(L"successfully deleted self from disk");
    return 1;
}

P.S.: Пока рылся в сети, нашёл ещё один подобный PoC на go только. Не помогло:(
https://github[.]com/secur30nly/go-self-delete/
 
Пытаюсь разобраться как работает этот PoC, потому что он у меня не отрабатывает до конца и завершается с ошибкой.

Посмотреть вложение 107138

Ошибка про то что при повторной попытке получения хендла нашего файла (из которого работает процесс) прога его не находит на диске. При этом сам экзешник переименовывается в имя потока :wtfbbq". Проводник файла с таким именем не видит. В cmd через dir видим что файл на месте и никуда не делся.

Открываю описание пока с гита и читаю:
Как это работает в данном POC?
1. Откройте HANDLE для текущего запущенного процесса с доступом DELETE. Обратите внимание, что требуется только доступ DELETE.
2. С помощью функции SetFileInformationByHandle переименуйте основной файловый поток, :$DATA, в :wtfbbq.
3. Закройте HANDLE.
4. Откройте HANDLE для текущего процесса и установите флаг DeleteFile класса FileDispositionInfo в TRUE.
и т.д.

Как я понимаю я дохожу только до пункта 4. Притом только до первой его половины. Хендл я второй раз уже не могу открыть. Подозреваю что из-за того, что нам не надо переименовывать файл целиком. Нужно только поток внутри файла переименовать. Предполагаю что именно в операции переименования и кроется вся проблема.

Из описания становится понятно что за переименование у нас отвечает функция SetFileInformationByHandle. И по задумке автора она должна переименовывать не весь файл целиком (что я имею у себя) а только поток :$DATA. Отлично, разбираемся чего такого мы в неё напередовали:

BOOL fRenameOk = SetFileInformationByHandle(hHandle, FileRenameInfo, pfRename, (DWORD)(sizeof(FILE_RENAME_INFO) + sizeof(WCHAR) * wcslen(lpwStream)))

hHandle - дескриптор на наш экзешник;
FileRenameInfo - режим работы с файлом. тут мы говорим что хотим переименовать файл.
pfRename - указатель на структуру типа FILE_RENAME_INFO в которой мы сообжаем как мы хотим переименовывать файл.
(DWORD)(sizeof(FILE_RENAME_INFO) + sizeof(WCHAR) * wcslen(lpwStream)) - это размер структуры pfRename.

Кажется двигаюсь в верном направлении. Начинаю смотреть какие режимы взаимодействия есть с файлом кроме FileRenameInfo. Лёрн мелкомягких говорит что их всего шесть ( https://learn.microsoft[.]com/ru-ru/windows/win32/api/fileapi/nf-fileapi-setfileinformationbyhandle ) Вроде наш самый подходящий. Тогда ковырнём что у нас храниться в самой структуре pfRename.

Посмотреть вложение 107139

BOOLEAN ReplaceIfExists; - Флаг, указывающий, следует ли заменять существующий файл с тем же именем. Пробовал оба значения, разницы не увидел.
DWORD Flags; - что это за параметр я узнать не смог. Но он есть!
HANDLE RootDirectory; - лерн сказал тут надо передавать NULL
DWORD FileNameLength; - тут размер нового имени
WCHAR FileName[1]; - сюда передаём имя новое. Потдерживается в том числе и имена потоков, начинающиеся с ":".

Понятно что в PoC мы обходим вниманием поля ReplaceIfExists, Flags и RootDirectory. Пробовал туда принудительно пропысывал рекомендованые значения, результат не поменялся.

В моём понимании при попытке с помощью функции SetFileInformationByHandle переименовать основной файловый поток, :$DATA, в :wtfbbq файл должен оставаться с таким же именем на диске, с небольшими изменениями внутри. Но этого не происходит. Или я что то не так понимаю или всё таки что то с поком не так. Подскажите как запустить эту шарманку.

Исходник вроде не правил сильно.

C:
#pragma comment(lib, "Shlwapi.lib")

#include <Windows.h>
#include <shlwapi.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>

#define DS_STREAM_RENAME L":wtfbbq"
#define DS_DEBUG_LOG(msg) wprintf(L"[LOG] - %s\n", msg)

static
HANDLE
ds_open_handle(
    PWCHAR pwPath
)
{
    return CreateFileW(pwPath, DELETE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);


}

static
void*
ds_rename_handle(
    HANDLE hHandle
)
{
    LPCWSTR lpwStream = DS_STREAM_RENAME;
    PFILE_RENAME_INFO pfRename = (PFILE_RENAME_INFO)malloc(sizeof(FILE_RENAME_INFO) + sizeof(WCHAR) * wcslen(lpwStream)); // FILE_RENAME_INFO contains space for 1 WCHAR without NULL-byte
    if (pfRename == NULL)
    {
        DS_DEBUG_LOG(L"could not allocate memory");
        return NULL;
    }
    RtlSecureZeroMemory(pfRename, sizeof(FILE_RENAME_INFO) + sizeof(WCHAR) * wcslen(lpwStream));

    // set our FileNameLength and FileName to DS_STREAM_RENAME
    pfRename->FileNameLength = (DWORD)(sizeof(WCHAR) * wcslen(lpwStream));
    RtlCopyMemory(pfRename->FileName, lpwStream, sizeof(WCHAR) * (wcslen(lpwStream) + 1));


    BOOL fRenameOk = SetFileInformationByHandle(hHandle, FileRenameInfo, pfRename, (DWORD)(sizeof(FILE_RENAME_INFO) + sizeof(WCHAR) * wcslen(lpwStream)));
    if (!fRenameOk)
    {
        free(pfRename);
        return NULL;
    }
    return pfRename;
}

static
BOOL
ds_deposite_handle(
    HANDLE hHandle
)
{
    // set FILE_DISPOSITION_INFO::DeleteFile to TRUE
    FILE_DISPOSITION_INFO fDelete;
    RtlSecureZeroMemory(&fDelete, sizeof(fDelete));

    fDelete.DeleteFile = TRUE;

    return SetFileInformationByHandle(hHandle, FileDispositionInfo, &fDelete, sizeof(fDelete));
}

int
main(
    int argc,
    char** argv
)
{
    WCHAR wcPath[MAX_PATH + 1];
    RtlSecureZeroMemory(wcPath, sizeof(wcPath));

    // get the path to the current running process ctx
     if (GetModuleFileNameW(NULL, wcPath, MAX_PATH) == 0)
    {
        DS_DEBUG_LOG(L"failed to get the current module handle");
        return 0;
    }

    HANDLE hCurrent = ds_open_handle(wcPath);
    if (hCurrent == INVALID_HANDLE_VALUE)
    {
        DS_DEBUG_LOG(L"failed to acquire handle to current running process");
        return 0;
    }

    // rename the associated HANDLE's file name
    DS_DEBUG_LOG(L"attempting to rename file name");
    void* pfRename = ds_rename_handle(hCurrent);
    if (pfRename == NULL)
    {
        DS_DEBUG_LOG(L"failed to rename to stream");
        return 0;
    }

    DS_DEBUG_LOG(L"successfully renamed file primary :$DATA ADS to specified stream, closing initial handle");
    CloseHandle(hCurrent);
    free(pfRename); // free memory allocated in ds_rename_handle
    pfRename = NULL;

    // open another handle, trigger deletion on close
    hCurrent = ds_open_handle(wcPath);
    if (hCurrent == INVALID_HANDLE_VALUE)
    {
        DS_DEBUG_LOG(L"failed to reopen current module");
        return 0;
    }

    if (!ds_deposite_handle(hCurrent))
    {
        DS_DEBUG_LOG(L"failed to set delete deposition");
        return 0;
    }

    // trigger the deletion deposition on hCurrent
    DS_DEBUG_LOG(L"closing handle to trigger deletion deposition");
    CloseHandle(hCurrent);

    // verify we've been deleted
    if (PathFileExistsW(wcPath))
    {
        DS_DEBUG_LOG(L"failed to delete copy, file still exists");
        return 0;
    }

    DS_DEBUG_LOG(L"successfully deleted self from disk");
    return 1;
}

P.S.: Пока рылся в сети, нашёл ещё один подобный PoC на go только. Не помогло:(
https://github[.]com/secur30nly/go-self-delete/
Чекни код ошибки какой там через GetLastError()


C++:
if (hCurrent == INVALID_HANDLE_VALUE)
{
    wprintf(L"GetLastError()=%d\r\n",GetLastError());
    DS_DEBUG_LOG(L"failed to reopen current module");
    return 0;
}
 
Чекни код ошибки какой там через GetLastError()


C++:
if (hCurrent == INVALID_HANDLE_VALUE)
{
    wprintf(L"GetLastError()=%d\r\n",GetLastError());
    DS_DEBUG_LOG(L"failed to reopen current module");
    return 0;
}
Снимок.JPG


ERROR_FILE_NOT_FOUND

2 (0x2)

The system cannot find the file specified.

Как я понял из перевода, системе не удаётся найти указанный файл.
 

Вложения

  • Снимок.JPG
    Снимок.JPG
    35.1 КБ · Просмотры: 17
Посмотреть вложение 107233

ERROR_FILE_NOT_FOUND

2 (0x2)

The system cannot find the file specified.

Как я понял из перевода, системе не удаётся найти указанный файл.
Вместо ::$DATA переименовывается сам файл в :wtfbbq поэтому второй вызов к CreateFile с оригинальным именем фейлится.
Надо явно открыть хендл на дефолтный нтфс поток и его переименовать.
Для этого надо заюзать более низкоуровневое апи NtOpenFile или NtCreateFile
В комментах расписал:

C++:
//Добавляем после инклудов в оригинальном поке

#include <winternl.h>

#define DS_ORIGINAL_STREAM L"::$DATA"
typedef NTSTATUS
(NTAPI *t_NtCreateFile)(
    OUT PHANDLE             FileHandle,
    IN ACCESS_MASK          DesiredAccess,
    IN POBJECT_ATTRIBUTES   ObjectAttributes,
    OUT PIO_STATUS_BLOCK    IoStatusBlock,
    IN PLARGE_INTEGER       AllocationSize OPTIONAL,
    IN ULONG                FileAttributes,
    IN ULONG                ShareAccess,
    IN ULONG                CreateDisposition,
    IN ULONG                CreateOptions,
    IN PVOID                EaBuffer OPTIONAL,
    IN ULONG                EaLength);

//меняем ds_open_handle добавляем dwShareMode = FILE_SHARE_DELETE в CreateFile ( иначе получим ошибку 0xc0000043 (STATUS_SHARING_VIOLATION) при попытке открыть стрим файла 

static
HANDLE
ds_open_handle(
    PWCHAR pwPath
)
{
    return CreateFileW(pwPath, DELETE, FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
}
//далее в main меняем логику таким образом:

int
main(
    int argc,
    char** argv
)
{
    WCHAR wcPath[MAX_PATH + 1];
    RtlSecureZeroMemory(wcPath, sizeof(wcPath));

    // get the path to the current running process ctx
    if (GetModuleFileNameW(NULL, wcPath, MAX_PATH) == 0)
    {
        DS_DEBUG_LOG(L"failed to get the current module handle");
        return 0;
    }
 
    HANDLE hCurrent = ds_open_handle(wcPath);
    if (hCurrent == INVALID_HANDLE_VALUE)
    {
        DS_DEBUG_LOG(L"failed to acquire handle to current running process");
        return 0;
    }

    //вот тут добавляем логику

    t_NtCreateFile NtCreateFile = (t_NtCreateFile)GetProcAddress(GetModuleHandle(L"ntdll"), "NtCreateFile");
    IO_STATUS_BLOCK iosb;
    NTSTATUS status;

    OBJECT_ATTRIBUTES stream;
    UNICODE_STRING DataStream;

     //Заполняем для NtCreateFile паараметры - создаём UNICODE_STRING с именем дефолтного нтфс потока
    DataStream.Length = sizeof(DS_ORIGINAL_STREAM) - 2;
    DataStream.MaximumLength = DataStream.Length;
    DataStream.Buffer = (PWSTR)DS_ORIGINAL_STREAM;

    /* Макросом InitializeObjectAttributes заполняем структуру OBJECT_ATTRIBUTES вписываем туда UNICODE_STRING заполненный выше
        и в параметр OBJECT_ATTRIBUTES->RootDirectory забиваем хендл на уже открытый нами сам файл нашего экзешника
        таким образом можем работать с его нтфс стримами указывая их имена ( и тип ) через двоеточие в OBJECT_ATTRIBUTES->ObjectName
    */
    InitializeObjectAttributes(
        &stream,
        &DataStream,
        OBJ_CASE_INSENSITIVE,
        hCurrent, // вот тут хендл нашего экзешника
        NULL
    );
    HANDLE hDataStream;

    DS_DEBUG_LOG(L"attempting to open :$DATA stream for renaming");

    status = NtCreateFile(&hDataStream, DELETE | SYNCHRONIZE, &stream, &iosb, NULL,FILE_ATTRIBUTE_NORMAL,FILE_SHARE_DELETE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT,0, NULL);
    /* Открываем теперь сам нтфс стрим с такими же флагами как в оригинальном поке
    ( FILE_OPEN, DELETE ).
    SYNCHRONIZE и FILE_SYNCHRONOUS_IO_ALERT нужны чтобы избежать асинхронных операций с хендлом стрима
    */
    if (status != 0) {
        DS_DEBUG_LOG(L"failed to open $DATA stream");
                CloseHandle(hCurrent);
        return 0;
    }
    // rename the associated HANDLE's file name
    DS_DEBUG_LOG(L"attempting to rename file stream");

    void* pfRename = ds_rename_handle(hDataStream);
    if (pfRename == NULL)
    {
        DS_DEBUG_LOG(L"failed to rename to stream");
        CloseHandle(hDataStream);
        CloseHandle(hCurrent);
        return 0;
    }
    CloseHandle(hDataStream);

Если сработает можно в гит ему написать тоже про это
 
Последнее редактирование:
Чекни код ошибки какой там через GetLastError()


C++:
if (hCurrent == INVALID_HANDLE_VALUE)
{
    wprintf(L"GetLastError()=%d\r\n",GetLastError());
    DS_DEBUG_LOG(L"failed to reopen current module");
    return 0;
}

Нашёл корень зла своей проблемы. Выполнил такие команды:

Снимок.JPG


И мне подумалось что проблема в сборке винды может быть. Запустил эти же команды на другой системе. Всё заработало. Запустил билд с PoC, тоже всё отработало как надо. Я с таким просто ещё не сталкивался. Сборки вроде не сильно должны отличаться. Тем не менее...

Поразбираюсь ещё с предложенным тобой вариантом.
 
Нашёл корень зла своей проблемы. Выполнил такие команды:

Посмотреть вложение 107240

И мне подумалось что проблема в сборке винды может быть. Запустил эти же команды на другой системе. Всё заработало. Запустил билд с PoC, тоже всё отработало как надо. Я с таким просто ещё не сталкивался. Сборки вроде не сильно должны отличаться. Тем не менее...

Поразбираюсь ещё с предложенным тобой вариантом.
Интерестно сработает ли на сборке способ с NtCreateFile/NtOpenFile и открытием потока напрямую
А какая сборка?
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Сборки вроде не сильно должны отличаться. Тем не менее...
Не должны, разве что какая-то совсем багованная винда (файловые фильтры например стоят), но это угадывание все.
 
Месяц или два назад тестил данный метод на виртуалке с windows 11, скачанная с сайта microsoft, не срабатывал и выкидывал ошибку ERROR_ACCESS_DENIED, подобное поведение программы было не только у меня.
На windows 10 всё отрабатывало как надо.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
не срабатывал
Может, пофиксили уже, все же это какой-то баг или андок.
 
Месяц или два назад тестил данный метод на виртуалке с windows 11, скачанная с сайта microsoft, не срабатывал и выкидывал ошибку ERROR_ACCESS_DENIED, подобное поведение программы было не только у меня.
На windows 10 всё отрабатывало как надо.
24h2 файл не удаляется но обнуляется до 0 байт
 


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