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

Обучаюсь плюсам

ah_for_no_one

CD-диск
Пользователь
Регистрация
18.08.2023
Сообщения
17
Реакции
1
Буду публиковать сюда свой код на c++ пока учусь писать. Раньше писал на асме, но сейчас понимаю что это такое себе, хочу перейти на c++. Переодически буду публиковать тут код на c++. Прошу тех кто шарит в плюсах помочь мне.
Сейчас пытаюсь понять как правильно писать шеллкоды на c++.
Вот моя первая попытка оформить всё в виде шеллкода
C++:
#include <windows.h>
#include <memory>
#include "NT_STRUCTS.h"
#include <stdalign.h>




// constants
#define PAGE_MINIMAL_SIZE 0x1000
#define HEAP_INITIAL_SIZE PAGE_MINIMAL_SIZE*2
#define MAX_HEAP_SIZE PAGE_MINIMAL_SIZE*64

using namespace std;

#define alloc(Type, PtrName) \
    std::unique_ptr<Type> PtrName = std::make_unique<Type>(sizeof(Type));
    



typedef HANDLE(*_HeapCreate)(DWORD flOptions, size_t dwInitialSize, size_t dwMaximumSize);
typedef void* (*_HeapAlloc)(HANDLE hHeap, DWORD dwFlags, size_t dwBytes);
typedef int (*_HeapFree)(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem);
typedef int (*_MessageBoxA)(HWND   hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT   uType);
typedef HMODULE (*_LoadLibraryW)(LPCWSTR lpLibFileName);


void* memset(void* s, int c, size_t sz)
{
    byte* p = (byte*)s;

    while (sz--) {
        *p++ = (byte)c;
    }
    return s;
}

void* memcpy(void* dst, void* src, size_t sz)
{
    byte* dstbuf = (byte*)dst;
    byte* srcbuf = (byte*)src;

    while (sz--) {
        dstbuf[sz] = srcbuf[sz];
    }
    return 0;
}

_TIMAGE_FILE_HEADER* GetPEHeader(HMODULE module)
{
    _IMAGE_DOS_HEADER* IMAGE_DOS_HEADER = (_IMAGE_DOS_HEADER*)module;
    DWORD e_lfanew = IMAGE_DOS_HEADER->e_lfanew;
    return (_TIMAGE_FILE_HEADER*)((UINT64)IMAGE_DOS_HEADER + (UINT64)e_lfanew);
};


_TIMAGE_OPTIONAL_HEADER* GetOptionalHeader(HMODULE module)
{
    return (_TIMAGE_OPTIONAL_HEADER*)((UINT64)GetPEHeader(module) + (UINT64)sizeof(_TIMAGE_FILE_HEADER));
};



struct _IMPORT
{
    _HeapCreate HeapCreate;
    _HeapAlloc HeapAlloc;
    _HeapFree HeapFree;
    _MessageBoxA MessageBoxA;
};
struct _GLOBAL_DATA
{
    HANDLE hHeap;
    _IMPORT import;
};


HMODULE GetKernel32()
{
    HMODULE module;
    __asm
    {
        xor rax, rax
        mov rax, gs: [rax + 0x60]
        mov rax, [rax + 0x18]
        mov rax, [rax + 0x20]
        mov rax, [rax]
        mov rax, [rax]
        mov rax, [rax + 0x20]
        mov[module], rax
    }
    return module;
}

HMODULE GetNtdll()
{
    HMODULE module;
    __asm
    {
        xor rax, rax
        mov rax, gs: [rax + 0x60]
        mov rax, [rax + 0x18]
        mov rax, [rax + 0x20]
        mov rax, [rax]
        mov rax, [rax + 0x20]
        mov[module], rax
    }
    return module;
}

bool strcmpb(LPCSTR str1, LPCSTR str2)
{
    int i = 0;
    while (true)
    {
        if (str1[i] == str2[i])
        {
            if (str2[i] == 0)
            {
                return true;
            }
            i++;
        }
        else
        {
            return false;
        }
    }
}

FARPROC EGetProcAddress(HMODULE module, LPCSTR procname)
{
    UINT64 ProcAddress = 0;

    _IMAGE_DOS_HEADER* IMAGE_DOS_HEADER = (_IMAGE_DOS_HEADER*)module;
    DWORD e_lfanew = IMAGE_DOS_HEADER->e_lfanew;
    _TIMAGE_FILE_HEADER* IMAGE_FILE_HEADER = (_TIMAGE_FILE_HEADER*)((UINT64)IMAGE_DOS_HEADER + (UINT64)e_lfanew);
    _TIMAGE_OPTIONAL_HEADER* IMAGE_OPTIONAL_HEADER = (_TIMAGE_OPTIONAL_HEADER*)((UINT64)IMAGE_FILE_HEADER + (UINT64)sizeof(_TIMAGE_FILE_HEADER));
    // broken _IMAGE_OPTIONAL_HEADER redefine this shit
    _IMAGE_EXPORT_DIRECTORY* IMAGE_EXPORT_DIRECTORY = (_IMAGE_EXPORT_DIRECTORY*)((UINT64)IMAGE_OPTIONAL_HEADER->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + (UINT64)module);
    DWORD* AddressOfNames = (DWORD*)((UINT64)IMAGE_EXPORT_DIRECTORY->AddressOfNames + (UINT64)module);


    for (UINT32 i = 0; i <= IMAGE_EXPORT_DIRECTORY->NumberOfNames; i++)
    {
        LPCSTR Name = (LPCSTR)((UINT64)AddressOfNames[i] + (UINT64)module);
        if (strcmpb((char*)procname, (char*)Name))
        {
            UINT16* Ordinals = (UINT16*)((UINT64)IMAGE_EXPORT_DIRECTORY->AddressOfNameOrdinals + (UINT64)module);
            UINT32* AddressOfFunctions = (UINT32*)((UINT64)IMAGE_EXPORT_DIRECTORY->AddressOfFunctions + (UINT64)module);
            ProcAddress = AddressOfFunctions[Ordinals[i]] + (UINT64)module;
        }
    }


    return (FARPROC)ProcAddress;
}



