diff options
Diffstat (limited to 'CRT')
-rw-r--r-- | CRT/DriverThread.cpp | 297 | ||||
-rw-r--r-- | CRT/DriverThread.hpp | 167 | ||||
-rw-r--r-- | CRT/eastl_compat.cpp | 189 | ||||
-rw-r--r-- | CRT/eastl_compat.hpp | 26 | ||||
-rw-r--r-- | CRT/eastl_user_config.hpp | 37 | ||||
-rw-r--r-- | CRT/except.h | 35 | ||||
-rwxr-xr-x | CRT/gen_wrapper.sh | 174 | ||||
-rw-r--r-- | CRT/kcrt.c | 313 | ||||
-rw-r--r-- | CRT/kcrt.cpp | 51 | ||||
-rw-r--r-- | CRT/nanoprintf.h | 1104 | ||||
-rw-r--r-- | CRT/ntdll_zw_functions.c | 409 | ||||
-rw-r--r-- | CRT/ntdll_zw_functions.txt | 19 | ||||
-rw-r--r-- | CRT/obfuscate.hpp | 163 | ||||
-rw-r--r-- | CRT/ucrt.cpp | 10 |
14 files changed, 2994 insertions, 0 deletions
diff --git a/CRT/DriverThread.cpp b/CRT/DriverThread.cpp new file mode 100644 index 0000000..3a77dc8 --- /dev/null +++ b/CRT/DriverThread.cpp @@ -0,0 +1,297 @@ +#include <DriverThread.hpp> + +class WorkQueueArgs : public DriverThread::ThreadArgs +{ + friend class WorkQueue; + +public: + WorkQueueArgs(DriverThread::WorkQueue * wq) : m_wq(wq){}; + WorkQueueArgs(const WorkQueueArgs &) = delete; + DriverThread::WorkQueue * getWorkQueue() + { + return m_wq; + } + +private: + DriverThread::WorkQueue * m_wq; +}; + +// Thread + +DriverThread::Thread::Thread(void) +{ +} + +DriverThread::Thread::~Thread(void) +{ + WaitForTermination(); +} + +extern "C" void InterceptorThreadRoutine(PVOID threadContext) +{ + NTSTATUS threadReturn; + DriverThread::Thread * self = (DriverThread::Thread *)threadContext; + + self->m_threadId = PsGetCurrentThreadId(); + threadReturn = self->m_routine(self->m_threadContext); + self->m_threadId = nullptr; + self->m_threadContext = nullptr; + PsTerminateSystemThread(threadReturn); +} + +NTSTATUS DriverThread::Thread::Start(ThreadRoutine routine, eastl::shared_ptr<ThreadArgs> args) +{ + HANDLE threadHandle; + NTSTATUS status; + + LockGuard lock(m_mutex); + if (m_threadObject != nullptr) + { + return STATUS_UNSUCCESSFUL; + } + + m_routine = routine; + m_threadContext = args; + status = PsCreateSystemThread(&threadHandle, (ACCESS_MASK)0, NULL, (HANDLE)0, NULL, InterceptorThreadRoutine, this); + + if (!NT_SUCCESS(status)) + { + return status; + } + + status = + ObReferenceObjectByHandle(threadHandle, THREAD_ALL_ACCESS, NULL, KernelMode, (PVOID *)&m_threadObject, NULL); + + if (!NT_SUCCESS(status)) + { + return status; + } + + return ZwClose(threadHandle); +} + +NTSTATUS DriverThread::Thread::WaitForTermination(LONGLONG timeout) +{ + if (PsGetCurrentThreadId() == m_threadId) + { + return STATUS_UNSUCCESSFUL; + } + + LockGuard lock(m_mutex); + if (m_threadObject == nullptr) + { + return STATUS_UNSUCCESSFUL; + } + + LARGE_INTEGER li_timeout = {.QuadPart = timeout}; + NTSTATUS status = + KeWaitForSingleObject(m_threadObject, Executive, KernelMode, FALSE, (timeout == 0 ? NULL : &li_timeout)); + + ObDereferenceObject(m_threadObject); + m_threadObject = nullptr; + return status; +} + +// Spinlock + +DriverThread::Spinlock::Spinlock(void) +{ + KeInitializeSpinLock(&m_spinLock); +} + +NTSTATUS DriverThread::Spinlock::Acquire(void) +{ + return KeAcquireSpinLock(&m_spinLock, &m_oldIrql); +} + +void DriverThread::Spinlock::Release(void) +{ + KeReleaseSpinLock(&m_spinLock, m_oldIrql); +} + +KIRQL DriverThread::Spinlock::GetOldIrql(void) +{ + return m_oldIrql; +} + +// Semaphore + +DriverThread::Semaphore::Semaphore(LONG initialValue, LONG maxValue) +{ + KeInitializeSemaphore(&m_semaphore, initialValue, maxValue); +} + +NTSTATUS DriverThread::Semaphore::Wait(LONGLONG timeout) +{ + LARGE_INTEGER li_timeout = {.QuadPart = timeout}; + return KeWaitForSingleObject(&m_semaphore, Executive, KernelMode, FALSE, (timeout == 0 ? NULL : &li_timeout)); +} + +LONG DriverThread::Semaphore::Release(LONG adjustment) +{ + return KeReleaseSemaphore(&m_semaphore, 0, adjustment, FALSE); +} + +// Event + +DriverThread::Event::Event() +{ + KeInitializeEvent(&m_event, NotificationEvent, FALSE); +} + +NTSTATUS DriverThread::Event::Wait(LONGLONG timeout) +{ + LARGE_INTEGER li_timeout = {.QuadPart = timeout}; + return KeWaitForSingleObject(&m_event, Executive, KernelMode, FALSE, (timeout == 0 ? NULL : &li_timeout)); +} + +NTSTATUS DriverThread::Event::Notify() +{ + return KeSetEvent(&m_event, 0, FALSE); +} + +// Mutex + +DriverThread::Mutex::Mutex(void) +{ +} + +DriverThread::Mutex::~Mutex(void) +{ +} + +void DriverThread::Mutex::Lock(void) +{ + while (m_interlock == 1 || InterlockedCompareExchange(&m_interlock, 1, 0) == 1) {} +} + +void DriverThread::Mutex::Unlock(void) +{ + m_interlock = 0; +} + +// LockGuard + +DriverThread::LockGuard::LockGuard(Mutex & m) : m_Lock(m) +{ + m_Lock.Lock(); +} + +DriverThread::LockGuard::~LockGuard(void) +{ + m_Lock.Unlock(); +} + +// WorkQueue + +DriverThread::WorkQueue::WorkQueue(void) + : m_mutex(), m_queue(), m_wakeEvent(), m_stopWorker(false), m_worker(), m_workerRoutine(nullptr) +{ +} + +DriverThread::WorkQueue::~WorkQueue(void) +{ + Stop(); +} + +NTSTATUS DriverThread::WorkQueue::Start(WorkerRoutine routine) +{ + NTSTATUS status; + + { + LockGuard lock(m_mutex); + m_workerRoutine = routine; + auto wqa = eastl::make_shared<WorkQueueArgs>(this); + status = m_worker.Start(WorkerInterceptorRoutine, wqa); + } + + if (!NT_SUCCESS(status) && status != STATUS_UNSUCCESSFUL) + { + Stop(); + } + + return status; +} + +void DriverThread::WorkQueue::Stop(bool wait) +{ + LockGuard lock(m_mutex); + if (m_stopWorker == true) + { + return; + } + m_stopWorker = true; + m_wakeEvent.Notify(); + if (wait) + { + m_worker.WaitForTermination(); + } +} + +void DriverThread::WorkQueue::Enqueue(WorkItem & item) +{ + { + LockGuard lock(m_mutex); + m_queue.emplace_back(item); + } + m_wakeEvent.Notify(); +} + +void DriverThread::WorkQueue::Enqueue(eastl::deque<WorkItem> & items) +{ + { + LockGuard lock(m_mutex); + m_queue.insert(m_queue.end(), items.begin(), items.end()); + } + m_wakeEvent.Notify(); +} + +NTSTATUS DriverThread::WorkQueue::WorkerInterceptorRoutine(eastl::shared_ptr<ThreadArgs> args) +{ + auto wqa = eastl::static_pointer_cast<WorkQueueArgs>(args); + WorkQueue * wq = wqa->getWorkQueue(); + + PAGED_CODE(); + + for (;;) + { + eastl::deque<WorkItem> doQueue; + std::size_t nItems; + + { + LockGuard lock(wq->m_mutex); + nItems = wq->m_queue.size(); + } + + if (nItems == 0) + { + if (wq->m_stopWorker == true) + { + break; + } + + wq->m_wakeEvent.Wait(); + continue; + } + + { + LockGuard lock(wq->m_mutex); + doQueue = wq->m_queue; + wq->m_queue.clear(); + } + + while (doQueue.size() > 0) + { + WorkItem & item = doQueue.front(); + + if (wq->m_workerRoutine(item) != STATUS_SUCCESS) + { + wq->m_stopWorker = true; + } + + doQueue.pop_front(); + } + } + + return STATUS_SUCCESS; +} diff --git a/CRT/DriverThread.hpp b/CRT/DriverThread.hpp new file mode 100644 index 0000000..ac2eddd --- /dev/null +++ b/CRT/DriverThread.hpp @@ -0,0 +1,167 @@ +#ifndef DDK_THREAD +#define DDK_THREAD 1 + +#include <ntddk.h> + +#include <EASTL/deque.h> +#include <EASTL/functional.h> +#include <EASTL/shared_ptr.h> + +extern "C" void InterceptorThreadRoutine(PVOID threadContext); + +namespace DriverThread +{ +class Mutex +{ +public: + Mutex(void); + ~Mutex(void); + +private: + void Lock(); + void Unlock(); + + volatile long int m_interlock; + + friend class LockGuard; +}; + +class LockGuard +{ +public: + LockGuard(Mutex & m); + ~LockGuard(void); + +private: + Mutex m_Lock; +}; + +class ThreadArgs : public virtual eastl::enable_shared_from_this<ThreadArgs> +{ +public: + ThreadArgs(void) + { + } + ThreadArgs(const ThreadArgs &) = delete; + virtual ~ThreadArgs(void) + { + } +}; + +using ThreadRoutine = eastl::function<NTSTATUS(eastl::shared_ptr<ThreadArgs> args)>; + +class Thread +{ +public: + Thread(void); + Thread(const Thread &) = delete; + ~Thread(void); + NTSTATUS Start(ThreadRoutine routine, eastl::shared_ptr<ThreadArgs> args); + NTSTATUS WaitForTermination(LONGLONG timeout = 0); + HANDLE GetThreadId(void) + { + return m_threadId; + } + bool isRunning(void) + { + return GetThreadId() != nullptr; + } + +private: + friend void ::InterceptorThreadRoutine(PVOID threadContext); + + HANDLE m_threadId = nullptr; + PETHREAD m_threadObject = nullptr; + Mutex m_mutex; + ThreadRoutine m_routine; + eastl::shared_ptr<ThreadArgs> m_threadContext; +}; + +class Spinlock +{ +public: + Spinlock(void); + NTSTATUS Acquire(void); + void Release(void); + KIRQL GetOldIrql(void); + +private: + KIRQL m_oldIrql; + KSPIN_LOCK m_spinLock; +}; + +class Semaphore +{ +public: + Semaphore(LONG initialValue = 0, LONG maxValue = MAXLONG); + NTSTATUS Wait(LONGLONG timeout = 0); + LONG Release(LONG adjustment = 1); + +private: + KSEMAPHORE m_semaphore; +}; + +class Event +{ +public: + Event(); + NTSTATUS Wait(LONGLONG timeout = 0); + NTSTATUS Notify(); + +private: + KEVENT m_event; +}; + +class WorkItem final +{ + friend class WorkQueue; + +public: + WorkItem(const eastl::shared_ptr<void> & user) : m_user(std::move(user)) + { + } + virtual ~WorkItem(void) + { + } + template <class T> + eastl::shared_ptr<T> Get(void) + { + return eastl::static_pointer_cast<T>(m_user); + } + template <class T> + void Get(eastl::shared_ptr<T> & dest) + { + dest = eastl::static_pointer_cast<T>(m_user); + } + +private: + eastl::shared_ptr<void> m_user; +}; + +using WorkerRoutine = eastl::function<NTSTATUS(WorkItem & item)>; + +class WorkQueue final +{ +public: + WorkQueue(void); + WorkQueue(const WorkQueue &) = delete; + ~WorkQueue(void); + NTSTATUS Start(WorkerRoutine routine); + void Stop(bool wait = true); + void Enqueue(WorkItem & item); + void Enqueue(eastl::deque<WorkItem> & items); + +private: + Mutex m_mutex; + eastl::deque<WorkItem> m_queue; + Event m_wakeEvent; + bool m_stopWorker; // Work LIST must be empty and StopWorker TRUE to be able to stop! + Thread m_worker; + WorkerRoutine m_workerRoutine; + + static NTSTATUS WorkerInterceptorRoutine(eastl::shared_ptr<ThreadArgs> args); +}; + +}; // namespace DriverThread + +#endif diff --git a/CRT/eastl_compat.cpp b/CRT/eastl_compat.cpp new file mode 100644 index 0000000..e252f5f --- /dev/null +++ b/CRT/eastl_compat.cpp @@ -0,0 +1,189 @@ +#include "eastl_compat.hpp" + +#define NANOPRINTF_VISIBILITY_STATIC 1 +#define NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS 1 +#define NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS 1 +#define NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS 1 +#define NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS 1 +#define NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS 0 +#define NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS 0 +#define NANOPRINTF_IMPLEMENTATION 1 +#include "nanoprintf.h" + +#ifndef NATIVE +#include <wdm.h> +#endif + +/* + * eastl::to_string(...) does not work yet event if with a provided Vsnprintf/Vsnprintf8 + * The issue seems to be caused by a broken va_list/va_copy. + */ + +eastl::string to_string(int value) +{ + int nbytes = npf_snprintf(nullptr, 0, "%d", value); + if (nbytes > 0) + { + char result[nbytes + 1] = {}; + npf_snprintf(result, nbytes, "%d", value); + return result; + } + else + return ""; +} + +eastl::string to_string(long value) +{ + int nbytes = npf_snprintf(nullptr, 0, "%ld", value); + if (nbytes > 0) + { + char result[nbytes + 1] = {}; + npf_snprintf(result, nbytes, "%ld", value); + return result; + } + else + return ""; +} + +eastl::string to_string(long long value) +{ + int nbytes = npf_snprintf(nullptr, 0, "%lld", value); + if (nbytes > 0) + { + char result[nbytes + 1] = {}; + npf_snprintf(result, nbytes, "%lld", value); + return result; + } + else + return ""; +} + +eastl::string to_string(unsigned int value) +{ + int nbytes = npf_snprintf(nullptr, 0, "%u", value); + if (nbytes > 0) + { + char result[nbytes + 1] = {}; + npf_snprintf(result, nbytes, "%u", value); + return result; + } + else + return ""; +} + +eastl::string to_string(unsigned long int value) +{ + int nbytes = npf_snprintf(nullptr, 0, "%lu", value); + if (nbytes > 0) + { + char result[nbytes + 1] = {}; + npf_snprintf(result, nbytes, "%lu", value); + return result; + } + else + return ""; +} + +eastl::string to_string(unsigned long long int value) +{ + int nbytes = npf_snprintf(nullptr, 0, "%llu", value); + if (nbytes > 0) + { + char result[nbytes + 1] = {}; + npf_snprintf(result, nbytes, "%llu", value); + return result; + } + else + return ""; +} + +eastl::string to_string(float value) +{ + int nbytes = npf_snprintf(nullptr, 0, "%f", value); + if (nbytes > 0) + { + char result[nbytes + 1] = {}; + npf_snprintf(result, nbytes, "%f", value); + return result; + } + else + return ""; +} + +eastl::string to_string(double value) +{ + int nbytes = npf_snprintf(nullptr, 0, "%lf", value); + if (nbytes > 0) + { + char result[nbytes + 1] = {}; + npf_snprintf(result, nbytes, "%lf", value); + return result; + } + else + return ""; +} + +template <typename T> +static eastl::string to_string_hex(T w, size_t hex_len = sizeof(T) << 1) +{ + static const char * const digits = "0123456789ABCDEF"; + const size_t len = sizeof(T) << 1; + + if (hex_len < len) + hex_len = len; + eastl::string rc(hex_len, '0'); + for (size_t i = hex_len - len, j = (hex_len - 1) * 4; i < hex_len; ++i, j -= 4) + rc[i] = digits[(w >> j) & 0x0f]; + return rc; +} + +eastl::string to_string_hex(int value, size_t fill_width) +{ + return to_string_hex<int>(value, fill_width); +} + +eastl::string to_string_hex(long value, size_t fill_width) +{ + return to_string_hex<long>(value, fill_width); +} + +eastl::string to_string_hex(long long value, size_t fill_width) +{ + return to_string_hex<long long>(value, fill_width); +} + +eastl::string to_string_hex(unsigned int value, size_t fill_width) +{ + return to_string_hex<unsigned int>(value, fill_width); +} + +eastl::string to_string_hex(unsigned long int value, size_t fill_width) +{ + return to_string_hex<unsigned long int>(value, fill_width); +} + +eastl::string to_string_hex(unsigned long long int value, size_t fill_width) +{ + return to_string_hex<unsigned long long int>(value, fill_width); +} + +#ifndef NATIVE +eastl::string from_unicode(wchar_t * wstr, unsigned short wlen, unsigned short wmax) +{ + ANSI_STRING ansi; + UNICODE_STRING unicode; + + unicode.Buffer = wstr; + unicode.Length = wlen; + unicode.MaximumLength = (wmax > 0 ? wmax : wlen); + + if (NT_SUCCESS(RtlUnicodeStringToAnsiString(&ansi, &unicode, TRUE))) + { + eastl::string result(ansi.Buffer, ansi.Length); + RtlFreeAnsiString(&ansi); + return result; + } + + return ""; +} +#endif diff --git a/CRT/eastl_compat.hpp b/CRT/eastl_compat.hpp new file mode 100644 index 0000000..2459e75 --- /dev/null +++ b/CRT/eastl_compat.hpp @@ -0,0 +1,26 @@ +#ifndef EASTL_COMPAT +#define EASTL_COMPAT 1 + +#include <EASTL/string.h> + +eastl::string to_string(int value); +eastl::string to_string(long value); +eastl::string to_string(long long value); +eastl::string to_string(unsigned int value); +eastl::string to_string(unsigned long int value); +eastl::string to_string(unsigned long long int value); +eastl::string to_string(float value); +eastl::string to_string(double value); + +eastl::string to_string_hex(int value, size_t fill_width = 0); +eastl::string to_string_hex(long value, size_t fill_width = 0); +eastl::string to_string_hex(long long value, size_t fill_width = 0); +eastl::string to_string_hex(unsigned int value, size_t fill_width = 0); +eastl::string to_string_hex(unsigned long int value, size_t fill_width = 0); +eastl::string to_string_hex(unsigned long long int value, size_t fill_width = 0); + +#ifndef NATIVE +eastl::string from_unicode(wchar_t * wstr, unsigned short wlen, unsigned short wmax = 0); +#endif + +#endif diff --git a/CRT/eastl_user_config.hpp b/CRT/eastl_user_config.hpp new file mode 100644 index 0000000..5a2f733 --- /dev/null +++ b/CRT/eastl_user_config.hpp @@ -0,0 +1,37 @@ +#ifndef EASTL_USER_CONFIG_HPP +#define EASTL_USER_CONFIG_HPP 1 + +#include <string.h> + +extern "C" { +static inline int snprintf_eastl_to_string_warning(char * out, unsigned long long int size) +{ + const char msg[] = "!!! DO NOT USE eastl::to_string !!!"; + const unsigned long long int msg_size = sizeof(msg); + + if (out == NULL || size < msg_size) + { + return sizeof(msg); + } + + memcpy(out, msg, msg_size); + return msg_size; +} + +static inline int Vsnprintf8(char * out, unsigned long long int size, const char *, char *) +{ + return snprintf_eastl_to_string_warning(out, size); +} + +static inline int Vsnprintf16(char * out, unsigned long long int size, const char *, char *) +{ + return snprintf_eastl_to_string_warning(out, size);; +} + +static inline int Vsnprintf32(char * out, unsigned long long int size, const char *, char *) +{ + return snprintf_eastl_to_string_warning(out, size); +} +}; + +#endif diff --git a/CRT/except.h b/CRT/except.h new file mode 100644 index 0000000..7f39eac --- /dev/null +++ b/CRT/except.h @@ -0,0 +1,35 @@ +#ifndef EXCEPT_H +#define EXCEPT_H 1 + +#ifndef __SEH__ +#error "SEH not supported by your toolchain!" +#endif + +#ifdef __try1 +#undef __try1 +#endif + +#ifdef __except1 +#undef __except1 +#endif + +#define __dpptry(handler, counter) \ + __asm__ goto( \ + ".seh_handler __C_specific_handler, @except\n\t" \ + ".seh_handlerdata\n\t" \ + ".long 1\n\t" \ + ".rva .l_startw" #counter ", .l_endw" #counter ", " #handler ", .l_exceptw" #counter \ + "\n\t" \ + ".section .text\n" \ + ".l_startw" #counter ":" :: ::except); + +#define __dppexcept(counter) \ + goto end; \ + except: \ + __asm__(".l_exceptw" #counter ":"); + +#define __dpptryend(counter) \ + end: \ + __asm__(".l_endw" #counter ":"); + +#endif diff --git a/CRT/gen_wrapper.sh b/CRT/gen_wrapper.sh new file mode 100755 index 0000000..35e0d43 --- /dev/null +++ b/CRT/gen_wrapper.sh @@ -0,0 +1,174 @@ +#!/usr/bin/env sh + +MYDIR="$(dirname ${0})" +FN_FILE="${1:-${MYDIR}/ntdll_zw_functions.txt}" + +TYPEDEFS="" +STATICS="" +CURLINE=0 +while read -r line; do + CURLINE=$(expr ${CURLINE} + 1) + VALID=1 + SYMBOL_EXISTS=0 + + if [ -z "${line}" ]; then + continue + fi + if [ $(printf '%s\n' "${line}" | grep -oE '^#*') ]; then + continue + fi + + rtype=$(printf '%s\n' "${line}" | grep -oE '(NTSTATUS NTAPI|VOID NTAPI|PVOID NTAPI)') + if [ -z "${rtype}" ]; then + printf '%s\n' "Line ${CURLINE}: Missing return value of either type 'NTSTATUS NTAPI' or 'VOID NTAPI'." >&2 + VALID=0 + fi + + fnname=$(printf '%s\n' "${line}" | grep -oE '(_|)(Zw|Rtl|Ob[^j]|Mm|Io)[^ (]*') + if [ -z "${fnname}" ]; then + printf '%s\n' "Line ${CURLINE}: Missing function name." >&2 + VALID=0 + fi + if [ $(printf '%s\n' "${fnname}" | wc -l) -ne 1 ]; then + printf '%s\n' "Invalid function name '${fnname}'." >&2 + VALID=0 + fi + if [ $(printf '%s\n' "${fnname}" | grep -oE '^_*') ]; then + SYMBOL_EXISTS=1 + fi + + fnsig=$(printf '%s\n' "${line}" | grep -oE '\([^;]*') + if [ -z "${fnsig}" ]; then + printf '%s\n' "Line ${CURLINE}: Missing function signature." >&2 + VALID=0 + fi + + params_without_braces=$(printf '%s\n' "${fnsig}" | tr -d '()') + if [ ! -z "${params_without_braces}" ]; then + param_names=$(printf '%s\n' "${params_without_braces}" | sed 's/\([^,]*\)/\1\n/g' | grep -oE '[^ ]*$') + if [ -z "${param_names}" ]; then + printf '%s\n' "Line ${CURLINE}: Could not parse function parameters." >&2 + VALID=0 + fi + else + param_names="" + fi + params="" + for param in ${param_names}; do + if [ -z "${param}" ]; then + printf '%s\n' "Line ${CURLINE}: Invalid parameter found. Please re-check regex'es used." >&2 + VALID=0 + fi + params="${params}${param}, " + done + params=$(printf '%s\n' "${params}" | sed 's/^\(.*\), $/\1/g') + if [ -z "${params}" -a ! -z "${params_without_braces}" ]; then + printf '%s\n' "Line ${CURLINE}: Parameters empty. Please re-check regex'es used." >&2 + VALID=0 + fi + + if [ ${VALID} -eq 1 ]; then + TYPE="${fnname}_t" + VAR="_${fnname}" + TYPEDEFS="${TYPEDEFS}\ntypedef ${rtype} (*${TYPE}) ${fnsig};" + STATICS="${STATICS}\nstatic ${TYPE} ${VAR} = NULL;" + if [ ${SYMBOL_EXISTS} -eq 1 ]; then + fnname_str=$(printf '%s\n' "${fnname}" | sed 's/^\(.\)\{1\}//g') + else + fnname_str="${fnname}" + fi + INITS=$(cat <<EOF +${INITS} +#ifdef __cplusplus + RtlInitUnicodeString(&fnName, skCrypt(L"${fnname_str}")); +#else + RtlInitUnicodeString(&fnName, L"${fnname_str}"); +#endif + ${VAR} = (${TYPE})MmGetSystemRoutineAddress(&fnName); + if (${VAR} == NULL) + { +#ifdef __cplusplus + DbgPrint(skCrypt("%s\\\n"), skCrypt("System routine ${fnname_str} not found.")); +#else + DbgPrint("%s\\\n", "System routine ${fnname_str} not found."); +#endif + retval++; + } +EOF + ) + WRAPPERS=$(cat <<EOF +${WRAPPERS} + +${rtype} ${fnname} ${fnsig} +{ +EOF + ) + case $rtype in + NTSTATUS*) + WRAPPERS=$(cat <<EOF +${WRAPPERS} + if (${VAR} == NULL) + return STATUS_PROCEDURE_NOT_FOUND; + + return ${VAR} (${params}); +} + +${rtype} Wrapper${fnname_str} ${fnsig} +{ + return ${VAR} (${params}); +} +EOF + ) + ;; + PVOID*) + WRAPPERS=$(cat <<EOF +${WRAPPERS} + return ${VAR} (${params}); +} + +${rtype} Wrapper${fnname} ${fnsig} +{ + return ${VAR} (${params}); +} +EOF + ) + esac + fi +done < "${FN_FILE}" + +cat <<EOF +/* This file was auto generated by $(basename ${0}) */ +#include <ntddk.h> + +#ifdef __cplusplus +#define _KERNEL_MODE 1 +#include "obfuscate.hpp" + +extern "C" { +#endif +EOF +echo "${TYPEDEFS}" +echo "${STATICS}" +cat <<EOF + +int __cdecl $(basename -a -s '.txt' ${FN_FILE}) (void) +{ + int retval = 0; + UNICODE_STRING fnName; +EOF +echo "${INITS}" +cat <<EOF + + return retval; +} +EOF +echo "${WRAPPERS}" + +cat <<EOF + +#ifdef __cplusplus +}; +#endif +EOF + +printf '%s lines parsed\n' "${CURLINE}" >&2 diff --git a/CRT/kcrt.c b/CRT/kcrt.c new file mode 100644 index 0000000..11f20d3 --- /dev/null +++ b/CRT/kcrt.c @@ -0,0 +1,313 @@ +/* + * Shameless copy pasta from: https://github.com/sidyhe/dxx + * and: https://github.com/liupengs/Mini-CRT + * and some minor modifications. + */ + +#include <ntddk.h> + +#include "except.h" + +#define KCRT_POOL_DEFAULT_TAG 0xDEADBEEF + +extern void (*__CTOR_LIST__)(); +extern void (*__DTOR_LIST__)(); +extern NTSTATUS __cdecl DriverEntry(struct _DRIVER_OBJECT * DriverObject, PUNICODE_STRING RegistryPath); +extern void __cdecl DriverUnload(struct _DRIVER_OBJECT * DriverObject); +extern int __cdecl ntdll_zw_functions(void); + +DRIVER_INITIALIZE __cdecl _CRT_DriverEntry; +DRIVER_UNLOAD __cdecl _CRT_DriverUnload; + +typedef void (*__cdecl init_and_deinit_fn)(void); +typedef void (*__cdecl atexit_func_t)(void); + +typedef struct _func_node +{ + atexit_func_t func; + struct _func_node * next; +} func_node; + +typedef struct _MALLOC_HEADER +{ + ULONG32 Tags; + ULONG32 _Resv0; + ULONG_PTR Size; +} MALLOC_HEADER, *PMALLOC_HEADER; +C_ASSERT(sizeof(MALLOC_HEADER) % sizeof(void *) == 0); + +static func_node * atexit_list = NULL; + +// dynamic memory mgmt + +PMALLOC_HEADER GET_MALLOC_HEADER(PVOID ptr) +{ + return (MALLOC_HEADER *)((PUCHAR)ptr - sizeof(MALLOC_HEADER)); +} + +PVOID GET_MALLOC_ADDRESS(PMALLOC_HEADER header) +{ + return (PVOID)((PUCHAR)header + sizeof(MALLOC_HEADER)); +} + +ULONG_PTR GET_MALLOC_SIZE(PVOID ptr) +{ + PMALLOC_HEADER header = GET_MALLOC_HEADER(ptr); + + if (header->Tags != KCRT_POOL_DEFAULT_TAG) + KeBugCheckEx(BAD_POOL_HEADER, 0, 0, 0, 0); + + return header->Size; +} + +// c runtime + +static int register_atexit(atexit_func_t func) +{ + func_node * node; + if (!func) + return -1; + + node = (func_node *)malloc(sizeof(func_node)); + + if (node == 0) + return -1; + + node->func = func; + node->next = atexit_list; + atexit_list = node; + return 0; +} + +int __cdecl atexit(atexit_func_t func) +{ + return register_atexit(func); +} + +void __cdecl free(void * ptr) +{ + if (ptr) + { + MALLOC_HEADER * mhdr = GET_MALLOC_HEADER(ptr); + + if (mhdr->Tags != KCRT_POOL_DEFAULT_TAG) + KeBugCheckEx(BAD_POOL_HEADER, 0, 0, 0, 0); + + ExFreePool(mhdr); + } +} + +void * __cdecl malloc(size_t size) +{ + PMALLOC_HEADER mhdr = NULL; + const size_t new_size = size + sizeof(MALLOC_HEADER); + + mhdr = (PMALLOC_HEADER)ExAllocatePoolWithTag(new_size > PAGE_SIZE ? PagedPool : NonPagedPool, new_size, KCRT_POOL_DEFAULT_TAG); + if (mhdr) + { + RtlZeroMemory(mhdr, new_size); + + mhdr->Tags = KCRT_POOL_DEFAULT_TAG; + mhdr->Size = size; + return GET_MALLOC_ADDRESS(mhdr); + } + + return NULL; +} + +void * __cdecl calloc(size_t nmemb, size_t size) +{ + return malloc(nmemb * size); +} + +void * __cdecl realloc(void * ptr, size_t new_size) +{ + if (!ptr) + { + return malloc(new_size); + } + else if (new_size == 0) + { + free(ptr); + return NULL; + } + else + { + size_t old_size = GET_MALLOC_SIZE(ptr); + + if (new_size <= old_size) + { + return ptr; + } + else + { + void * new_ptr = malloc(new_size); + + if (new_ptr) + { + memcpy(new_ptr, ptr, old_size); + free(ptr); + return new_ptr; + } + } + } + + return NULL; +} + +void __cdecl __cxa_pure_virtual(void) +{ + // definitly not perfect, but we get at least a notification + while (1) + { + DbgPrint("Pure virtual function call..\n"); + LARGE_INTEGER li = {.QuadPart = -10000000}; + KeDelayExecutionThread(KernelMode, TRUE, &li); + } +} + +// stolen from musl: https://elixir.bootlin.com/musl/v1.1.9/source/src/math/ceilf.c +#define FORCE_EVAL(x) \ + do \ + { \ + if (sizeof(x) == sizeof(float)) \ + { \ + volatile float __x __attribute__((unused)); \ + __x = (x); \ + } \ + else if (sizeof(x) == sizeof(double)) \ + { \ + volatile double __x __attribute__((unused)); \ + __x = (x); \ + } \ + else \ + { \ + volatile long double __x __attribute__((unused)); \ + __x = (x); \ + } \ + } while (0) + +float __cdecl ceilf(float x) +{ + union + { + float f; + UINT32 i; + } u = {x}; + int e = (int)(u.i >> 23 & 0xff) - 0x7f; + UINT32 m; + + if (e >= 23) + return x; + if (e >= 0) + { + m = 0x007fffff >> e; + if ((u.i & m) == 0) + return x; + FORCE_EVAL(x + 0x1p120f); + if (u.i >> 31 == 0) + u.i += m; + u.i &= ~m; + } + else + { + FORCE_EVAL(x + 0x1p120f); + if (u.i >> 31) + u.f = -0.0; + else if (u.i << 1) + u.f = 1.0; + } + return u.f; +} + +// functions called in DRIVER_INITIALIZE and DRIVER_UNLOAD + +static void __cdecl __ctors(void) +{ + unsigned long long int const * const * const l = (unsigned long long int const * const * const) & __CTOR_LIST__; + unsigned long long int i = (unsigned long long int)*l; + init_and_deinit_fn const * p; + + if (i == (unsigned long long int)-1) + { + for (i = 1; l[i] != NULL; i++) + ; + i--; + } + + p = (init_and_deinit_fn *)&l[i]; + + while (i--) + { + (**p--)(); + } +} + +static void __cdecl __dtors(void) +{ + func_node * p = atexit_list; + for (; p != NULL; p = p->next) + { + p->func(); + free(p); + } + atexit_list = NULL; +} + +void __cdecl KCRT_OnDriverEntry(void) +{ + __ctors(); +} + +void __cdecl KCRT_OnDriverUnload(void) +{ + __dtors(); +} + +void __cdecl _CRT_DriverUnload(struct _DRIVER_OBJECT * DriverObject) +{ + DriverUnload(DriverObject); + + KCRT_OnDriverUnload(); +} + +NTSTATUS __cdecl _CRT_DriverEntry(struct _DRIVER_OBJECT * DriverObject, PUNICODE_STRING RegistryPath) +{ + NTSTATUS retval; + + KCRT_OnDriverEntry(); + + int zw_retval = ntdll_zw_functions(); + if (zw_retval != 0) + { + return STATUS_NOT_IMPLEMENTED; + } + + retval = DriverEntry(DriverObject, RegistryPath); + + /* support for service stopping and CRT de-init */ + DriverObject->DriverUnload = _CRT_DriverUnload; + + return retval; +} + +void __cdecl _enable(void) +{ + __asm__ __volatile__("sti"); +} + +void __cdecl _disable(void) +{ + __asm__ __volatile__("cli"); +} + +void * __cdecl _AddressOfReturnAddress(void) +{ + return __builtin_extract_return_addr(__builtin_return_address(0)); +} + +char __cdecl _putchar(char c) +{ + DbgPrint("%c", c); + return c; +} diff --git a/CRT/kcrt.cpp b/CRT/kcrt.cpp new file mode 100644 index 0000000..32681ab --- /dev/null +++ b/CRT/kcrt.cpp @@ -0,0 +1,51 @@ +/* + * Shameless copy pasta from: https://github.com/sidyhe/dxx + * and: https://github.com/liupengs/Mini-CRT + * and some minor modifications. + */ + +#include <cstdio> +#include <cstdlib> + +// new & delete + +void * __cdecl operator new(std::size_t size) +{ + return malloc(size); +} + +void * __cdecl operator new[](size_t size) +{ + return malloc(size); +} + +void __cdecl operator delete(void * ptr) +{ + free(ptr); +} + +void __cdecl operator delete(void * ptr, size_t) +{ + free(ptr); +} + +void __cdecl operator delete[](void * ptr, long long unsigned int) +{ + free(ptr); +} + +void __cdecl operator delete[](void * ptr) +{ + free(ptr); +} + +// EASTL + +void * operator new[](size_t size, const char *, int, unsigned, const char *, int) +{ + return malloc(size); +} +void * operator new[](size_t size, size_t, size_t, const char *, int, unsigned, const char *, int) +{ + return malloc(size); +} diff --git a/CRT/nanoprintf.h b/CRT/nanoprintf.h new file mode 100644 index 0000000..290ddfe --- /dev/null +++ b/CRT/nanoprintf.h @@ -0,0 +1,1104 @@ +/* nanoprintf: a tiny embeddable printf replacement written in C. + https://github.com/charlesnicholson/nanoprintf + charles.nicholson+nanoprintf@gmail.com + dual-licensed under 0bsd and unlicense, take your pick. see eof for details. */ + +#ifndef NANOPRINTF_H_INCLUDED +#define NANOPRINTF_H_INCLUDED + +#include <stdarg.h> +#include <stddef.h> + +// Define this to fully sandbox nanoprintf inside of a translation unit. +#ifdef NANOPRINTF_VISIBILITY_STATIC + #define NPF_VISIBILITY static +#else + #define NPF_VISIBILITY extern +#endif + +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #define NPF_PRINTF_ATTR(FORMAT_INDEX, VARGS_INDEX) \ + __attribute__((format(printf, FORMAT_INDEX, VARGS_INDEX))) +#else + #define NPF_PRINTF_ATTR(FORMAT_INDEX, VARGS_INDEX) +#endif + +// Public API + +#ifdef __cplusplus +extern "C" { +#endif + +// The npf_ functions all return the number of bytes required to express the +// fully-formatted string, not including the null terminator character. +// The npf_ functions do not return negative values, since the lack of 'l' length +// modifier support makes encoding errors impossible. + +NPF_VISIBILITY int npf_snprintf( + char *buffer, size_t bufsz, const char *format, ...) NPF_PRINTF_ATTR(3, 4); + +NPF_VISIBILITY int npf_vsnprintf( + char *buffer, size_t bufsz, char const *format, va_list vlist) NPF_PRINTF_ATTR(3, 0); + +typedef void (*npf_putc)(int c, void *ctx); +NPF_VISIBILITY int npf_pprintf( + npf_putc pc, void *pc_ctx, char const *format, ...) NPF_PRINTF_ATTR(3, 4); + +NPF_VISIBILITY int npf_vpprintf( + npf_putc pc, void *pc_ctx, char const *format, va_list vlist) NPF_PRINTF_ATTR(3, 0); + +#ifdef __cplusplus +} +#endif + +#endif // NANOPRINTF_H_INCLUDED + +/* The implementation of nanoprintf begins here, to be compiled only if + NANOPRINTF_IMPLEMENTATION is defined. In a multi-file library what follows would + be nanoprintf.c. */ + +#ifdef NANOPRINTF_IMPLEMENTATION + +#ifndef NANOPRINTF_IMPLEMENTATION_INCLUDED +#define NANOPRINTF_IMPLEMENTATION_INCLUDED + +#include <inttypes.h> +#include <stdint.h> + +// Pick reasonable defaults if nothing's been configured. +#if !defined(NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS) + #define NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS 1 + #define NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS 1 + #define NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS 1 + #define NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS 0 + #define NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS 0 + #define NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS 0 +#endif + +// If anything's been configured, everything must be configured. +#ifndef NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS + #error NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS + #error NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS + #error NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS + #error NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS + #error NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS + #error NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif + +// Ensure flags are compatible. +#if (NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1) && \ + (NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 0) + #error Precision format specifiers must be enabled if float support is enabled. +#endif + +#if defined(NANOPRINTF_SNPRINTF_SAFE_EMPTY_STRING_ON_OVERFLOW) && \ + defined(NANOPRINTF_SNPRINTF_SAFE_TRIM_STRING_ON_OVERFLOW) + #error snprintf safety flags are mutually exclusive. +#endif + +// intmax_t / uintmax_t require stdint from c99 / c++11 +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + #ifndef _MSC_VER + #ifdef __cplusplus + #if __cplusplus < 201103L + #error large format specifier support requires C++11 or later. + #endif + #else + #if __STDC_VERSION__ < 199409L + #error nanoprintf requires C99 or later. + #endif + #endif + #endif +#endif + +// Figure out if we can disable warnings with pragmas. +#ifdef __clang__ + #define NANOPRINTF_CLANG 1 + #define NANOPRINTF_GCC_PAST_4_6 0 +#else + #define NANOPRINTF_CLANG 0 + #if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 6))) + #define NANOPRINTF_GCC_PAST_4_6 1 + #else + #define NANOPRINTF_GCC_PAST_4_6 0 + #endif +#endif + +#if NANOPRINTF_CLANG || NANOPRINTF_GCC_PAST_4_6 + #define NANOPRINTF_HAVE_GCC_WARNING_PRAGMAS 1 +#else + #define NANOPRINTF_HAVE_GCC_WARNING_PRAGMAS 0 +#endif + +#if NANOPRINTF_HAVE_GCC_WARNING_PRAGMAS + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-function" + #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" + #ifdef __cplusplus + #pragma GCC diagnostic ignored "-Wold-style-cast" + #endif + #pragma GCC diagnostic ignored "-Wpadded" + #pragma GCC diagnostic ignored "-Wfloat-equal" + #if NANOPRINTF_CLANG + #pragma GCC diagnostic ignored "-Wc++98-compat-pedantic" + #pragma GCC diagnostic ignored "-Wcovered-switch-default" + #pragma GCC diagnostic ignored "-Wdeclaration-after-statement" + #elif NANOPRINTF_GCC_PAST_4_6 + #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" + #endif +#endif + +#ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4514) // unreferenced inline function removed + #pragma warning(disable:4505) // unreferenced function removed + #pragma warning(disable:4701) // possibly uninitialized + #pragma warning(disable:4706) // assignment in conditional + #pragma warning(disable:4710) // not inlined + #pragma warning(disable:4711) // selected for inline + #pragma warning(disable:4820) // padding after data member + #pragma warning(disable:5039) // extern "C" throw + #pragma warning(disable:5045) // spectre mitigation + #pragma warning(disable:5262) // implicit switch fall-through +#endif + +#if (NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1) || \ + (NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1) +typedef enum { + NPF_FMT_SPEC_OPT_NONE, + NPF_FMT_SPEC_OPT_LITERAL, + NPF_FMT_SPEC_OPT_STAR, +} npf_fmt_spec_opt_t; +#endif + +typedef enum { + NPF_FMT_SPEC_LEN_MOD_NONE, + NPF_FMT_SPEC_LEN_MOD_SHORT, // 'h' + NPF_FMT_SPEC_LEN_MOD_LONG_DOUBLE, // 'L' + NPF_FMT_SPEC_LEN_MOD_CHAR, // 'hh' + NPF_FMT_SPEC_LEN_MOD_LONG, // 'l' +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + NPF_FMT_SPEC_LEN_MOD_LARGE_LONG_LONG, // 'll' + NPF_FMT_SPEC_LEN_MOD_LARGE_INTMAX, // 'j' + NPF_FMT_SPEC_LEN_MOD_LARGE_SIZET, // 'z' + NPF_FMT_SPEC_LEN_MOD_LARGE_PTRDIFFT, // 't' +#endif +} npf_format_spec_length_modifier_t; + +typedef enum { + NPF_FMT_SPEC_CONV_PERCENT, // '%' + NPF_FMT_SPEC_CONV_CHAR, // 'c' + NPF_FMT_SPEC_CONV_STRING, // 's' + NPF_FMT_SPEC_CONV_SIGNED_INT, // 'i', 'd' +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + NPF_FMT_SPEC_CONV_BINARY, // 'b' +#endif + NPF_FMT_SPEC_CONV_OCTAL, // 'o' + NPF_FMT_SPEC_CONV_HEX_INT, // 'x', 'X' + NPF_FMT_SPEC_CONV_UNSIGNED_INT, // 'u' + NPF_FMT_SPEC_CONV_POINTER, // 'p' +#if NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS == 1 + NPF_FMT_SPEC_CONV_WRITEBACK, // 'n' +#endif +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + NPF_FMT_SPEC_CONV_FLOAT_DEC, // 'f', 'F' + NPF_FMT_SPEC_CONV_FLOAT_SCI, // 'e', 'E' + NPF_FMT_SPEC_CONV_FLOAT_SHORTEST, // 'g', 'G' + NPF_FMT_SPEC_CONV_FLOAT_HEX, // 'a', 'A' +#endif +} npf_format_spec_conversion_t; + +typedef struct npf_format_spec { + char prepend; // ' ' or '+' + char alt_form; // '#' + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + npf_fmt_spec_opt_t field_width_opt; + int field_width; + char left_justified; // '-' + char leading_zero_pad; // '0' +#endif + +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + npf_fmt_spec_opt_t prec_opt; + int prec; +#endif + + npf_format_spec_length_modifier_t length_modifier; + npf_format_spec_conversion_t conv_spec; + char case_adjust; +} npf_format_spec_t; + +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 0 + typedef long npf_int_t; + typedef unsigned long npf_uint_t; +#else + typedef intmax_t npf_int_t; + typedef uintmax_t npf_uint_t; +#endif + +typedef struct npf_bufputc_ctx { + char *dst; + size_t len; + size_t cur; +} npf_bufputc_ctx_t; + +static int npf_parse_format_spec(char const *format, npf_format_spec_t *out_spec); +static void npf_bufputc(int c, void *ctx); +static void npf_bufputc_nop(int c, void *ctx); +static int npf_itoa_rev(char *buf, npf_int_t i); +static int npf_utoa_rev(char *buf, npf_uint_t i, unsigned base, unsigned case_adjust); + +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 +static int npf_fsplit_abs(float f, + uint64_t *out_int_part, + uint64_t *out_frac_part, + int *out_frac_base10_neg_e); +static int npf_ftoa_rev(char *buf, float f, char case_adj, int *out_frac_chars); +#endif + +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 +static int npf_bin_len(npf_uint_t i); +#endif + +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + #ifdef _MSC_VER + #include <BaseTsd.h> + typedef SSIZE_T ssize_t; + #else + #include <sys/types.h> + #endif +#endif + +#ifdef _MSC_VER + #include <intrin.h> +#endif + +static int npf_max(int x, int y) { return (x > y) ? x : y; } + +int npf_parse_format_spec(char const *format, npf_format_spec_t *out_spec) { + char const *cur = format; + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + out_spec->left_justified = 0; + out_spec->leading_zero_pad = 0; +#endif + out_spec->case_adjust = 'a'-'A'; // lowercase + out_spec->prepend = 0; + out_spec->alt_form = 0; + + while (*++cur) { // cur points at the leading '%' character + switch (*cur) { // Optional flags +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + case '-': out_spec->left_justified = '-'; out_spec->leading_zero_pad = 0; continue; + case '0': out_spec->leading_zero_pad = !out_spec->left_justified; continue; +#endif + case '+': out_spec->prepend = '+'; continue; + case ' ': if (out_spec->prepend == 0) { out_spec->prepend = ' '; } continue; + case '#': out_spec->alt_form = '#'; continue; + default: break; + } + break; + } + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + out_spec->field_width_opt = NPF_FMT_SPEC_OPT_NONE; + if (*cur == '*') { + out_spec->field_width_opt = NPF_FMT_SPEC_OPT_STAR; + ++cur; + } else { + out_spec->field_width = 0; + while ((*cur >= '0') && (*cur <= '9')) { + out_spec->field_width_opt = NPF_FMT_SPEC_OPT_LITERAL; + out_spec->field_width = (out_spec->field_width * 10) + (*cur++ - '0'); + } + } +#endif + +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + out_spec->prec = 0; + out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; + if (*cur == '.') { + ++cur; + if (*cur == '*') { + out_spec->prec_opt = NPF_FMT_SPEC_OPT_STAR; + ++cur; + } else { + if (*cur == '-') { + ++cur; + out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; + } else { + out_spec->prec_opt = NPF_FMT_SPEC_OPT_LITERAL; + } + while ((*cur >= '0') && (*cur <= '9')) { + out_spec->prec = (out_spec->prec * 10) + (*cur++ - '0'); + } + } + } +#endif + + int tmp_conv = -1; + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_NONE; + switch (*cur++) { // Length modifier + case 'h': + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_SHORT; + if (*cur == 'h') { + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_CHAR; + ++cur; + } + break; + case 'l': + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LONG; +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + if (*cur == 'l') { + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_LONG_LONG; + ++cur; + } +#endif + break; +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + case 'L': out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LONG_DOUBLE; break; +#endif +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + case 'j': out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_INTMAX; break; + case 'z': out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_SIZET; break; + case 't': out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_PTRDIFFT; break; +#endif + default: --cur; break; + } + + switch (*cur++) { // Conversion specifier + case '%': out_spec->conv_spec = NPF_FMT_SPEC_CONV_PERCENT; +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; +#endif + break; + + case 'c': out_spec->conv_spec = NPF_FMT_SPEC_CONV_CHAR; +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; +#endif + break; + + case 's': out_spec->conv_spec = NPF_FMT_SPEC_CONV_STRING; +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + out_spec->leading_zero_pad = 0; +#endif + break; + + case 'i': + case 'd': tmp_conv = NPF_FMT_SPEC_CONV_SIGNED_INT; + case 'o': if (tmp_conv == -1) { tmp_conv = NPF_FMT_SPEC_CONV_OCTAL; } + case 'u': if (tmp_conv == -1) { tmp_conv = NPF_FMT_SPEC_CONV_UNSIGNED_INT; } + case 'X': if (tmp_conv == -1) { out_spec->case_adjust = 0; } + case 'x': if (tmp_conv == -1) { tmp_conv = NPF_FMT_SPEC_CONV_HEX_INT; } + out_spec->conv_spec = (npf_format_spec_conversion_t)tmp_conv; +#if (NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1) && \ + (NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1) + if (out_spec->prec_opt != NPF_FMT_SPEC_OPT_NONE) { out_spec->leading_zero_pad = 0; } +#endif + break; + +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + case 'F': out_spec->case_adjust = 0; + case 'f': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_DEC; + if (out_spec->prec_opt == NPF_FMT_SPEC_OPT_NONE) { out_spec->prec = 6; } + break; + + case 'E': out_spec->case_adjust = 0; + case 'e': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_SCI; + if (out_spec->prec_opt == NPF_FMT_SPEC_OPT_NONE) { out_spec->prec = 6; } + break; + + case 'G': out_spec->case_adjust = 0; + case 'g': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_SHORTEST; + if (out_spec->prec_opt == NPF_FMT_SPEC_OPT_NONE) { out_spec->prec = 6; } + break; + + case 'A': out_spec->case_adjust = 0; + case 'a': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_HEX; + if (out_spec->prec_opt == NPF_FMT_SPEC_OPT_NONE) { out_spec->prec = 6; } + break; +#endif + +#if NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS == 1 + case 'n': + // todo: reject string if flags or width or precision exist + out_spec->conv_spec = NPF_FMT_SPEC_CONV_WRITEBACK; +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; +#endif + break; +#endif + + case 'p': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_POINTER; +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; +#endif + break; + +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + case 'B': + out_spec->case_adjust = 0; + case 'b': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_BINARY; + break; +#endif + + default: return 0; + } + + return (int)(cur - format); +} + +int npf_itoa_rev(char *buf, npf_int_t i) { + int n = 0; + int const sign = (i >= 0) ? 1 : -1; + do { *buf++ = (char)('0' + (sign * (i % 10))); i /= 10; ++n; } while (i); + return n; +} + +int npf_utoa_rev(char *buf, npf_uint_t i, unsigned base, unsigned case_adj) { + int n = 0; + do { + unsigned const d = (unsigned)(i % base); + *buf++ = (char)((d < 10) ? ('0' + d) : ('A' + case_adj + (d - 10))); + i /= base; + ++n; + } while (i); + return n; +} + +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 +enum { + NPF_MANTISSA_BITS = 23, + NPF_EXPONENT_BITS = 8, + NPF_EXPONENT_BIAS = 127, + NPF_FRACTION_BIN_DIGITS = 64, + NPF_MAX_FRACTION_DEC_DIGITS = 8 +}; + +int npf_fsplit_abs(float f, uint64_t *out_int_part, uint64_t *out_frac_part, + int *out_frac_base10_neg_exp) { + /* conversion algorithm by Wojciech Muła (zdjęcia@garnek.pl) + http://0x80.pl/notesen/2015-12-29-float-to-string.html + grisu2 (https://bit.ly/2JgMggX) and ryu (https://bit.ly/2RLXSg0) + are fast + precise + round, but require large lookup tables. */ + + uint32_t f_bits; { // union-cast is UB, let compiler optimize byte-copy loop. + char const *src = (char const *)&f; + char *dst = (char *)&f_bits; + for (unsigned i = 0; i < sizeof(f_bits); ++i) { dst[i] = src[i]; } + } + + int const exponent = + ((int)((f_bits >> NPF_MANTISSA_BITS) & ((1u << NPF_EXPONENT_BITS) - 1u)) - + NPF_EXPONENT_BIAS) - NPF_MANTISSA_BITS; + + if (exponent >= (64 - NPF_MANTISSA_BITS)) { return 0; } // value is out of range + + uint32_t const implicit_one = ((uint32_t)1) << NPF_MANTISSA_BITS; + uint32_t const mantissa = f_bits & (implicit_one - 1); + uint32_t const mantissa_norm = mantissa | implicit_one; + + if (exponent > 0) { + *out_int_part = (uint64_t)mantissa_norm << exponent; + } else if (exponent < 0) { + if (-exponent > NPF_MANTISSA_BITS) { + *out_int_part = 0; + } else { + *out_int_part = mantissa_norm >> -exponent; + } + } else { + *out_int_part = mantissa_norm; + } + + uint64_t frac; { + int const shift = NPF_FRACTION_BIN_DIGITS + exponent - 4; + if ((shift >= (NPF_FRACTION_BIN_DIGITS - 4)) || (shift < 0)) { + frac = 0; + } else { + frac = ((uint64_t)mantissa_norm) << shift; + } + // multiply off the leading one's digit + frac &= 0x0fffffffffffffffllu; + frac *= 10; + } + + { // Count the number of 0s at the beginning of the fractional part. + int frac_base10_neg_exp = 0; + while (frac && ((frac >> (NPF_FRACTION_BIN_DIGITS - 4))) == 0) { + ++frac_base10_neg_exp; + frac &= 0x0fffffffffffffffllu; + frac *= 10; + } + *out_frac_base10_neg_exp = frac_base10_neg_exp; + } + + { // Convert the fractional part to base 10. + uint64_t frac_part = 0; + for (int i = 0; frac && (i < NPF_MAX_FRACTION_DEC_DIGITS); ++i) { + frac_part *= 10; + frac_part += (uint64_t)(frac >> (NPF_FRACTION_BIN_DIGITS - 4)); + frac &= 0x0fffffffffffffffllu; + frac *= 10; + } + *out_frac_part = frac_part; + } + return 1; +} + +int npf_ftoa_rev(char *buf, float f, char case_adj, int *out_frac_chars) { + uint32_t f_bits; { // union-cast is UB, let compiler optimize byte-copy loop. + char const *src = (char const *)&f; + char *dst = (char *)&f_bits; + for (unsigned i = 0; i < sizeof(f_bits); ++i) { dst[i] = src[i]; } + } + + if ((uint8_t)(f_bits >> 23) == 0xFF) { + if (f_bits & 0x7fffff) { + for (int i = 0; i < 3; ++i) { *buf++ = (char)("NAN"[i] + case_adj); } + } else { + for (int i = 0; i < 3; ++i) { *buf++ = (char)("FNI"[i] + case_adj); } + } + return -3; + } + + uint64_t int_part, frac_part; + int frac_base10_neg_exp; + if (npf_fsplit_abs(f, &int_part, &frac_part, &frac_base10_neg_exp) == 0) { + for (int i = 0; i < 3; ++i) { *buf++ = (char)("ROO"[i] + case_adj); } + return -3; + } + + char *dst = buf; + + while (frac_part) { // write the fractional digits + *dst++ = (char)('0' + (frac_part % 10)); + frac_part /= 10; + } + + // write the 0 digits between the . and the first fractional digit + while (frac_base10_neg_exp-- > 0) { *dst++ = '0'; } + *out_frac_chars = (int)(dst - buf); + *dst++ = '.'; + + // write the integer digits + do { *dst++ = (char)('0' + (int_part % 10)); int_part /= 10; } while (int_part); + return (int)(dst - buf); +} + +#endif // NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS + +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 +int npf_bin_len(npf_uint_t u) { + // Return the length of the binary string format of 'u', preferring intrinsics. + if (!u) { return 1; } + +#ifdef _MSC_VER // Win64, use _BSR64 for everything. If x86, use _BSR when non-large. + #ifdef _M_X64 + #define NPF_HAVE_BUILTIN_CLZ + #define NPF_CLZ _BitScanReverse64 + #elif NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 0 + #define NPF_HAVE_BUILTIN_CLZ + #define NPF_CLZ _BitScanReverse + #endif + #ifdef NPF_HAVE_BUILTIN_CLZ + unsigned long idx; + NPF_CLZ(&idx, u); + return (int)(idx + 1); + #endif +#elif defined(NANOPRINTF_CLANG) || defined(NANOPRINTF_GCC_PAST_4_6) + #define NPF_HAVE_BUILTIN_CLZ + #if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + #define NPF_CLZ(X) ((sizeof(long long) * 8) - (size_t)__builtin_clzll(X)) + #else + #define NPF_CLZ(X) ((sizeof(long) * 8) - (size_t)__builtin_clzl(X)) + #endif + return (int)NPF_CLZ(u); +#endif + +#ifndef NPF_HAVE_BUILTIN_CLZ + int n; + for (n = 0; u; ++n, u >>= 1); // slow but small software fallback + return n; +#else + #undef NPF_HAVE_BUILTIN_CLZ + #undef NPF_CLZ +#endif +} +#endif + +void npf_bufputc(int c, void *ctx) { + npf_bufputc_ctx_t *bpc = (npf_bufputc_ctx_t *)ctx; + if (bpc->cur < bpc->len) { bpc->dst[bpc->cur++] = (char)c; } +} + +void npf_bufputc_nop(int c, void *ctx) { (void)c; (void)ctx; } + +typedef struct npf_cnt_putc_ctx { + npf_putc pc; + void *ctx; + int n; +} npf_cnt_putc_ctx_t; + +static void npf_putc_cnt(int c, void *ctx) { + npf_cnt_putc_ctx_t *pc_cnt = (npf_cnt_putc_ctx_t *)ctx; + ++pc_cnt->n; + pc_cnt->pc(c, pc_cnt->ctx); // sibling-call optimization +} + +#define NPF_PUTC(VAL) do { npf_putc_cnt((int)(VAL), &pc_cnt); } while (0) + +#define NPF_EXTRACT(MOD, CAST_TO, EXTRACT_AS) \ + case NPF_FMT_SPEC_LEN_MOD_##MOD: val = (CAST_TO)va_arg(args, EXTRACT_AS); break + +#define NPF_WRITEBACK(MOD, TYPE) \ + case NPF_FMT_SPEC_LEN_MOD_##MOD: *(va_arg(args, TYPE *)) = (TYPE)pc_cnt.n; break + +int npf_vpprintf(npf_putc pc, void *pc_ctx, char const *format, va_list args) { + npf_format_spec_t fs; + char const *cur = format; + npf_cnt_putc_ctx_t pc_cnt; + pc_cnt.pc = pc; + pc_cnt.ctx = pc_ctx; + pc_cnt.n = 0; + + while (*cur) { + int const fs_len = (*cur != '%') ? 0 : npf_parse_format_spec(cur, &fs); + if (!fs_len) { NPF_PUTC(*cur++); continue; } + cur += fs_len; + + // Extract star-args immediately +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + if (fs.field_width_opt == NPF_FMT_SPEC_OPT_STAR) { + fs.field_width_opt = NPF_FMT_SPEC_OPT_LITERAL; + fs.field_width = va_arg(args, int); + if (fs.field_width < 0) { + fs.field_width = -fs.field_width; + fs.left_justified = 1; + } + } +#endif +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + if (fs.prec_opt == NPF_FMT_SPEC_OPT_STAR) { + fs.prec_opt = NPF_FMT_SPEC_OPT_NONE; + fs.prec = va_arg(args, int); + if (fs.prec >= 0) { fs.prec_opt = NPF_FMT_SPEC_OPT_LITERAL; } + } +#endif + + union { char cbuf_mem[32]; npf_uint_t binval; } u; + char *cbuf = u.cbuf_mem, sign_c = 0; + int cbuf_len = 0, need_0x = 0; +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + int field_pad = 0; + char pad_c = 0; +#endif +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + int prec_pad = 0; +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + int zero = 0; +#endif +#endif +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + int frac_chars = 0, inf_or_nan = 0; +#endif + + // Extract and convert the argument to string, point cbuf at the text. + switch (fs.conv_spec) { + case NPF_FMT_SPEC_CONV_PERCENT: + *cbuf = '%'; + cbuf_len = 1; + break; + + case NPF_FMT_SPEC_CONV_CHAR: + *cbuf = (char)va_arg(args, int); + cbuf_len = 1; + break; + + case NPF_FMT_SPEC_CONV_STRING: { + cbuf = va_arg(args, char *); +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + for (char const *s = cbuf; + *s && ((fs.prec_opt == NPF_FMT_SPEC_OPT_NONE) || (cbuf_len < fs.prec)); + ++s, ++cbuf_len); +#else + for (char const *s = cbuf; *s; ++s, ++cbuf_len); // strlen +#endif + } break; + + case NPF_FMT_SPEC_CONV_SIGNED_INT: { + npf_int_t val = 0; + switch (fs.length_modifier) { + NPF_EXTRACT(NONE, int, int); + NPF_EXTRACT(SHORT, short, int); + NPF_EXTRACT(LONG_DOUBLE, int, int); + NPF_EXTRACT(CHAR, char, int); + NPF_EXTRACT(LONG, long, long); +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + NPF_EXTRACT(LARGE_LONG_LONG, long long, long long); + NPF_EXTRACT(LARGE_INTMAX, intmax_t, intmax_t); + NPF_EXTRACT(LARGE_SIZET, ssize_t, ssize_t); + NPF_EXTRACT(LARGE_PTRDIFFT, ptrdiff_t, ptrdiff_t); +#endif + default: break; + } + + sign_c = (val < 0) ? '-' : fs.prepend; + +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + zero = !val; +#endif + // special case, if prec and value are 0, skip + if (!val && (fs.prec_opt == NPF_FMT_SPEC_OPT_LITERAL) && !fs.prec) { + cbuf_len = 0; + } else +#endif + { cbuf_len = npf_itoa_rev(cbuf, val); } + } break; + +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + case NPF_FMT_SPEC_CONV_BINARY: +#endif + case NPF_FMT_SPEC_CONV_OCTAL: + case NPF_FMT_SPEC_CONV_HEX_INT: + case NPF_FMT_SPEC_CONV_UNSIGNED_INT: { + npf_uint_t val = 0; + + switch (fs.length_modifier) { + NPF_EXTRACT(NONE, unsigned, unsigned); + NPF_EXTRACT(SHORT, unsigned short, unsigned); + NPF_EXTRACT(LONG_DOUBLE, unsigned, unsigned); + NPF_EXTRACT(CHAR, unsigned char, unsigned); + NPF_EXTRACT(LONG, unsigned long, unsigned long); +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + NPF_EXTRACT(LARGE_LONG_LONG, unsigned long long, unsigned long long); + NPF_EXTRACT(LARGE_INTMAX, uintmax_t, uintmax_t); + NPF_EXTRACT(LARGE_SIZET, size_t, size_t); + NPF_EXTRACT(LARGE_PTRDIFFT, size_t, size_t); +#endif + default: break; + } + +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + zero = !val; +#endif + if (!val && (fs.prec_opt == NPF_FMT_SPEC_OPT_LITERAL) && !fs.prec) { + // Zero value and explicitly-requested zero precision means "print nothing". + if ((fs.conv_spec == NPF_FMT_SPEC_CONV_OCTAL) && fs.alt_form) { + fs.prec = 1; // octal special case, print a single '0' + } + } else +#endif +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + if (fs.conv_spec == NPF_FMT_SPEC_CONV_BINARY) { + cbuf_len = npf_bin_len(val); u.binval = val; + } else +#endif + { + unsigned const base = (fs.conv_spec == NPF_FMT_SPEC_CONV_OCTAL) ? + 8u : ((fs.conv_spec == NPF_FMT_SPEC_CONV_HEX_INT) ? 16u : 10u); + cbuf_len = npf_utoa_rev(cbuf, val, base, (unsigned)fs.case_adjust); + } + + if (val && fs.alt_form && (fs.conv_spec == NPF_FMT_SPEC_CONV_OCTAL)) { + cbuf[cbuf_len++] = '0'; // OK to add leading octal '0' immediately. + } + + if (val && fs.alt_form) { // 0x or 0b but can't write it yet. + if (fs.conv_spec == NPF_FMT_SPEC_CONV_HEX_INT) { need_0x = 'X'; } +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + else if (fs.conv_spec == NPF_FMT_SPEC_CONV_BINARY) { need_0x = 'B'; } +#endif + if (need_0x) { need_0x += fs.case_adjust; } + } + } break; + + case NPF_FMT_SPEC_CONV_POINTER: { + cbuf_len = + npf_utoa_rev(cbuf, (npf_uint_t)(uintptr_t)va_arg(args, void *), 16, 'a'-'A'); + need_0x = 'x'; + } break; + +#if NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS == 1 + case NPF_FMT_SPEC_CONV_WRITEBACK: + switch (fs.length_modifier) { + NPF_WRITEBACK(NONE, int); + NPF_WRITEBACK(SHORT, short); + NPF_WRITEBACK(LONG, long); + NPF_WRITEBACK(LONG_DOUBLE, double); + NPF_WRITEBACK(CHAR, signed char); +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + NPF_WRITEBACK(LARGE_LONG_LONG, long long); + NPF_WRITEBACK(LARGE_INTMAX, intmax_t); + NPF_WRITEBACK(LARGE_SIZET, size_t); + NPF_WRITEBACK(LARGE_PTRDIFFT, ptrdiff_t); +#endif + default: break; + } break; +#endif + +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + case NPF_FMT_SPEC_CONV_FLOAT_DEC: + case NPF_FMT_SPEC_CONV_FLOAT_SCI: + case NPF_FMT_SPEC_CONV_FLOAT_SHORTEST: + case NPF_FMT_SPEC_CONV_FLOAT_HEX: { + float val; + if (fs.length_modifier == NPF_FMT_SPEC_LEN_MOD_LONG_DOUBLE) { + val = (float)va_arg(args, long double); + } else { + val = (float)va_arg(args, double); + } + + sign_c = (val < 0.f) ? '-' : fs.prepend; +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + zero = (val == 0.f); +#endif + cbuf_len = npf_ftoa_rev(cbuf, val, fs.case_adjust, &frac_chars); + + if (cbuf_len < 0) { + cbuf_len = -cbuf_len; + inf_or_nan = 1; + } else { + int const prec_adj = npf_max(0, frac_chars - fs.prec); + cbuf += prec_adj; + cbuf_len -= prec_adj; + } + } break; +#endif + default: break; + } + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + // Compute the field width pad character + if (fs.field_width_opt == NPF_FMT_SPEC_OPT_LITERAL) { + if (fs.leading_zero_pad) { // '0' flag is only legal with numeric types + if ((fs.conv_spec != NPF_FMT_SPEC_CONV_STRING) && + (fs.conv_spec != NPF_FMT_SPEC_CONV_CHAR) && + (fs.conv_spec != NPF_FMT_SPEC_CONV_PERCENT)) { +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + if ((fs.prec_opt == NPF_FMT_SPEC_OPT_LITERAL) && !fs.prec && zero) { + pad_c = ' '; + } else +#endif + { pad_c = '0'; } + } + } else { pad_c = ' '; } + } +#endif + + // Compute the number of bytes to truncate or '0'-pad. + if (fs.conv_spec != NPF_FMT_SPEC_CONV_STRING) { +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + if (!inf_or_nan) { // float precision is after the decimal point + int const prec_start = + (fs.conv_spec == NPF_FMT_SPEC_CONV_FLOAT_DEC) ? frac_chars : cbuf_len; + prec_pad = npf_max(0, fs.prec - prec_start); + } +#elif NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + prec_pad = npf_max(0, fs.prec - cbuf_len); +#endif + } + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + // Given the full converted length, how many pad bytes? + field_pad = fs.field_width - cbuf_len - !!sign_c; + if (need_0x) { field_pad -= 2; } + +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + if ((fs.conv_spec == NPF_FMT_SPEC_CONV_FLOAT_DEC) && !fs.prec && !fs.alt_form) { + ++field_pad; // 0-pad, no decimal point. + } +#endif +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + field_pad -= prec_pad; +#endif + field_pad = npf_max(0, field_pad); +#endif // NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + // Apply right-justified field width if requested + if (!fs.left_justified && pad_c) { // If leading zeros pad, sign goes first. + if (pad_c == '0') { + if (sign_c) { NPF_PUTC(sign_c); sign_c = 0; } + // Pad byte is '0', write '0x' before '0' pad chars. + if (need_0x) { NPF_PUTC('0'); NPF_PUTC(need_0x); } + } + while (field_pad-- > 0) { NPF_PUTC(pad_c); } + // Pad byte is ' ', write '0x' after ' ' pad chars but before number. + if ((pad_c != '0') && need_0x) { NPF_PUTC('0'); NPF_PUTC(need_0x); } + } else +#endif + { if (need_0x) { NPF_PUTC('0'); NPF_PUTC(need_0x); } } // no pad, '0x' requested. + + // Write the converted payload + if (fs.conv_spec == NPF_FMT_SPEC_CONV_STRING) { + for (int i = 0; i < cbuf_len; ++i) { NPF_PUTC(cbuf[i]); } + } else { + if (sign_c) { NPF_PUTC(sign_c); } +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + if (fs.conv_spec != NPF_FMT_SPEC_CONV_FLOAT_DEC) { +#endif + +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + while (prec_pad-- > 0) { NPF_PUTC('0'); } // int precision leads. +#endif + +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + } else { + // if 0 precision, skip the fractional part and '.' + // if 0 prec + alternative form, keep the '.' + if (!fs.prec && !fs.alt_form) { ++cbuf; --cbuf_len; } + } +#endif + +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + if (fs.conv_spec == NPF_FMT_SPEC_CONV_BINARY) { + while (cbuf_len) { NPF_PUTC('0' + ((u.binval >> --cbuf_len) & 1)); } + } else +#endif + { while (cbuf_len-- > 0) { NPF_PUTC(cbuf[cbuf_len]); } } // payload is reversed + +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + // real precision comes after the number. + if ((fs.conv_spec == NPF_FMT_SPEC_CONV_FLOAT_DEC) && !inf_or_nan) { + while (prec_pad-- > 0) { NPF_PUTC('0'); } + } +#endif + } + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + if (fs.left_justified && pad_c) { // Apply left-justified field width + while (field_pad-- > 0) { NPF_PUTC(pad_c); } + } +#endif + } + + return pc_cnt.n; +} + +#undef NPF_PUTC +#undef NPF_EXTRACT +#undef NPF_WRITEBACK + +int npf_pprintf(npf_putc pc, void *pc_ctx, char const *format, ...) { + va_list val; + va_start(val, format); + int const rv = npf_vpprintf(pc, pc_ctx, format, val); + va_end(val); + return rv; +} + +int npf_snprintf(char *buffer, size_t bufsz, const char *format, ...) { + va_list val; + va_start(val, format); + int const rv = npf_vsnprintf(buffer, bufsz, format, val); + va_end(val); + return rv; +} + +int npf_vsnprintf(char *buffer, size_t bufsz, char const *format, va_list vlist) { + npf_bufputc_ctx_t bufputc_ctx; + bufputc_ctx.dst = buffer; + bufputc_ctx.len = bufsz; + bufputc_ctx.cur = 0; + + npf_putc const pc = buffer ? npf_bufputc : npf_bufputc_nop; + int const n = npf_vpprintf(pc, &bufputc_ctx, format, vlist); + pc('\0', &bufputc_ctx); + +#ifdef NANOPRINTF_SNPRINTF_SAFE_EMPTY_STRING_ON_OVERFLOW + if (bufsz && (n >= (int)bufsz)) { buffer[0] = '\0'; } +#elif defined(NANOPRINTF_SNPRINTF_SAFE_TRIM_STRING_ON_OVERFLOW) + if (bufsz && (n >= (int)bufsz)) { buffer[bufsz - 1] = '\0'; } +#endif + + return n; +} + +#if NANOPRINTF_HAVE_GCC_WARNING_PRAGMAS + #pragma GCC diagnostic pop +#endif + +#ifdef _MSC_VER + #pragma warning(pop) +#endif + +#endif // NANOPRINTF_IMPLEMENTATION_INCLUDED +#endif // NANOPRINTF_IMPLEMENTATION + +/* + nanoprintf is dual-licensed under both the "Unlicense" and the + "Zero-Clause BSD" (0BSD) licenses. The intent of this dual-licensing + structure is to make nanoprintf as consumable as possible in as many + environments / countries / companies as possible without any + encumberances. + + The text of the two licenses follows below: + + ============================== UNLICENSE ============================== + + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to <http://unlicense.org> + + ================================ 0BSD ================================= + + Copyright (C) 2019- by Charles Nicholson <charles.nicholson+nanoprintf@gmail.com> + + Permission to use, copy, modify, and/or distribute this software for + any purpose with or without fee is hereby granted. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + diff --git a/CRT/ntdll_zw_functions.c b/CRT/ntdll_zw_functions.c new file mode 100644 index 0000000..312440c --- /dev/null +++ b/CRT/ntdll_zw_functions.c @@ -0,0 +1,409 @@ +/* This file was auto generated by gen_wrapper.sh */ +#include <ntddk.h> + +#ifdef __cplusplus +#define _KERNEL_MODE 1 +#include "obfuscate.hpp" + +extern "C" { +#endif + +typedef PVOID NTAPI (*MmMapIoSpaceEx_t) (_In_ PHYSICAL_ADDRESS PhysicalAddress, _In_ SIZE_T NumberOfBytes, _In_ ULONG Protect); +typedef NTSTATUS NTAPI (*ObOpenObjectByPointer_t) (_In_ PVOID obj, _In_ ULONG HandleAttributes, _In_ PACCESS_STATE PassedAccessState, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_TYPE objType, _In_ KPROCESSOR_MODE AccessMode, _Out_ PHANDLE Handle); +typedef NTSTATUS NTAPI (*MmCopyMemory_t) (_In_ PVOID TargetAddress, _In_ PVOID SourceAddress, _In_ SIZE_T NumberOfBytes, _In_ ULONG Flags, _Out_ PSIZE_T NumberOfBytesTransferred); +typedef NTSTATUS NTAPI (*MmCopyVirtualMemory_t) (_In_ PEPROCESS SourceProcess, _In_ PVOID SourceAddress, _In_ PEPROCESS TargetProcess, _In_ PVOID TargetAddress, _In_ SIZE_T BufferSize, _In_ KPROCESSOR_MODE PreviousMode, _Out_ PSIZE_T ReturnSize); +typedef PVOID NTAPI (*RtlLookupFunctionEntry_t) (_In_ DWORD64 ControlPc, _Out_ PDWORD64 ImageBase, _Out_ PVOID HistoryTable); +typedef NTSTATUS NTAPI (*ZwTraceControl_t) (_In_ ULONG FunctionCode, PVOID InBuffer, _In_ ULONG InBufferLen, PVOID OutBuffer, _In_ ULONG OutBufferLen, _Out_ PULONG ReturnLength); +typedef NTSTATUS NTAPI (*ZwTraceEvent_t) (_In_ HANDLE TraceHandle, _In_ ULONG Flags, _In_ ULONG FieldSize, _In_ PVOID Fields); +typedef NTSTATUS NTAPI (*ZwQueryVirtualMemory_t) (_In_ HANDLE ProcessHandle, _In_ PVOID BaseAddress, _In_ int MemoryInformationClass, _Out_ PVOID MemoryInformation, _In_ SIZE_T MemoryInformationLength, _Out_ PSIZE_T ReturnLength); +typedef NTSTATUS NTAPI (*ZwProtectVirtualMemory_t) (_In_ HANDLE ProcessHandle, _In_ _Out_ PVOID* BaseAddress, _In_ _Out_ PSIZE_T NumberOfBytesToProtect, _In_ ULONG NewAccessProtection, _Out_ PULONG OldAccessProtection); +typedef NTSTATUS NTAPI (*ZwQuerySystemInformation_t) (_In_ int SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength); +typedef NTSTATUS NTAPI (*_ZwCreateFile_t) (_Out_ PHANDLE FileHandle, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes, _Out_ PIO_STATUS_BLOCK StatusBlock, _In_ PLARGE_INTEGER AllocationSize, _In_ ULONG FileAttributes, _In_ ULONG ShareAccess, _In_ ULONG CreateDisposition, _In_ ULONG CreateOptions, _In_ PVOID EaBuffer, _In_ ULONG EaLength); +typedef NTSTATUS NTAPI (*_ZwClose_t) (_In_ HANDLE Handle); +typedef NTSTATUS NTAPI (*_ZwWriteFile_t) (_In_ HANDLE FileHandle, _In_ HANDLE Event, _In_ PIO_APC_ROUTINE ApcRoutine, _In_ PVOID ApcContext, _Out_ PIO_STATUS_BLOCK StatusBlock, _In_ PVOID Buffer, _In_ ULONG Length, _In_ PLARGE_INTEGER ByteOffset, _In_ PULONG Key); + +static MmMapIoSpaceEx_t _MmMapIoSpaceEx = NULL; +static ObOpenObjectByPointer_t _ObOpenObjectByPointer = NULL; +static MmCopyMemory_t _MmCopyMemory = NULL; +static MmCopyVirtualMemory_t _MmCopyVirtualMemory = NULL; +static RtlLookupFunctionEntry_t _RtlLookupFunctionEntry = NULL; +static ZwTraceControl_t _ZwTraceControl = NULL; +static ZwTraceEvent_t _ZwTraceEvent = NULL; +static ZwQueryVirtualMemory_t _ZwQueryVirtualMemory = NULL; +static ZwProtectVirtualMemory_t _ZwProtectVirtualMemory = NULL; +static ZwQuerySystemInformation_t _ZwQuerySystemInformation = NULL; +static _ZwCreateFile_t __ZwCreateFile = NULL; +static _ZwClose_t __ZwClose = NULL; +static _ZwWriteFile_t __ZwWriteFile = NULL; + +int __cdecl ntdll_zw_functions (void) +{ + int retval = 0; + UNICODE_STRING fnName; + +#ifdef __cplusplus + RtlInitUnicodeString(&fnName, skCrypt(L"MmMapIoSpaceEx")); +#else + RtlInitUnicodeString(&fnName, L"MmMapIoSpaceEx"); +#endif + _MmMapIoSpaceEx = (MmMapIoSpaceEx_t)MmGetSystemRoutineAddress(&fnName); + if (_MmMapIoSpaceEx == NULL) + { +#ifdef __cplusplus + DbgPrint(skCrypt("%s\n"), skCrypt("System routine MmMapIoSpaceEx not found.")); +#else + DbgPrint("%s\n", "System routine MmMapIoSpaceEx not found."); +#endif + retval++; + } +#ifdef __cplusplus + RtlInitUnicodeString(&fnName, skCrypt(L"ObOpenObjectByPointer")); +#else + RtlInitUnicodeString(&fnName, L"ObOpenObjectByPointer"); +#endif + _ObOpenObjectByPointer = (ObOpenObjectByPointer_t)MmGetSystemRoutineAddress(&fnName); + if (_ObOpenObjectByPointer == NULL) + { +#ifdef __cplusplus + DbgPrint(skCrypt("%s\n"), skCrypt("System routine ObOpenObjectByPointer not found.")); +#else + DbgPrint("%s\n", "System routine ObOpenObjectByPointer not found."); +#endif + retval++; + } +#ifdef __cplusplus + RtlInitUnicodeString(&fnName, skCrypt(L"MmCopyMemory")); +#else + RtlInitUnicodeString(&fnName, L"MmCopyMemory"); +#endif + _MmCopyMemory = (MmCopyMemory_t)MmGetSystemRoutineAddress(&fnName); + if (_MmCopyMemory == NULL) + { +#ifdef __cplusplus + DbgPrint(skCrypt("%s\n"), skCrypt("System routine MmCopyMemory not found.")); +#else + DbgPrint("%s\n", "System routine MmCopyMemory not found."); +#endif + retval++; + } +#ifdef __cplusplus + RtlInitUnicodeString(&fnName, skCrypt(L"MmCopyVirtualMemory")); +#else + RtlInitUnicodeString(&fnName, L"MmCopyVirtualMemory"); +#endif + _MmCopyVirtualMemory = (MmCopyVirtualMemory_t)MmGetSystemRoutineAddress(&fnName); + if (_MmCopyVirtualMemory == NULL) + { +#ifdef __cplusplus + DbgPrint(skCrypt("%s\n"), skCrypt("System routine MmCopyVirtualMemory not found.")); +#else + DbgPrint("%s\n", "System routine MmCopyVirtualMemory not found."); +#endif + retval++; + } +#ifdef __cplusplus + RtlInitUnicodeString(&fnName, skCrypt(L"RtlLookupFunctionEntry")); +#else + RtlInitUnicodeString(&fnName, L"RtlLookupFunctionEntry"); +#endif + _RtlLookupFunctionEntry = (RtlLookupFunctionEntry_t)MmGetSystemRoutineAddress(&fnName); + if (_RtlLookupFunctionEntry == NULL) + { +#ifdef __cplusplus + DbgPrint(skCrypt("%s\n"), skCrypt("System routine RtlLookupFunctionEntry not found.")); +#else + DbgPrint("%s\n", "System routine RtlLookupFunctionEntry not found."); +#endif + retval++; + } +#ifdef __cplusplus + RtlInitUnicodeString(&fnName, skCrypt(L"ZwTraceControl")); +#else + RtlInitUnicodeString(&fnName, L"ZwTraceControl"); +#endif + _ZwTraceControl = (ZwTraceControl_t)MmGetSystemRoutineAddress(&fnName); + if (_ZwTraceControl == NULL) + { +#ifdef __cplusplus + DbgPrint(skCrypt("%s\n"), skCrypt("System routine ZwTraceControl not found.")); +#else + DbgPrint("%s\n", "System routine ZwTraceControl not found."); +#endif + retval++; + } +#ifdef __cplusplus + RtlInitUnicodeString(&fnName, skCrypt(L"ZwTraceEvent")); +#else + RtlInitUnicodeString(&fnName, L"ZwTraceEvent"); +#endif + _ZwTraceEvent = (ZwTraceEvent_t)MmGetSystemRoutineAddress(&fnName); + if (_ZwTraceEvent == NULL) + { +#ifdef __cplusplus + DbgPrint(skCrypt("%s\n"), skCrypt("System routine ZwTraceEvent not found.")); +#else + DbgPrint("%s\n", "System routine ZwTraceEvent not found."); +#endif + retval++; + } +#ifdef __cplusplus + RtlInitUnicodeString(&fnName, skCrypt(L"ZwQueryVirtualMemory")); +#else + RtlInitUnicodeString(&fnName, L"ZwQueryVirtualMemory"); +#endif + _ZwQueryVirtualMemory = (ZwQueryVirtualMemory_t)MmGetSystemRoutineAddress(&fnName); + if (_ZwQueryVirtualMemory == NULL) + { +#ifdef __cplusplus + DbgPrint(skCrypt("%s\n"), skCrypt("System routine ZwQueryVirtualMemory not found.")); +#else + DbgPrint("%s\n", "System routine ZwQueryVirtualMemory not found."); +#endif + retval++; + } +#ifdef __cplusplus + RtlInitUnicodeString(&fnName, skCrypt(L"ZwProtectVirtualMemory")); +#else + RtlInitUnicodeString(&fnName, L"ZwProtectVirtualMemory"); +#endif + _ZwProtectVirtualMemory = (ZwProtectVirtualMemory_t)MmGetSystemRoutineAddress(&fnName); + if (_ZwProtectVirtualMemory == NULL) + { +#ifdef __cplusplus + DbgPrint(skCrypt("%s\n"), skCrypt("System routine ZwProtectVirtualMemory not found.")); +#else + DbgPrint("%s\n", "System routine ZwProtectVirtualMemory not found."); +#endif + retval++; + } +#ifdef __cplusplus + RtlInitUnicodeString(&fnName, skCrypt(L"ZwQuerySystemInformation")); +#else + RtlInitUnicodeString(&fnName, L"ZwQuerySystemInformation"); +#endif + _ZwQuerySystemInformation = (ZwQuerySystemInformation_t)MmGetSystemRoutineAddress(&fnName); + if (_ZwQuerySystemInformation == NULL) + { +#ifdef __cplusplus + DbgPrint(skCrypt("%s\n"), skCrypt("System routine ZwQuerySystemInformation not found.")); +#else + DbgPrint("%s\n", "System routine ZwQuerySystemInformation not found."); +#endif + retval++; + } +#ifdef __cplusplus + RtlInitUnicodeString(&fnName, skCrypt(L"ZwCreateFile")); +#else + RtlInitUnicodeString(&fnName, L"ZwCreateFile"); +#endif + __ZwCreateFile = (_ZwCreateFile_t)MmGetSystemRoutineAddress(&fnName); + if (__ZwCreateFile == NULL) + { +#ifdef __cplusplus + DbgPrint(skCrypt("%s\n"), skCrypt("System routine ZwCreateFile not found.")); +#else + DbgPrint("%s\n", "System routine ZwCreateFile not found."); +#endif + retval++; + } +#ifdef __cplusplus + RtlInitUnicodeString(&fnName, skCrypt(L"ZwClose")); +#else + RtlInitUnicodeString(&fnName, L"ZwClose"); +#endif + __ZwClose = (_ZwClose_t)MmGetSystemRoutineAddress(&fnName); + if (__ZwClose == NULL) + { +#ifdef __cplusplus + DbgPrint(skCrypt("%s\n"), skCrypt("System routine ZwClose not found.")); +#else + DbgPrint("%s\n", "System routine ZwClose not found."); +#endif + retval++; + } +#ifdef __cplusplus + RtlInitUnicodeString(&fnName, skCrypt(L"ZwWriteFile")); +#else + RtlInitUnicodeString(&fnName, L"ZwWriteFile"); +#endif + __ZwWriteFile = (_ZwWriteFile_t)MmGetSystemRoutineAddress(&fnName); + if (__ZwWriteFile == NULL) + { +#ifdef __cplusplus + DbgPrint(skCrypt("%s\n"), skCrypt("System routine ZwWriteFile not found.")); +#else + DbgPrint("%s\n", "System routine ZwWriteFile not found."); +#endif + retval++; + } + + return retval; +} + + +PVOID NTAPI MmMapIoSpaceEx (_In_ PHYSICAL_ADDRESS PhysicalAddress, _In_ SIZE_T NumberOfBytes, _In_ ULONG Protect) +{ + return _MmMapIoSpaceEx (PhysicalAddress, NumberOfBytes, Protect); +} + +PVOID NTAPI WrapperMmMapIoSpaceEx (_In_ PHYSICAL_ADDRESS PhysicalAddress, _In_ SIZE_T NumberOfBytes, _In_ ULONG Protect) +{ + return _MmMapIoSpaceEx (PhysicalAddress, NumberOfBytes, Protect); +} + +NTSTATUS NTAPI ObOpenObjectByPointer (_In_ PVOID obj, _In_ ULONG HandleAttributes, _In_ PACCESS_STATE PassedAccessState, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_TYPE objType, _In_ KPROCESSOR_MODE AccessMode, _Out_ PHANDLE Handle) +{ + if (_ObOpenObjectByPointer == NULL) + return STATUS_PROCEDURE_NOT_FOUND; + + return _ObOpenObjectByPointer (obj, HandleAttributes, PassedAccessState, DesiredAccess, objType, AccessMode, Handle); +} + +NTSTATUS NTAPI WrapperObOpenObjectByPointer (_In_ PVOID obj, _In_ ULONG HandleAttributes, _In_ PACCESS_STATE PassedAccessState, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_TYPE objType, _In_ KPROCESSOR_MODE AccessMode, _Out_ PHANDLE Handle) +{ + return _ObOpenObjectByPointer (obj, HandleAttributes, PassedAccessState, DesiredAccess, objType, AccessMode, Handle); +} + +NTSTATUS NTAPI MmCopyMemory (_In_ PVOID TargetAddress, _In_ PVOID SourceAddress, _In_ SIZE_T NumberOfBytes, _In_ ULONG Flags, _Out_ PSIZE_T NumberOfBytesTransferred) +{ + if (_MmCopyMemory == NULL) + return STATUS_PROCEDURE_NOT_FOUND; + + return _MmCopyMemory (TargetAddress, SourceAddress, NumberOfBytes, Flags, NumberOfBytesTransferred); +} + +NTSTATUS NTAPI WrapperMmCopyMemory (_In_ PVOID TargetAddress, _In_ PVOID SourceAddress, _In_ SIZE_T NumberOfBytes, _In_ ULONG Flags, _Out_ PSIZE_T NumberOfBytesTransferred) +{ + return _MmCopyMemory (TargetAddress, SourceAddress, NumberOfBytes, Flags, NumberOfBytesTransferred); +} + +NTSTATUS NTAPI MmCopyVirtualMemory (_In_ PEPROCESS SourceProcess, _In_ PVOID SourceAddress, _In_ PEPROCESS TargetProcess, _In_ PVOID TargetAddress, _In_ SIZE_T BufferSize, _In_ KPROCESSOR_MODE PreviousMode, _Out_ PSIZE_T ReturnSize) +{ + if (_MmCopyVirtualMemory == NULL) + return STATUS_PROCEDURE_NOT_FOUND; + + return _MmCopyVirtualMemory (SourceProcess, SourceAddress, TargetProcess, TargetAddress, BufferSize, PreviousMode, ReturnSize); +} + +NTSTATUS NTAPI WrapperMmCopyVirtualMemory (_In_ PEPROCESS SourceProcess, _In_ PVOID SourceAddress, _In_ PEPROCESS TargetProcess, _In_ PVOID TargetAddress, _In_ SIZE_T BufferSize, _In_ KPROCESSOR_MODE PreviousMode, _Out_ PSIZE_T ReturnSize) +{ + return _MmCopyVirtualMemory (SourceProcess, SourceAddress, TargetProcess, TargetAddress, BufferSize, PreviousMode, ReturnSize); +} + +PVOID NTAPI RtlLookupFunctionEntry (_In_ DWORD64 ControlPc, _Out_ PDWORD64 ImageBase, _Out_ PVOID HistoryTable) +{ + return _RtlLookupFunctionEntry (ControlPc, ImageBase, HistoryTable); +} + +PVOID NTAPI WrapperRtlLookupFunctionEntry (_In_ DWORD64 ControlPc, _Out_ PDWORD64 ImageBase, _Out_ PVOID HistoryTable) +{ + return _RtlLookupFunctionEntry (ControlPc, ImageBase, HistoryTable); +} + +NTSTATUS NTAPI ZwTraceControl (_In_ ULONG FunctionCode, PVOID InBuffer, _In_ ULONG InBufferLen, PVOID OutBuffer, _In_ ULONG OutBufferLen, _Out_ PULONG ReturnLength) +{ + if (_ZwTraceControl == NULL) + return STATUS_PROCEDURE_NOT_FOUND; + + return _ZwTraceControl (FunctionCode, InBuffer, InBufferLen, OutBuffer, OutBufferLen, ReturnLength); +} + +NTSTATUS NTAPI WrapperZwTraceControl (_In_ ULONG FunctionCode, PVOID InBuffer, _In_ ULONG InBufferLen, PVOID OutBuffer, _In_ ULONG OutBufferLen, _Out_ PULONG ReturnLength) +{ + return _ZwTraceControl (FunctionCode, InBuffer, InBufferLen, OutBuffer, OutBufferLen, ReturnLength); +} + +NTSTATUS NTAPI ZwTraceEvent (_In_ HANDLE TraceHandle, _In_ ULONG Flags, _In_ ULONG FieldSize, _In_ PVOID Fields) +{ + if (_ZwTraceEvent == NULL) + return STATUS_PROCEDURE_NOT_FOUND; + + return _ZwTraceEvent (TraceHandle, Flags, FieldSize, Fields); +} + +NTSTATUS NTAPI WrapperZwTraceEvent (_In_ HANDLE TraceHandle, _In_ ULONG Flags, _In_ ULONG FieldSize, _In_ PVOID Fields) +{ + return _ZwTraceEvent (TraceHandle, Flags, FieldSize, Fields); +} + +NTSTATUS NTAPI ZwQueryVirtualMemory (_In_ HANDLE ProcessHandle, _In_ PVOID BaseAddress, _In_ int MemoryInformationClass, _Out_ PVOID MemoryInformation, _In_ SIZE_T MemoryInformationLength, _Out_ PSIZE_T ReturnLength) +{ + if (_ZwQueryVirtualMemory == NULL) + return STATUS_PROCEDURE_NOT_FOUND; + + return _ZwQueryVirtualMemory (ProcessHandle, BaseAddress, MemoryInformationClass, MemoryInformation, MemoryInformationLength, ReturnLength); +} + +NTSTATUS NTAPI WrapperZwQueryVirtualMemory (_In_ HANDLE ProcessHandle, _In_ PVOID BaseAddress, _In_ int MemoryInformationClass, _Out_ PVOID MemoryInformation, _In_ SIZE_T MemoryInformationLength, _Out_ PSIZE_T ReturnLength) +{ + return _ZwQueryVirtualMemory (ProcessHandle, BaseAddress, MemoryInformationClass, MemoryInformation, MemoryInformationLength, ReturnLength); +} + +NTSTATUS NTAPI ZwProtectVirtualMemory (_In_ HANDLE ProcessHandle, _In_ _Out_ PVOID* BaseAddress, _In_ _Out_ PSIZE_T NumberOfBytesToProtect, _In_ ULONG NewAccessProtection, _Out_ PULONG OldAccessProtection) +{ + if (_ZwProtectVirtualMemory == NULL) + return STATUS_PROCEDURE_NOT_FOUND; + + return _ZwProtectVirtualMemory (ProcessHandle, BaseAddress, NumberOfBytesToProtect, NewAccessProtection, OldAccessProtection); +} + +NTSTATUS NTAPI WrapperZwProtectVirtualMemory (_In_ HANDLE ProcessHandle, _In_ _Out_ PVOID* BaseAddress, _In_ _Out_ PSIZE_T NumberOfBytesToProtect, _In_ ULONG NewAccessProtection, _Out_ PULONG OldAccessProtection) +{ + return _ZwProtectVirtualMemory (ProcessHandle, BaseAddress, NumberOfBytesToProtect, NewAccessProtection, OldAccessProtection); +} + +NTSTATUS NTAPI ZwQuerySystemInformation (_In_ int SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength) +{ + if (_ZwQuerySystemInformation == NULL) + return STATUS_PROCEDURE_NOT_FOUND; + + return _ZwQuerySystemInformation (SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength); +} + +NTSTATUS NTAPI WrapperZwQuerySystemInformation (_In_ int SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength) +{ + return _ZwQuerySystemInformation (SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength); +} + +NTSTATUS NTAPI _ZwCreateFile (_Out_ PHANDLE FileHandle, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes, _Out_ PIO_STATUS_BLOCK StatusBlock, _In_ PLARGE_INTEGER AllocationSize, _In_ ULONG FileAttributes, _In_ ULONG ShareAccess, _In_ ULONG CreateDisposition, _In_ ULONG CreateOptions, _In_ PVOID EaBuffer, _In_ ULONG EaLength) +{ + if (__ZwCreateFile == NULL) + return STATUS_PROCEDURE_NOT_FOUND; + + return __ZwCreateFile (FileHandle, DesiredAccess, ObjectAttributes, StatusBlock, AllocationSize, FileAttributes, ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength); +} + +NTSTATUS NTAPI WrapperZwCreateFile (_Out_ PHANDLE FileHandle, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes, _Out_ PIO_STATUS_BLOCK StatusBlock, _In_ PLARGE_INTEGER AllocationSize, _In_ ULONG FileAttributes, _In_ ULONG ShareAccess, _In_ ULONG CreateDisposition, _In_ ULONG CreateOptions, _In_ PVOID EaBuffer, _In_ ULONG EaLength) +{ + return __ZwCreateFile (FileHandle, DesiredAccess, ObjectAttributes, StatusBlock, AllocationSize, FileAttributes, ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength); +} + +NTSTATUS NTAPI _ZwClose (_In_ HANDLE Handle) +{ + if (__ZwClose == NULL) + return STATUS_PROCEDURE_NOT_FOUND; + + return __ZwClose (Handle); +} + +NTSTATUS NTAPI WrapperZwClose (_In_ HANDLE Handle) +{ + return __ZwClose (Handle); +} + +NTSTATUS NTAPI _ZwWriteFile (_In_ HANDLE FileHandle, _In_ HANDLE Event, _In_ PIO_APC_ROUTINE ApcRoutine, _In_ PVOID ApcContext, _Out_ PIO_STATUS_BLOCK StatusBlock, _In_ PVOID Buffer, _In_ ULONG Length, _In_ PLARGE_INTEGER ByteOffset, _In_ PULONG Key) +{ + if (__ZwWriteFile == NULL) + return STATUS_PROCEDURE_NOT_FOUND; + + return __ZwWriteFile (FileHandle, Event, ApcRoutine, ApcContext, StatusBlock, Buffer, Length, ByteOffset, Key); +} + +NTSTATUS NTAPI WrapperZwWriteFile (_In_ HANDLE FileHandle, _In_ HANDLE Event, _In_ PIO_APC_ROUTINE ApcRoutine, _In_ PVOID ApcContext, _Out_ PIO_STATUS_BLOCK StatusBlock, _In_ PVOID Buffer, _In_ ULONG Length, _In_ PLARGE_INTEGER ByteOffset, _In_ PULONG Key) +{ + return __ZwWriteFile (FileHandle, Event, ApcRoutine, ApcContext, StatusBlock, Buffer, Length, ByteOffset, Key); +} + +#ifdef __cplusplus +}; +#endif diff --git a/CRT/ntdll_zw_functions.txt b/CRT/ntdll_zw_functions.txt new file mode 100644 index 0000000..2cb1964 --- /dev/null +++ b/CRT/ntdll_zw_functions.txt @@ -0,0 +1,19 @@ +# Some functions that get resolved at runtime +# They can always be called prefixed with "Wrapper" i.e. MmCopyMemory(...) becomes WrapperMmCopyMemory(...) +# If not prefixed with '_', MmCopyMemory(...) should also work + +PVOID NTAPI MmMapIoSpaceEx(_In_ PHYSICAL_ADDRESS PhysicalAddress, _In_ SIZE_T NumberOfBytes, _In_ ULONG Protect); +NTSTATUS NTAPI ObOpenObjectByPointer (_In_ PVOID obj, _In_ ULONG HandleAttributes, _In_ PACCESS_STATE PassedAccessState, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_TYPE objType, _In_ KPROCESSOR_MODE AccessMode, _Out_ PHANDLE Handle); +NTSTATUS NTAPI MmCopyMemory (_In_ PVOID TargetAddress, _In_ PVOID SourceAddress, _In_ SIZE_T NumberOfBytes, _In_ ULONG Flags, _Out_ PSIZE_T NumberOfBytesTransferred); +NTSTATUS NTAPI MmCopyVirtualMemory (_In_ PEPROCESS SourceProcess, _In_ PVOID SourceAddress, _In_ PEPROCESS TargetProcess, _In_ PVOID TargetAddress, _In_ SIZE_T BufferSize, _In_ KPROCESSOR_MODE PreviousMode, _Out_ PSIZE_T ReturnSize); +NTSYSAPI PVOID NTAPI RtlLookupFunctionEntry (_In_ DWORD64 ControlPc, _Out_ PDWORD64 ImageBase, _Out_ PVOID HistoryTable); +NTSYSCALLAPI NTSTATUS NTAPI ZwTraceControl (_In_ ULONG FunctionCode, PVOID InBuffer, _In_ ULONG InBufferLen, PVOID OutBuffer, _In_ ULONG OutBufferLen, _Out_ PULONG ReturnLength) +NTSYSCALLAPI NTSTATUS NTAPI ZwTraceEvent (_In_ HANDLE TraceHandle, _In_ ULONG Flags, _In_ ULONG FieldSize, _In_ PVOID Fields) +NTSYSCALLAPI NTSTATUS NTAPI ZwQueryVirtualMemory(_In_ HANDLE ProcessHandle, _In_ PVOID BaseAddress, _In_ int MemoryInformationClass, _Out_ PVOID MemoryInformation, _In_ SIZE_T MemoryInformationLength, _Out_ PSIZE_T ReturnLength); +NTSYSAPI NTSTATUS NTAPI ZwProtectVirtualMemory(_In_ HANDLE ProcessHandle, _In_ _Out_ PVOID* BaseAddress, _In_ _Out_ PSIZE_T NumberOfBytesToProtect, _In_ ULONG NewAccessProtection, _Out_ PULONG OldAccessProtection); +NTSYSCALLAPI NTSTATUS NTAPI ZwQuerySystemInformation(_In_ int SystemInformationClass, _Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength); + +# Prefixed with a '_', the resulting function should only get called as "Wrapper" i.e. _ZwClose(...) will become WrapperZwClose(...) +NTSYSAPI NTSTATUS NTAPI _ZwCreateFile(_Out_ PHANDLE FileHandle, _In_ ACCESS_MASK DesiredAccess, _In_ POBJECT_ATTRIBUTES ObjectAttributes, _Out_ PIO_STATUS_BLOCK StatusBlock, _In_ PLARGE_INTEGER AllocationSize, _In_ ULONG FileAttributes, _In_ ULONG ShareAccess, _In_ ULONG CreateDisposition, _In_ ULONG CreateOptions, _In_ PVOID EaBuffer, _In_ ULONG EaLength); +NTSYSAPI NTSTATUS NTAPI _ZwClose(_In_ HANDLE Handle); +NTSYSAPI NTSTATUS NTAPI _ZwWriteFile(_In_ HANDLE FileHandle, _In_ HANDLE Event, _In_ PIO_APC_ROUTINE ApcRoutine, _In_ PVOID ApcContext, _Out_ PIO_STATUS_BLOCK StatusBlock, _In_ PVOID Buffer, _In_ ULONG Length, _In_ PLARGE_INTEGER ByteOffset, _In_ PULONG Key); diff --git a/CRT/obfuscate.hpp b/CRT/obfuscate.hpp new file mode 100644 index 0000000..5522aad --- /dev/null +++ b/CRT/obfuscate.hpp @@ -0,0 +1,163 @@ +#pragma once + +/*____________________________________________________________________________________________________________ + +Original Author: skadro +Github: https://github.com/skadro-official +License: See end of file + +skCrypter + Compile-time, Usermode + Kernelmode, safe and lightweight string crypter library for C++11+ + + *Not removing this part is appreciated* +____________________________________________________________________________________________________________*/ + +#ifdef _KERNEL_MODE + namespace std + { + // STRUCT TEMPLATE remove_reference + template <class _Ty> + struct remove_reference { + using type = _Ty; + }; + + template <class _Ty> + struct remove_reference<_Ty&> { + using type = _Ty; + }; + + template <class _Ty> + struct remove_reference<_Ty&&> { + using type = _Ty; + }; + + template <class _Ty> + using remove_reference_t = typename remove_reference<_Ty>::type; + + // STRUCT TEMPLATE remove_const + template <class _Ty> + struct remove_const { // remove top-level const qualifier + using type = _Ty; + }; + + template <class _Ty> + struct remove_const<const _Ty> { + using type = _Ty; + }; + + template <class _Ty> + using remove_const_t = typename remove_const<_Ty>::type; + } +#else + #include <type_traits> +#endif + +namespace skc +{ + template<class _Ty> + using clean_type = typename std::remove_const_t<std::remove_reference_t<_Ty>>; + + template <int _size, char _key1, char _key2, typename T> + class skCrypter + { + public: + __forceinline constexpr skCrypter(T* data) + { + crypt(data); + } + + __forceinline T* get() + { + return _storage; + } + + __forceinline int size() // (w)char count + { + return _size; + } + + __forceinline char key() + { + return _key1; + } + + __forceinline T* encrypt() + { + if (!isEncrypted()) + crypt(_storage); + + return _storage; + } + + __forceinline T* decrypt() + { + if (isEncrypted()) + crypt(_storage); + + return _storage; + } + + __forceinline bool isEncrypted() + { + return _storage[_size - 1] != 0; + } + + __forceinline void clear() // set full storage to 0 + { + for (int i = 0; i < _size; i++) + { + _storage[i] = 0; + } + } + + __forceinline operator T* () + { + decrypt(); + + return _storage; + } + + private: + __forceinline constexpr void crypt(T* data) + { + for (int i = 0; i < _size; i++) + { + _storage[i] = data[i] ^ (_key1 + i % (1 + _key2)); + } + } + + T _storage[_size]{}; + }; +} + +#define skCrypt(str) skCrypt_key(str, __TIME__[4], __TIME__[7]) +#define skCrypt_key(str, key1, key2) []() { \ + constexpr static auto crypted = skc::skCrypter \ + <sizeof(str) / sizeof(str[0]), key1, key2, skc::clean_type<decltype(str[0])>>((skc::clean_type<decltype(str[0])>*)str); \ + return crypted; }() + +/*________________________________________________________________________________ + +MIT License + +Copyright (c) 2020 skadro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +________________________________________________________________________________*/ diff --git a/CRT/ucrt.cpp b/CRT/ucrt.cpp new file mode 100644 index 0000000..19ea583 --- /dev/null +++ b/CRT/ucrt.cpp @@ -0,0 +1,10 @@ +#include <cstdlib> + +void * operator new[](size_t size, const char *, int, unsigned, const char *, int) +{ + return malloc(size); +} +void * operator new[](size_t size, size_t, size_t, const char *, int, unsigned, const char *, int) +{ + return malloc(size); +} |