Skip to content

Commit f619440

Browse files
psrok1kuberto773
andauthored
fix: Hooked SslGenerateSessionKeys function to get TLS keys for newer versions - reduced complexity (tklengyel#1856)
* fix: Hooked SslGenerateSessionKeys function to get TLS keys for newer versions * Try to reduce the cognitive complexity --------- Co-authored-by: kuberto773 <kuebrto773@github.com>
1 parent 474c557 commit f619440

File tree

1 file changed

+117
-120
lines changed

1 file changed

+117
-120
lines changed

src/plugins/tlsmon/tlsmon.cpp

Lines changed: 117 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -114,151 +114,148 @@
114114
#include "tlsmon.h"
115115

116116

117-
struct ssl_generate_master_key_result_t: public call_result_t
117+
static std::optional<std::string> ssl_get_master_key(
118+
drakvuf_t drakvuf, drakvuf_trap_info* info, vmi_instance_t vmi, access_context_t ctx
119+
)
118120
{
119-
addr_t master_key_handle_addr;
120-
addr_t parameter_list_addr;
121-
ssl_generate_master_key_result_t(): call_result_t(), master_key_handle_addr(), parameter_list_addr() {}
122-
};
121+
tlsmon_priv::ssl_master_secret_t master_secret;
122+
123+
// We first extract master key by tracing down relevant structures starting with master_key_handle.
124+
addr_t ncrypt_ssl_key_addr = drakvuf_get_function_argument(drakvuf, info, 2);
125+
126+
// master_key_handle points to NCryptSslKey structure.
127+
tlsmon_priv::ncrypt_ssl_key_t ncrypt_ssl_key;
128+
ctx.addr = ncrypt_ssl_key_addr;
129+
if (VMI_SUCCESS != vmi_read(vmi, &ctx, sizeof(ncrypt_ssl_key), &ncrypt_ssl_key, nullptr))
130+
{
131+
PRINT_DEBUG("[TLSMON] Can't read NCryptSslKey structure\n");
132+
return {};
133+
}
134+
135+
// We can validate that we indeed found NCryptSslKey by checking magic bytes value.
136+
if (ncrypt_ssl_key.magic != tlsmon_priv::NCRYPT_SSL_KEY_MAGIC_BYTES)
137+
{
138+
PRINT_DEBUG("[TLSMON] Wrong NCryptSslKey magic\n");
139+
return {};
140+
}
141+
142+
// NCryptSslKey contains a pointer to SslMasterSecret structure.
143+
ctx.addr = (addr_t) ncrypt_ssl_key.master_secret;
144+
if (VMI_SUCCESS != vmi_read(vmi, &ctx, sizeof(master_secret), &master_secret, nullptr))
145+
{
146+
PRINT_DEBUG("[TLSMON] Can't read SslMasterSecret structure\n");
147+
return {};
148+
}
149+
150+
// Again we can validate that we found SslMasterSecret structure bychecking magic bytes.
151+
if (master_secret.magic != tlsmon_priv::MASTER_SECRET_MAGIC_BYTES)
152+
{
153+
PRINT_DEBUG("[TLSMON] Wrong SslMasterSecret magic\n");
154+
return {};
155+
}
156+
157+
// Output retrieved master secret in hex format.
158+
std::string master_key_str = tlsmon_priv::byte2str(master_secret.master_key, tlsmon_priv::MASTER_KEY_SZ);
159+
return master_key_str;
160+
}
161+
162+
static
163+
std::optional< std::vector<tlsmon_priv::ncrypt_buffer_t> > ssl_get_ncrypt_buffers(
164+
drakvuf_t drakvuf, drakvuf_trap_info* info, vmi_instance_t vmi, access_context_t ctx
165+
)
166+
{
167+
// Now retrieve client random and server random values. pParameterList points to an array of
168+
// NCryptBuffer buffers which contains at least client and server random
169+
// values.
170+
ctx.addr = drakvuf_get_function_argument(drakvuf, info, 5);
171+
tlsmon_priv::ncrypt_buffer_desc_t ncrypt_buffer_desc;
172+
if (VMI_SUCCESS != vmi_read(vmi, &ctx, sizeof(ncrypt_buffer_desc), &ncrypt_buffer_desc, nullptr))
173+
{
174+
PRINT_DEBUG("[TLSMON] Failed to read ncrypt parameter list\n");
175+
return {};
176+
}
177+
178+
size_t ncrypt_buffers_size = ncrypt_buffer_desc.cbuffers;
179+
if ( ncrypt_buffers_size != 2 )
180+
{
181+
PRINT_DEBUG("[TLSMON] Ncrypt parameter list has different size than 2\n");
182+
return {};
183+
}
184+
185+
std::vector<tlsmon_priv::ncrypt_buffer_t> ncrypt_buffers = std::vector<tlsmon_priv::ncrypt_buffer_t>(ncrypt_buffers_size);
186+
ctx.addr = (addr_t) ncrypt_buffer_desc.buffers;
187+
if (VMI_SUCCESS != vmi_read(vmi, &ctx, (ncrypt_buffers_size * sizeof(tlsmon_priv::ncrypt_buffer_t)), ncrypt_buffers.data(), nullptr))
188+
{
189+
PRINT_DEBUG("[TLSMON] Failed to read ncrypt parameter list buffers\n");
190+
return {};
191+
}
192+
return ncrypt_buffers;
193+
}
123194

