mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-11 16:46:16 +00:00
Implement ALPC port abstraction and implement DNS resolver port
This commit is contained in:
@@ -26,6 +26,7 @@
|
||||
#include "network.hpp"
|
||||
#include "threading.hpp"
|
||||
#include "window.hpp"
|
||||
#include "port.hpp"
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
80
src/common/platform/port.hpp
Normal file
80
src/common/platform/port.hpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
// NOLINTBEGIN(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
|
||||
#define LPC_REQUEST 1
|
||||
#define LPC_REPLY 2
|
||||
#define LPC_DATAGRAM 3
|
||||
#define LPC_LOST_REPLY 4
|
||||
#define LPC_PORT_CLOSED 5
|
||||
#define LPC_CLIENT_DIED 6
|
||||
#define LPC_EXCEPTION 7
|
||||
#define LPC_DEBUG_EVENT 8
|
||||
#define LPC_ERROR_EVENT 9
|
||||
#define LPC_CONNECTION_REQUEST 10
|
||||
#define LPC_NO_IMPERSONATE 0x4000
|
||||
#define LPC_KERNELMODE_MESSAGE 0x8000
|
||||
|
||||
#define LpcpGetMessageType(x) ((x)->u2.s2.Type & ~LPC_KERNELMODE_MESSAGE)
|
||||
|
||||
struct PORT_MESSAGE64
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
CSHORT DataLength;
|
||||
CSHORT TotalLength;
|
||||
} s1;
|
||||
|
||||
ULONG Length;
|
||||
} u1;
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
CSHORT Type;
|
||||
CSHORT DataInfoOffset;
|
||||
} s2;
|
||||
|
||||
ULONG ZeroInit;
|
||||
} u2;
|
||||
|
||||
union
|
||||
{
|
||||
CLIENT_ID64 ClientId;
|
||||
double DoNotUseThisField;
|
||||
};
|
||||
|
||||
ULONG MessageId;
|
||||
|
||||
union
|
||||
{
|
||||
EmulatorTraits<Emu64>::SIZE_T ClientViewSize; // only valid for LPC_CONNECTION_REQUEST messages
|
||||
ULONG CallbackId; // only valid for LPC_REQUEST messages
|
||||
};
|
||||
};
|
||||
|
||||
struct ALPC_MESSAGE_ATTRIBUTES
|
||||
{
|
||||
ULONG AllocatedAttributes;
|
||||
ULONG ValidAttributes;
|
||||
};
|
||||
|
||||
template <typename Traits>
|
||||
struct PORT_DATA_ENTRY
|
||||
{
|
||||
typename Traits::PVOID Base;
|
||||
ULONG Size;
|
||||
};
|
||||
|
||||
template <typename Traits>
|
||||
struct ALPC_SECURITY_ATTR
|
||||
{
|
||||
ULONG Flags;
|
||||
typename Traits::PVOID SecurityQos;
|
||||
typename Traits::HANDLE ContextHandle;
|
||||
};
|
||||
|
||||
// NOLINTEND(modernize-use-using,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
||||
@@ -983,58 +983,6 @@ struct CLIENT_ID64
|
||||
DWORD64 UniqueThread;
|
||||
};
|
||||
|
||||
struct PORT_MESSAGE64
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
CSHORT DataLength;
|
||||
CSHORT TotalLength;
|
||||
} s1;
|
||||
|
||||
ULONG Length;
|
||||
} u1;
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
CSHORT Type;
|
||||
CSHORT DataInfoOffset;
|
||||
} s2;
|
||||
|
||||
ULONG ZeroInit;
|
||||
} u2;
|
||||
|
||||
union
|
||||
{
|
||||
CLIENT_ID64 ClientId;
|
||||
double DoNotUseThisField;
|
||||
};
|
||||
|
||||
ULONG MessageId;
|
||||
|
||||
union
|
||||
{
|
||||
EmulatorTraits<Emu64>::SIZE_T ClientViewSize; // only valid for LPC_CONNECTION_REQUEST messages
|
||||
ULONG CallbackId; // only valid for LPC_REQUEST messages
|
||||
};
|
||||
};
|
||||
|
||||
struct ALPC_MESSAGE_ATTRIBUTES
|
||||
{
|
||||
ULONG AllocatedAttributes;
|
||||
ULONG ValidAttributes;
|
||||
};
|
||||
|
||||
template <typename Traits>
|
||||
struct PORT_DATA_ENTRY
|
||||
{
|
||||
typename Traits::PVOID Base;
|
||||
ULONG Size;
|
||||
};
|
||||
|
||||
template <typename Traits>
|
||||
struct EMU_RTL_SRWLOCK
|
||||
{
|
||||
|
||||
98
src/emulator/binary_writer.hpp
Normal file
98
src/emulator/binary_writer.hpp
Normal file
@@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
namespace utils
|
||||
{
|
||||
template <typename Traits>
|
||||
class aligned_binary_writer;
|
||||
|
||||
template <typename T, typename Traits>
|
||||
concept Writable = requires(const T ac, aligned_binary_writer<Traits>& writer) {
|
||||
{ ac.write(writer) } -> std::same_as<void>;
|
||||
};
|
||||
|
||||
template <typename Traits>
|
||||
class aligned_binary_writer
|
||||
{
|
||||
public:
|
||||
aligned_binary_writer(memory_interface& mem, uint64_t address)
|
||||
: memory(mem),
|
||||
base_address(address),
|
||||
current_position(address)
|
||||
{
|
||||
}
|
||||
|
||||
void write(const void* data, size_t size, size_t alignment = 1)
|
||||
{
|
||||
align_to(alignment);
|
||||
memory.write_memory(current_position, data, size);
|
||||
current_position += size;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires(!is_optional<T>::value)
|
||||
void write(const T& value)
|
||||
{
|
||||
constexpr auto is_trivially_copyable = std::is_trivially_copyable_v<T>;
|
||||
|
||||
if constexpr (Writable<T, Traits>)
|
||||
{
|
||||
value.write(*this);
|
||||
}
|
||||
else if constexpr (is_trivially_copyable)
|
||||
{
|
||||
write(&value, sizeof(T), alignof(T));
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Type must be trivially copyable or be writable!");
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
void write_ndr_pointer(bool not_null)
|
||||
{
|
||||
write<typename Traits::PVOID>(not_null ? 0x20000 : 0);
|
||||
}
|
||||
|
||||
void write_ndr_u16string(const std::u16string& str)
|
||||
{
|
||||
size_t char_count = str.size() + 1;
|
||||
size_t byte_length = char_count * sizeof(char16_t);
|
||||
|
||||
write<typename Traits::SIZE_T>(char_count);
|
||||
write<typename Traits::SIZE_T>(0);
|
||||
write<typename Traits::SIZE_T>(char_count);
|
||||
write(str.c_str(), byte_length);
|
||||
}
|
||||
|
||||
void pad(size_t count)
|
||||
{
|
||||
std::vector<uint8_t> padding(count, 0);
|
||||
write(padding.data(), count);
|
||||
}
|
||||
|
||||
void align_to(size_t alignment)
|
||||
{
|
||||
size_t offset_val = static_cast<size_t>(current_position) % alignment;
|
||||
if (offset_val != 0)
|
||||
{
|
||||
pad(alignment - offset_val);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t position() const
|
||||
{
|
||||
return current_position;
|
||||
}
|
||||
|
||||
uint64_t offset() const
|
||||
{
|
||||
return current_position - base_address;
|
||||
}
|
||||
|
||||
private:
|
||||
memory_interface& memory; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
|
||||
uint64_t base_address;
|
||||
uint64_t current_position;
|
||||
};
|
||||
}
|
||||
@@ -136,6 +136,7 @@ CALL :collect srvcli.dll
|
||||
CALL :collect wlanapi.dll
|
||||
CALL :collect windowscodecs.dll
|
||||
CALL :collect mobilenetworking.dll
|
||||
CALL :collect FWPUCLNT.dll
|
||||
|
||||
CALL :collect locale.nls
|
||||
CALL :collect c_1252.nls
|
||||
|
||||
109
src/windows-emulator/port.cpp
Normal file
109
src/windows-emulator/port.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
#include "std_include.hpp"
|
||||
#include "port.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "windows_emulator.hpp"
|
||||
#include "ports/api_port.hpp"
|
||||
#include "ports/dns_resolver.hpp"
|
||||
#include "utils/finally.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
struct dummy_port : port
|
||||
{
|
||||
NTSTATUS handle_request(windows_emulator& win_emu, const lpc_request_context&) override
|
||||
{
|
||||
win_emu.log.error("!!! BAD PORT\n");
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
std::unique_ptr<port> create_port(const std::u16string_view port)
|
||||
{
|
||||
if (port == u"\\Windows\\ApiPort")
|
||||
{
|
||||
return create_api_port();
|
||||
}
|
||||
|
||||
if (port == u"\\RPC Control\\DNSResolver")
|
||||
{
|
||||
return create_dns_resolver();
|
||||
}
|
||||
|
||||
return std::make_unique<dummy_port>();
|
||||
}
|
||||
|
||||
NTSTATUS port::handle_message(windows_emulator& win_emu, const lpc_message_context& c)
|
||||
{
|
||||
const auto send_header = c.send_message.read();
|
||||
|
||||
auto recv_header = send_header;
|
||||
recv_header.u2.s2.Type = LPC_REPLY;
|
||||
|
||||
if (send_header.u2.s2.Type == LPC_NO_IMPERSONATE)
|
||||
{
|
||||
recv_header.u2.s2.Type |= LPC_NO_IMPERSONATE;
|
||||
}
|
||||
|
||||
lpc_request_context context{};
|
||||
context.send_buffer = c.send_message.value() + sizeof(PORT_MESSAGE64);
|
||||
context.send_buffer_length = send_header.u1.s1.DataLength;
|
||||
context.recv_buffer = c.receive_message.value() + sizeof(PORT_MESSAGE64);
|
||||
context.recv_buffer_length = recv_header.u1.s1.DataLength;
|
||||
|
||||
NTSTATUS status = this->handle_request(win_emu, context);
|
||||
|
||||
recv_header.u1.s1.DataLength = static_cast<CSHORT>(context.recv_buffer_length);
|
||||
recv_header.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(PORT_MESSAGE64) + context.recv_buffer_length);
|
||||
c.receive_message.write(recv_header);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
NTSTATUS rpc_port::handle_request(windows_emulator& win_emu, const lpc_request_context& c)
|
||||
{
|
||||
const auto operation = win_emu.emu().read_memory<uint32_t>(c.send_buffer);
|
||||
|
||||
switch (operation)
|
||||
{
|
||||
case 1: // Handshake
|
||||
return handle_handshake(win_emu, c);
|
||||
case 0: // Call
|
||||
return handle_rpc_call(win_emu, c);
|
||||
default:
|
||||
win_emu.log.print(color::gray, "Unexpected RPC operation: 0x%X\n", operation);
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
NTSTATUS rpc_port::handle_handshake(windows_emulator& win_emu, const lpc_request_context& c)
|
||||
{
|
||||
win_emu.emu().write_memory<uint32_t>(c.recv_buffer + 8, 0);
|
||||
|
||||
if (win_emu.emu().read_memory<uint32_t>(c.send_buffer + 32) == 3)
|
||||
{
|
||||
win_emu.emu().write_memory<uint32_t>(c.recv_buffer + 32, 2);
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS rpc_port::handle_rpc_call(windows_emulator& win_emu, const lpc_request_context& c)
|
||||
{
|
||||
const auto procedure_id = win_emu.emu().read_memory<uint8_t>(c.send_buffer + 12);
|
||||
|
||||
std::array<uint8_t, 24> header = {0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
procedure_id, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
win_emu.emu().write_memory(c.recv_buffer, header);
|
||||
|
||||
lpc_request_context rpc_context{};
|
||||
rpc_context.send_buffer = c.send_buffer + 0x40;
|
||||
rpc_context.send_buffer_length = c.send_buffer_length - 0x40;
|
||||
rpc_context.recv_buffer = c.recv_buffer + sizeof(header);
|
||||
rpc_context.recv_buffer_length = c.recv_buffer_length;
|
||||
|
||||
NTSTATUS status = this->handle_rpc(win_emu, procedure_id, rpc_context);
|
||||
c.recv_buffer_length = sizeof(header) + rpc_context.recv_buffer_length;
|
||||
|
||||
return status;
|
||||
}
|
||||
187
src/windows-emulator/port.hpp
Normal file
187
src/windows-emulator/port.hpp
Normal file
@@ -0,0 +1,187 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <arch_emulator.hpp>
|
||||
#include <serialization.hpp>
|
||||
|
||||
#include "emulator_utils.hpp"
|
||||
#include "handles.hpp"
|
||||
|
||||
class windows_emulator;
|
||||
struct process_context;
|
||||
|
||||
struct lpc_message_context
|
||||
{
|
||||
emulator_object<PORT_MESSAGE64> send_message;
|
||||
emulator_object<PORT_MESSAGE64> receive_message;
|
||||
|
||||
lpc_message_context(x86_64_emulator& emu)
|
||||
: send_message(emu),
|
||||
receive_message(emu)
|
||||
{
|
||||
}
|
||||
|
||||
lpc_message_context(utils::buffer_deserializer& buffer)
|
||||
: lpc_message_context(buffer.read<x64_emulator_wrapper>().get())
|
||||
{
|
||||
}
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
buffer.write(send_message);
|
||||
buffer.write(receive_message);
|
||||
}
|
||||
|
||||
void deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read(send_message);
|
||||
buffer.read(receive_message);
|
||||
}
|
||||
};
|
||||
|
||||
struct lpc_request_context
|
||||
{
|
||||
emulator_pointer send_buffer{};
|
||||
ULONG send_buffer_length{};
|
||||
emulator_pointer recv_buffer{};
|
||||
mutable ULONG recv_buffer_length{};
|
||||
|
||||
void serialize(utils::buffer_serializer& buffer) const
|
||||
{
|
||||
buffer.write(send_buffer);
|
||||
buffer.write(send_buffer_length);
|
||||
buffer.write(recv_buffer);
|
||||
buffer.write(recv_buffer_length);
|
||||
}
|
||||
|
||||
void deserialize(utils::buffer_deserializer& buffer)
|
||||
{
|
||||
buffer.read(send_buffer);
|
||||
buffer.read(send_buffer_length);
|
||||
buffer.read(recv_buffer);
|
||||
buffer.read(recv_buffer_length);
|
||||
}
|
||||
};
|
||||
|
||||
struct port_creation_data
|
||||
{
|
||||
uint64_t view_base;
|
||||
int64_t view_size;
|
||||
};
|
||||
|
||||
struct port : ref_counted_object
|
||||
{
|
||||
uint64_t view_base{};
|
||||
int64_t view_size{};
|
||||
|
||||
port() = default;
|
||||
~port() override = default;
|
||||
|
||||
port(port&&) = default;
|
||||
port& operator=(port&&) = default;
|
||||
|
||||
port(const port&) = delete;
|
||||
port& operator=(const port&) = delete;
|
||||
|
||||
void serialize_object(utils::buffer_serializer& buffer) const override
|
||||
{
|
||||
buffer.write(this->view_base);
|
||||
buffer.write(this->view_size);
|
||||
}
|
||||
|
||||
void deserialize_object(utils::buffer_deserializer& buffer) override
|
||||
{
|
||||
buffer.read(this->view_base);
|
||||
buffer.read(this->view_size);
|
||||
}
|
||||
|
||||
virtual void create(windows_emulator& win_emu, const port_creation_data& data)
|
||||
{
|
||||
(void)win_emu;
|
||||
view_base = data.view_base;
|
||||
view_size = data.view_size;
|
||||
}
|
||||
|
||||
NTSTATUS handle_message(windows_emulator& win_emu, const lpc_message_context& c);
|
||||
|
||||
virtual NTSTATUS handle_request(windows_emulator& win_emu, const lpc_request_context& c) = 0;
|
||||
};
|
||||
|
||||
struct rpc_port : port
|
||||
{
|
||||
NTSTATUS handle_request(windows_emulator& win_emu, const lpc_request_context& c) override;
|
||||
|
||||
virtual NTSTATUS handle_rpc(windows_emulator& win_emu, uint32_t procedure_id, const lpc_request_context& c) = 0;
|
||||
|
||||
private:
|
||||
static NTSTATUS handle_handshake(windows_emulator& win_emu, const lpc_request_context& c);
|
||||
NTSTATUS handle_rpc_call(windows_emulator& win_emu, const lpc_request_context& c);
|
||||
};
|
||||
|
||||
std::unique_ptr<port> create_port(std::u16string_view port);
|
||||
|
||||
class port_container : public port
|
||||
{
|
||||
public:
|
||||
port_container() = default;
|
||||
|
||||
port_container(std::u16string port, windows_emulator& win_emu, const port_creation_data& data)
|
||||
: port_name_(std::move(port))
|
||||
{
|
||||
this->setup();
|
||||
this->port_->create(win_emu, data);
|
||||
}
|
||||
|
||||
NTSTATUS handle_request(windows_emulator& win_emu, const lpc_request_context& c) override
|
||||
{
|
||||
this->assert_validity();
|
||||
return this->port_->handle_request(win_emu, c);
|
||||
}
|
||||
|
||||
void serialize_object(utils::buffer_serializer& buffer) const override
|
||||
{
|
||||
this->assert_validity();
|
||||
|
||||
buffer.write_string(this->port_name_);
|
||||
this->port_->serialize(buffer);
|
||||
}
|
||||
|
||||
void deserialize_object(utils::buffer_deserializer& buffer) override
|
||||
{
|
||||
buffer.read_string(this->port_name_);
|
||||
this->setup();
|
||||
this->port_->deserialize(buffer);
|
||||
}
|
||||
|
||||
template <typename T = port>
|
||||
requires(std::is_base_of_v<port, T> || std::is_same_v<port, T>)
|
||||
T* get_internal_port() const
|
||||
{
|
||||
this->assert_validity();
|
||||
auto* value = this->port_.get();
|
||||
return dynamic_cast<T*>(value);
|
||||
}
|
||||
|
||||
std::u16string_view get_port_name() const
|
||||
{
|
||||
this->assert_validity();
|
||||
return this->port_name_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::u16string port_name_{};
|
||||
std::unique_ptr<port> port_{};
|
||||
|
||||
void setup()
|
||||
{
|
||||
this->port_ = create_port(this->port_name_);
|
||||
}
|
||||
|
||||
void assert_validity() const
|
||||
{
|
||||
if (!this->port_)
|
||||
{
|
||||
throw std::runtime_error("Port not created!");
|
||||
}
|
||||
}
|
||||
};
|
||||
36
src/windows-emulator/ports/api_port.cpp
Normal file
36
src/windows-emulator/ports/api_port.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "../std_include.hpp"
|
||||
#include "api_port.hpp"
|
||||
|
||||
#include "../windows_emulator.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
struct api_port : port
|
||||
{
|
||||
NTSTATUS handle_request(windows_emulator& win_emu, const lpc_request_context& c) override
|
||||
{
|
||||
// TODO: Fix this. This is broken and wrong.
|
||||
|
||||
try
|
||||
{
|
||||
const emulator_object<PORT_DATA_ENTRY<EmulatorTraits<Emu64>>> data{win_emu.emu(), c.recv_buffer + 0x20};
|
||||
const auto dest = data.read();
|
||||
const auto base = dest.Base;
|
||||
|
||||
const auto value = base + 0x10;
|
||||
win_emu.emu().write_memory(base + 8, &value, sizeof(value));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
std::unique_ptr<port> create_api_port()
|
||||
{
|
||||
return std::make_unique<api_port>();
|
||||
}
|
||||
4
src/windows-emulator/ports/api_port.hpp
Normal file
4
src/windows-emulator/ports/api_port.hpp
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include "../port.hpp"
|
||||
|
||||
std::unique_ptr<port> create_api_port();
|
||||
225
src/windows-emulator/ports/dns_resolver.cpp
Normal file
225
src/windows-emulator/ports/dns_resolver.cpp
Normal file
@@ -0,0 +1,225 @@
|
||||
#include "../std_include.hpp"
|
||||
#include "dns_resolver.hpp"
|
||||
|
||||
#include "binary_writer.hpp"
|
||||
#include "../windows_emulator.hpp"
|
||||
|
||||
#define DNS_TYPE_A 0x01
|
||||
#define DNS_TYPE_CNAME 0x05
|
||||
#define DNS_TYPE_AAAA 0x1C
|
||||
|
||||
#ifndef OS_WINDOWS
|
||||
#define ERROR_SUCCESS 0x0
|
||||
#define DNS_ERROR_RCODE_NAME_ERROR 0x232B
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
using IP4_ADDRESS = DWORD;
|
||||
|
||||
struct DNS_A_DATA
|
||||
{
|
||||
IP4_ADDRESS IpAddress;
|
||||
};
|
||||
|
||||
struct IP6_ADDRESS
|
||||
{
|
||||
std::array<uint64_t, 2> IP6Qword;
|
||||
};
|
||||
|
||||
struct DNS_AAAA_DATA
|
||||
{
|
||||
IP6_ADDRESS Ip6Address;
|
||||
};
|
||||
|
||||
template <typename Traits>
|
||||
struct DNS_RECORDW
|
||||
{
|
||||
EMULATOR_CAST(Traits::PVOID, DNS_RECORDW*) pNext;
|
||||
EMULATOR_CAST(Traits::PVOID, const char16_t*) pName;
|
||||
WORD wType;
|
||||
WORD wDataLength;
|
||||
DWORD Flags;
|
||||
DWORD dwTtl;
|
||||
DWORD dwReserved;
|
||||
union
|
||||
{
|
||||
DNS_A_DATA A;
|
||||
DNS_AAAA_DATA AAAA;
|
||||
} Data;
|
||||
|
||||
void write(utils::aligned_binary_writer<Traits>& writer) const
|
||||
{
|
||||
writer.write_ndr_pointer(this->pNext);
|
||||
writer.write_ndr_pointer(this->pName);
|
||||
writer.write(this->wType);
|
||||
writer.write(this->wDataLength);
|
||||
writer.write(this->Flags);
|
||||
writer.write(this->dwTtl);
|
||||
writer.write(this->dwReserved);
|
||||
|
||||
writer.write(this->wType); // union identifier
|
||||
writer.write(&this->Data, this->wDataLength, sizeof(typename Traits::PVOID));
|
||||
|
||||
writer.write_ndr_u16string(reinterpret_cast<const char16_t*>(this->pName));
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(DNS_RECORDW<EmulatorTraits<Emu64>>) == 48);
|
||||
|
||||
template <typename Traits>
|
||||
struct DNS_QUERY_RESPONSE
|
||||
{
|
||||
std::optional<DNS_RECORDW<Traits>> dns_record;
|
||||
uint64_t error_code{};
|
||||
|
||||
void write(utils::aligned_binary_writer<Traits>& writer) const
|
||||
{
|
||||
// NOTE: The response is pretty much just an array of DNS_RECORD, marshalled using NDR64.
|
||||
if (!this->dns_record)
|
||||
{
|
||||
writer.write_ndr_pointer(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.write_ndr_pointer(true);
|
||||
writer.write(*this->dns_record);
|
||||
}
|
||||
|
||||
writer.align_to(sizeof(typename Traits::PVOID));
|
||||
writer.pad(24);
|
||||
writer.write(error_code);
|
||||
writer.pad(64);
|
||||
}
|
||||
};
|
||||
|
||||
WORD convert_socket_famity_to_dns_type(const int family)
|
||||
{
|
||||
switch (family)
|
||||
{
|
||||
case AF_INET:
|
||||
return DNS_TYPE_A;
|
||||
case AF_INET6:
|
||||
return DNS_TYPE_AAAA;
|
||||
default:
|
||||
throw std::runtime_error("Unexpected DNS type!");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
std::optional<DNS_RECORDW<Traits>> resolve_host_address(const std::u16string& host, const WORD dns_type)
|
||||
{
|
||||
addrinfo hints{};
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
|
||||
DNS_RECORDW<Traits> result{};
|
||||
result.pName = reinterpret_cast<Traits::PVOID>(host.c_str());
|
||||
result.Flags = 0x2009;
|
||||
result.dwTtl = 0x708;
|
||||
|
||||
addrinfo* res = nullptr;
|
||||
int status = getaddrinfo(u16_to_u8(host).c_str(), nullptr, &hints, &res);
|
||||
if (status == 0)
|
||||
{
|
||||
for (addrinfo* p = res; p != nullptr && dns_type != result.wType; p = p->ai_next)
|
||||
{
|
||||
if (p->ai_family == AF_INET)
|
||||
{
|
||||
auto* ipv4 = reinterpret_cast<sockaddr_in*>(p->ai_addr);
|
||||
memset(&result.Data, 0, 16);
|
||||
memcpy(&result.Data, &ipv4->sin_addr, sizeof(ipv4->sin_addr));
|
||||
result.wDataLength = sizeof(ipv4->sin_addr);
|
||||
}
|
||||
else if (p->ai_family == AF_INET6)
|
||||
{
|
||||
auto* ipv6 = reinterpret_cast<sockaddr_in6*>(p->ai_addr);
|
||||
memset(&result.Data, 0, 16);
|
||||
memcpy(&result.Data, &ipv6->sin6_addr, sizeof(ipv6->sin6_addr));
|
||||
result.wDataLength = sizeof(ipv6->sin6_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
result.wType = convert_socket_famity_to_dns_type(p->ai_family);
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
}
|
||||
|
||||
if (result.wType == DNS_TYPE_A && dns_type == DNS_TYPE_AAAA)
|
||||
{
|
||||
auto* addr = reinterpret_cast<uint8_t*>(&result.Data);
|
||||
addr[10] = 0xff;
|
||||
addr[11] = 0xff;
|
||||
memcpy(addr + 12, addr, 4);
|
||||
memset(addr, 0, 10);
|
||||
result.wType = DNS_TYPE_AAAA;
|
||||
}
|
||||
|
||||
if (result.wType != dns_type || result.wType == 0)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
struct dns_resolver : rpc_port
|
||||
{
|
||||
NTSTATUS handle_rpc(windows_emulator& win_emu, const uint32_t procedure_id, const lpc_request_context& c) override
|
||||
{
|
||||
std::array<uint8_t, 8> request_cookie;
|
||||
win_emu.emu().read_memory(c.send_buffer + c.send_buffer_length - 8, request_cookie.data(), request_cookie.size());
|
||||
|
||||
utils::aligned_binary_writer<EmulatorTraits<Emu64>> writer(win_emu.emu(), c.recv_buffer);
|
||||
writer.write(request_cookie);
|
||||
|
||||
switch (procedure_id)
|
||||
{
|
||||
case 2:
|
||||
handle_dns_query(win_emu, c, writer);
|
||||
break;
|
||||
case 3:
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
default:
|
||||
throw std::runtime_error("Unimplemented procedure!");
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
template <typename Traits>
|
||||
static void handle_dns_query(windows_emulator& win_emu, const lpc_request_context& c, utils::aligned_binary_writer<Traits>& writer)
|
||||
{
|
||||
auto& emu = win_emu.emu();
|
||||
|
||||
const auto hostname_length = static_cast<size_t>(emu.read_memory<uint64_t>(c.send_buffer + 0x08));
|
||||
const auto hostname_offset = c.send_buffer + 0x20;
|
||||
|
||||
std::u16string hostname;
|
||||
hostname.resize(hostname_length - 1);
|
||||
emu.read_memory(hostname_offset, hostname.data(), (hostname_length - 1) * sizeof(char16_t));
|
||||
|
||||
const auto query_type = emu.read_memory<uint16_t>(hostname_offset + hostname_length * sizeof(char16_t));
|
||||
|
||||
if (query_type != DNS_TYPE_A && query_type != DNS_TYPE_AAAA)
|
||||
{
|
||||
throw std::runtime_error("Unexpected DNS query type!");
|
||||
}
|
||||
|
||||
win_emu.callbacks.on_generic_activity("DNS query: " + u16_to_u8(hostname));
|
||||
|
||||
DNS_QUERY_RESPONSE<Traits> response{};
|
||||
response.dns_record = resolve_host_address<Traits>(hostname, query_type);
|
||||
response.error_code = response.dns_record ? ERROR_SUCCESS : DNS_ERROR_RCODE_NAME_ERROR;
|
||||
writer.write(response);
|
||||
|
||||
c.recv_buffer_length = static_cast<ULONG>(writer.offset());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
std::unique_ptr<port> create_dns_resolver()
|
||||
{
|
||||
return std::make_unique<dns_resolver>();
|
||||
}
|
||||
4
src/windows-emulator/ports/dns_resolver.hpp
Normal file
4
src/windows-emulator/ports/dns_resolver.hpp
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include "../port.hpp"
|
||||
|
||||
std::unique_ptr<port> create_dns_resolver();
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "kusd_mmio.hpp"
|
||||
#include "windows_objects.hpp"
|
||||
#include "emulator_thread.hpp"
|
||||
#include "port.hpp"
|
||||
|
||||
#include "apiset/apiset.hpp"
|
||||
|
||||
@@ -107,7 +108,7 @@ struct process_context
|
||||
handle_store<handle_types::section, section> sections{};
|
||||
handle_store<handle_types::device, io_device_container> devices{};
|
||||
handle_store<handle_types::semaphore, semaphore> semaphores{};
|
||||
handle_store<handle_types::port, port> ports{};
|
||||
handle_store<handle_types::port, port_container> ports{};
|
||||
handle_store<handle_types::mutant, mutant> mutants{};
|
||||
handle_store<handle_types::window, window> windows{};
|
||||
handle_store<handle_types::timer, timer> timers{};
|
||||
|
||||
@@ -170,8 +170,23 @@ namespace syscalls
|
||||
emulator_object<REMOTE_PORT_VIEW64> server_shared_memory,
|
||||
emulator_object<ULONG> maximum_message_length, emulator_pointer connection_info,
|
||||
emulator_object<ULONG> connection_info_length);
|
||||
NTSTATUS handle_NtAlpcConnectPort(const syscall_context& c, emulator_object<handle> port_handle,
|
||||
emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> server_port_name,
|
||||
emulator_object<OBJECT_ATTRIBUTES<EmulatorTraits<Emu64>>> /*object_attributes*/,
|
||||
emulator_pointer /*port_attributes*/, ULONG /*flags*/, emulator_pointer /*required_server_sid*/,
|
||||
emulator_pointer /*connection_message*/,
|
||||
emulator_object<EmulatorTraits<Emu64>::SIZE_T> /*buffer_length*/,
|
||||
emulator_pointer /*out_message_attributes*/, emulator_pointer /*in_message_attributes*/,
|
||||
emulator_object<LARGE_INTEGER> /*timeout*/);
|
||||
NTSTATUS handle_NtAlpcConnectPortEx(const syscall_context& c, emulator_object<handle> port_handle,
|
||||
emulator_object<OBJECT_ATTRIBUTES<EmulatorTraits<Emu64>>> connection_port_object_attributes,
|
||||
emulator_object<OBJECT_ATTRIBUTES<EmulatorTraits<Emu64>>> /*client_port_object_attributes*/,
|
||||
emulator_pointer port_attributes, ULONG flags, emulator_pointer /*server_security_requirements*/,
|
||||
emulator_pointer connection_message, emulator_object<EmulatorTraits<Emu64>::SIZE_T> buffer_length,
|
||||
emulator_pointer out_message_attributes, emulator_pointer in_message_attributes,
|
||||
emulator_object<LARGE_INTEGER> timeout);
|
||||
NTSTATUS handle_NtAlpcSendWaitReceivePort(const syscall_context& c, handle port_handle, ULONG /*flags*/,
|
||||
emulator_object<PORT_MESSAGE64> /*send_message*/,
|
||||
emulator_object<PORT_MESSAGE64> send_message,
|
||||
emulator_object<ALPC_MESSAGE_ATTRIBUTES>
|
||||
/*send_message_attributes*/,
|
||||
emulator_object<PORT_MESSAGE64> receive_message,
|
||||
@@ -179,8 +194,9 @@ namespace syscalls
|
||||
emulator_object<ALPC_MESSAGE_ATTRIBUTES>
|
||||
/*receive_message_attributes*/,
|
||||
emulator_object<LARGE_INTEGER> /*timeout*/);
|
||||
NTSTATUS handle_NtAlpcConnectPort();
|
||||
NTSTATUS handle_NtAlpcConnectPortEx();
|
||||
NTSTATUS handle_NtAlpcQueryInformation();
|
||||
NTSTATUS handle_NtAlpcCreateSecurityContext();
|
||||
NTSTATUS handle_NtAlpcDeleteSecurityContext();
|
||||
|
||||
// syscalls/process.cpp:
|
||||
NTSTATUS handle_NtQueryInformationProcess(const syscall_context& c, handle process_handle, uint32_t info_class,
|
||||
@@ -1070,8 +1086,9 @@ void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& ha
|
||||
add_handler(NtReleaseSemaphore);
|
||||
add_handler(NtEnumerateKey);
|
||||
add_handler(NtEnumerateValueKey);
|
||||
add_handler(NtAlpcConnectPort);
|
||||
add_handler(NtAlpcConnectPortEx);
|
||||
add_handler(NtAlpcConnectPort);
|
||||
add_handler(NtAlpcQueryInformation);
|
||||
add_handler(NtGetNextThread);
|
||||
add_handler(NtSetInformationObject);
|
||||
add_handler(NtUserGetCursorPos);
|
||||
@@ -1133,6 +1150,8 @@ void syscall_dispatcher::add_handlers(std::map<std::string, syscall_handler>& ha
|
||||
add_handler(NtRemoveIoCompletionEx);
|
||||
add_handler(NtCreateDebugObject);
|
||||
add_handler(NtReleaseWorkerFactoryWorker);
|
||||
add_handler(NtAlpcCreateSecurityContext);
|
||||
add_handler(NtAlpcDeleteSecurityContext);
|
||||
|
||||
#undef add_handler
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "../std_include.hpp"
|
||||
#include "../emulator_utils.hpp"
|
||||
#include "../syscall_utils.hpp"
|
||||
#include "../port.hpp"
|
||||
|
||||
namespace syscalls
|
||||
{
|
||||
@@ -15,8 +16,18 @@ namespace syscalls
|
||||
auto port_name = read_unicode_string(c.emu, server_port_name);
|
||||
c.win_emu.callbacks.on_generic_access("Connecting port", port_name);
|
||||
|
||||
port p{};
|
||||
p.name = std::move(port_name);
|
||||
port_creation_data data{};
|
||||
client_shared_memory.access([&](PORT_VIEW64& view) {
|
||||
data.view_size = view.ViewSize;
|
||||
data.view_base = c.win_emu.memory.allocate_memory(static_cast<size_t>(data.view_size), memory_permission::read_write);
|
||||
view.ViewBase = data.view_base;
|
||||
view.ViewRemoteBase = view.ViewBase;
|
||||
});
|
||||
|
||||
port_container container{std::u16string(port_name), c.win_emu, data};
|
||||
|
||||
const auto handle = c.proc.ports.store(std::move(container));
|
||||
client_port_handle.write(handle);
|
||||
|
||||
if (connection_info)
|
||||
{
|
||||
@@ -25,15 +36,6 @@ namespace syscalls
|
||||
c.emu.write_memory(connection_info, zero_mem.data(), zero_mem.size());
|
||||
}
|
||||
|
||||
client_shared_memory.access([&](PORT_VIEW64& view) {
|
||||
p.view_base = c.win_emu.memory.allocate_memory(static_cast<size_t>(view.ViewSize), memory_permission::read_write);
|
||||
view.ViewBase = p.view_base;
|
||||
view.ViewRemoteBase = view.ViewBase;
|
||||
});
|
||||
|
||||
const auto handle = c.proc.ports.store(std::move(p));
|
||||
client_port_handle.write(handle);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -49,8 +51,53 @@ namespace syscalls
|
||||
maximum_message_length, connection_info, connection_info_length);
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtAlpcConnectPort(const syscall_context& c, const emulator_object<handle> port_handle,
|
||||
const emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> server_port_name,
|
||||
const emulator_object<OBJECT_ATTRIBUTES<EmulatorTraits<Emu64>>> /*object_attributes*/,
|
||||
const emulator_pointer /*port_attributes*/, const ULONG /*flags*/,
|
||||
const emulator_pointer /*required_server_sid*/, const emulator_pointer /*connection_message*/,
|
||||
const emulator_object<EmulatorTraits<Emu64>::SIZE_T> /*buffer_length*/,
|
||||
const emulator_pointer /*out_message_attributes*/, const emulator_pointer /*in_message_attributes*/,
|
||||
const emulator_object<LARGE_INTEGER> /*timeout*/)
|
||||
{
|
||||
auto port_name = read_unicode_string(c.emu, server_port_name);
|
||||
c.win_emu.log.print(color::dark_gray, "NtAlpcConnectPort: %s\n", u16_to_u8(port_name).c_str());
|
||||
|
||||
port_container container{std::u16string(port_name), c.win_emu, {}};
|
||||
|
||||
const auto handle = c.proc.ports.store(std::move(container));
|
||||
port_handle.write(handle);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtAlpcConnectPortEx(const syscall_context& c, const emulator_object<handle> port_handle,
|
||||
const emulator_object<OBJECT_ATTRIBUTES<EmulatorTraits<Emu64>>> connection_port_object_attributes,
|
||||
const emulator_object<OBJECT_ATTRIBUTES<EmulatorTraits<Emu64>>> /*client_port_object_attributes*/,
|
||||
const emulator_pointer port_attributes, const ULONG flags,
|
||||
const emulator_pointer /*server_security_requirements*/, const emulator_pointer connection_message,
|
||||
const emulator_object<EmulatorTraits<Emu64>::SIZE_T> buffer_length,
|
||||
const emulator_pointer out_message_attributes, const emulator_pointer in_message_attributes,
|
||||
const emulator_object<LARGE_INTEGER> timeout)
|
||||
{
|
||||
if (!connection_port_object_attributes)
|
||||
{
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
const auto attributes = connection_port_object_attributes.read();
|
||||
if (!attributes.ObjectName)
|
||||
{
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
emulator_object<UNICODE_STRING<EmulatorTraits<Emu64>>> port_name{c.emu, attributes.ObjectName};
|
||||
return handle_NtAlpcConnectPort(c, port_handle, port_name, connection_port_object_attributes, port_attributes, flags, {},
|
||||
connection_message, buffer_length, out_message_attributes, in_message_attributes, timeout);
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtAlpcSendWaitReceivePort(const syscall_context& c, const handle port_handle, const ULONG /*flags*/,
|
||||
const emulator_object<PORT_MESSAGE64> /*send_message*/,
|
||||
const emulator_object<PORT_MESSAGE64> send_message,
|
||||
const emulator_object<ALPC_MESSAGE_ATTRIBUTES>
|
||||
/*send_message_attributes*/,
|
||||
const emulator_object<PORT_MESSAGE64> receive_message,
|
||||
@@ -59,38 +106,30 @@ namespace syscalls
|
||||
/*receive_message_attributes*/,
|
||||
const emulator_object<LARGE_INTEGER> /*timeout*/)
|
||||
{
|
||||
const auto* port = c.proc.ports.get(port_handle);
|
||||
auto* port = c.proc.ports.get(port_handle);
|
||||
if (!port)
|
||||
{
|
||||
return STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (port->name != u"\\Windows\\ApiPort")
|
||||
{
|
||||
c.win_emu.log.error("!!! BAD PORT\n");
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
lpc_message_context context{c.emu};
|
||||
context.send_message = send_message;
|
||||
context.receive_message = receive_message;
|
||||
|
||||
// TODO: Fix this. This is broken and wrong.
|
||||
|
||||
try
|
||||
{
|
||||
const emulator_object<PORT_DATA_ENTRY<EmulatorTraits<Emu64>>> data{c.emu, receive_message.value() + 0x48};
|
||||
const auto dest = data.read();
|
||||
const auto base = dest.Base;
|
||||
|
||||
const auto value = base + 0x10;
|
||||
c.emu.write_memory(base + 8, &value, sizeof(value));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
return port->handle_message(c.win_emu, context);
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtAlpcConnectPort()
|
||||
NTSTATUS handle_NtAlpcQueryInformation()
|
||||
{
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtAlpcCreateSecurityContext()
|
||||
{
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
NTSTATUS handle_NtAlpcDeleteSecurityContext()
|
||||
{
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
@@ -289,21 +289,3 @@ struct semaphore : ref_counted_object
|
||||
buffer.read(this->max_count);
|
||||
}
|
||||
};
|
||||
|
||||
struct port : ref_counted_object
|
||||
{
|
||||
std::u16string name{};
|
||||
uint64_t view_base{};
|
||||
|
||||
void serialize_object(utils::buffer_serializer& buffer) const override
|
||||
{
|
||||
buffer.write(this->name);
|
||||
buffer.write(this->view_base);
|
||||
}
|
||||
|
||||
void deserialize_object(utils::buffer_deserializer& buffer) override
|
||||
{
|
||||
buffer.read(this->name);
|
||||
buffer.read(this->view_base);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user