void StartUp(_GLOBAL_DATA* maintable)
{
    HMODULE kernel32 = GetKernel32();
    HMODULE ntdll = GetNtdll();

    maintable->import.HeapCreate = (_HeapCreate)EGetProcAddress(kernel32, "HeapCreate");
    maintable->hHeap = maintable->import.HeapCreate(0, HEAP_INITIAL_SIZE, 0);
    maintable->import.HeapAlloc = (_HeapAlloc)EGetProcAddress(ntdll, "RtlAllocateHeap");
    maintable->import.HeapFree = (_HeapFree)EGetProcAddress(kernel32, "HeapFree");
    _LoadLibraryW LoadLibraryW = (_LoadLibraryW)EGetProcAddress(kernel32, "LoadLibraryW");

    HMODULE user32 = LoadLibraryW(L"user32.dll");
    maintable->import.MessageBoxA = (_MessageBoxA)EGetProcAddress(user32, "MessageBoxA");


}






class MyClass
{
public:
    int someval;
    LPCSTR* teststr;
};



void* operator new(size_t size, _GLOBAL_DATA* maintable)
{
    if (size)
    {
        return maintable->import.HeapAlloc(maintable->hHeap, 0, size);
    }
    return 0;
}


void operator delete(void* p, _GLOBAL_DATA* maintable)
{
    if (p)
    {
        return (void)maintable->import.HeapFree(maintable->hHeap, 0, p);
    }
    return (void)0;
}



int main()
{
    _GLOBAL_DATA maintable;
    StartUp(&maintable);
    
    MyClass* obj = new(&maintable) MyClass;
    obj->someval = 100;
    obj->teststr = new(&maintable) LPCSTR("hello");

    maintable.import.MessageBoxA(0, *obj->teststr, *obj->teststr, 0);

    return 0;
}
1692473276555.png

Проблемы которые я вижу:
1. Строки хранятся в секции данных
2. Нельзя использовать unique_ptr, т.к. нельзя вызвать delete с аргументом, а раз нельзя вызвать с аргументом нужно создавать глобальную переменную которая будет хранить hHeap и HeapFree, то есть как минимум должна быть структурка которая хранит функции для особождения/выделения памяти и она должна быть глобальной - то есть не на стэке.
Насколько я знаю строки можно как-то перенести на стэк, да и вообще в будущем всё буду вызывать по хэшам, меня больше интересует второй вопрос - как организовать нормальное выделение памяти в шеллкоде, можно конечно сделать структуру на стэке, написать функции alloc/unalloc но вызывать их вручную как по мне такое себе, так что надо делать через unique_ptr
 
Сделал GetProcAddress по хэшам

C++:
#include <windows.h>
#include "NT_STRUCTS.h"





// constants
#define PAGE_MINIMAL_SIZE 0x1000
#define HEAP_INITIAL_SIZE PAGE_MINIMAL_SIZE*2
#define MAX_HEAP_SIZE PAGE_MINIMAL_SIZE*64

using namespace std;

#define alloc(Type, PtrName) \
    std::unique_ptr<Type> PtrName = std::make_unique<Type>(sizeof(Type));
    



typedef HANDLE(*_HeapCreate)(DWORD flOptions, size_t dwInitialSize, size_t dwMaximumSize);
typedef void* (*_HeapAlloc)(HANDLE hHeap, DWORD dwFlags, size_t dwBytes);
typedef int (*_HeapFree)(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem);
typedef int (*_MessageBoxA)(HWND   hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT   uType);
typedef HMODULE (*_LoadLibraryW)(LPCWSTR lpLibFileName);


void* memset(void* s, int c, size_t sz)
{
    byte* p = (byte*)s;

    while (sz--) {
        *p++ = (byte)c;
    }
    return s;
}

void* memcpy(void* dst, void* src, size_t sz)
{
    byte* dstbuf = (byte*)dst;
    byte* srcbuf = (byte*)src;

    while (sz--) {
        dstbuf[sz] = srcbuf[sz];
    }
    return 0;
}

_TIMAGE_FILE_HEADER* GetPEHeader(HMODULE module)
{
    _IMAGE_DOS_HEADER* IMAGE_DOS_HEADER = (_IMAGE_DOS_HEADER*)module;
    DWORD e_lfanew = IMAGE_DOS_HEADER->e_lfanew;
    return (_TIMAGE_FILE_HEADER*)((UINT64)IMAGE_DOS_HEADER + (UINT64)e_lfanew);
};


_TIMAGE_OPTIONAL_HEADER* GetOptionalHeader(HMODULE module)
{
    return (_TIMAGE_OPTIONAL_HEADER*)((UINT64)GetPEHeader(module) + (UINT64)sizeof(_TIMAGE_FILE_HEADER));
};

int pow(int val, int power)
{
    for (int i = 0; i < power; i++)
    {
        val *= val;
    }
    return val;
}

struct _IMPORT
{
    _HeapCreate HeapCreate;
    _HeapAlloc HeapAlloc;
    _HeapFree HeapFree;
    _MessageBoxA MessageBoxA;
};
struct _GLOBAL_DATA
{
    HANDLE hHeap;
    _IMPORT import;
};

int strlenA(LPCSTR str)
{
    int i = 0;
    while (char ch = str[i] != 0)
    {
        i++;
    }
    return i;
}

int strHashA(LPCSTR str)
{
    int hashval = 0;
    int s = 0;
    int i = 0;
    char ch = str[i];
    while (ch != 0)
    {
        s += ch;
        hashval += ((ch << (0x0F | ch) + (0xF0 | ch) * s)) + (pow(ch, ch^ (0x0F | ch)));
        i++;
        ch = str[i];
    }
    return hashval;
}


HMODULE GetKernel32()
{
    HMODULE module;
    __asm
    {
        xor rax, rax
        mov rax, gs: [rax + 0x60]
        mov rax, [rax + 0x18]
        mov rax, [rax + 0x20]
        mov rax, [rax]
        mov rax, [rax]
        mov rax, [rax + 0x20]
        mov[module], rax
    }
    return module;
}

