mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-21 04:33:56 +00:00
11
.clang-tidy
Normal file
11
.clang-tidy
Normal file
@@ -0,0 +1,11 @@
|
||||
Checks: '-*,bugprone-*,cert-dcl21-cpp,cert-dcl50-cpp,cert-env33-c,cert-err52-cpp,cert-err60-cpp,cert-flp30-c,cert-msc50-cpp,cert-msc51-cpp,cppcoreguidelines-*,-cppcoreguidelines-macro-usage,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-type-union-access,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-type-vararg,google-build-using-namespace,google-explicit-constructor,google-global-names-in-headers,google-readability-casting,google-runtime-int,google-runtime-operator,hicpp-*,-hicpp-vararg,misc-*,modernize-*,performance-*,readability-*,-readability-named-parameter,-modernize-use-trailing-return-type,-cppcoreguidelines-avoid-magic-numbers,-readability-magic-numbers,-hicpp-signed-bitwise,-hicpp-uppercase-literal-suffix,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-hicpp-named-parameter,-cppcoreguidelines-avoid-goto,-cppcoreguidelines-avoid-non-const-global-variables,-hicpp-avoid-goto,-cppcoreguidelines-owning-memory,-readability-uppercase-literal-suffix,-readability-implicit-bool-conversion,-hicpp-no-array-decay,-hicpp-no-malloc,-readability-use-anyofallof,-cppcoreguidelines-prefer-member-initializer,-performance-no-int-to-ptr,-cppcoreguidelines-pro-type-const-cast,-cppcoreguidelines-special-member-functions,-hicpp-special-member-functions,-bugprone-reserved-identifier,-bugprone-easily-swappable-parameters, -bugprone-implicit-widening-of-multiplication-result,-google-explicit-constructor,-bugprone-exception-escape,-hicpp-exception-baseclass,-cppcoreguidelines-prefer-member-initializer,-bugprone-macro-parentheses,-bugprone-suspicious-missing-comma,-bugprone-sizeof-expression,-bugprone-throw-keyword-missing,-cppcoreguidelines-no-malloc,-bugprone-branch-clone,-cppcoreguidelines-pro-bounds-constant-array-index,-hicpp-explicit-conversions,-cppcoreguidelines-pro-type-member-init,-hicpp-member-init,-bugprone-lambda-function-name,-readability-function-cognitive-complexity,-misc-no-recursion,-misc-throw-by-value-catch-by-reference,-readability-simplify-boolean-expr,-readability-identifier-length,-readability-container-data-pointer,-cppcoreguidelines-virtual-class-destructor,-misc-non-private-member-variables-in-classes,-modernize-use-default-member-init,-google-readability-casting,-bugprone-suspicious-memory-comparison,-modernize-use-nodiscard,-modernize-concat-nested-namespaces,-cppcoreguidelines-avoid-do-while,-modernize-macro-to-enum,-misc-const-correctness,-cppcoreguidelines-avoid-const-or-ref-data-members,-misc-use-anonymous-namespace,-misc-misplaced-const,-readability-redundant-member-init,-cppcoreguidelines-macro-to-enum,-misc-include-cleaner,-performance-enum-size,-bugprone-empty-catch,-readability-duplicate-include,-readability-avoid-unconditional-preprocessor-if,-cppcoreguidelines-rvalue-reference-param-not-moved,-cppcoreguidelines-missing-std-forward,-bugprone-casting-through-void,-cppcoreguidelines-non-private-member-variables-in-classes,-bugprone-multi-level-implicit-pointer-conversion,-bugprone-unchecked-optional-access'
|
||||
CheckOptions:
|
||||
- key: bugprone-argument-comment.StrictMode
|
||||
value: 1
|
||||
- key: bugprone-exception-escape.FunctionsThatShouldNotThrow
|
||||
value: WinMain,SDL_main
|
||||
- key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic
|
||||
value: 1
|
||||
FormatStyle: 'file'
|
||||
WarningsAsErrors: '*'
|
||||
HeaderFilterRegex: '.*src.*\.hpp$'
|
||||
38
.github/workflows/build.yml
vendored
38
.github/workflows/build.yml
vendored
@@ -24,6 +24,42 @@ on:
|
||||
# cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
clang-tidy:
|
||||
name: Run Clang Tidy
|
||||
runs-on: ubuntu-24.04
|
||||
env:
|
||||
LLVM_VERSION: 20
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install Ninja
|
||||
uses: seanmiddleditch/gha-setup-ninja@v6
|
||||
|
||||
- name: Install Clang
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh ${{ env.LLVM_VERSION }}
|
||||
sudo apt install -y clang-tidy-${{ env.LLVM_VERSION }}
|
||||
sudo apt install -y clang-${{ env.LLVM_VERSION }} lld-${{ env.LLVM_VERSION }}
|
||||
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-${{ env.LLVM_VERSION }} 100
|
||||
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-${{ env.LLVM_VERSION }} 100
|
||||
sudo update-alternatives --set cc /usr/bin/clang-${{ env.LLVM_VERSION }}
|
||||
sudo update-alternatives --set c++ /usr/bin/clang++-${{ env.LLVM_VERSION }}
|
||||
|
||||
- name: CMake Build
|
||||
run: cmake --preset=${{matrix.preset}} -DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/cmake/toolchain/android-ndk.cmake && cmake --build --preset=${{matrix.preset}}
|
||||
if: ${{ startsWith(matrix.platform, 'Android') }}
|
||||
env:
|
||||
ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||
ANDROID_ABI: ${{matrix.abi}}
|
||||
|
||||
- name: CMake Build
|
||||
run: cmake --preset=release -DMOMO_ENABLE_CLANG_TIDY=On && cmake --build --preset=release
|
||||
|
||||
verify-formatting:
|
||||
name: Verify Formatting
|
||||
runs-on: ubuntu-24.04
|
||||
@@ -349,7 +385,7 @@ jobs:
|
||||
summary:
|
||||
name: Pipeline Summary
|
||||
runs-on: ubuntu-24.04
|
||||
needs: [build-apiset-dumper, smoke-test-android, create-emulation-root, build, test, verify-formatting]
|
||||
needs: [clang-tidy, build-apiset-dumper, smoke-test-android, create-emulation-root, build, test, verify-formatting]
|
||||
if: always()
|
||||
steps:
|
||||
- uses: geekyeggo/delete-artifact@v5
|
||||
|
||||
@@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.26.4)
|
||||
##########################################
|
||||
|
||||
option(MOMO_ENABLE_SANITIZER "Enable sanitizer" OFF)
|
||||
option(MOMO_ENABLE_CLANG_TIDY "Enable clang-tidy checks" OFF)
|
||||
option(MOMO_BUILD_AS_LIBRARY "Configure and Build the emulator as a shared library (without the samples and tests)" OFF)
|
||||
|
||||
set(MOMO_REFLECTION_LEVEL "0" CACHE STRING "Reflection level for the build")
|
||||
@@ -62,3 +63,4 @@ momo_targets_disable_warnings(${EXTERNAL_TARGETS})
|
||||
|
||||
momo_targets_expose_includes(${OWN_TARGETS})
|
||||
momo_targets_set_warnings_as_errors(${OWN_TARGETS})
|
||||
momo_targets_enable_clang_tidy(${OWN_TARGETS})
|
||||
|
||||
@@ -346,3 +346,22 @@ endfunction()
|
||||
macro(momo_assign_source_group)
|
||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${ARGN})
|
||||
endmacro()
|
||||
|
||||
##########################################
|
||||
|
||||
function(momo_target_enable_clang_tidy target)
|
||||
if(MOMO_ENABLE_CLANG_TIDY)
|
||||
set(CLANG_TIDY_COMMAND "clang-tidy;--use-color;--config-file=${CMAKE_CURRENT_SOURCE_DIR}/.clang-tidy")
|
||||
|
||||
set_target_properties(${target} PROPERTIES C_CLANG_TIDY "${CLANG_TIDY_COMMAND}")
|
||||
set_target_properties(${target} PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_COMMAND}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
##########################################
|
||||
|
||||
function(momo_targets_enable_clang_tidy)
|
||||
foreach(target ${ARGV})
|
||||
momo_target_enable_clang_tidy(${target})
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
@@ -10,7 +10,9 @@ add_executable(analyzer ${SRC_FILES})
|
||||
|
||||
momo_assign_source_group(${SRC_FILES})
|
||||
|
||||
target_precompile_headers(analyzer PRIVATE std_include.hpp)
|
||||
if(NOT MOMO_ENABLE_CLANG_TIDY)
|
||||
target_precompile_headers(analyzer PRIVATE std_include.hpp)
|
||||
endif()
|
||||
|
||||
target_link_libraries(analyzer PRIVATE
|
||||
reflect
|
||||
|
||||
@@ -164,7 +164,9 @@ namespace
|
||||
static uint64_t count{0};
|
||||
++count;
|
||||
if (count > 100 && count % 10000 != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
win_emu.log.print(color::green,
|
||||
@@ -184,7 +186,9 @@ namespace
|
||||
static uint64_t count{0};
|
||||
++count;
|
||||
if (count > 100 && count % 10000 != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
win_emu.log.print(color::blue, "Writing to executable section %s at 0x%" PRIx64 " via 0x%" PRIx64 "\n",
|
||||
|
||||
@@ -27,4 +27,5 @@
|
||||
|
||||
#include <platform/platform.hpp>
|
||||
|
||||
// NOLINTNEXTLINE(google-global-names-in-headers)
|
||||
using namespace std::literals;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "address.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "../utils/finally.hpp"
|
||||
@@ -125,7 +126,7 @@ namespace network
|
||||
}
|
||||
}
|
||||
|
||||
void address::set_port(const unsigned short port)
|
||||
void address::set_port(const uint16_t port)
|
||||
{
|
||||
switch (this->get_family())
|
||||
{
|
||||
@@ -140,7 +141,7 @@ namespace network
|
||||
}
|
||||
}
|
||||
|
||||
unsigned short address::get_port() const
|
||||
uint16_t address::get_port() const
|
||||
{
|
||||
switch (this->get_family())
|
||||
{
|
||||
@@ -155,27 +156,27 @@ namespace network
|
||||
|
||||
std::string address::to_string() const
|
||||
{
|
||||
char buffer[1000] = {};
|
||||
std::string addr;
|
||||
std::string addr{};
|
||||
std::array<char, 1000> buffer{};
|
||||
|
||||
switch (this->get_family())
|
||||
{
|
||||
case AF_INET:
|
||||
inet_ntop(this->get_family(), &this->address4_.sin_addr, buffer, sizeof(buffer));
|
||||
addr = std::string(buffer);
|
||||
inet_ntop(this->get_family(), &this->address4_.sin_addr, buffer.data(), buffer.size());
|
||||
addr = std::string(buffer.data());
|
||||
break;
|
||||
case AF_INET6:
|
||||
inet_ntop(this->get_family(), &this->address6_.sin6_addr, buffer, sizeof(buffer));
|
||||
addr = "[" + std::string(buffer) + "]";
|
||||
inet_ntop(this->get_family(), &this->address6_.sin6_addr, buffer.data(), buffer.size());
|
||||
addr = "[" + std::string(buffer.data()) + "]";
|
||||
break;
|
||||
default:
|
||||
buffer[0] = '?';
|
||||
buffer[1] = 0;
|
||||
addr = std::string(buffer);
|
||||
addr = std::string(buffer.data());
|
||||
break;
|
||||
}
|
||||
|
||||
return addr + ":"s + std::to_string(this->get_port());
|
||||
return addr + ":" + std::to_string(this->get_port());
|
||||
}
|
||||
|
||||
bool address::is_local() const
|
||||
@@ -187,8 +188,8 @@ namespace network
|
||||
|
||||
// According to: https://en.wikipedia.org/wiki/Private_network
|
||||
|
||||
uint8_t bytes[4];
|
||||
memcpy(bytes, &this->address4_.sin_addr.s_addr, sizeof(bytes));
|
||||
std::array<uint8_t, 4> bytes{};
|
||||
memcpy(bytes.data(), &this->address4_.sin_addr.s_addr, bytes.size());
|
||||
|
||||
// 10.X.X.X
|
||||
if (bytes[0] == 10)
|
||||
@@ -346,7 +347,7 @@ namespace network
|
||||
{
|
||||
address a{};
|
||||
a.set_address(i->ai_addr, static_cast<socklen_t>(i->ai_addrlen));
|
||||
results.emplace_back(std::move(a));
|
||||
results.emplace_back(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -372,6 +373,8 @@ std::size_t std::hash<network::address>::operator()(const network::address& a) c
|
||||
std::string_view{reinterpret_cast<const char*>(a.get_in6_addr().sin6_addr.s6_addr),
|
||||
sizeof(a.get_in6_addr().sin6_addr.s6_addr)});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return hash;
|
||||
|
||||
@@ -63,8 +63,8 @@ namespace network
|
||||
void set_ipv6(const in6_addr& addr);
|
||||
void set_address(const sockaddr* addr, socklen_t length);
|
||||
|
||||
void set_port(unsigned short port);
|
||||
[[nodiscard]] unsigned short get_port() const;
|
||||
void set_port(uint16_t port);
|
||||
[[nodiscard]] uint16_t get_port() const;
|
||||
|
||||
sockaddr& get_addr();
|
||||
sockaddr_in& get_in_addr();
|
||||
|
||||
@@ -71,11 +71,13 @@ namespace network
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-make-member-function-const)
|
||||
bool socket::bind(const address& target)
|
||||
{
|
||||
return ::bind(this->socket_, &target.get_addr(), target.get_size()) == 0;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-make-member-function-const)
|
||||
bool socket::set_blocking(const bool blocking)
|
||||
{
|
||||
return socket::set_blocking(this->socket_, blocking);
|
||||
@@ -89,7 +91,10 @@ namespace network
|
||||
#else
|
||||
int flags = fcntl(s, F_GETFL, 0);
|
||||
if (flags == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
flags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
|
||||
return fcntl(s, F_SETFL, flags) == 0;
|
||||
#endif
|
||||
@@ -97,30 +102,6 @@ namespace network
|
||||
|
||||
bool socket::sleep(const std::chrono::milliseconds timeout, const bool in_poll) const
|
||||
{
|
||||
/*fd_set fdr;
|
||||
FD_ZERO(&fdr);
|
||||
FD_SET(this->socket_, &fdr);
|
||||
|
||||
const auto msec = timeout.count();
|
||||
|
||||
timeval tv{};
|
||||
tv.tv_sec = static_cast<long>(msec / 1000ll);
|
||||
tv.tv_usec = static_cast<long>((msec % 1000) * 1000);
|
||||
|
||||
const auto retval = select(static_cast<int>(this->socket_) + 1, &fdr, nullptr, nullptr, &tv);
|
||||
if (retval == SOCKET_ERROR)
|
||||
{
|
||||
std::this_thread::sleep_for(1ms);
|
||||
return socket_is_ready;
|
||||
}
|
||||
|
||||
if (retval > 0)
|
||||
{
|
||||
return socket_is_ready;
|
||||
}
|
||||
|
||||
return !socket_is_ready;*/
|
||||
|
||||
std::vector<const socket*> sockets{};
|
||||
sockets.push_back(this);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "tcp_client_socket.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
|
||||
namespace network
|
||||
@@ -58,13 +59,13 @@ namespace network
|
||||
|
||||
std::optional<std::string> tcp_client_socket::receive(const std::optional<size_t> max_size)
|
||||
{
|
||||
char buffer[0x2000];
|
||||
const auto size = std::min(sizeof(buffer), max_size.value_or(sizeof(buffer)));
|
||||
std::array<char, 0x2000> buffer{};
|
||||
const auto size = std::min(buffer.size(), max_size.value_or(buffer.size()));
|
||||
|
||||
const auto result = recv(this->get_socket(), buffer, static_cast<int>(size), 0);
|
||||
const auto result = recv(this->get_socket(), buffer.data(), static_cast<int>(size), 0);
|
||||
if (result > 0)
|
||||
{
|
||||
return std::string(buffer, result);
|
||||
return std::string(buffer.data(), static_cast<size_t>(result));
|
||||
}
|
||||
|
||||
if (result == 0 || (result < 0 && GET_SOCKET_ERROR() == SERR(ECONNRESET)))
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "udp_socket.hpp"
|
||||
#include <array>
|
||||
|
||||
namespace network
|
||||
{
|
||||
@@ -31,17 +32,17 @@ namespace network
|
||||
|
||||
std::optional<std::pair<address, std::string>> udp_socket::receive() const
|
||||
{
|
||||
char buffer[0x2000];
|
||||
std::array<char, 0x2000> buffer{};
|
||||
address source{};
|
||||
auto len = source.get_max_size();
|
||||
|
||||
const auto result =
|
||||
recvfrom(this->get_socket(), buffer, static_cast<int>(sizeof(buffer)), 0, &source.get_addr(), &len);
|
||||
recvfrom(this->get_socket(), buffer.data(), static_cast<int>(buffer.size()), 0, &source.get_addr(), &len);
|
||||
if (result == SOCKET_ERROR)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return {{source, std::string(buffer, result)}};
|
||||
return {{source, std::string(buffer.data(), static_cast<size_t>(result))}};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
// NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
#define ACCESS_MASK DWORD
|
||||
#define DEVICE_TYPE DWORD
|
||||
|
||||
@@ -408,3 +410,5 @@ typedef struct _REMOTE_PORT_VIEW64
|
||||
EMULATOR_CAST(std::int64_t, SIZE_T) ViewSize;
|
||||
EmulatorTraits<Emu64>::PVOID ViewBase;
|
||||
} REMOTE_PORT_VIEW64, *PREMOTE_PORT_VIEW64;
|
||||
|
||||
// NOLINTEND(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
#define PROCESSOR_FEATURE_MAX 64
|
||||
#define GDI_HANDLE_BUFFER_SIZE64 60
|
||||
#define RTL_ACTIVATION_CONTEXT_STACK_FRAME_FLAG_RELEASE_ON_DEACTIVATION 0x00000001
|
||||
@@ -388,10 +390,10 @@ typedef struct _GDI_TEB_BATCH64
|
||||
#ifndef OS_WINDOWS
|
||||
typedef struct _GUID
|
||||
{
|
||||
unsigned long Data1;
|
||||
unsigned short Data2;
|
||||
unsigned short Data3;
|
||||
unsigned char Data4[8];
|
||||
uint32_t Data1;
|
||||
uint16_t Data2;
|
||||
uint16_t Data3;
|
||||
uint8_t Data4[8];
|
||||
} GUID;
|
||||
|
||||
typedef struct _PROCESSOR_NUMBER
|
||||
@@ -913,3 +915,5 @@ struct PROCESS_TLS_INFO
|
||||
};
|
||||
|
||||
static_assert(sizeof(PROCESS_TLS_INFO) - sizeof(THREAD_TLS_INFO) == 0x10);
|
||||
|
||||
// NOLINTEND(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
// NOLINTBEGIN(modernize-use-using)
|
||||
|
||||
#define PAGE_EXECUTE 0x10
|
||||
#define PAGE_EXECUTE_READ 0x20
|
||||
#define PAGE_EXECUTE_READWRITE 0x40
|
||||
@@ -127,3 +129,5 @@ typedef struct _MEMORY_REGION_INFORMATION
|
||||
DWORD64 PartitionId; // 19H1
|
||||
DWORD64 NodePreference; // 20H1
|
||||
} MEMORY_REGION_INFORMATION64, *PMEMORY_REGION_INFORMATION64;
|
||||
|
||||
// NOLINTEND(modernize-use-using)
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4201) // nameless struct/union
|
||||
#pragma warning(disable : 4702) // unreachable code
|
||||
#else
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||
#endif
|
||||
@@ -24,8 +26,10 @@
|
||||
#include "network.hpp"
|
||||
#include "threading.hpp"
|
||||
|
||||
#ifdef OS_WINDOWS
|
||||
#pragma warning(pop)
|
||||
#else
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#ifdef OS_WINDOWS
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// NOLINTBEGIN(modernize-use-using)
|
||||
|
||||
#ifdef OS_WINDOWS
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
@@ -51,10 +53,10 @@ using BYTE = std::uint8_t;
|
||||
|
||||
using WORD = std::uint16_t;
|
||||
|
||||
#define UCHAR unsigned char
|
||||
#define UCHAR uint8_t
|
||||
#define BOOLEAN UCHAR
|
||||
|
||||
using CSHORT = short;
|
||||
using CSHORT = int16_t;
|
||||
using USHORT = WORD;
|
||||
|
||||
#define DUMMYSTRUCTNAME
|
||||
@@ -63,3 +65,5 @@ using USHORT = WORD;
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
// NOLINTEND(modernize-use-using)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
// NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
#define CONTEXT_X86_MAIN 0x00010000
|
||||
#define CONTEXT_AMD64_MAIN 0x100000
|
||||
#define CONTEXT_CONTROL_32 (CONTEXT_X86_MAIN | 0x1L)
|
||||
@@ -1005,3 +1007,5 @@ _Struct_size_bytes_(Size) struct EMU_SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX64
|
||||
EMU_GROUP_RELATIONSHIP64 Group;
|
||||
};
|
||||
};
|
||||
|
||||
// NOLINTEND(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
// NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
typedef enum _KEY_INFORMATION_CLASS
|
||||
{
|
||||
KeyBasicInformation, // KEY_BASIC_INFORMATION
|
||||
@@ -77,3 +79,5 @@ struct KEY_VALUE_FULL_INFORMATION
|
||||
ULONG NameLength;
|
||||
char16_t Name[1];
|
||||
};
|
||||
|
||||
// NOLINTEND(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
// NOLINTBEGIN(modernize-use-using)
|
||||
|
||||
typedef enum _EVENT_TYPE
|
||||
{
|
||||
NotificationEvent,
|
||||
@@ -14,3 +16,5 @@ typedef enum _WAIT_TYPE
|
||||
WaitDequeue,
|
||||
WaitDpc,
|
||||
} WAIT_TYPE;
|
||||
|
||||
// NOLINTEND(modernize-use-using)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
// NOLINTBEGIN(modernize-use-using)
|
||||
|
||||
typedef enum _THREADINFOCLASS
|
||||
{
|
||||
ThreadBasicInformation, // q: THREAD_BASIC_INFORMATION
|
||||
@@ -87,3 +89,5 @@ typedef struct _THREAD_TEB_INFORMATION
|
||||
ULONG TebOffset; // Offset in TEB to begin reading from.
|
||||
ULONG BytesToRead; // Number of bytes to read.
|
||||
} THREAD_TEB_INFORMATION, *PTHREAD_TEB_INFORMATION;
|
||||
|
||||
// NOLINTEND(modernize-use-using)
|
||||
|
||||
@@ -13,7 +13,7 @@ struct UNICODE_STRING
|
||||
|
||||
inline std::u16string u8_to_u16(const std::string_view view)
|
||||
{
|
||||
return std::u16string(view.begin(), view.end());
|
||||
return {view.begin(), view.end()};
|
||||
}
|
||||
|
||||
inline std::string u16_to_u8(const std::u16string_view u16_view)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
|
||||
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory
|
||||
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory
|
||||
@@ -339,3 +341,5 @@ struct SECTION_IMAGE_INFORMATION
|
||||
ULONG ImageFileSize;
|
||||
ULONG CheckSum;
|
||||
};
|
||||
|
||||
// NOLINTEND(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "compression.hpp"
|
||||
|
||||
#include <zlib.h>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
|
||||
namespace utils::compression
|
||||
@@ -58,20 +59,20 @@ namespace utils::compression
|
||||
|
||||
int ret{};
|
||||
size_t offset = 0;
|
||||
static thread_local uint8_t dest[ZCHUNK_SIZE] = {0};
|
||||
static thread_local std::array<uint8_t, ZCHUNK_SIZE> dest{};
|
||||
auto& stream = stream_container.get();
|
||||
|
||||
do
|
||||
{
|
||||
const auto input_size = std::min(sizeof(dest), data.size() - offset);
|
||||
const auto input_size = std::min(dest.size(), data.size() - offset);
|
||||
stream.avail_in = static_cast<uInt>(input_size);
|
||||
stream.next_in = reinterpret_cast<const Bytef*>(data.data()) + offset;
|
||||
offset += stream.avail_in;
|
||||
|
||||
do
|
||||
{
|
||||
stream.avail_out = sizeof(dest);
|
||||
stream.next_out = dest;
|
||||
stream.avail_out = static_cast<uInt>(dest.size());
|
||||
stream.next_out = dest.data();
|
||||
|
||||
ret = inflate(&stream, Z_NO_FLUSH);
|
||||
if (ret != Z_OK && ret != Z_STREAM_END)
|
||||
@@ -79,7 +80,7 @@ namespace utils::compression
|
||||
return {};
|
||||
}
|
||||
|
||||
buffer.insert(buffer.end(), dest, dest + sizeof(dest) - stream.avail_out);
|
||||
buffer.insert(buffer.end(), dest.data(), dest.data() + dest.size() - stream.avail_out);
|
||||
} while (stream.avail_out == 0);
|
||||
} while (ret != Z_STREAM_END);
|
||||
|
||||
@@ -102,4 +103,4 @@ namespace utils::compression
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace utils
|
||||
class final_action
|
||||
{
|
||||
public:
|
||||
static_assert(!std::is_reference<F>::value && !std::is_const<F>::value && !std::is_volatile<F>::value,
|
||||
static_assert(!std::is_reference_v<F> && !std::is_const_v<F> && !std::is_volatile_v<F>,
|
||||
"Final_action should store its callable by value");
|
||||
|
||||
explicit final_action(F f) noexcept
|
||||
@@ -33,14 +33,16 @@ namespace utils
|
||||
|
||||
~final_action() noexcept
|
||||
{
|
||||
if (invoke_)
|
||||
f_();
|
||||
if (this->invoke_)
|
||||
{
|
||||
this->f_();
|
||||
}
|
||||
}
|
||||
|
||||
// Added by momo5502
|
||||
void cancel()
|
||||
{
|
||||
invoke_ = false;
|
||||
this->invoke_ = false;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -49,8 +51,8 @@ namespace utils
|
||||
};
|
||||
|
||||
template <class F>
|
||||
final_action<typename std::remove_cv<typename std::remove_reference<F>::type>::type> finally(F&& f) noexcept
|
||||
final_action<std::remove_cv_t<std::remove_reference_t<F>>> finally(F&& f) noexcept
|
||||
{
|
||||
return final_action<typename std::remove_cv<typename std::remove_reference<F>::type>::type>(std::forward<F>(f));
|
||||
return final_action<std::remove_cv_t<std::remove_reference_t<F>>>(std::forward<F>(f));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,12 +51,17 @@ namespace utils::io
|
||||
bool read_file(const std::filesystem::path& file, std::vector<uint8_t>* data)
|
||||
{
|
||||
if (!data)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
data->clear();
|
||||
|
||||
std::ifstream stream(file, std::ios::binary);
|
||||
if (!stream)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*data = std::vector<uint8_t>{(std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>()};
|
||||
return true;
|
||||
@@ -108,14 +113,14 @@ namespace utils::io
|
||||
|
||||
if (recursive)
|
||||
{
|
||||
for (auto& file : std::filesystem::recursive_directory_iterator(directory, code))
|
||||
for (const auto& file : std::filesystem::recursive_directory_iterator(directory, code))
|
||||
{
|
||||
files.push_back(file.path());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto& file : std::filesystem::directory_iterator(directory, code))
|
||||
for (const auto& file : std::filesystem::directory_iterator(directory, code))
|
||||
{
|
||||
files.push_back(file.path());
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace utils::string
|
||||
|
||||
inline wchar_t char_to_lower(const wchar_t val)
|
||||
{
|
||||
return std::towlower(val);
|
||||
return static_cast<wchar_t>(std::towlower(val));
|
||||
}
|
||||
|
||||
template <class Elem, class Traits, class Alloc>
|
||||
|
||||
@@ -15,22 +15,22 @@ enum class memory_permission : uint8_t
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
inline constexpr memory_permission operator&(const memory_permission x, const memory_permission y)
|
||||
constexpr memory_permission operator&(const memory_permission x, const memory_permission y)
|
||||
{
|
||||
return static_cast<memory_permission>(static_cast<uint8_t>(x) & static_cast<uint8_t>(y));
|
||||
}
|
||||
|
||||
inline constexpr memory_permission operator|(const memory_permission x, const memory_permission y)
|
||||
constexpr memory_permission operator|(const memory_permission x, const memory_permission y)
|
||||
{
|
||||
return static_cast<memory_permission>(static_cast<uint8_t>(x) | static_cast<uint8_t>(y));
|
||||
}
|
||||
|
||||
inline constexpr memory_permission operator^(const memory_permission x, const memory_permission y)
|
||||
constexpr memory_permission operator^(const memory_permission x, const memory_permission y)
|
||||
{
|
||||
return static_cast<memory_permission>(static_cast<uint8_t>(x) ^ static_cast<uint8_t>(y));
|
||||
}
|
||||
|
||||
inline constexpr memory_permission operator~(memory_permission x)
|
||||
constexpr memory_permission operator~(memory_permission x)
|
||||
{
|
||||
return static_cast<memory_permission>(~static_cast<uint8_t>(x));
|
||||
}
|
||||
|
||||
@@ -499,8 +499,8 @@ namespace utils
|
||||
|
||||
std::optional<size_t> get_diff(const buffer_serializer& other) const
|
||||
{
|
||||
auto& b1 = this->get_buffer();
|
||||
auto& b2 = other.get_buffer();
|
||||
const auto& b1 = this->get_buffer();
|
||||
const auto& b2 = other.get_buffer();
|
||||
|
||||
const auto s1 = b1.size();
|
||||
const auto s2 = b2.size();
|
||||
|
||||
@@ -10,7 +10,9 @@ add_executable(fuzzer ${SRC_FILES})
|
||||
|
||||
momo_assign_source_group(${SRC_FILES})
|
||||
|
||||
target_precompile_headers(fuzzer PRIVATE std_include.hpp)
|
||||
if(NOT MOMO_ENABLE_CLANG_TIDY)
|
||||
target_precompile_headers(fuzzer PRIVATE std_include.hpp)
|
||||
endif()
|
||||
|
||||
target_link_libraries(fuzzer PRIVATE
|
||||
fuzzing-engine
|
||||
|
||||
@@ -27,4 +27,5 @@
|
||||
|
||||
#include <platform/platform.hpp>
|
||||
|
||||
// NOLINTNEXTLINE(google-global-names-in-headers)
|
||||
using namespace std::literals;
|
||||
|
||||
@@ -64,7 +64,9 @@ namespace gdb_stub
|
||||
|
||||
void send_xfer_data(connection_handler& connection, const std::string& args, const std::string_view data)
|
||||
{
|
||||
size_t offset{}, length{};
|
||||
size_t offset{};
|
||||
size_t length{};
|
||||
|
||||
rt_assert(sscanf_s(args.c_str(), "%zx,%zx", &offset, &length) == 2);
|
||||
|
||||
if (offset >= data.size())
|
||||
|
||||
@@ -10,7 +10,9 @@ add_library(windows-emulator ${SRC_FILES})
|
||||
|
||||
momo_assign_source_group(${SRC_FILES})
|
||||
|
||||
target_precompile_headers(windows-emulator PRIVATE std_include.hpp)
|
||||
if(NOT MOMO_ENABLE_CLANG_TIDY)
|
||||
target_precompile_headers(windows-emulator PRIVATE std_include.hpp)
|
||||
endif()
|
||||
|
||||
target_link_libraries(windows-emulator PRIVATE
|
||||
unicorn-emulator
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include "../std_include.hpp"
|
||||
|
||||
#include "apiset.hpp"
|
||||
#include "default_apiset.hpp"
|
||||
|
||||
@@ -44,7 +46,10 @@ namespace apiset
|
||||
{
|
||||
auto buffer = utils::compression::zlib::decompress(apiset);
|
||||
if (buffer.empty())
|
||||
{
|
||||
throw std::runtime_error("Failed to decompress API-SET");
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@@ -66,7 +71,10 @@ namespace apiset
|
||||
case location::file: {
|
||||
const auto apiset = utils::io::read_file(root / "api-set.bin");
|
||||
if (apiset.empty())
|
||||
{
|
||||
throw std::runtime_error("Failed to read file api-set.bin");
|
||||
}
|
||||
|
||||
return decompress_apiset(apiset);
|
||||
}
|
||||
case location::default_windows_10: {
|
||||
@@ -123,9 +131,9 @@ namespace apiset
|
||||
api_set.HashOffset = static_cast<ULONG>(hash_entries_obj.value() - api_set_map_obj.value());
|
||||
});
|
||||
|
||||
const auto orig_ns_entries =
|
||||
const auto* orig_ns_entries =
|
||||
offset_pointer<API_SET_NAMESPACE_ENTRY>(&orig_api_set_map, orig_api_set_map.EntryOffset);
|
||||
const auto orig_hash_entries =
|
||||
const auto* orig_hash_entries =
|
||||
offset_pointer<API_SET_HASH_ENTRY>(&orig_api_set_map, orig_api_set_map.HashOffset);
|
||||
|
||||
for (ULONG i = 0; i < orig_api_set_map.Count; ++i)
|
||||
@@ -142,7 +150,7 @@ namespace apiset
|
||||
}
|
||||
|
||||
const auto values_obj = allocator.reserve<API_SET_VALUE_ENTRY>(ns_entry.ValueCount);
|
||||
const auto orig_values = offset_pointer<API_SET_VALUE_ENTRY>(&orig_api_set_map, ns_entry.ValueOffset);
|
||||
const auto* orig_values = offset_pointer<API_SET_VALUE_ENTRY>(&orig_api_set_map, ns_entry.ValueOffset);
|
||||
|
||||
ns_entry.ValueOffset = static_cast<ULONG>(values_obj.value() - api_set_map_obj.value());
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <cstdint>
|
||||
|
||||
// Windows 11 - APISET-W11.24H2-26100.2605
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
const uint8_t apiset_w11[] = {
|
||||
0x78, 0xDA, 0xCD, 0x7D, 0x0F, 0x7C, 0xCD, 0xD5, 0xFF, 0xFF, 0x9D, 0x26, 0xCA, 0xD2, 0xAA, 0x29, 0xA2, 0x52, 0x11,
|
||||
0x0A, 0x6D, 0xD7, 0xBF, 0x91, 0x24, 0xAD, 0xA2, 0x54, 0xF2, 0x2F, 0xB1, 0xD8, 0xFF, 0x3F, 0xDC, 0x6D, 0xD7, 0xBD,
|
||||
@@ -1369,6 +1371,8 @@ const uint8_t apiset_w11[] = {
|
||||
};
|
||||
|
||||
// Windows 10 - APISET-W10.22H2-19045.5247
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
const uint8_t apiset_w10[] = {
|
||||
0x78, 0xDA, 0xCD, 0x7D, 0x0B, 0x5C, 0x94, 0x45, 0xF7, 0xFF, 0x62, 0x58, 0x98, 0x64, 0x64, 0x98, 0x9A, 0x98, 0x9A,
|
||||
0x98, 0x5A, 0x6A, 0x88, 0x37, 0x2C, 0x53, 0xF2, 0x52, 0x5A, 0x54, 0xE6, 0x2D, 0x2F, 0xA4, 0xDC, 0x2F, 0xBA, 0x0B,
|
||||
|
||||
@@ -138,7 +138,7 @@ namespace cpu_context
|
||||
}
|
||||
}
|
||||
|
||||
if ((context.ContextFlags & CONTEXT_XSTATE_64) == CONTEXT_INTEGER_64)
|
||||
if ((context.ContextFlags & CONTEXT_INTEGER_64) == CONTEXT_INTEGER_64)
|
||||
{
|
||||
context.MxCsr = emu.reg<uint32_t>(x64_register::mxcsr);
|
||||
for (int i = 0; i < 16; i++)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "../std_include.hpp"
|
||||
#include "afd_endpoint.hpp"
|
||||
#include "afd_types.hpp"
|
||||
|
||||
@@ -11,6 +12,8 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
// NOLINTBEGIN(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
struct afd_creation_data
|
||||
{
|
||||
uint64_t unk1;
|
||||
@@ -45,6 +48,8 @@ namespace
|
||||
uint32_t sin6_scope_id;
|
||||
};
|
||||
|
||||
// NOLINTEND(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
static_assert(sizeof(win_sockaddr) == 16);
|
||||
static_assert(sizeof(win_sockaddr_in) == 16);
|
||||
static_assert(sizeof(win_sockaddr_in6) == 28);
|
||||
@@ -77,7 +82,7 @@ namespace
|
||||
|
||||
int16_t translate_host_to_win_address_family(const int host_af)
|
||||
{
|
||||
for (auto& entry : address_family_map)
|
||||
for (const auto& entry : address_family_map)
|
||||
{
|
||||
if (entry.second == host_af)
|
||||
{
|
||||
@@ -130,7 +135,7 @@ namespace
|
||||
win_addr.sin_port = htons(win_emu.get_emulator_port(a.get_port()));
|
||||
memcpy(&win_addr.sin_addr, &a.get_in_addr().sin_addr, sizeof(win_addr.sin_addr));
|
||||
|
||||
const auto ptr = reinterpret_cast<std::byte*>(&win_addr);
|
||||
const auto* ptr = reinterpret_cast<std::byte*>(&win_addr);
|
||||
return {ptr, ptr + sizeof(win_addr)};
|
||||
}
|
||||
|
||||
@@ -140,12 +145,12 @@ namespace
|
||||
win_addr.sin6_family = translate_host_to_win_address_family(a.get_family());
|
||||
win_addr.sin6_port = htons(win_emu.get_emulator_port(a.get_port()));
|
||||
|
||||
auto& addr = a.get_in6_addr();
|
||||
const auto& addr = a.get_in6_addr();
|
||||
memcpy(&win_addr.sin6_addr, &addr.sin6_addr, sizeof(win_addr.sin6_addr));
|
||||
win_addr.sin6_flowinfo = addr.sin6_flowinfo;
|
||||
win_addr.sin6_scope_id = addr.sin6_scope_id;
|
||||
|
||||
const auto ptr = reinterpret_cast<std::byte*>(&win_addr);
|
||||
const auto* ptr = reinterpret_cast<std::byte*>(&win_addr);
|
||||
return {ptr, ptr + sizeof(win_addr)};
|
||||
}
|
||||
|
||||
@@ -241,7 +246,7 @@ namespace
|
||||
handle_info.emplace_back(handle_info_obj.read(i));
|
||||
}
|
||||
|
||||
return {std::move(poll_info), std::move(handle_info)};
|
||||
return {poll_info, std::move(handle_info)};
|
||||
}
|
||||
|
||||
int16_t map_afd_request_events_to_socket(const ULONG poll_events)
|
||||
@@ -317,7 +322,7 @@ namespace
|
||||
for (size_t i = 0; i < endpoints.size() && i < handles.size(); ++i)
|
||||
{
|
||||
auto& pfd = poll_data.at(i);
|
||||
auto& handle = handles[i];
|
||||
const auto& handle = handles[i];
|
||||
|
||||
pfd.fd = endpoints[i];
|
||||
pfd.events = map_afd_request_events_to_socket(handle.PollEvents);
|
||||
@@ -525,6 +530,11 @@ namespace
|
||||
|
||||
NTSTATUS ioctl_bind(windows_emulator& win_emu, const io_device_context& c) const
|
||||
{
|
||||
if (!this->s_)
|
||||
{
|
||||
throw std::runtime_error("Invalid AFD endpoint socket!");
|
||||
}
|
||||
|
||||
auto data = win_emu.emu().read_memory(c.input_buffer, c.input_buffer_length);
|
||||
|
||||
constexpr auto address_offset = 4;
|
||||
@@ -561,9 +571,9 @@ namespace
|
||||
}
|
||||
|
||||
const auto* endpoint = device->get_internal_device<afd_endpoint>();
|
||||
if (!endpoint)
|
||||
if (!endpoint || !endpoint->s_)
|
||||
{
|
||||
throw std::runtime_error("Device is not an AFD endpoint!");
|
||||
throw std::runtime_error("Invalid AFD endpoint!");
|
||||
}
|
||||
|
||||
endpoints.push_back(*endpoint->s_);
|
||||
@@ -604,6 +614,11 @@ namespace
|
||||
|
||||
NTSTATUS ioctl_receive_datagram(windows_emulator& win_emu, const io_device_context& c)
|
||||
{
|
||||
if (!this->s_)
|
||||
{
|
||||
throw std::runtime_error("Invalid AFD endpoint socket!");
|
||||
}
|
||||
|
||||
auto& emu = win_emu.emu();
|
||||
|
||||
if (c.input_buffer_length < sizeof(AFD_RECV_DATAGRAM_INFO<EmulatorTraits<Emu64>>))
|
||||
@@ -668,6 +683,11 @@ namespace
|
||||
|
||||
NTSTATUS ioctl_send_datagram(windows_emulator& win_emu, const io_device_context& c)
|
||||
{
|
||||
if (!this->s_)
|
||||
{
|
||||
throw std::runtime_error("Invalid AFD endpoint socket!");
|
||||
}
|
||||
|
||||
const auto& emu = win_emu.emu();
|
||||
|
||||
if (c.input_buffer_length < sizeof(AFD_SEND_DATAGRAM_INFO<EmulatorTraits<Emu64>>))
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "../std_include.hpp"
|
||||
|
||||
// NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
typedef LONG TDI_STATUS;
|
||||
|
||||
template <typename Traits>
|
||||
@@ -171,3 +173,5 @@ struct AFD_POLL_INFO64
|
||||
#define AFD_NO_OPERATION 39
|
||||
#define AFD_VALIDATE_GROUP 40
|
||||
#define AFD_GET_UNACCEPTED_CONNECT_DATA 41
|
||||
|
||||
// NOLINTEND(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "../std_include.hpp"
|
||||
#include "mount_point_manager.hpp"
|
||||
|
||||
#include "../windows_emulator.hpp"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "std_include.hpp"
|
||||
#include "emulator_thread.hpp"
|
||||
|
||||
#include "cpu_context.hpp"
|
||||
@@ -219,6 +220,11 @@ bool emulator_thread::is_thread_ready(process_context& process, utils::clock& cl
|
||||
|
||||
void emulator_thread::setup_registers(x64_emulator& emu, const process_context& context) const
|
||||
{
|
||||
if (!this->gs_segment)
|
||||
{
|
||||
throw std::runtime_error("Missing GS segment");
|
||||
}
|
||||
|
||||
setup_stack(emu, this->stack_base, this->stack_size);
|
||||
setup_gs_segment(emu, *this->gs_segment);
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#include "std_include.hpp"
|
||||
#include "exception_dispatch.hpp"
|
||||
#include "process_context.hpp"
|
||||
#include "cpu_context.hpp"
|
||||
|
||||
#include <platform/status.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
using exception_record = EMU_EXCEPTION_RECORD<EmulatorTraits<Emu64>>;
|
||||
|
||||
@@ -43,8 +43,8 @@ class file_system
|
||||
}
|
||||
#endif
|
||||
|
||||
const char root_drive[2] = {win_path.get_drive().value_or('c'), 0};
|
||||
auto root = this->root_ / root_drive;
|
||||
const std::array<char, 2> root_drive{win_path.get_drive().value_or('c'), 0};
|
||||
auto root = this->root_ / root_drive.data();
|
||||
|
||||
auto path = this->root_ / win_path.to_portable_path();
|
||||
path = weakly_canonical(path);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "std_include.hpp"
|
||||
#include "io_device.hpp"
|
||||
#include "devices/afd_endpoint.hpp"
|
||||
#include "devices/mount_point_manager.hpp"
|
||||
|
||||
@@ -151,7 +151,7 @@ class io_device_container : public io_device
|
||||
void work(windows_emulator& win_emu) override
|
||||
{
|
||||
this->assert_validity();
|
||||
return this->device_->work(win_emu);
|
||||
this->device_->work(win_emu);
|
||||
}
|
||||
|
||||
void serialize_object(utils::buffer_serializer& buffer) const override
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "std_include.hpp"
|
||||
#include "kusd_mmio.hpp"
|
||||
#include <utils/time.hpp>
|
||||
#include "windows_emulator.hpp"
|
||||
|
||||
@@ -73,17 +73,20 @@ namespace
|
||||
|
||||
std::string_view format(va_list* ap, const char* message)
|
||||
{
|
||||
thread_local char buffer[0x1000];
|
||||
thread_local std::array<char, 0x1000> buffer{};
|
||||
|
||||
#ifdef _WIN32
|
||||
const int count = _vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer), message, *ap);
|
||||
const int count = _vsnprintf_s(buffer.data(), buffer.size(), buffer.size(), message, *ap);
|
||||
#else
|
||||
const int count = vsnprintf(buffer, sizeof(buffer), message, *ap);
|
||||
const int count = vsnprintf(buffer.data(), buffer.size(), message, *ap);
|
||||
#endif
|
||||
|
||||
if (count < 0)
|
||||
{
|
||||
return {};
|
||||
return {buffer, static_cast<size_t>(count)};
|
||||
}
|
||||
|
||||
return {buffer.data(), static_cast<size_t>(count)};
|
||||
}
|
||||
|
||||
#define format_to_string(msg, str) \
|
||||
@@ -110,36 +113,42 @@ void logger::print(const color c, const std::string_view message) const
|
||||
print_colored(message, get_color_type(c));
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(cert-dcl50-cpp)
|
||||
void logger::print(const color c, const char* message, ...) const
|
||||
{
|
||||
format_to_string(message, data);
|
||||
this->print(c, data);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(cert-dcl50-cpp)
|
||||
void logger::info(const char* message, ...) const
|
||||
{
|
||||
format_to_string(message, data);
|
||||
this->print(color::cyan, data);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(cert-dcl50-cpp)
|
||||
void logger::warn(const char* message, ...) const
|
||||
{
|
||||
format_to_string(message, data);
|
||||
this->print(color::yellow, data);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(cert-dcl50-cpp)
|
||||
void logger::error(const char* message, ...) const
|
||||
{
|
||||
format_to_string(message, data);
|
||||
this->print(color::red, data);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(cert-dcl50-cpp)
|
||||
void logger::success(const char* message, ...) const
|
||||
{
|
||||
format_to_string(message, data);
|
||||
this->print(color::green, data);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(cert-dcl50-cpp)
|
||||
void logger::log(const char* message, ...) const
|
||||
{
|
||||
format_to_string(message, data);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "std_include.hpp"
|
||||
#include "memory_manager.hpp"
|
||||
|
||||
#include "memory_region.hpp"
|
||||
|
||||
@@ -41,7 +41,7 @@ struct mapped_module
|
||||
|
||||
uint64_t find_export(const std::string_view export_name) const
|
||||
{
|
||||
for (auto& symbol : this->exports)
|
||||
for (const auto& symbol : this->exports)
|
||||
{
|
||||
if (symbol.name == export_name)
|
||||
{
|
||||
|
||||
@@ -9,11 +9,11 @@ namespace
|
||||
{
|
||||
uint64_t get_first_section_offset(const PENTHeaders_t<std::uint64_t>& nt_headers, const uint64_t nt_headers_offset)
|
||||
{
|
||||
const uint8_t* nt_headers_addr = reinterpret_cast<const uint8_t*>(&nt_headers);
|
||||
const auto* nt_headers_addr = reinterpret_cast<const uint8_t*>(&nt_headers);
|
||||
size_t optional_header_offset =
|
||||
reinterpret_cast<uintptr_t>(&(nt_headers.OptionalHeader)) - reinterpret_cast<uintptr_t>(&nt_headers);
|
||||
size_t optional_header_size = nt_headers.FileHeader.SizeOfOptionalHeader;
|
||||
const uint8_t* first_section_addr = nt_headers_addr + optional_header_offset + optional_header_size;
|
||||
const auto* first_section_addr = nt_headers_addr + optional_header_offset + optional_header_size;
|
||||
|
||||
const auto first_section_absolute = reinterpret_cast<uint64_t>(first_section_addr);
|
||||
const auto absolute_base = reinterpret_cast<uint64_t>(&nt_headers);
|
||||
@@ -32,7 +32,7 @@ namespace
|
||||
void collect_exports(mapped_module& binary, const utils::safe_buffer_accessor<const uint8_t> buffer,
|
||||
const PEOptionalHeader_t<std::uint64_t>& optional_header)
|
||||
{
|
||||
auto& export_directory_entry = optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
||||
const auto& export_directory_entry = optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
||||
if (export_directory_entry.VirtualAddress == 0 || export_directory_entry.Size == 0)
|
||||
{
|
||||
return;
|
||||
@@ -88,7 +88,7 @@ namespace
|
||||
return;
|
||||
}
|
||||
|
||||
const auto directory = &optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
|
||||
const auto* directory = &optional_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
|
||||
if (directory->Size == 0)
|
||||
{
|
||||
return;
|
||||
@@ -209,7 +209,7 @@ mapped_module map_module_from_data(memory_manager& memory, const std::span<const
|
||||
const auto nt_headers_offset = dos_header.e_lfanew;
|
||||
|
||||
const auto nt_headers = buffer.as<PENTHeaders_t<std::uint64_t>>(nt_headers_offset).get();
|
||||
auto& optional_header = nt_headers.OptionalHeader;
|
||||
const auto& optional_header = nt_headers.OptionalHeader;
|
||||
|
||||
if (nt_headers.FileHeader.Machine != PEMachineType::AMD64)
|
||||
{
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "std_include.hpp"
|
||||
#include "process_context.hpp"
|
||||
|
||||
#include "emulator_utils.hpp"
|
||||
@@ -15,6 +16,7 @@ namespace
|
||||
|
||||
void setup_gdt(x64_emulator& emu, memory_manager& memory)
|
||||
{
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
constexpr uint64_t gdtr[4] = {0, GDT_ADDR, GDT_LIMIT, 0};
|
||||
emu.write_register(x64_register::gdtr, &gdtr, sizeof(gdtr));
|
||||
memory.allocate_memory(GDT_ADDR, GDT_LIMIT, memory_permission::read);
|
||||
|
||||
@@ -50,8 +50,7 @@ struct process_context
|
||||
void setup(x64_emulator& emu, memory_manager& memory, const application_settings& app_settings,
|
||||
const mapped_module& executable, const mapped_module& ntdll, const apiset::container& apiset_container);
|
||||
|
||||
handle create_thread(memory_manager& memory, const uint64_t start_address, const uint64_t argument,
|
||||
const uint64_t stack_size);
|
||||
handle create_thread(memory_manager& memory, uint64_t start_address, uint64_t argument, uint64_t stack_size);
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const;
|
||||
void deserialize(utils::buffer_deserializer& buffer);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "../std_include.hpp"
|
||||
#include "hive_parser.hpp"
|
||||
#include <utils/string.hpp>
|
||||
|
||||
@@ -8,6 +9,8 @@ namespace
|
||||
constexpr uint64_t MAIN_ROOT_OFFSET = 0x1000;
|
||||
constexpr uint64_t MAIN_KEY_BLOCK_OFFSET = MAIN_ROOT_OFFSET + 0x20;
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
struct offset_entry_t
|
||||
{
|
||||
int32_t offset;
|
||||
@@ -52,6 +55,8 @@ namespace
|
||||
char name[255];
|
||||
};
|
||||
|
||||
// NOLINTEND(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
bool read_file_data_safe(std::ifstream& file, const uint64_t offset, void* buffer, const size_t size)
|
||||
{
|
||||
if (file.bad())
|
||||
@@ -170,7 +175,7 @@ void hive_key::parse(std::ifstream& file)
|
||||
const auto offset = read_file_object<int>(file, MAIN_ROOT_OFFSET + this->value_offsets_ + 4, i);
|
||||
const auto value = read_file_object<value_block_t>(file, MAIN_ROOT_OFFSET + offset);
|
||||
|
||||
std::string value_name(value.name, std::min(value.name_len, static_cast<short>(sizeof(value.name))));
|
||||
std::string value_name(value.name, std::min(value.name_len, static_cast<int16_t>(sizeof(value.name))));
|
||||
|
||||
raw_hive_value raw_value{};
|
||||
raw_value.parsed = false;
|
||||
@@ -199,7 +204,7 @@ void hive_key::parse(std::ifstream& file)
|
||||
|
||||
const auto entry_offsets = this->subkey_block_offset_ + offsetof(offsets_t, entries);
|
||||
|
||||
for (short i = 0; i < item.count; ++i)
|
||||
for (int16_t i = 0; i < item.count; ++i)
|
||||
{
|
||||
const auto offset_entry = read_file_object<offset_entry_t>(file, MAIN_ROOT_OFFSET + entry_offsets, i);
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ class hive_key
|
||||
return &entry->second;
|
||||
}
|
||||
|
||||
const hive_value* get_value(std::ifstream& file, const std::string_view name);
|
||||
const hive_value* get_value(std::ifstream& file, std::string_view name);
|
||||
|
||||
private:
|
||||
struct raw_hive_value : hive_value
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "../std_include.hpp"
|
||||
#include "registry_manager.hpp"
|
||||
|
||||
#include <serialization_helper.hpp>
|
||||
@@ -104,7 +105,7 @@ std::optional<registry_key> registry_manager::get_key(const utils::path_key& key
|
||||
return {std::move(reg_key)};
|
||||
}
|
||||
|
||||
const auto entry = iterator->second->get_sub_key(reg_key.path.get());
|
||||
const auto* entry = iterator->second->get_sub_key(reg_key.path.get());
|
||||
if (!entry)
|
||||
{
|
||||
return std::nullopt;
|
||||
@@ -123,7 +124,7 @@ std::optional<registry_value> registry_manager::get_value(const registry_key& ke
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto* entry = iterator->second->get_value(key.path.get(), name);
|
||||
const auto* entry = iterator->second->get_value(key.path.get(), name);
|
||||
if (!entry)
|
||||
{
|
||||
return std::nullopt;
|
||||
|
||||
@@ -62,4 +62,5 @@
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
// NOLINTNEXTLINE(google-global-names-in-headers)
|
||||
using namespace std::literals;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "std_include.hpp"
|
||||
#include "syscall_dispatcher.hpp"
|
||||
#include "syscall_utils.hpp"
|
||||
|
||||
|
||||
@@ -375,7 +375,7 @@ namespace
|
||||
NTSTATUS handle_NtSetEvent(const syscall_context& c, const uint64_t handle,
|
||||
const emulator_object<LONG> previous_state)
|
||||
{
|
||||
const auto entry = c.proc.events.get(handle);
|
||||
auto* entry = c.proc.events.get(handle);
|
||||
if (!entry)
|
||||
{
|
||||
return STATUS_INVALID_HANDLE;
|
||||
@@ -427,7 +427,7 @@ namespace
|
||||
|
||||
if (h.value.type == handle_types::thread)
|
||||
{
|
||||
const auto t = c.proc.threads.get(h);
|
||||
const auto* t = c.proc.threads.get(h);
|
||||
if (t == c.proc.active_thread && t->ref_count == 1)
|
||||
{
|
||||
// TODO: Better handle ref counting
|
||||
@@ -750,7 +750,7 @@ namespace
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
const auto section_entry = c.proc.sections.get(section_handle);
|
||||
auto* section_entry = c.proc.sections.get(section_handle);
|
||||
if (!section_entry)
|
||||
{
|
||||
return STATUS_INVALID_HANDLE;
|
||||
@@ -758,7 +758,7 @@ namespace
|
||||
|
||||
if (section_entry->is_image())
|
||||
{
|
||||
const auto binary = c.win_emu.mod_manager.map_module(section_entry->file_name, c.win_emu.log);
|
||||
const auto* binary = c.win_emu.mod_manager.map_module(section_entry->file_name, c.win_emu.log);
|
||||
if (!binary)
|
||||
{
|
||||
return STATUS_FILE_INVALID;
|
||||
@@ -855,14 +855,14 @@ namespace
|
||||
const auto region_info = c.win_emu.memory.get_region_info(base_address);
|
||||
|
||||
assert(!region_info.is_committed || region_info.is_reserved);
|
||||
|
||||
const auto state = region_info.is_reserved ? MEM_RESERVE : MEM_FREE;
|
||||
image_info.State = region_info.is_committed ? MEM_COMMIT : state;
|
||||
image_info.BaseAddress = reinterpret_cast<void*>(region_info.start);
|
||||
image_info.AllocationBase = reinterpret_cast<void*>(region_info.allocation_base);
|
||||
image_info.AllocationProtect = 0;
|
||||
image_info.PartitionId = 0;
|
||||
image_info.RegionSize = static_cast<int64_t>(region_info.length);
|
||||
image_info.State =
|
||||
region_info.is_committed ? MEM_COMMIT : (region_info.is_reserved ? MEM_RESERVE : MEM_FREE);
|
||||
|
||||
image_info.Protect = map_emulator_to_nt_protection(region_info.permissions);
|
||||
image_info.Type = MEM_PRIVATE;
|
||||
});
|
||||
@@ -882,7 +882,7 @@ namespace
|
||||
return STATUS_BUFFER_OVERFLOW;
|
||||
}
|
||||
|
||||
const auto mod = c.win_emu.mod_manager.find_by_address(base_address);
|
||||
const auto* mod = c.win_emu.mod_manager.find_by_address(base_address);
|
||||
if (!mod)
|
||||
{
|
||||
c.win_emu.log.error("Bad address for memory image request: 0x%" PRIx64 "\n", base_address);
|
||||
@@ -893,7 +893,7 @@ namespace
|
||||
|
||||
info.access([&](MEMORY_IMAGE_INFORMATION64& image_info) {
|
||||
image_info.ImageBase = reinterpret_cast<void*>(mod->image_base);
|
||||
image_info.SizeOfImage = mod->size_of_image;
|
||||
image_info.SizeOfImage = static_cast<int64_t>(mod->size_of_image);
|
||||
image_info.ImageFlags = 0;
|
||||
});
|
||||
|
||||
@@ -1998,11 +1998,11 @@ namespace
|
||||
thread_iterator->second.teb->access([&](TEB64& teb) {
|
||||
entry.ThreadId = teb.ClientId.UniqueThread;
|
||||
|
||||
const auto tls_vector = teb.ThreadLocalStoragePointer;
|
||||
auto* tls_vector = teb.ThreadLocalStoragePointer;
|
||||
|
||||
if (tls_info.TlsRequest == ProcessTlsReplaceIndex)
|
||||
{
|
||||
const auto tls_entry_ptr = tls_vector + tls_info.TlsIndex;
|
||||
auto* tls_entry_ptr = tls_vector + tls_info.TlsIndex;
|
||||
|
||||
const auto old_entry = c.emu.read_memory<EmulatorTraits<Emu64>::PVOID>(tls_entry_ptr);
|
||||
c.emu.write_memory<EmulatorTraits<Emu64>::PVOID>(tls_entry_ptr, entry.TlsModulePointer);
|
||||
@@ -2011,11 +2011,11 @@ namespace
|
||||
}
|
||||
else if (tls_info.TlsRequest == ProcessTlsReplaceVector)
|
||||
{
|
||||
const auto new_tls_vector = entry.TlsVector;
|
||||
auto* new_tls_vector = entry.TlsVector;
|
||||
|
||||
for (uint32_t index = 0; index < tls_info.TlsVectorLength; ++index)
|
||||
{
|
||||
const auto old_entry = c.emu.read_memory<void*>(tls_vector + index);
|
||||
auto* old_entry = c.emu.read_memory<void*>(tls_vector + index);
|
||||
c.emu.write_memory<void*>(new_tls_vector + index, old_entry);
|
||||
}
|
||||
|
||||
@@ -2270,7 +2270,7 @@ namespace
|
||||
const auto attributes = object_attributes.read();
|
||||
if (attributes.ObjectName)
|
||||
{
|
||||
const auto name = read_unicode_string(
|
||||
auto name = read_unicode_string(
|
||||
c.emu, reinterpret_cast<UNICODE_STRING<EmulatorTraits<Emu64>>*>(attributes.ObjectName));
|
||||
c.win_emu.log.print(color::dark_gray, "--> Section with name %s\n", u16_to_u8(name).c_str());
|
||||
s.name = std::move(name);
|
||||
@@ -2510,6 +2510,7 @@ namespace
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
const uint8_t sid[] = {
|
||||
0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x15, 0x00, 0x00, 0x00, 0x84, 0x94,
|
||||
0xD4, 0x04, 0x4B, 0x68, 0x42, 0x34, 0x23, 0xBE, 0x69, 0x4E, 0xE9, 0x03, 0x00, 0x00,
|
||||
@@ -3229,7 +3230,7 @@ namespace
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
const auto file = c.proc.files.get(handle);
|
||||
const auto* file = c.proc.files.get(handle);
|
||||
if (!file)
|
||||
{
|
||||
return STATUS_INVALID_HANDLE;
|
||||
|
||||
@@ -292,7 +292,7 @@ void windows_emulator::perform_thread_switch()
|
||||
|
||||
bool windows_emulator::activate_thread(const uint32_t id)
|
||||
{
|
||||
const auto thread = get_thread_by_id(this->process, id);
|
||||
auto* thread = get_thread_by_id(this->process, id);
|
||||
if (!thread)
|
||||
{
|
||||
return false;
|
||||
@@ -576,6 +576,7 @@ void windows_emulator::deserialize(utils::buffer_deserializer& buffer)
|
||||
this->dispatcher.deserialize(buffer);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
||||
void windows_emulator::save_snapshot()
|
||||
{
|
||||
throw std::runtime_error("Not supported");
|
||||
@@ -591,6 +592,7 @@ void windows_emulator::save_snapshot()
|
||||
// this->process_snapshot_ = this->process;*/
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
|
||||
void windows_emulator::restore_snapshot()
|
||||
{
|
||||
throw std::runtime_error("Not supported");
|
||||
|
||||
@@ -163,7 +163,7 @@ class windows_path
|
||||
throw std::runtime_error("Device path can not be computed for relative paths!");
|
||||
}
|
||||
|
||||
const auto drive_index = *this->drive_ - 'a';
|
||||
const auto drive_index = this->drive_.value_or('a') - 'a';
|
||||
const auto drive_number = std::to_string(drive_index + 1);
|
||||
const std::u16string number(drive_number.begin(), drive_number.end());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user