124195

125196
/**
126-
* Extracts and logs 48-bytes-long master key along with client random which
127-
* can be then loaded to wireshark to automatically decrypt the TLS traffic.
197+
* Sets a trap on return from SslGenerateSessionKeys function to obtain the
198+
* calculated master key.
128199
*/
129200
static
130-
event_response_t ssl_generate_master_key_ret_cb(drakvuf_t drakvuf, drakvuf_trap_info* info)
201+
event_response_t ssl_generate_session_keys_cb(drakvuf_t drakvuf, drakvuf_trap_info* info)
131202
{
132-
auto plugin = get_trap_plugin<tlsmon>(info);
133-
auto params = get_trap_params<ssl_generate_master_key_result_t>(info);
134-
if (!params->verify_result_call_params(drakvuf, info))
135-
return VMI_EVENT_RESPONSE_NONE;
136-
203+
auto plugin = static_cast<tlsmon*>(drakvuf_get_extra_from_running_trap(info->trap));
137204
ACCESS_CONTEXT(ctx,
138205
.translate_mechanism = VMI_TM_PROCESS_DTB,
139206
.dtb = info->regs->cr3
140207
);
141208

142-
tlsmon_priv::ssl_master_secret_t master_secret;
143-
std::array<char, tlsmon_priv::CLIENT_RANDOM_SZ> client_random = std::array<char, tlsmon_priv::CLIENT_RANDOM_SZ>();
144-
{
145-
auto vmi = vmi_lock_guard(drakvuf);
146-
// We first extract master key by tracing down relevant structures starting
147-
// with master_key_handle.
148-
addr_t ncrypt_sll_key_addr = 0;
149-
ctx.addr = params->master_key_handle_addr;
150-
if (VMI_SUCCESS != vmi_read_addr(vmi, &ctx, &ncrypt_sll_key_addr))
151-
{
152-
plugin->destroy_trap(info->trap);
153-
return VMI_EVENT_RESPONSE_NONE;
154-
}
209+
auto vmi = vmi_lock_guard(drakvuf);
155210

156-
// master_key_handle points to NCryptSslKey structure.
157-
tlsmon_priv::ncrypt_ssl_key_t ncrypt_ssl_key;
158-
ctx.addr = ncrypt_sll_key_addr;
159-
if (VMI_SUCCESS != vmi_read(vmi, &ctx, sizeof(ncrypt_ssl_key), &ncrypt_ssl_key, nullptr))
160-
{
161-
plugin->destroy_trap(info->trap);
162-
return VMI_EVENT_RESPONSE_NONE;
163-
}
164-
// We can validate that we indeed found NCryptSslKey by checking magic
165-
// bytes value.
166-
if (ncrypt_ssl_key.magic != tlsmon_priv::NCRYPT_SSL_KEY_MAGIC_BYTES)
167-
{
168-
plugin->destroy_trap(info->trap);
169-
return VMI_EVENT_RESPONSE_NONE;
170-
}
211+
auto master_key = ssl_get_master_key(drakvuf, info, vmi, ctx);
212+
if (!master_key)
213+
{
214+
return VMI_EVENT_RESPONSE_NONE;
215+
}
171216

172-
// NCryptSslKey contains a pointer to SslMasterSecret structure.
173-
ctx.addr = (addr_t) ncrypt_ssl_key.master_secret;
174-
if (VMI_SUCCESS != vmi_read(vmi, &ctx, sizeof(master_secret), &master_secret, nullptr))
175-
{
176-
plugin->destroy_trap(info->trap);
177-
return VMI_EVENT_RESPONSE_NONE;
178-
}
179-
// Again we can validate that we found SslMasterSecret structure by
180-
// checking magic bytes.
181-
if (master_secret.magic != tlsmon_priv::MASTER_SECRET_MAGIC_BYTES)
182-
{
183-
plugin->destroy_trap(info->trap);
184-
return VMI_EVENT_RESPONSE_NONE;
185-
}
217+
auto ncrypt_buffers = ssl_get_ncrypt_buffers(drakvuf, info, vmi, ctx);
218+
if (!ncrypt_buffers)
219+
{
220+
return VMI_EVENT_RESPONSE_NONE;
221+
}
186222

223+
// buffer for both ClientRandom and ServerRandom
224+
std::array<char, tlsmon_priv::CLIENT_RANDOM_SZ> randoms_buffer = std::array<char, tlsmon_priv::CLIENT_RANDOM_SZ>();
187225

188-
// Now retrieve client random value. pParameterList points to an array of
189-
// NCryptBuffer buffers which contains at least client and server random
190-
// values.
191-
ctx.addr = params->parameter_list_addr;
192-
tlsmon_priv::ncrypt_buffer_desc_t ncrypt_buffer_desc;
193-
if (VMI_SUCCESS != vmi_read(vmi, &ctx, sizeof(ncrypt_buffer_desc), &ncrypt_buffer_desc, nullptr))
226+
for (tlsmon_priv::ncrypt_buffer_t ncrypt_buffer_iter: *ncrypt_buffers)
227+
{
228+
uint32_t buffer_type = ncrypt_buffer_iter.buffer_type;
229+
uint32_t size = ncrypt_buffer_iter.cbbuffer;;
230+
if ( size != tlsmon_priv::CLIENT_RANDOM_SZ )
194231
{
195-
plugin->destroy_trap(info->trap);
196-
return VMI_EVENT_RESPONSE_NONE;
232+
PRINT_DEBUG("[TLSMON] Wrong ncrypt buffer size\n");
233+
continue;
197234
}
198235

199-
std::vector<tlsmon_priv::ncrypt_buffer_t> ncrypt_buffers = std::vector<tlsmon_priv::ncrypt_buffer_t>(ncrypt_buffer_desc.cbuffers);
200-
ctx.addr = (addr_t) ncrypt_buffer_desc.buffers;
201-
if (VMI_SUCCESS != vmi_read(vmi, &ctx, ncrypt_buffer_desc.cbuffers * sizeof(tlsmon_priv::ncrypt_buffer_t), ncrypt_buffers.data(), nullptr))
236+
// read the buffer
237+
ctx.addr = (addr_t) ncrypt_buffer_iter.buffer;
238+
if (VMI_SUCCESS != vmi_read(vmi, &ctx, randoms_buffer.size(), randoms_buffer.data(), nullptr))
202239
{
203-
plugin->destroy_trap(info->trap);
204-
return VMI_EVENT_RESPONSE_NONE;
240+
PRINT_DEBUG("[TLSMON] Failed to read ncrypt buffer\n");
241+
continue;
205242
}
243+
// convert bytes to string
244+
std::string client_random_str = tlsmon_priv::byte2str((unsigned char*)randoms_buffer.data(), 32);
206245

207-
// Find the buffer containing client random.
208-
auto it = std::find_if(ncrypt_buffers.begin(), ncrypt_buffers.end(), [&](const auto& e)
246+
if (buffer_type == tlsmon_priv::NCRYPTBUFFER_SSL_CLIENT_RANDOM)
209247
{
210-
return e.buffer_type == tlsmon_priv::NCRYPTBUFFER_SSL_CLIENT_RANDOM;
211-
});
212-
if (it == ncrypt_buffers.end())
213-
{
214-
plugin->destroy_trap(info->trap);
215-
return VMI_EVENT_RESPONSE_NONE;
248+
fmt::print(plugin->m_output_format, "tlsmon", drakvuf, info,
249+
keyval("client_random", fmt::Qstr(client_random_str)),
250+
keyval("master_key", fmt::Qstr(*master_key))
251+
);
216252
}
217-
218-
// And finally read it.
219-
ctx.addr = (addr_t) it->buffer;
220-
if (VMI_SUCCESS != vmi_read(vmi, &ctx, client_random.size(), client_random.data(), nullptr))
253+
else if (buffer_type != tlsmon_priv::NCRYPTBUFFER_SSL_SERVER_RANDOM)
221254
{
222-
plugin->destroy_trap(info->trap);
223-
return VMI_EVENT_RESPONSE_NONE;
255+
PRINT_DEBUG("[TLSMON] Unknown ncrypt buffer type.\n");
256+
continue;
224257
}
225-
} // Unlock vmi.
226-
227-
// Output retrieved data in hex format.
228-
std::string master_key_str = tlsmon_priv::byte2str(master_secret.master_key, tlsmon_priv::MASTER_KEY_SZ);
229-
std::string client_random_str = tlsmon_priv::byte2str((unsigned char*)client_random.data(), tlsmon_priv::CLIENT_RANDOM_SZ);
230-
fmt::print(plugin->m_output_format, "tlsmon", drakvuf, info,
231-
keyval("client_random", fmt::Qstr(client_random_str)),
232-
keyval("master_key", fmt::Qstr(master_key_str))
233-
);
234-
235-
plugin->destroy_trap(info->trap);
236-
return VMI_EVENT_RESPONSE_NONE;
237-
}
238-
239-
240-
/**
241-
* Sets a trap on return from SslGenerateMasterKey function to obtain the
242-
* calculated master key.
243-
*/
244-
static
245-
event_response_t ssl_generate_master_key_cb(drakvuf_t drakvuf, drakvuf_trap_info* info)
246-
{
247-
auto plugin = static_cast<tlsmon*>(drakvuf_get_extra_from_running_trap(info->trap));
248-
auto trap = plugin->register_trap<ssl_generate_master_key_result_t>(
249-
info,
250-
ssl_generate_master_key_ret_cb,
251-
breakpoint_by_dtb_searcher(),
252-
"SslGenerateMasterKey"
253-
);
254-
if (!trap)
255-
return VMI_EVENT_RESPONSE_NONE;
256-
257-
auto params = get_trap_params<ssl_generate_master_key_result_t>(trap);
258-
params->set_result_call_params(info);
259-
params->master_key_handle_addr = drakvuf_get_function_argument(drakvuf, info, 4);
260-
params->parameter_list_addr = drakvuf_get_function_argument(drakvuf, info, 7);
261-
258+
}
262259
return VMI_EVENT_RESPONSE_NONE;
263260
}
264261

@@ -274,7 +271,7 @@ void tlsmon::hook_lsass(drakvuf_t drakvuf)
274271
addr_t lsass_base = 0;
275272
if (!drakvuf_find_process(drakvuf, ~0, "lsass.exe", &lsass_base))
276273
return;
277-
drakvuf_request_userhook_on_running_process(drakvuf, lsass_base, "ncrypt.dll", "SslGenerateMasterKey", ssl_generate_master_key_cb, this);
274+
drakvuf_request_userhook_on_running_process(drakvuf, lsass_base, "ncrypt.dll", "SslGenerateSessionKeys", ssl_generate_session_keys_cb, this);
278275
}
279276

280277

0 commit comments

Comments
 (0)