HMODULE GetNtdll()
{
    HMODULE module;
    __asm
    {
        xor rax, rax
        mov rax, gs: [rax + 0x60]
        mov rax, [rax + 0x18]
        mov rax, [rax + 0x20]
        mov rax, [rax]
        mov rax, [rax + 0x20]
        mov[module], rax
    }
    return module;
}

bool strcmpb(LPCSTR str1, LPCSTR str2)
{
    int i = 0;
    while (true)
    {
        if (str1[i] == str2[i])
        {
            if (str2[i] == 0)
            {
                return true;
            }
            i++;
        }
        else
        {
            return false;
        }
    }
}

FARPROC EGetProcAddress(HMODULE module, LPCSTR procname)
{
    UINT64 ProcAddress = 0;

    _IMAGE_DOS_HEADER* IMAGE_DOS_HEADER = (_IMAGE_DOS_HEADER*)module;
    DWORD e_lfanew = IMAGE_DOS_HEADER->e_lfanew;
    _TIMAGE_FILE_HEADER* IMAGE_FILE_HEADER = (_TIMAGE_FILE_HEADER*)((UINT64)IMAGE_DOS_HEADER + (UINT64)e_lfanew);
    _TIMAGE_OPTIONAL_HEADER* IMAGE_OPTIONAL_HEADER = (_TIMAGE_OPTIONAL_HEADER*)((UINT64)IMAGE_FILE_HEADER + (UINT64)sizeof(_TIMAGE_FILE_HEADER));
    // broken _IMAGE_OPTIONAL_HEADER redefine this shit
    _IMAGE_EXPORT_DIRECTORY* IMAGE_EXPORT_DIRECTORY = (_IMAGE_EXPORT_DIRECTORY*)((UINT64)IMAGE_OPTIONAL_HEADER->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + (UINT64)module);
    DWORD* AddressOfNames = (DWORD*)((UINT64)IMAGE_EXPORT_DIRECTORY->AddressOfNames + (UINT64)module);


    for (UINT32 i = 0; i <= IMAGE_EXPORT_DIRECTORY->NumberOfNames; i++)
    {
        LPCSTR Name = (LPCSTR)((UINT64)AddressOfNames[i] + (UINT64)module);
        if (strcmpb((char*)procname, (char*)Name))
        {
            UINT16* Ordinals = (UINT16*)((UINT64)IMAGE_EXPORT_DIRECTORY->AddressOfNameOrdinals + (UINT64)module);
            UINT32* AddressOfFunctions = (UINT32*)((UINT64)IMAGE_EXPORT_DIRECTORY->AddressOfFunctions + (UINT64)module);
            ProcAddress = AddressOfFunctions[Ordinals[i]] + (UINT64)module;
        }
    }


    return (FARPROC)ProcAddress;
}

FARPROC HashGetProcAddress(HMODULE module, int hash)
{
    UINT64 ProcAddress = 0;

    _IMAGE_DOS_HEADER* IMAGE_DOS_HEADER = (_IMAGE_DOS_HEADER*)module;
    DWORD e_lfanew = IMAGE_DOS_HEADER->e_lfanew;
    _TIMAGE_FILE_HEADER* IMAGE_FILE_HEADER = (_TIMAGE_FILE_HEADER*)((UINT64)IMAGE_DOS_HEADER + (UINT64)e_lfanew);
    _TIMAGE_OPTIONAL_HEADER* IMAGE_OPTIONAL_HEADER = (_TIMAGE_OPTIONAL_HEADER*)((UINT64)IMAGE_FILE_HEADER + (UINT64)sizeof(_TIMAGE_FILE_HEADER));
    // broken _IMAGE_OPTIONAL_HEADER redefine this shit
    _IMAGE_EXPORT_DIRECTORY* IMAGE_EXPORT_DIRECTORY = (_IMAGE_EXPORT_DIRECTORY*)((UINT64)IMAGE_OPTIONAL_HEADER->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + (UINT64)module);
    DWORD* AddressOfNames = (DWORD*)((UINT64)IMAGE_EXPORT_DIRECTORY->AddressOfNames + (UINT64)module);


    for (UINT32 i = 0; i <= IMAGE_EXPORT_DIRECTORY->NumberOfNames; i++)
    {
        LPCSTR Name = (LPCSTR)((UINT64)AddressOfNames[i] + (UINT64)module);
        if (hash == strHashA(Name))
        {
            UINT16* Ordinals = (UINT16*)((UINT64)IMAGE_EXPORT_DIRECTORY->AddressOfNameOrdinals + (UINT64)module);
            UINT32* AddressOfFunctions = (UINT32*)((UINT64)IMAGE_EXPORT_DIRECTORY->AddressOfFunctions + (UINT64)module);
            ProcAddress = AddressOfFunctions[Ordinals[i]] + (UINT64)module;
        }
    }


    return (FARPROC)ProcAddress;
}


LPSTR encodestringA(LPSTR str)
{
    int i = 0;
    char ch = str[i];
    while (ch != 0)
    {
        str[i] = ((ch << (0x0F^ch))*ch);
        if (i)
        {
            str[i] = ((ch << (0x0F ^ ch)) * ch);
        }
        i++;
        ch = str[i];
    }
    return str;
}

void StartUp(_GLOBAL_DATA* maintable)
{
    HMODULE kernel32 = GetKernel32();
    HMODULE ntdll = GetNtdll();

    maintable->import.HeapCreate = (_HeapCreate)HashGetProcAddress(kernel32, strHashA("HeapCreate"));
    maintable->hHeap = maintable->import.HeapCreate(0, HEAP_INITIAL_SIZE, 0);
    maintable->import.HeapAlloc = (_HeapAlloc)HashGetProcAddress(ntdll, strHashA("RtlAllocateHeap"));
    maintable->import.HeapFree = (_HeapFree)HashGetProcAddress(kernel32, strHashA("HeapFree"));
    _LoadLibraryW LoadLibraryW = (_LoadLibraryW)HashGetProcAddress(kernel32, strHashA("LoadLibraryW"));

    HMODULE user32 = LoadLibraryW(L"user32.dll");

    maintable->import.MessageBoxA = (_MessageBoxA)HashGetProcAddress(user32, strHashA("MessageBoxA"));


}






