Исходник Shadow_syscall | C++20 Syscalls & Runtime DLL Utils

Залил фикс с мобилы, проверь пожалуйста осталась ли ошибка
пропала,но лично у меня в дебаге
1728150991744.png
.Хз может только у меня так
 
Тоже есть ошибка. Появляется только с мсвц в дебаге. В остальных режиах, и с клангом(оба режима) всё гуд
1728199871108.png
 
MessageBoxA примеру с хаба, вызов проходит,текст вижу далее ошибка выше
Тоже есть ошибка. Появляется только с мсвц в дебаге. В остальных режиах, и с клангом(оба режима) всё гуд
Посмотреть вложение 287321
спасибо за инфу, всё зафиксил, проблема была в самом примере
upd: теперь другая проблема для Debug x86 :NotLikeThis:, сейчас займусь фиксом

зафиксил
 
Последнее редактирование:
Достаточно удобный в использовании враппер сискола.
мой первый (но не последний) гитхаб репо:
Пожалуйста, авторизуйтесь для просмотра ссылки.

в будущем будет обновляться.

буду рад адекватному фидбэку и советам.
добавь поддержку xor для export_name пожалуйста
1728207545541.png
 
добавь поддержку xor для export_name пожалуйста Посмотреть вложение 287324
тебе не нужно ксорить эти строки, эти строки на этапе компиляции со 100% гарантией превращаются в число из-за consteval конструктора hash**_t, у тебя после компиляции эта строка будет чем-то вроде unsigned long long v1 = 438239898534892398

upd: вот как это выглядит:
1728211030822.png

1728210954096.png


более детально:
1728210791153.png

в таком случае строка "This will not be tranformed in compiletime" останется в бинаре после компиляции, потому что оператор () перегружен на хэширование объектов в рантайме, в то время как конструктор hash**_t работает только на этапе компиляции, с "сырыми" строками
 
Последнее редактирование:
выше у чувака была иссуе - он запихнул ксорстринговый ввод в конструктор что строку ожидает.

просто подчеркнуть контракт функции - к тому же, концепты тебе дают способ указать кастомные сообщения об ошибках.
 
к тому же, концепты тебе дают способ указать кастомные сообщения об ошибках.
не видел такого, можешь пожалуйста дать ссылку или пример подобного фьючера? видел пропозал
Пожалуйста, авторизуйтесь для просмотра ссылки.
, но у него с 2018 висит пустой disposition (а ведь предложение было толковым, даже если не в таком виде, то в любом случае это не было бы лишним)
или ты подразумеваешь какой-то workaround со static_assert-ом?
 
Последнее редактирование:
не видел такого, можешь пожалуйста дать ссылку или пример подобного фьючера? видел пропозал
Пожалуйста, авторизуйтесь для просмотра ссылки.
, но у него с 2018 висит пустой disposition (а ведь предложение было толковым, даже если не в таком виде, то в любом случае это не было бы лишним)
или ты подразумеваешь какой-то workaround со static_assert-ом?
я имел ввиду вот такой прикол какой-то:
Пожалуйста, авторизуйтесь для просмотра ссылки.


не совсем элегантно, но вроде бы работает
 
добавил чмаке-листы
добавил тесты

отрефакторил чуток кода:
- добавил новые методы в address_t

Пожалуйста, авторизуйтесь для просмотра ссылки.
 
зафиксил неприятную проблему связанную с кэшом в разных единицах трансляции (ошибка была довольно глупой :D), сорян если кто-то уже успел испытать её на своём коде

подробнее можно почитать
Пожалуйста, авторизуйтесь для просмотра ссылки.
 
Вопрос, что это?
Какую функцию оно выполняет?
привет вся библиотека рассчитана на платформу Windows, она будет полезной если перед тобой стоит задача как-либо распарсить загруженную в твой процесс DLL, их экспорт-таблицы, заголовки и прочую информацию PE файла (через функцию image(), спасибо linuxpe) в коде C++

вот пара примеров из реального кода, который решает реальные проблемы в C++ стиле, которое можно было бы реализовать через C-style WinAPI

