Creating "Ransomware" Using WinRT

This isn't ransomware.

This is the blueprint for a ransomware testing payload for a "Purple Team" scenario. I am curious of EDR visibility into WinRT (Universal Windows Platform (UWP) apps) — so I crafted a C++ application, which strictly uses WinRT functionality from WINAPI-like-C++, compiled as a WIN32 app, to see how it looks.

This proof-of-concept is essentially a glorified asychronous file string console printer. What makes it unique is it relying entirely on WinRT from a Win32 app.

WinRT possesses the ability to encrypt files. I opted to not introduce file encryption functionality (although it would be bare-bones, plain password protected) into this proof-of-concept because I think ransomware in general is highly susceptible to abuse even in its most basic forms.

Regardless, I think this code is interesting and I wanted to share it. Maybe it'll inspire someone else to review WinRT more, or someone will pick up this code and experiment with it in an enterprise environment. - smelly smellington

#include <windows.h>
#include <stdio.h>
#include <roapi.h>
#include <windows.storage.h>
#include <windows.storage.search.h>
#include <windows.foundation.h>
#include <windows.system.threading.h>

#pragma comment(lib, "runtimeobject.lib")

using namespace ABI::Windows::Storage;
using namespace ABI::Windows::Storage::Search;
using namespace ABI::Windows::System::Threading;
using namespace ABI::Windows::Foundation;

typedef __FIAsyncOperation_1___FIVectorView_1_Windows__CStorage__CIStorageItem IAsyncOperationOfFiles;
typedef __FIVectorView_1_Windows__CStorage__CIStorageItem IAsyncItems;

class ThreadFileProcessor : public IWorkItemHandler {
public:

    ThreadFileProcessor(IStorageItem* FileObject) : ReferenceCount(1), File(FileObject)
    {
        if (File)
            File->AddRef();
    }

    virtual ~ThreadFileProcessor() 
    {
        if (File)
            File->Release();
    }

    HRESULT __stdcall QueryInterface(REFIID Riid, PVOID* ppv)
    {
        if (!ppv)
            return E_POINTER;

        if (Riid == __uuidof(IUnknown) || Riid == __uuidof(IWorkItemHandler))
        {
            *ppv = (IWorkItemHandler*)this;
            AddRef();
            return S_OK;
        }

        *ppv = NULL;
        return E_NOINTERFACE;
    }

    ULONG __stdcall AddRef() { return InterlockedIncrement(&ReferenceCount); }

    ULONG __stdcall Release()
    {
        ULONG Result = _InterlockedDecrement(&ReferenceCount);
        if (Result == 0)
            delete this;

        return Result;
    }

    HRESULT __stdcall Invoke(IAsyncAction* Action)
    {
        HSTRING FileName = NULL;
        HRESULT Result = S_OK;

        if (!File)
            return E_ABORT;

        Result = File->get_Name(&FileName);
        if (SUCCEEDED(Result))
        {
            UINT32 Length = 0;
            LPCWSTR Buffer = NULL;

            Buffer = WindowsGetStringRawBuffer(FileName, &Length);

            printf("File processing: %ws\r\n", Buffer);
        }

        if (FileName)
            WindowsDeleteString(FileName);

        if (File)
            File->Release();

        return S_OK;
    }

private:
    LONG ReferenceCount;
    IStorageItem* File;
};

SIZE_T StringLengthW(_In_ LPCWSTR String)
{
    LPCWSTR String2;

    for (String2 = String; *String2; ++String2);

    return (String2 - String);
}