class MyClass
{
public:
    int someval;
    LPCSTR* teststr;
};



void* operator new(size_t size, _GLOBAL_DATA* maintable)
{
    if (size)
    {
        return maintable->import.HeapAlloc(maintable->hHeap, 0, size);
    }
    return 0;
}


void operator delete(void* p, _GLOBAL_DATA* maintable)
{
    if (p)
    {
        return (void)maintable->import.HeapFree(maintable->hHeap, 0, p);
    }
    return (void)0;
}


_GLOBAL_DATA maintable;
int main()
{
    StartUp(&maintable);
    
    MyClass* obj = new(&maintable) MyClass;
    obj->someval = 100;
    obj->teststr = new(&maintable) LPCSTR("hello");

    volatile char test[] = { 'h', 'e', 'l','l','o', 0 };

    maintable.import.MessageBoxA(0, *obj->teststr, *obj->teststr, 0);

    return 0;
}

Никак не могу сделать так чтобы строки хранились на стэке, volatile не помогает и запись такого вида `char test[] = { 'h', 'e', 'l','l','o', 0 }`
1692549183056.png

Как вы можете видеть "hello" всё равно хранится в секции .rdata компилятор: llvm
 
Раз уж мы пишем на си/плюсах, то советую не стрелять будущему себе в ногу и не использовать структуры по типу _IMPORT - каждый раз лезть дописывать хэши/типы очень не очень, лучше хранить адреса функций в одной таблице(например). Желательно конечно все это добро запихнуть в какой-нибудь класс, чтобы при необходимости иметь несколько независимых экземпляров, но это уже на твое усмотрение. По поводу паст из typedef-ов с типами функций - аналогично, писал тут.

По хэшам - делаем consteval функцию, которая нам все во время компиляции посчитает и оставит вместо строк хэши
C++:
consteval uint MyConstexprHash(const char* str) {
    uint hash{0};
    // считаем хэш
    return hash;
}

/*один раз написал, больше об этом не думаешь*/
#define API(DLL, FUNC) static_cast<function_type<FUNC>>(importTable.GetFunctionAddress(DLL, MyConstexprHash(# FUNC)));

API(USER32, MessageBoxA)(0, "hello", "wrld", 0);
Да, если у тебя реализовано в классе, то придется завести глобальную переменную, но это не проблема - кидаешь экземпляр в какой-нибудь namespace типа import_table_details и все. А, ну и проблемы с stl исчерпаны, переопределяешь new/delete нормально и не паришься
 
Последнее редактирование:
Пожалуйста, обратите внимание, что пользователь заблокирован
C++:
HMODULE GetKernel32()
{
    HMODULE module;
    __asm
    {
        xor rax, rax
        mov rax, gs: [rax + 0x60]
        mov rax, [rax + 0x18]
        mov rax, [rax + 0x20]
        mov rax, [rax]
        mov rax, [rax]
        mov rax, [rax + 0x20]
        mov[module], rax
    }
    return module;
}

HMODULE GetNtdll()
{
    HMODULE module;
    __asm
    {
        xor rax, rax
        mov rax, gs: [rax + 0x60]
        mov rax, [rax + 0x18]
        mov rax, [rax + 0x20]
        mov rax, [rax]
        mov rax, [rax + 0x20]
        mov[module], rax
    }
    return module;
}

Тебе не нужно писать код на асме, чтобы работать с пебом. Используй вместо этого __readfsdword и __readgsqword и работай напрямую со структурами, а не со смещениями. Получишь возможность писать универсальный код и собирать шк под нужную архитектуру без нужды в том чтобы переписывать что то.
1. Строки хранятся в секции данных
В сиплюсах нужно выносить строки на стек посимвольно, в коде это реализуется вот так:
char Str[] = { 's', 't', 'r', 0 };
Иначе тебе прийдется формировать из своего шеллкода масм листинг (флаг /FA) и модифицировать его, обьявляя необходимые строки в конце кода, чтобы адресовать их по смещению. Я видел на гитхабе проект, который автоматически обрабатывает такой код, готовые решения точно есть, можешь поискать

2. Нельзя использовать unique_ptr, т.к. нельзя вызвать delete с аргументом, а раз нельзя вызвать с аргументом нужно создавать глобальную переменную которая будет хранить hHeap и HeapFree, то есть как минимум должна быть структурка которая хранит функции для особождения/выделения памяти и она должна быть глобальной - то есть не на стэке.
Насколько я знаю строки можно как-то перенести на стэк, да и вообще в будущем всё буду вызывать по хэшам, меня больше интересует второй вопрос - как организовать нормальное выделение памяти в шеллкоде, можно конечно сделать структуру на стэке, написать функции alloc/unalloc но вызывать их вручную как по мне такое себе, так что надо делать через unique_ptr
Ну если тебя не очень беспокоит скорость, ты можешь в рантайме каждый раз находить адреса нужных функций и вызывать их. По производительности это особо не ударит и будет работать. Конечно не очень красивое решение, но что имеем то имеем

И напоследок... нет смысла переопределять структуры для работы с PE файлом, всё это давным давно уже есть в Windows.h
 
Последнее редактирование:
Шел тем же путем, с асма на плюсы.
Подскажу о чем ты будшь горько сожалеть - доступ к атрибутам только через геттеры и сеттеры, работа со строками только черз класс, то же самое можно сказать и про работу с файлами и про синхронизацию и вообще про любые подобные механизмы.
Еще ты будешь горько сожалеть что начал с быка за рога а не с того что бы вникнуть SOLID, GRASP, шаблоны, SFINAE, а затем тренить с паттернами GOF.
Кому то обязательно надо набить шишек прежде чем пойти учится системно, кто то про все это даже и не слышал.
Плюсы это такой язык который корый при неверном подходе может тебя годами окунать в говно, и тебе все равно придется пойти и системно учить все перечисленное, вопрос только в том - сколько лет тебя плюсы будут говном кормить прежде чем ты не смиришся что учится все же придется и не чисто по ходу дела написания малвари, а с книжками, курсами, с решением тичных задач правельным способом.
Всякие штуки дрюки типа [[nodiscard]] в плюсах не просто так появились.
 