C Style:
Expand Collapse Copy
void find_export() {
  static NTSTATUS(__stdcall * NtSetTimerResolution)(ULONG, BOOLEAN, PULONG);
  NtSetTimerResolution = reinterpret_cast<decltype(NtSetTimerResolution)>(
      GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtSetTimerResolution"));
}
С++ Style:
Expand Collapse Copy
void find_export() {
  static NTSTATUS(__stdcall * NtSetTimerResolution)(ULONG, BOOLEAN, PULONG);
  NtSetTimerResolution = shadow::dll_export("NtSetTimerResolution", "ntdll.dll")
                              .address()
                              .as<decltype(NtSetTimerResolution)>();
}


Old RunPE Windows 11 problem solving:
Expand Collapse Copy
const auto system = shadow::shared_data().system();
// 22631 || 26100
if ( system.build_number() >= 26100 && system.major_version() == 10 ) {
    // https://www.unknowncheats.me/forum/general-programming-and-reversing/667623-win11-24h2-hollowing-process-error-2.html
    const auto ntdll = shadow::dll( "ntdll.dll" );
    const auto patch_address = ntdll.base_address().offset( 0x7BE0 );
    const auto second_patch_address = ntdll.base_address().offset( 0x161A40 );
    const auto thrid_patch_address = ntdll.base_address().offset( 0x5610 );
    const auto fourth_patch_address = shadow::dll_export( "ZwManageHotPatch" ).address();

    const uint8_t patch[4] = { 0x48, 0x31, 0xC0, 0xC3 };

    shadowcall( "WriteProcessMemory", child_process, patch_address.ptr(), patch, 4, 0 );
    shadowcall( "WriteProcessMemory", child_process, second_patch_address.ptr(), patch, 4, 0 );
    shadowcall( "WriteProcessMemory", child_process, thrid_patch_address.ptr(), patch, 4, 0 );
    shadowcall( "WriteProcessMemory", child_process, fourth_patch_address.ptr(), patch, 4, 0 );
}


так же для кого-то будет плюсом то что каждая строка которую ты отдаёшь этой библиотеке (к примеру "NtSetTimerResolution", "ntdll.dll") перестанут быть строками после компиляции, что сделает невозможным статический анализ в целях узнать какой экспорт всё таки ищет функция dll_export

весь IDA 7.5 Pseudo ниже представлен в билде (Debug, loaded with PDB) для упрощения понимания:

C++:
Expand Collapse Copy
int (__fastcall *find_export())(unsigned int, unsigned __int8, unsigned int *)
{
  int (__fastcall *v0)(unsigned int, unsigned __int8, unsigned int *); // rax
  shadow::detail::dll_export result; // [rsp+38h] [rbp-20h] BYREF

  shadow::dll_export(
    &result,
    (shadow::detail::basic_hash<unsigned long long>)0xDF704B8A8D360572ui64,
    (shadow::detail::basic_hash<unsigned long long>)0x98C947DC3D0ED9ACui64);
  shadow::detail::dll_export::address(&result.m_address);
  v0 = shadow::address_t::as<long (*)(unsigned long,unsigned char,unsigned long *)>();
  NtSetTimerResolution = v0;
  return v0;
}

функционал в честь которого названа библиотека (хотя её функционал уже давно расширился, имя репозитория уже явно устарело) позволяет вызывать в коде Nt экспорты через syscallы, что это такое здесь пытаться объяснять не буду, слишком много текста, если хочешь то можешь почитать в интернете "how windows syscalls works".

C++:
Expand Collapse Copy
shadowsyscall<NTSTATUS>( "NtTerminateProcess", reinterpret_cast< HANDLE >( -1 ), -6932 );

и в таком же стиле библиотека позволяет динамически (в рантайме) искать функцию и исполнять её:

C++:
Expand Collapse Copy
shadowcall<int>( "MessageBoxA", nullptr, "string 1", "string 2", MB_OK );

опять же, есть плюс против статического анализа:

Code:
Expand Collapse Copy
void call_dynamically_resolved_function() {
  shadowcall<int>("MessageBoxA", nullptr, "string 1", "string 2", MB_OK);
}

void call_statically_resolved_function() {
  MessageBoxA(nullptr, "string 1", "string 2", MB_OK);
}

int main() {
  call_dynamically_resolved_function();
  call_statically_resolved_function();

  return 0;
}


Pseudocode after compilation:
Expand Collapse Copy
shadow::importer<int,int> *call_dynamically_resolved_function()
{
  shadow::importer<int,int> v1; // [rsp+38h] [rbp-30h] BYREF
  __int64 v2; // [rsp+50h] [rbp-18h] BYREF
  int v3; // [rsp+5Ch] [rbp-Ch] BYREF

  v3 = 0;
  v2 = 0i64;
  return shadowcall<int,std::nullptr_t,char const (&)[9],char const (&)[9],long>(
           &v1,
           (shadow::detail::basic_hash<unsigned long long>)0x5212B3558DC4AC3i64,
           (__int16 *)&v2,
           (const char (*)[9])"string 1",
           (const char (*)[9])"string 2",
           &v3);
}

int call_statically_resolved_function()
{
  return MessageBoxA(0i64, "string 1", "string 2", 0);
}

__int64 __fastcall main()
{
  call_dynamically_resolved_function();
  call_statically_resolved_function();
  return 0i64;
}


P.S. все примеры для тестов доступны в
Пожалуйста, авторизуйтесь для просмотра ссылки.
, по минусам библиотеки - в ближайшее время стоит быть осторожными с неявными кастами при использовании результата от функций shadowcall, shadowsyscall, ошибка проектирования, в ближайшее время текущий коммит будет закреплён на теге, а эта проблема будет вырезана
 
добавил парсер ApiSetMap. потенциальный юзкейс - применение в собственной реализации резольвера forwarded экспортов, чуть позже добавлю примеры в гитхаб репо
Пожалуйста, авторизуйтесь для просмотра ссылки.

C++:
Expand Collapse Copy
#include "shadowsyscall.hpp"

template <class... Types>
void debug_log(const std::wformat_string<Types...> fmt, Types... args) {
  std::wcout << std::format(fmt, std::forward<Types>(args)...) << "\n";
}

int main() {
  auto string_appender = [](std::wstring acc, auto host) {
    auto [value, alias] = host;
    auto separator = acc.empty() ? L"" : L", ";
    return acc.append(separator).append(value);
  };

  for (auto& entry : shadow::api_set_map_enumerator()) {
    auto hosts = std::accumulate(entry.hosts().begin(), entry.hosts().end(), std::wstring{},
                                 string_appender);

    if (hosts.empty())
      hosts.assign(L"none");

    debug_log(L"{} -> {}", entry.contract().clean_name(), hosts);
  }
}

Код:
Expand Collapse Copy
api-ms-onecoreuap-print-render -> printrenderapihost.dll
api-ms-win-appmodel-advertisingid -> kernel.appcore.dll
api-ms-win-appmodel-identity -> kernel.appcore.dll
api-ms-win-appmodel-lifecyclepolicy -> rmclient.dll
api-ms-win-appmodel-runtime-internal -> kernel.appcore.dll
api-ms-win-appmodel-runtime -> kernel.appcore.dll
api-ms-win-appmodel-state -> kernel.appcore.dll
api-ms-win-appmodel-state -> kernel.appcore.dll
api-ms-win-appmodel-unlock -> kernel.appcore.dll
api-ms-win-audiocore-spatial-config -> windows.media.devices.dll
api-ms-win-base-bootconfig -> advapi32.dll
api-ms-win-base-util -> advapi32.dll
api-ms-win-composition-redirection -> dwmredir.dll
api-ms-win-composition-windowmanager -> udwm.dll
api-ms-win-containers-cmclient -> cmclient.dll
api-ms-win-containers-cmclient -> cmclient.dll
api-ms-win-containers-cmclient -> cmclient.dll
api-ms-win-containers-cmclient -> cmclient.dll
api-ms-win-containers-cmclient -> cmclient.dll
api-ms-win-containers-cmdiagclient -> cmclient.dll
api-ms-win-containers-cmservicingclient -> cmclient.dll
api-ms-win-containers-cmservicingclient -> cmclient.dll
api-ms-win-core-apiquery -> ntdll.dll
api-ms-win-core-apiquery -> kernelbase.dll
api-ms-win-core-appcompat -> kernelbase.dll
api-ms-win-core-appinit -> kernel32.dll, kernelbase.dll
api-ms-win-core-atoms -> kernel32.dll
api-ms-win-core-backgroundtask -> kernelbase.dll
api-ms-win-core-bicltapi -> bi.dll
api-ms-win-core-biplmapi -> twinapi.appcore.dll
api-ms-win-core-biplmapi -> twinapi.appcore.dll
api-ms-win-core-biptcltapi -> twinapi.appcore.dll
api-ms-win-core-calendar -> kernel32.dll
api-ms-win-core-com -> combase.dll
api-ms-win-core-com -> coml2.dll
api-ms-win-core-com-midlproxystub -> combase.dll
api-ms-win-core-com-private -> combase.dll
api-ms-win-core-com-private -> combase.dll
api-ms-win-core-com-private -> combase.dll
api-ms-win-core-comm -> kernelbase.dll
api-ms-win-core-commandlinetoargv -> kernelbase.dll
api-ms-win-core-console-ansi -> kernel32.dll
api-ms-win-core-console-internal -> kernelbase.dll
api-ms-win-core-console -> kernelbase.dll
api-ms-win-core-console -> kernelbase.dll
api-ms-win-core-console -> kernelbase.dll
api-ms-win-core-console -> kernelbase.dll
api-ms-win-core-console -> kernelbase.dll
api-ms-win-core-console -> kernelbase.dll
api-ms-win-core-crt -> ntdll.dll
api-ms-win-core-crt -> kernelbase.dll
api-ms-win-core-datetime -> kernelbase.dll
api-ms-win-core-debug -> kernelbase.dll
api-ms-win-core-debug-minidump -> dbgcore.dll
api-ms-win-core-delayload -> kernelbase.dll
api-ms-win-core-enclave -> kernelbase.dll
api-ms-win-core-errorhandling -> kernelbase.dll
api-ms-win-core-featurestaging -> shcore.dll
api-ms-win-core-featuretoggles -> kernelbase.dll
api-ms-win-core-fibers -> kernelbase.dll
api-ms-win-core-fibers -> kernelbase.dll
api-ms-win-core-file-ansi -> kernel32.dll
api-ms-win-core-file-ansi -> kernel32.dll
api-ms-win-core-file-fromapp -> kernelbase.dll
api-ms-win-core-file -> kernelbase.dll
api-ms-win-core-file -> kernelbase.dll
api-ms-win-core-file -> kernelbase.dll
api-ms-win-core-firmware -> kernel32.dll
api-ms-win-core-guard -> kernelbase.dll
api-ms-win-core-handle -> kernelbase.dll
api-ms-win-core-heap -> kernelbase.dll
api-ms-win-core-heap -> kernelbase.dll
api-ms-win-core-heap -> kernelbase.dll
api-ms-win-core-heap-obsolete -> kernelbase.dll
api-ms-win-core-interlocked -> kernelbase.dll
api-ms-win-core-interlocked -> kernelbase.dll
api-ms-win-core-io -> kernel32.dll, kernelbase.dll
api-ms-win-core-ioring -> kernelbase.dll
api-ms-win-core-job -> kernelbase.dll
api-ms-win-core-job -> kernel32.dll
api-ms-win-core-kernel32-legacy-ansi -> kernel32.dll

Many more lines...
 
Добавлены перегрузки для типизированных вызовов shadowcall и shadowsyscall
Для тех кто не понимает зачем: функции shadowcall & shadowsyscall в дефолтном варианте принимают в себя parameter-pack, это механизм в C++ который позволяет компилятору передавать произвольное количество аргументов в функцию, никак не ограничивая передаваемые типы (компилятор выводит типы для каждого из аргументов самостоятельно). Представьте что вы вызываете
Example:
Expand Collapse Copy
std::string title = "title";
shadowcall("MessageBoxA", nullptr, title, "Caption", MB_OK);
В то время как MessageBoxA во втором аргументе ожидает LPCSTR (const char*), передавать туда std::string для C++ будет считаться UB. На компиляции несоответствие типов мы отловить не сможем (потому что нет источника "оригинальной" функции откуда мы могли бы спарсить ожидаемые типы), соответственно при попытке вызова адреса памяти на котором расположен MessageBoxA, пытаясь передать туда std::string вместо LPCSTR мы словим 0xC0000005 (AV).
Именно эту проблему решает это обновление.

Example:
Expand Collapse Copy
int code = shadowcall<MessageBoxA>(nullptr, "Text 1", "Caption 1", MB_OK);
code = shadowcall<MessageBoxA, "user32">(nullptr, "Text 2", "Caption 2", MB_OK);

// works on WinAPI defines
// (FindWindow is a define that expands into FindWindowA depending on the encoding)
HWND window = shadowcall<FindWindow>("", "");
Если в качестве шаблона передаётся функция (к примеру MessageBoxA с существующим определением из Windows.h), то библиотека сама выводит её имя и превращает в хэш на этапе компиляции (через трюк с __PRETTY_FUNCTION__)
Если же передаётся тип функции (пример: typedef NTSTATUS(WINAPI* NtCreateThreadEx)(...)), то нужно дополнительно передать имя функции первым аргументом, потому что в С++20 (23 тоже) вывести имя типа пока что невозможно. В этом обновлении я (и ещё 1 контрибьютор) сознательно отказались от использования препроцессора (#define) и их "stringize" (#func) функционала в пользу единого современного интерфейса, и ещё пары причин, одна из которых - невозможность вывести имя "настоящей функции" скрытой за определением Windows.h. В пример MessageBox:

WinAPI defines:
Expand Collapse Copy
#ifdef UNICODE
#define MessageBox  MessageBoxW
#else
#define MessageBox  MessageBoxA
#endif // !UNICODE
DLL (такая как к примеру user32.dll) экспортит именно MessageBoxA & MessageBoxW, соответственно экспорта MessageBox банально не существует, поэтому его нельзя вызвать. Это я к тому, что #define со своим stringize на уровне препроцессора захватывает имя чего-бы то ни было, что было передано в его скобки, то есть передавая дефайн MessageBox, препроцессор превратит его в "MessageBox". Трюк с __PRETTY_FUNCTION__ работает на уровне компилятора, соответственно компилятор который работает с результатом препроцессора автоматически будет видеть "настоящую" функцию лежащую за дефайном MessageBox (MessageBoxA или MessageBoxW вариант).

не думаю что буду выпускать какие-либо обновления до того момента как освобожусь, и наконец-то разобью этот гигантский single-header на нормальный header-only репозиторий. спасибо всем кто пользуется репозиторием и репортит баги/проблемы (я стараюсь их фиксить в пределах своего понимания C++), и отдельная благодарность
Пожалуйста, авторизуйтесь для просмотра ссылки.
который
Пожалуйста, авторизуйтесь для просмотра ссылки.



Example:
Expand Collapse Copy
#include <Windows.h>
#include <winternl.h> // for NtClose declaration
#include <thread>

#include <cstring>

#define SHADOW_RELAXED_POINTER_COMPAT  // allows implicit pointer conversions
#define SHADOW_ALLOW_INTEGRAL_AS_PTR
#include "shadowsyscall.hpp"

typedef void(CALLBACK* PRTL_THREAD_START_ROUTINE)(LPVOID);
typedef NTSTATUS(WINAPI* NtCreateThreadEx)(HANDLE*, ACCESS_MASK, OBJECT_ATTRIBUTES*, HANDLE,
                                           PRTL_THREAD_START_ROUTINE, void*, ULONG, ULONG_PTR,
                                           SIZE_T, SIZE_T, void*);

int main() {
  shadowcall<LoadLibrary>("user32");

  void* thread_handle;
  auto current_process = GetCurrentProcess();
  auto start_routine = [](void*) -> DWORD {
    std::cout << "Hello from thread " << std::this_thread::get_id() << "\n";
    return 0;
  };

  int return_code = shadowcall<int>("MessageBoxA", nullptr, "Text", "Caption 1", MB_OK);
  return_code = shadowcall<int>({"MessageBoxA", "user32"}, NULL, "Text", "Caption 2", MB_OK);
  return_code = shadowcall<MessageBoxA>(NULL, "Text", "Caption 3", MB_OK);
  return_code = shadowcall<MessageBoxA, "user32">(0, "Text", "Caption 4", MB_OK);

  // works on WinAPI defines
  // (FindWindow is a define that expands into FindWindowA depending on the encoding)
  HWND window = shadowcall<FindWindow>("", "");

  NTSTATUS status;
#if _WIN64
  // if the given template is a type rather than a linkable symbol - we
  // cannot deduce the name of this type (at least without reflection),
  // so the function name will have to be duplicated in the parameter list
  status = shadowsyscall<NtCreateThreadEx>(
      "NtCreateThreadEx", &thread_handle, THREAD_ALL_ACCESS, NULL, current_process,
      static_cast<LPTHREAD_START_ROUTINE>(start_routine), 0, FALSE, NULL, NULL, NULL, 0);

  status = shadowsyscall<NtClose>(thread_handle);
#endif

  status = shadowcall<NtCreateThreadEx>(
      "NtCreateThreadEx", &thread_handle, THREAD_ALL_ACCESS, NULL, current_process,
      static_cast<LPTHREAD_START_ROUTINE>(start_routine), 0, FALSE, NULL, NULL, NULL, 0);

  status = shadowcall<NtCreateThreadEx>(
      {"NtCreateThreadEx", "ntdll.dll"}, &thread_handle, THREAD_ALL_ACCESS, NULL, current_process,
      static_cast<LPTHREAD_START_ROUTINE>(start_routine), 0, FALSE, NULL, NULL, NULL, 0);

  shadowcall<Sleep>(1000);

  // APIs with a calling convention other than __stdcall
  // are not available on x86. memcpy = CRT = __cdecl
#if _WIN64
  int* val_arr = new int[4];
  std::array<int, 4> filled_val_arr{1, 2, 3, 4};

  shadowcall<std::memcpy>(val_arr, filled_val_arr.data(), filled_val_arr.size() * sizeof(int));
  std::cout << "val_arr[3] = " << val_arr[3] << "\n";
#endif
}
 
Назад
Сверху Снизу