INT main(VOID)
{
    HRESULT Result = S_OK;
    
    IKnownFoldersStatics* KnownFolders = NULL;
    IStorageFolder* DocumentsFolder = NULL;
    IInspectable* Inspectable = NULL;
    IQueryOptions* Options = NULL;
    IStorageItemQueryResult* ItemResult = NULL;
    IStorageFolderQueryOperations* Operation = NULL;
    IAsyncOperationOfFiles* AsyncOperation = NULL;
    IAsyncItems* Items = NULL;
    IAsyncInfo* Information = NULL;
    IThreadPoolStatics* ThreadPoolStatics = NULL;
    
    WCHAR StorageString[] = L"Windows.Storage.KnownFolders";
    WCHAR QueryString[] = L"Windows.Storage.Search.QueryOptions";
    WCHAR ThreadString[] = L"Windows.System.Threading.ThreadPool";

    HSTRING KnownFolderClassId = NULL;
    HSTRING QueryOptionsClassId = NULL;
    HSTRING ThreadPoolClassId = NULL;

    UINT32 ObjectCount = 0;

    Result = RoInitialize(RO_INIT_MULTITHREADED);
    if (!SUCCEEDED(Result))
        goto EXIT_ROUTINE;

    Result = WindowsCreateString(StorageString, (UINT32)StringLengthW(StorageString), &KnownFolderClassId);
    if (!SUCCEEDED(Result) || !KnownFolderClassId)
        goto EXIT_ROUTINE;

    Result = RoGetActivationFactory(KnownFolderClassId, IID_IKnownFoldersStatics, (PVOID*)&KnownFolders);
    if (!SUCCEEDED(Result))
        goto EXIT_ROUTINE;

    Result = KnownFolders->get_DocumentsLibrary(&DocumentsFolder);
    if (!SUCCEEDED(Result))
        goto EXIT_ROUTINE;

    Result = WindowsCreateString(QueryString, (UINT32)StringLengthW(QueryString), &QueryOptionsClassId);
    if (!SUCCEEDED(Result) || !QueryOptionsClassId)
        goto EXIT_ROUTINE;

    Result = RoActivateInstance(QueryOptionsClassId, &Inspectable);
    if (!SUCCEEDED(Result))
        goto EXIT_ROUTINE;

    Result = Inspectable->QueryInterface(IID_IQueryOptions, (PVOID*)&Options);
    if (!SUCCEEDED(Result))
        goto EXIT_ROUTINE;

    Result = Options->put_FolderDepth(FolderDepth_Deep);
    if (!SUCCEEDED(Result))
        goto EXIT_ROUTINE;

    Result = DocumentsFolder->QueryInterface(IID_IStorageFolderQueryOperations, (PVOID*)&Operation);
    if (!SUCCEEDED(Result))
        goto EXIT_ROUTINE;

    Result = Operation->CreateItemQueryWithOptions(Options, &ItemResult);
    if (!SUCCEEDED(Result))
        goto EXIT_ROUTINE;

    Result = ItemResult->GetItemsAsyncDefaultStartAndCount(&AsyncOperation);
    if (!SUCCEEDED(Result))
        goto EXIT_ROUTINE;

    Result = AsyncOperation->QueryInterface(IID_IAsyncInfo, (PVOID*)&Information);
    if (!SUCCEEDED(Result))
        goto EXIT_ROUTINE;

    Result = WindowsCreateString(ThreadString, (UINT32)StringLengthW(ThreadString), &ThreadPoolClassId);
    if (!SUCCEEDED(Result) || !ThreadPoolClassId)
        goto EXIT_ROUTINE;

    
    while (TRUE)
    {
        AsyncStatus Status;
        Result = Information->get_Status(&Status);
        if (!SUCCEEDED(Result))
            goto EXIT_ROUTINE;

        if (Status == AsyncStatus::Completed)
            break;
    }

    Result = AsyncOperation->GetResults(&Items);
    if (!SUCCEEDED(Result))
        goto EXIT_ROUTINE;

    Result = Items->get_Size(&ObjectCount);
    if (!SUCCEEDED(Result))
        goto EXIT_ROUTINE;

    Result = RoGetActivationFactory(ThreadPoolClassId, IID_IThreadPoolStatics, (PVOID*)&ThreadPoolStatics);
    if (!SUCCEEDED(Result))
        goto EXIT_ROUTINE;

    for (UINT32 i = 0; i < ObjectCount; i++)
    {
        IStorageItem* File = NULL;
        
        Result = Items->GetAt(i, &File);
        if (SUCCEEDED(Result) && File)
        {
            IAsyncAction* Action = NULL;
            ThreadFileProcessor* FileProcessor = new ThreadFileProcessor(File);

            Result = ThreadPoolStatics->RunAsync(FileProcessor, &Action);
            
            if (FileProcessor)
                FileProcessor->Release();

            if (Action)
                Action->Release();
        }

        if (File)
            File->Release();
    }

    Sleep(1);

EXIT_ROUTINE:

    if (KnownFolderClassId)
        WindowsDeleteString(KnownFolderClassId);

    if (QueryOptionsClassId)
        WindowsDeleteString(QueryOptionsClassId);

    if (ThreadPoolClassId)
        WindowsDeleteString(ThreadPoolClassId);

    if (KnownFolders)
        KnownFolders->Release();

    if (DocumentsFolder)
        DocumentsFolder->Release();

    if (Inspectable)
        Inspectable->Release();

    if (Operation)
        Operation->Release();

    if (ItemResult)
        ItemResult->Release();

    if (AsyncOperation)
        AsyncOperation->Release();

    if (Items)
        Items->Release();

    if (Information)
        Information->Release();

    if (ThreadPoolStatics)
        ThreadPoolStatics->Release();

    RoUninitialize();

    return ERROR_SUCCESS;
}

Last updated