Всем огромное спасибо за ответы! Сделал вызов по хэшам с constval, спасибо за совет с импортом, стало гораздо удобнее
C++:
#include <Windows.h>
#include <winternl.h>

#pragma comment(lib, "ntdll.lib")
#include <typeinfo>
#include "NT_STRUCTS.h"


NTSYSAPI PVOID RtlAllocateHeap(
    [in]           PVOID  HeapHandle,
    [in, optional] ULONG  Flags,
    [in]           SIZE_T Size
);


// constants
#define PAGE_MINIMAL_SIZE 0x1000
#define HEAP_INITIAL_SIZE PAGE_MINIMAL_SIZE*2
#define MAX_HEAP_SIZE PAGE_MINIMAL_SIZE*64


#define HASH_IMPORT(DLL, FUNC) \
    using FUNC##_type = decltype(&FUNC); \
    maintable->import.FUNC = reinterpret_cast<FUNC##_type>(HashGetProcAddress(DLL, conststrHashA(# FUNC)));

#define FFIELD(FUNC) \
    using FUNC##_type = decltype(&FUNC); FUNC##_type FUNC;


#define x64





void* memset(void* s, int c, size_t sz)
{
    byte* p = (byte*)s;

    while (sz--) {
        *p++ = (byte)c;
    }
    return s;
}

void* memcpy(void* dst, void* src, size_t sz)
{
    byte* dstbuf = (byte*)dst;
    byte* srcbuf = (byte*)src;

    while (sz--) {
        dstbuf[sz] = srcbuf[sz];
    }
    return 0;
}

PPEB GetPeb()
{
    PPEB peb;
#ifdef x64
    __asm
    {
        xor rax, rax
        mov rax, gs: [rax + 0x60]
        mov peb, rax
    }
    return peb;
#endif
#ifdef x32
        __asm
    {
        xor rax, rax
        mov rax, fs: [rax + 0x30]
        mov peb, rax
    }
    return peb
#endif
}

_TIMAGE_FILE_HEADER* GetPEHeader(HMODULE module)
{
    _IMAGE_DOS_HEADER* IMAGE_DOS_HEADER = (_IMAGE_DOS_HEADER*)module;
    DWORD e_lfanew = IMAGE_DOS_HEADER->e_lfanew;
    return (_TIMAGE_FILE_HEADER*)((UINT64)IMAGE_DOS_HEADER + (UINT64)e_lfanew);
};


_TIMAGE_OPTIONAL_HEADER* GetOptionalHeader(HMODULE module)
{
    return (_TIMAGE_OPTIONAL_HEADER*)((UINT64)GetPEHeader(module) + (UINT64)sizeof(_TIMAGE_FILE_HEADER));
};


struct _IMPORT
{
    FFIELD(HeapCreate);
    FFIELD(HeapAlloc);
    FFIELD(HeapFree);
    FFIELD(MessageBoxA);
    FFIELD(LoadLibraryW);
    FFIELD(RtlAllocateHeap);
};
struct _GLOBAL_DATA
{
    HANDLE hHeap;
    _IMPORT import;
};

int strlenA(LPCSTR str)
{
    int i = 0;
    while (char ch = str[i] != 0)
    {
        i++;
    }
    return i;
}

consteval int conststrlenA(LPCSTR str)
{
    int i = 0;
    while (char ch = str[i] != 0)
    {
        i++;
    }
    return i;
}

UINT strHashA(LPCSTR str)
{
    UINT hashval = 0;
    int s = 0;
    char ch;
    for (int i = 0; i < strlenA(str); i++)
    {
        ch = str[i];
        s += ch;
        int j = s;
        for (int n = 0; n < i; n++)
        {
            j ^= ((str[i - n] << (n % 16)) + (ch * n) * (ch | 0x0F)) | ((str[i - n] ^ s) << (18 - (n % 18)));
        }
        hashval ^= j;
    }

    return hashval;
}

consteval UINT conststrHashA(const char* str)
{
    UINT hashval = 0;
    int s = 0;
    char ch;
    for (int i = 0; i < conststrlenA(str); i++)
    {
        ch = str[i];
        s += ch;
        int j = s;
        for (int n = 0; n < i; n++)
        {
            j ^= ((str[i - n] << (n % 16)) + (ch*n) * (ch|0x0F)) | ((str[i - n]^s) << (18-(n%18)));
        }
        hashval ^= j;
    }

    return hashval;
}

HMODULE GetKernel32()
{
    PPEB peb = GetPeb();
    _TLDR_DATA_TABLE_ENTRY* entry = (_TLDR_DATA_TABLE_ENTRY*)peb->Ldr->InMemoryOrderModuleList.Flink;
    entry = (_TLDR_DATA_TABLE_ENTRY*)entry->InLoadOrderLinks.Flink->Flink;
    return (HMODULE)entry->ImageBase;
}

HMODULE GetNtdll()
{
    PPEB peb = GetPeb();
    _TLDR_DATA_TABLE_ENTRY* entry = (_TLDR_DATA_TABLE_ENTRY*)peb->Ldr->InMemoryOrderModuleList.Flink;
    entry = (_TLDR_DATA_TABLE_ENTRY*) entry->InLoadOrderLinks.Flink;
    return (HMODULE)entry->ImageBase;
}

bool strcmpb(LPCSTR str1, LPCSTR str2)
{
    int i = 0;
    while (true)
    {
        if (str1[i] == str2[i])
        {
            if (str2[i] == 0)
            {
                return true;
            }
            i++;
        }
        else
        {
            return false;
        }
    }
}

