mirror of
https://github.com/mirkobrombin/raven.git
synced 2026-11-03 21:46:32 +00:00
431 lines
19 KiB
C++
431 lines
19 KiB
C++
#include <cstdio>
|
|
#include "hash.h"
|
|
#include "kernel_context.h"
|
|
#include "text.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
#include <map>
|
|
#include <unistd.h>
|
|
|
|
namespace raven {
|
|
|
|
namespace {
|
|
|
|
constexpr uint32_t e_fail = 0x80004005U, e_handle = 0x80070006U, e_invalidarg = 0x80070057U, e_notimpl = 0x80004001U, s_ok = 0, s_false = 1;
|
|
constexpr uint32_t class_not_registered = 0x80040154U, class_string = 0x800401f3U, no_signature = 0x800b0100U;
|
|
|
|
// A CryptoAPI provider or hash handle is a small integer into these tables.
|
|
struct CryptState {
|
|
std::map<uint64_t, bool> providers;
|
|
std::map<uint64_t, std::unique_ptr<Digest>> hashes;
|
|
uint64_t next = 0x1000;
|
|
};
|
|
|
|
struct ComState {
|
|
std::map<uint64_t, int> initialized;
|
|
};
|
|
|
|
}
|
|
|
|
void register_platform_api(ApiRegistry ®istry, KernelContext &context)
|
|
{
|
|
// uxtheme: visual styles are not provided, so every themed drawing path reports "no theme" and apps use classic drawing.
|
|
registry.add("IsThemeActive", 0, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("IsAppThemed", 0, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("OpenThemeData", 2, [](ApiCall &call) { call.fail(e_handle, 0); });
|
|
registry.add("OpenThemeDataEx", 3, [](ApiCall &call) { call.fail(e_handle, 0); });
|
|
registry.add("CloseThemeData", 1, [](ApiCall &call) { call.ret(call[0] ? e_handle : s_ok); });
|
|
registry.add("SetWindowTheme", 3, [](ApiCall &call) { call.ret(s_ok); });
|
|
registry.add("EnableThemeDialogTexture", 2, [](ApiCall &call) { call.ret(s_ok); });
|
|
registry.add("EnableTheming", 1, [](ApiCall &call) { call.ret(s_ok); });
|
|
registry.add("GetThemeAppProperties", 0, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("IsThemeBackgroundPartiallyTransparent", 3, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("DrawThemeBackground", 6, [](ApiCall &call) { call.ret(e_handle); });
|
|
registry.add("DrawThemeBackgroundEx", 6, [](ApiCall &call) { call.ret(e_handle); });
|
|
registry.add("DrawThemeParentBackground", 3, [](ApiCall &call) { call.ret(e_fail); });
|
|
registry.add("DrawThemeText", 9, [](ApiCall &call) { call.ret(e_handle); });
|
|
registry.add("DrawThemeTextEx", 9, [](ApiCall &call) { call.ret(e_handle); });
|
|
registry.add("GetThemeBackgroundContentRect", 6, [](ApiCall &call) { call.ret(e_handle); });
|
|
registry.add("GetThemePartSize", 7, [](ApiCall &call) { call.ret(e_handle); });
|
|
registry.add("GetThemeFont", 6, [](ApiCall &call) { call.ret(e_handle); });
|
|
registry.add("GetThemeColor", 5, [](ApiCall &call) { call.ret(e_handle); });
|
|
registry.add("GetThemeMetric", 6, [](ApiCall &call) { call.ret(e_handle); });
|
|
registry.add("GetThemeInt", 5, [](ApiCall &call) { call.ret(e_handle); });
|
|
registry.add("GetThemeTransitionDuration", 6, [](ApiCall &call) { call.ret(e_handle); });
|
|
registry.add("BufferedPaintInit", 0, [](ApiCall &call) { call.ret(s_ok); });
|
|
registry.add("BufferedPaintUnInit", 0, [](ApiCall &call) { call.ret(s_ok); });
|
|
registry.add("BeginBufferedPaint", 5, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("EndBufferedPaint", 2, [](ApiCall &call) { call.ret(s_ok); });
|
|
registry.add("BeginBufferedAnimation", 8, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("EndBufferedAnimation", 2, [](ApiCall &call) { call.ret(s_ok); });
|
|
registry.add("BufferedPaintStopAllAnimations", 1, [](ApiCall &call) { call.ret(s_ok); });
|
|
registry.add("BufferedPaintRenderAnimation", 2, [](ApiCall &call) { call.ret(0); });
|
|
|
|
// dwmapi: no compositor interface; attribute requests are accepted and ignored.
|
|
registry.add("DwmSetWindowAttribute", 4, [](ApiCall &call) { call.ret(s_ok); });
|
|
registry.add("DwmGetWindowAttribute", 4, [](ApiCall &call) { call.ret(e_invalidarg); });
|
|
registry.add("DwmIsCompositionEnabled", 1, [](ApiCall &call) {
|
|
call.store<uint32_t>(call[0], 0);
|
|
call.ret(s_ok);
|
|
});
|
|
registry.add("DwmExtendFrameIntoClientArea", 2, [](ApiCall &call) { call.ret(s_ok); });
|
|
registry.add("DwmFlush", 0, [](ApiCall &call) { call.ret(s_ok); });
|
|
|
|
// imm32: there is no input method, so contexts do not exist and composition calls fail.
|
|
registry.add("ImmGetContext", 1, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("ImmReleaseContext", 2, [](ApiCall &call) { call.ret(1); });
|
|
registry.add("ImmCreateContext", 0, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("ImmDestroyContext", 1, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("ImmAssociateContext", 2, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("ImmAssociateContextEx", 3, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("ImmGetDefaultIMEWnd", 1, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("ImmIsIME", 1, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("ImmGetCompositionStringW", 4, [](ApiCall &call) { call.ret(uint32_t(-1)); });
|
|
registry.add("ImmSetCompositionStringW", 6, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("ImmSetCompositionWindow", 2, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("ImmSetCompositionFontW", 2, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("ImmSetCandidateWindow", 2, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("ImmNotifyIME", 4, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("ImmEscapeW", 4, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("ImmGetOpenStatus", 1, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("ImmSetOpenStatus", 2, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("ImmGetConversionStatus", 3, [](ApiCall &call) { call.ret(0); });
|
|
|
|
// Network and trust services: no signatures are verifiable and no network is reported.
|
|
registry.add("WinVerifyTrust", 3, [](ApiCall &call) { call.ret(no_signature); });
|
|
registry.add("IsNetworkAlive", 1, [](ApiCall &call) {
|
|
if (call[0]) call.store<uint32_t>(call[0], 0);
|
|
call.ret(0);
|
|
});
|
|
registry.add("IsDestinationReachableW", 2, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("CertCloseStore", 2, [](ApiCall &call) { call.ret(1); });
|
|
registry.add("CertFindCertificateInStore", 6, [](ApiCall &call) { call.fail(0x80092004U, 0); });
|
|
registry.add("CertGetCertificateContextProperty", 4, [](ApiCall &call) { call.fail(0x80092004U, 0); });
|
|
registry.add("CertGetNameStringW", 6, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("CertNameToStrW", 5, [](ApiCall &call) { call.ret(0); });
|
|
registry.add("CryptQueryObject", 11, [](ApiCall &call) { call.fail(0x80092009U, 0); });
|
|
registry.add("CryptMsgGetParam", 5, [](ApiCall &call) { call.fail(0x80091004U, 0); });
|
|
registry.add("CryptMsgClose", 1, [](ApiCall &call) { call.ret(1); });
|
|
registry.add("ImageNtHeader", 1, [](ApiCall &call) {
|
|
const auto offset = call.load<uint32_t>(call[0] + 0x3c);
|
|
call.ret(call[0] + offset);
|
|
});
|
|
|
|
// wininet: URL cracking is pure text processing.
|
|
registry.add("InternetCrackUrlW", 4, [](ApiCall &call) {
|
|
const auto url = call.wide(call[0], 4096);
|
|
const auto scheme_end = url.find(u"://");
|
|
if (scheme_end == url.npos || !call[3]) {
|
|
call.fail(12006);
|
|
return;
|
|
}
|
|
const auto scheme = url.substr(0, scheme_end);
|
|
const auto host_start = scheme_end + 3;
|
|
const auto path_start = url.find_first_of(u"/?#", host_start);
|
|
auto authority = url.substr(host_start, path_start == url.npos ? url.npos : path_start - host_start);
|
|
std::u16string user, password;
|
|
if (const auto at = authority.rfind(u'@'); at != authority.npos) {
|
|
const auto credentials = authority.substr(0, at);
|
|
authority = authority.substr(at + 1);
|
|
const auto colon = credentials.find(u':');
|
|
user = credentials.substr(0, colon);
|
|
password = colon == credentials.npos ? u"" : credentials.substr(colon + 1);
|
|
}
|
|
uint32_t port = scheme == u"https" ? 443 : scheme == u"ftp" ? 21 : 80;
|
|
std::u16string host = authority;
|
|
if (const auto colon = authority.rfind(u':'); colon != authority.npos && authority.find(u']') == authority.npos) {
|
|
host = authority.substr(0, colon);
|
|
port = uint32_t(std::stoul(to_utf8(authority.substr(colon + 1))));
|
|
}
|
|
const auto rest = path_start == url.npos ? std::u16string() : url.substr(path_start);
|
|
const auto extra_at = rest.find_first_of(u"?#");
|
|
const auto path = rest.substr(0, extra_at);
|
|
const auto extra = extra_at == rest.npos ? std::u16string() : rest.substr(extra_at);
|
|
// URL_COMPONENTSW: size, scheme ptr/len, scheme id, host ptr/len, port, user ptr/len, password ptr/len, path ptr/len, extra ptr/len.
|
|
const uint64_t components = call[3];
|
|
const auto fill = [&](uint64_t pointer_at, uint64_t length_at, const std::u16string &value) {
|
|
const uint64_t buffer = call.load<uint64_t>(components + pointer_at);
|
|
const uint32_t capacity = call.load<uint32_t>(components + length_at);
|
|
if (buffer) {
|
|
if (capacity <= value.size()) {
|
|
call.fail(122);
|
|
return false;
|
|
}
|
|
call.put_wide(buffer, value, capacity);
|
|
} else if (value.empty()) {
|
|
call.store<uint64_t>(components + pointer_at, 0);
|
|
} else {
|
|
call.store<uint64_t>(components + pointer_at, call[0] + (url.size() - (url.size() - url.find(value))) * 2);
|
|
}
|
|
call.store<uint32_t>(components + length_at, uint32_t(value.size()));
|
|
return true;
|
|
};
|
|
if (!fill(8, 16, scheme) || !fill(24, 32, host) || !fill(48, 56, user) || !fill(64, 72, password) || !fill(80, 88, path) || !fill(96, 104, extra))
|
|
return;
|
|
call.store<uint32_t>(components + 20, scheme == u"https" ? 2 : scheme == u"http" ? 3 : scheme == u"ftp" ? 1 : 0);
|
|
call.store<uint32_t>(components + 40, port & 0xffff);
|
|
call.ret(1);
|
|
});
|
|
|
|
// COM: no class registry exists, so object creation reports "class not registered".
|
|
auto com = std::make_shared<ComState>();
|
|
registry.add("CoInitialize", 1, [com, &context](ApiCall &call) {
|
|
auto &count = com->initialized[context.environment.teb()];
|
|
call.ret(count++ ? s_false : s_ok);
|
|
});
|
|
registry.add("CoInitializeEx", 2, [com, &context](ApiCall &call) {
|
|
auto &count = com->initialized[context.environment.teb()];
|
|
call.ret(count++ ? s_false : s_ok);
|
|
});
|
|
registry.add("OleInitialize", 1, [com, &context](ApiCall &call) {
|
|
auto &count = com->initialized[context.environment.teb()];
|
|
call.ret(count++ ? s_false : s_ok);
|
|
});
|
|
const auto uninitialize = [com, &context](ApiCall &) {
|
|
auto &count = com->initialized[context.environment.teb()];
|
|
if (count > 0)
|
|
--count;
|
|
};
|
|
registry.add("CoUninitialize", 0, uninitialize);
|
|
registry.add("OleUninitialize", 0, uninitialize);
|
|
registry.add("CoCreateInstance", 5, [](ApiCall &call) {
|
|
if (std::getenv("RAVEN_TRACE_API")) {
|
|
const auto bytes = call.read(call[0], 16);
|
|
std::fprintf(stderr, "com class");
|
|
for (const auto byte : bytes) std::fprintf(stderr, " %02x", byte);
|
|
std::fprintf(stderr, "\n");
|
|
}
|
|
if (call[4]) call.store<uint64_t>(call[4], 0);
|
|
call.ret(class_not_registered);
|
|
});
|
|
registry.add("CoGetClassObject", 5, [](ApiCall &call) {
|
|
if (call[4]) call.store<uint64_t>(call[4], 0);
|
|
call.ret(class_not_registered);
|
|
});
|
|
registry.add("CLSIDFromProgID", 2, [](ApiCall &call) { call.ret(class_string); });
|
|
registry.add("CLSIDFromString", 2, [](ApiCall &call) { call.ret(class_string); });
|
|
registry.add("CoTaskMemAlloc", 1, [&context](ApiCall &call) { call.ret(context.allocator.allocate(call[0], false)); });
|
|
registry.add("CoTaskMemFree", 1, [&context](ApiCall &call) {
|
|
if (call[0]) context.allocator.release(call[0]);
|
|
});
|
|
registry.add("CoTaskMemRealloc", 2, [&context](ApiCall &call) {
|
|
if (!call[0]) {
|
|
call.ret(context.allocator.allocate(call[1], false));
|
|
return;
|
|
}
|
|
const auto old = context.allocator.size(call[0]);
|
|
const auto address = context.allocator.allocate(call[1], false);
|
|
if (address && old != UINT64_MAX)
|
|
call.write(address, call.read(call[0], std::min<uint64_t>(old, call[1])));
|
|
if (address)
|
|
context.allocator.release(call[0]);
|
|
call.ret(address);
|
|
});
|
|
registry.add("RegisterDragDrop", 2, [](ApiCall &call) { call.ret(s_ok); });
|
|
registry.add("RevokeDragDrop", 1, [](ApiCall &call) { call.ret(s_ok); });
|
|
registry.add("DoDragDrop", 4, [](ApiCall &call) {
|
|
if (call[3]) call.store<uint32_t>(call[3], 0);
|
|
call.ret(0x40101);
|
|
});
|
|
registry.add("ReleaseStgMedium", 1, [](ApiCall &) {});
|
|
registry.add("OleFlushClipboard", 0, [](ApiCall &call) { call.ret(s_ok); });
|
|
registry.add("OleGetClipboard", 1, [](ApiCall &call) {
|
|
if (call[0]) call.store<uint64_t>(call[0], 0);
|
|
call.ret(e_notimpl);
|
|
});
|
|
|
|
// BSTRs: a four-byte byte count precedes the characters, which are zero-terminated.
|
|
const auto make_bstr = [&context](ApiCall &call, const uint8_t *bytes, size_t length) {
|
|
const auto block = context.allocator.allocate(length + 4 + 2, false);
|
|
if (!block) {
|
|
call.ret(0);
|
|
return;
|
|
}
|
|
std::vector<uint8_t> data(length + 6);
|
|
std::memcpy(data.data(), &(const uint32_t &)(uint32_t(length)), 4);
|
|
if (bytes)
|
|
std::memcpy(data.data() + 4, bytes, length);
|
|
call.write(block, data);
|
|
call.ret(block + 4);
|
|
};
|
|
registry.add("SysAllocString", 1, [make_bstr](ApiCall &call) {
|
|
if (!call[0]) {
|
|
call.ret(0);
|
|
return;
|
|
}
|
|
const auto text = call.wide(call[0], 1 << 24);
|
|
make_bstr(call, reinterpret_cast<const uint8_t *>(text.data()), text.size() * 2);
|
|
});
|
|
registry.add("SysAllocStringLen", 2, [make_bstr](ApiCall &call) {
|
|
const size_t count = call.u32(1);
|
|
std::vector<uint8_t> bytes(count * 2);
|
|
if (call[0] && count)
|
|
bytes = call.read(call[0], count * 2);
|
|
make_bstr(call, bytes.data(), bytes.size());
|
|
});
|
|
registry.add("SysAllocStringByteLen", 2, [make_bstr](ApiCall &call) {
|
|
const size_t count = call.u32(1);
|
|
std::vector<uint8_t> bytes(count);
|
|
if (call[0] && count)
|
|
bytes = call.read(call[0], count);
|
|
make_bstr(call, bytes.data(), bytes.size());
|
|
});
|
|
registry.add("SysFreeString", 1, [&context](ApiCall &call) {
|
|
if (call[0]) context.allocator.release(call[0] - 4);
|
|
});
|
|
registry.add("SysStringLen", 1, [](ApiCall &call) { call.ret(call[0] ? call.load<uint32_t>(call[0] - 4) / 2 : 0); });
|
|
registry.add("SysStringByteLen", 1, [](ApiCall &call) { call.ret(call[0] ? call.load<uint32_t>(call[0] - 4) : 0); });
|
|
// SysReAllocString replaces the string a BSTR variable holds: the new copy is made before the old one is released.
|
|
const auto reallocate = [&context, make_bstr](ApiCall &call, bool counted) {
|
|
if (!call[0]) {
|
|
call.fail(87);
|
|
return;
|
|
}
|
|
const uint64_t old = call.load<uint64_t>(call[0]);
|
|
std::vector<uint8_t> bytes;
|
|
if (call[1]) {
|
|
const size_t count = counted ? call.u32(2) : call.wide(call[1], 1 << 24).size();
|
|
bytes = count ? call.read(call[1], count * 2) : std::vector<uint8_t>{};
|
|
}
|
|
make_bstr(call, bytes.data(), bytes.size());
|
|
if (!call.result)
|
|
return;
|
|
call.store<uint64_t>(call[0], call.result);
|
|
if (old)
|
|
context.allocator.release(old - 4);
|
|
call.ret(1);
|
|
};
|
|
registry.add("SysReAllocString", 2, [reallocate](ApiCall &call) { reallocate(call, false); });
|
|
registry.add("SysReAllocStringLen", 3, [reallocate](ApiCall &call) { reallocate(call, true); });
|
|
|
|
// advapi32 security helpers that need no token or policy database.
|
|
registry.add("AllocateAndInitializeSid", 11, [&context](ApiCall &call) {
|
|
const uint8_t count = uint8_t(call[1]);
|
|
if (count < 1 || count > 8) {
|
|
call.fail(87);
|
|
return;
|
|
}
|
|
std::vector<uint8_t> sid(8 + count * 4);
|
|
sid[0] = 1;
|
|
sid[1] = count;
|
|
const auto authority = call.read(call[0], 6);
|
|
std::memcpy(sid.data() + 2, authority.data(), 6);
|
|
for (uint8_t index = 0; index < count; ++index) {
|
|
const uint32_t part = call.u32(2 + index);
|
|
std::memcpy(sid.data() + 8 + index * 4, &part, 4);
|
|
}
|
|
const auto block = context.allocator.allocate(sid.size(), false);
|
|
if (!block) {
|
|
call.fail(8);
|
|
return;
|
|
}
|
|
call.write(block, sid);
|
|
call.store<uint64_t>(call[10], block);
|
|
call.ret(1);
|
|
});
|
|
registry.add("FreeSid", 1, [&context](ApiCall &call) {
|
|
if (call[0]) context.allocator.release(call[0]);
|
|
call.ret(0);
|
|
});
|
|
registry.add("CheckTokenMembership", 3, [](ApiCall &call) {
|
|
// Membership in the local Administrators group follows the host account; every other group is not held.
|
|
const auto count = call.read(call[1] + 1, 1)[0];
|
|
const auto authority = call.read(call[1] + 2, 6);
|
|
const bool nt_authority = authority[5] == 5;
|
|
uint32_t first = count ? call.load<uint32_t>(call[1] + 8) : 0, second = count > 1 ? call.load<uint32_t>(call[1] + 12) : 0;
|
|
const bool administrators = nt_authority && count == 2 && first == 32 && second == 544;
|
|
call.store<uint32_t>(call[2], administrators && ::geteuid() == 0);
|
|
call.ret(1);
|
|
});
|
|
registry.add("IsTextUnicode", 3, [](ApiCall &call) {
|
|
const size_t size = call.u32(1);
|
|
const auto bytes = call.read(call[0], size);
|
|
uint32_t tests = call[2] ? call.load<uint32_t>(call[2]) : 0xffffffffU;
|
|
uint32_t found = 0;
|
|
const bool even = size % 2 == 0;
|
|
if (even && size >= 2 && (tests & 0x20) && bytes[0] == 0xff && bytes[1] == 0xfe) found |= 0x20;
|
|
if (even && size >= 2 && (tests & 0x40) && bytes[0] == 0xfe && bytes[1] == 0xff) found |= 0x40;
|
|
// Plain text with zero bytes in every second position is UTF-16 in this host's byte order.
|
|
if (even && size >= 4 && (tests & 0x8000)) {
|
|
size_t zeros = 0;
|
|
for (size_t at = 1; at < size; at += 2)
|
|
if (bytes[at] == 0 && bytes[at - 1] != 0)
|
|
++zeros;
|
|
if (zeros * 3 > size / 2 * 2)
|
|
found |= 0x8000;
|
|
}
|
|
if (call[2]) call.store<uint32_t>(call[2], found);
|
|
call.ret(found != 0);
|
|
});
|
|
|
|
// CryptoAPI hashing over the digests Raven implements.
|
|
auto crypt = std::make_shared<CryptState>();
|
|
registry.add("CryptAcquireContextW", 5, [crypt](ApiCall &call) {
|
|
const auto handle = crypt->next++;
|
|
crypt->providers[handle] = true;
|
|
call.store<uint64_t>(call[0], handle);
|
|
call.ret(1);
|
|
});
|
|
registry.add("CryptReleaseContext", 2, [crypt](ApiCall &call) {
|
|
crypt->providers.erase(call[0]);
|
|
call.ret(1);
|
|
});
|
|
registry.add("CryptCreateHash", 5, [crypt](ApiCall &call) {
|
|
auto digest = make_digest(call.u32(1));
|
|
if (!crypt->providers.contains(call[0]) || !digest) {
|
|
call.fail(digest ? 0x80090016U : 0x80090008U);
|
|
return;
|
|
}
|
|
const auto handle = crypt->next++;
|
|
crypt->hashes[handle] = std::move(digest);
|
|
call.store<uint64_t>(call[4], handle);
|
|
call.ret(1);
|
|
});
|
|
registry.add("CryptHashData", 4, [crypt](ApiCall &call) {
|
|
const auto found = crypt->hashes.find(call[0]);
|
|
if (found == crypt->hashes.end()) {
|
|
call.fail(6);
|
|
return;
|
|
}
|
|
found->second->update(call.read(call[1], call.u32(2)));
|
|
call.ret(1);
|
|
});
|
|
registry.add("CryptGetHashParam", 5, [crypt](ApiCall &call) {
|
|
const auto found = crypt->hashes.find(call[0]);
|
|
if (found == crypt->hashes.end()) {
|
|
call.fail(6);
|
|
return;
|
|
}
|
|
const uint32_t what = call.u32(1);
|
|
if (what == 4) {
|
|
call.store<uint32_t>(call[2], uint32_t(found->second->size()));
|
|
call.store<uint32_t>(call[3], 4);
|
|
call.ret(1);
|
|
return;
|
|
}
|
|
if (what != 2) {
|
|
call.fail(0x80090008U);
|
|
return;
|
|
}
|
|
auto value = found->second->clone()->finish();
|
|
const uint32_t capacity = call.load<uint32_t>(call[3]);
|
|
call.store<uint32_t>(call[3], uint32_t(value.size()));
|
|
if (!call[2]) {
|
|
call.ret(1);
|
|
return;
|
|
}
|
|
if (capacity < value.size()) {
|
|
call.fail(234);
|
|
return;
|
|
}
|
|
call.write(call[2], value);
|
|
call.ret(1);
|
|
});
|
|
registry.add("CryptDestroyHash", 1, [crypt](ApiCall &call) {
|
|
call.ret(crypt->hashes.erase(call[0]) ? 1 : 0);
|
|
});
|
|
}
|
|
|
|
}
|