FARPROC EGetProcAddress(HMODULE module, LPCSTR procname)
{
    UINT64 ProcAddress = 0;

    _IMAGE_DOS_HEADER* IMAGE_DOS_HEADER = (_IMAGE_DOS_HEADER*)module;
    DWORD e_lfanew = IMAGE_DOS_HEADER->e_lfanew;
    _TIMAGE_FILE_HEADER* IMAGE_FILE_HEADER = (_TIMAGE_FILE_HEADER*)((UINT64)IMAGE_DOS_HEADER + (UINT64)e_lfanew);
    _TIMAGE_OPTIONAL_HEADER* IMAGE_OPTIONAL_HEADER = (_TIMAGE_OPTIONAL_HEADER*)((UINT64)IMAGE_FILE_HEADER + (UINT64)sizeof(_TIMAGE_FILE_HEADER));
    // broken _IMAGE_OPTIONAL_HEADER redefine this shit
    _IMAGE_EXPORT_DIRECTORY* IMAGE_EXPORT_DIRECTORY = (_IMAGE_EXPORT_DIRECTORY*)((UINT64)IMAGE_OPTIONAL_HEADER->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + (UINT64)module);
    DWORD* AddressOfNames = (DWORD*)((UINT64)IMAGE_EXPORT_DIRECTORY->AddressOfNames + (UINT64)module);


    for (UINT32 i = 0; i <= IMAGE_EXPORT_DIRECTORY->NumberOfNames; i++)
    {
        LPCSTR Name = (LPCSTR)((UINT64)AddressOfNames[i] + (UINT64)module);
        if (strcmpb((char*)procname, (char*)Name))
        {
            UINT16* Ordinals = (UINT16*)((UINT64)IMAGE_EXPORT_DIRECTORY->AddressOfNameOrdinals + (UINT64)module);
            UINT32* AddressOfFunctions = (UINT32*)((UINT64)IMAGE_EXPORT_DIRECTORY->AddressOfFunctions + (UINT64)module);
            ProcAddress = AddressOfFunctions[Ordinals[i]] + (UINT64)module;
        }
    }


    return (FARPROC)ProcAddress;
}

FARPROC HashGetProcAddress(HMODULE module, UINT hash)
{
    UINT64 ProcAddress = 0;

    _IMAGE_DOS_HEADER* IMAGE_DOS_HEADER = (_IMAGE_DOS_HEADER*)module;
    DWORD e_lfanew = IMAGE_DOS_HEADER->e_lfanew;
    _TIMAGE_FILE_HEADER* IMAGE_FILE_HEADER = (_TIMAGE_FILE_HEADER*)((UINT64)IMAGE_DOS_HEADER + (UINT64)e_lfanew);
    _TIMAGE_OPTIONAL_HEADER* IMAGE_OPTIONAL_HEADER = (_TIMAGE_OPTIONAL_HEADER*)((UINT64)IMAGE_FILE_HEADER + (UINT64)sizeof(_TIMAGE_FILE_HEADER));
    // broken _IMAGE_OPTIONAL_HEADER redefine this shit
    _IMAGE_EXPORT_DIRECTORY* IMAGE_EXPORT_DIRECTORY = (_IMAGE_EXPORT_DIRECTORY*)((UINT64)IMAGE_OPTIONAL_HEADER->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + (UINT64)module);
    DWORD* AddressOfNames = (DWORD*)((UINT64)IMAGE_EXPORT_DIRECTORY->AddressOfNames + (UINT64)module);


    for (UINT32 i = 0; i <= IMAGE_EXPORT_DIRECTORY->NumberOfNames; i++)
    {
        LPCSTR Name = (LPCSTR)((UINT64)AddressOfNames[i] + (UINT64)module);
        if (hash == strHashA(Name))
        {
            UINT16* Ordinals = (UINT16*)((UINT64)IMAGE_EXPORT_DIRECTORY->AddressOfNameOrdinals + (UINT64)module);
            UINT32* AddressOfFunctions = (UINT32*)((UINT64)IMAGE_EXPORT_DIRECTORY->AddressOfFunctions + (UINT64)module);
            ProcAddress = AddressOfFunctions[Ordinals[i]] + (UINT64)module;
        }
    }


    return (FARPROC)ProcAddress;
}


LPSTR encodestringA(LPSTR str)
{
    int i = 0;
    char ch = str[i];
    while (ch != 0)
    {
        str[i] = ((ch << (0x0F^ch))*ch);
        if (i)
        {
            str[i] = ((ch << (0x0F ^ ch)) * ch);
        }
        i++;
        ch = str[i];
    }
    return str;
}


void StartUp(_GLOBAL_DATA* maintable)
{
    HMODULE kernel32 = GetKernel32();
    HMODULE ntdll = GetNtdll();
    HASH_IMPORT(kernel32, HeapCreate);
    maintable->hHeap = maintable->import.HeapCreate(0, HEAP_INITIAL_SIZE, 0);
    HASH_IMPORT(ntdll, RtlAllocateHeap);
    HASH_IMPORT(kernel32, HeapFree);
    HASH_IMPORT(kernel32, LoadLibraryW);

    HMODULE user32 = maintable->import.LoadLibraryW(L"user32.dll");

    HASH_IMPORT(user32, MessageBoxA);

    
}




void* operator new(size_t size, _GLOBAL_DATA* maintable)
{
    if (size)
    {
        return maintable->import.RtlAllocateHeap(maintable->hHeap, 0, size);
    }
    return 0;
}


void operator delete(void* p, _GLOBAL_DATA* maintable)
{
    if (p)
    {
        return (void)maintable->import.HeapFree(maintable->hHeap, 0, p);
    }
    return (void)0;
}

namespace ns
{
    class Test
    {
    public:
        LPCSTR str1 = "value";
    };
    Test test;
}

_GLOBAL_DATA maintable;
int main()
{
    StartUp(&maintable);

    char test[] = { 'h', 'e', 'l','l','o', 0 };


    maintable.import.MessageBoxA(0, ns::test.str1, test, 0);

    return 0;
}
Переписал GetKernel32 и GetNtdll __readfsdword и __readgsqword и я использовать не стал, потому что насколько я знаю инструкции типа mov rax, fs:[0x60] являются неперемещаемыми должно быть именно так:
C:
;для x64
xor rax,rax
mov rax, fs:[rax+0x60]

;для x32
xor eax,rax
mov eax, gs:[eax+0x30]
Да, если у тебя реализовано в классе, то придется завести глобальную переменную, но это не проблема - кидаешь экземпляр в какой-нибудь namespace типа import_table_details и все
Может я что-то не понял но я создал класс в namespace но он всё равно в секции данных
В сиплюсах нужно выносить строки на стек посимвольно, в коде это реализуется вот так:
char Str[] = { 's', 't', 'r', 0 };
Всё равно строки попадают в секцию данных, даже с таким оформлением
1692637532604.png

Value из namespace'а и выше байты это char test[] = { 'h', 'e', 'l','l','o', 0 };
 
На всякий случай если ты решил идти тем же путем, типа ты умный и научишся в процессе, подкину идей куда стелить соломку.
Логи, они у тебя должны быть настолько охуенными что бы ты в отладчик лез в одном случае из 100, когда у тебя появится морфинг то в дебаге разбирать - а че там случилось и где это что в находится в неморфленном коде будет так себе занятие, особенно если у тебя мультипоток и херня случается в 10% запусков.
Теги, все что ты захватываешь как ресурс снабжай тегами, что бы ты хоть как то мог искать кто бл#ть эту херню захватил и что это вообще за херня.
Стоп эвент, код должен прекращать работу когда этот эвент прилетает и твой менеджер ресурсов должен тебе в логе отмечать что нет объектов которые ты проебал освободить а если есть то их теги.
Крэш дампы, позаботься что бы они были для тебя максимально информативны, в креш дампах морфленного кода искать причины это как то не весело.
Короче в первую очередь продумай как ты будешь искать источники проблем.
Инфраструктуру учись делать, это главное.
 
Может я что-то не понял но я создал класс в namespace но он всё равно в секции данных
Я не совсем это имел ввиду - речь скорее про удобство, а для него можно сделать так(см псевдокод) и FFIELD(FunctionName) вообще не писать. Если ты каждый раз при написании кода будешь ходить и править _IMPORT, ты остатки своего желания кодить проебешь. Все что не прибито стоит автоматизировать, если планируешь использовать это в дальнейшем:
C++:
struct LoadFuncs {
    LoadFuncs() {
        init();
    }
    void init() noexcept {
        /*грузим ntdll и kernel32, инициализируем _LoadLibrary*/
    }
    void clear() noexcept {
        /*освобождаем загруженные dll*/
    }

    HANDLE GetFunctionAddress(int dllNumber, uint32_t hash) noexcept {
        /*смотрим в уже найденных, чтобы не тратить время*/
        for (int i = 0; i < funcCounter; i++) {
            if (hash == funcsHash[i]) return funcs[i];
        }
        /*смотрим в dlls[dllNumber], если nullptr - грузим длл туда чтобы не проебать хэндл, ищем функу по хэшу.
          После нахождения сохраняем в массив и инкрементируем счетчик, усе
        */
        return nullptr;
    }
private:
    size_t funcCounter = 0;
    uint32_t funcsHash[128];
    HANDLE funcs[128];
    HANDLE dlls[16];
    function_type<LoadLibraryW> _LoadLibrary;
};

namespace details {
    LoadFuncs apiLoader;
}

#define API(DLL, FUNC) (reinterpret_cast<function_type<FUNC>>(details::apiLoader.GetFunctionAddress(DLL, MyConstexprHash(# FUNC))))

void* operator new(size_t size)
{
    if (size)
    {
        return API(KERNEL32, HeapAlloc)(API(KERNEL32, GetProcessHeap)(), 0, size);
    }
    return nullptr;
}

void operator delete(void* p)
{
    if (p)
    {
        API(KERNEL32, HeapFree)(API(KERNEL32, GetProcessHeap)(), 0, p);
    }
}
 
Пожалуйста, обратите внимание, что пользователь заблокирован
void* operator new(size_t size) { if (size) { return API(KERNEL32, HeapAlloc)(API(KERNEL32, GetProcessHeap)(), 0, size); } return nullptr; } void operator delete(void* p) { if (p) { API(KERNEL32, HeapFree)(API(KERNEL32, GetProcessHeap)(), 0, p); } }
Лучше так наверное:
C++:
void* operator new(size_t size)
{
    static HANDLE process_heap = API(KERNEL32, GetProcessHeap)();
    if (size)
    {
        return API(KERNEL32, HeapAlloc)(process_heap, 0, size);
    }
    return nullptr;
}

void operator delete(void* p)
{
    static HANDLE process_heap = API(KERNEL32, GetProcessHeap)();
    if (p)
    {
        API(KERNEL32, HeapFree)(process_heap, 0, p);
    }
}
uint32_t funcsHash[128]; HANDLE funcs[128]; HANDLE dlls[16];
Лучше всё-таки их в куче выделить. А то в x64 получается 1664 байта на стэке, что не есть хорошо.
C++:
struct LoadFuncs {
    LoadFuncs()
    {
        funcsHash = new uint32_t[128];
        funcs = new HANDLE[128];
        dlls = new HANDLE[16];
        init();
    }
    void init() noexcept {
        /*грузим ntdll и kernel32, инициализируем _LoadLibrary*/
    }
   // void clear() noexcept {
        /*освобождаем загруженные dll*/
   // }
    
    ~LoadFuncs(){
        // зачем clear, если есть деструктор?
        
         /*освобождаем загруженные dll*/
        
        if(funcsHash)
            delete[] funcsHash;
        if(funcs)
            delete[] funcs;
        if(dlls)
            delete[] dlls;
        // при желании if(ptr) delete[] ptr; можно обернуть в макрос
        // #define SAFE_RELEASE_ARRAY(ptr) if(ptr) delete[] ptr;
        
    }
    // другие конструкторы, если нужны...

    // GetFunctionAddress ...
private:
    size_t funcCounter = 0;
    uint32_t* funcsHash;
    HANDLE* funcs;
    HANDLE* dlls;
    function_type<LoadLibraryW> _LoadLibrary;
};
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Всё равно строки попадают в секцию данных, даже с таким оформлением
Отключи оптимизатор в настройках проекта, перестанет. Или можешь оборачивать функции содержащие такие строки в #pragma optimize("", off), сможешь так отключить оптимизацию для конкретной функции

Пример:

C++:
#pragma optimize("", off)
void DoSomething()
{
    char Str[] = { 's', 't', 'r', 0 };
    MessageBoxA(0, nullptr, nullptr, MB_OK);
}
#pragma optimize("", on)
 
Я не совсем это имел ввиду - речь скорее про удобство, а для него можно сделать так(см псевдокод) и FFIELD(FunctionName) вообще не писать. Если ты каждый раз при написании кода будешь ходить и править _IMPORT, ты остатки своего желания кодить проебешь. Все что не прибито стоит автоматизировать, если планируешь использовать это в дальнейшем:
C++:
struct LoadFuncs {
    LoadFuncs() {
        init();
    }
    void init() noexcept {
        /*грузим ntdll и kernel32, инициализируем _LoadLibrary*/
    }
    void clear() noexcept {
        /*освобождаем загруженные dll*/
    }

    HANDLE GetFunctionAddress(int dllNumber, uint32_t hash) noexcept {
        /*смотрим в уже найденных, чтобы не тратить время*/
        for (int i = 0; i < funcCounter; i++) {
            if (hash == funcsHash[i]) return funcs[i];
        }
        /*смотрим в dlls[dllNumber], если nullptr - грузим длл туда чтобы не проебать хэндл, ищем функу по хэшу.
          После нахождения сохраняем в массив и инкрементируем счетчик, усе
        */
        return nullptr;
    }
private:
    size_t funcCounter = 0;
    uint32_t funcsHash[128];
    HANDLE funcs[128];
    HANDLE dlls[16];
    function_type<LoadLibraryW> _LoadLibrary;
};

namespace details {
    LoadFuncs apiLoader;
}

#define API(DLL, FUNC) (reinterpret_cast<function_type<FUNC>>(details::apiLoader.GetFunctionAddress(DLL, MyConstexprHash(# FUNC))))

void* operator new(size_t size)
{
    if (size)
    {
        return API(KERNEL32, HeapAlloc)(API(KERNEL32, GetProcessHeap)(), 0, size);
    }
    return nullptr;
}

void operator delete(void* p)
{
    if (p)
    {
        API(KERNEL32, HeapFree)(API(KERNEL32, GetProcessHeap)(), 0, p);
    }
}
А для чего использовался struct если потом был private?
Для хеша лучше использовать пользовательский тип, вам будет очень грустно когда через пол года разработки вдруг окажется что uint_32t не катит.
 
Да и еще, советую погуглить c++ style guide, посмотреть требования разных корп и комманд.
Выбираешь стиль и потом жестко его придерживаешся, гугли про тулзу clang format(она не для clang, ее можно спокойно юзать и в студии для мсвц, она будет автоформатить прописанный стиль, кстати она в иде визуал студии встроена, надо включить и настроить).
 
А для чего использовался struct если потом был private?
Чтобы не писать в начале public - обычно объявляю переменные в конце класса. Можно и class оставить, вопрос вкуса

Лучше всё-таки их в куче выделить.
Да, но тогда лучше оболочку вроде вектора набросать, чтобы вручную с памятью не работать. По clear/init - на случай, если захотим переиспользовать чистый экземпляр потом + при написании dll с глобальным объектом компиль автоматом тянул crt при попытке использовать деструкторы. Но в целом тут как и с uint32_t у хэша - набросал псевдокод без особой конкретики
 
Чтобы не писать в начале public - обычно объявляю переменные в конце класса. Можно и class оставить, вопрос вкуса


Да, но тогда лучше оболочку вроде вектора набросать, чтобы вручную с памятью не работать. По clear/init - на случай, если захотим переиспользовать чистый экземпляр потом + при написании dll с глобальным объектом компиль автоматом тянул crt при попытке использовать деструкторы. Но в целом тут как и с uint32_t у хэша - набросал псевдокод без особой конкретики
Думаю очень важно предупреждать про подводные камни, кто уже наелся говна от таких штук как использование билт ин типа тот понимает, но лучше писать предупреждения, здесь же новички.

ТС - Полюсы это огромный объем инфы, разберись с тем как ты будешь вести документацию и как будешь по ней осуществлять навигацию.
 
Кстати вот size_t тоже ведь стремная штука, размазывать ее по своему коду это как бы тоже нарыватся на неприятности.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Так же хорошим тоном будет проверять параметры передаваемые в функции/методы, чтобы не получить сюрприз когда количество кода вырастет

Пример:
C++:
FARPROC EGetProcAddress(HMODULE module, LPCSTR procname)
{
    if (!module || !procname)
    {
        return proc
    }
    
    return proc;
}
 
Так же хорошим тоном будет проверять параметры передаваемые в функции/методы, чтобы не получить сюрприз когда количество кода вырастет

Пример:
C++:
FARPROC EGetProcAddress(HMODULE module, LPCSTR procname)
{
    if (!module || !procname)
    {
        return proc
    }
  
    return proc;
}
А вот поспорю. Это не хорошо а очень даже плохо, даже пипец как плохо.
Смотри проверять мы должны только то что пришло в наш код из вне, например от апи, или от внешних файлов конфигураций, и проверять мы должны не отходя от кассы то есть получили что то и сразу это проверили, что бы не искать - бля а кокого хрена сюда прилетел нуль, и откуда он вообще прилетел. Действуя таким образом мы сразу будем видеть проблему у самого ее истока раз, мы не будем по своему коду размазывать ифы которые проверяют данные полученные от своего же кода, мы доверяем своему коду потому что все пришедшее из вне мы уже проверили, а еще мы хотим что бы ошибки в нашем коде вылазили вот прям сразу иначе у нас борьба с самими собой.
В твоем примере - какого хрена мы попали в эту функцию если где то раньше не смогли получить хендл модуля?, какого как вообще в эту фнуку может попасть нуль для имени процедуры, это явно где то ошибка.
А хорошим тоном будет делать анотации к параметрам что бы отличать входящие от исходящих,
и для строк всегда использовать классы.
 
В твоем примере - какого хрена мы попали в эту функцию если где то раньше не смогли получить хендл модуля?
я бы вообще ассертов понапихал бы да побольше
 
Это потому что ты утка а не котитк.
1692780549349.png

Ну в дебаг режиме нормально же
 


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