remotemono
RMonoAPIBackend_Impl.h
1 /*
2  Copyright 2020 David "Alemarius Nexus" Lerch
3 
4  This file is part of RemoteMono.
5 
6  RemoteMono is free software: you can redistribute it and/or modify
7  it under the terms of the GNU Lesser General Public License as published
8  by the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  RemoteMono is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU Lesser General Public License for more details.
15 
16  You should have received a copy of the GNU Lesser General Public License
17  along with RemoteMono. If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 #pragma once
21 
22 #include "../config.h"
23 
24 #include "RMonoAPIBackend_Def.h"
25 
26 #include <exception>
27 #include <map>
28 #include <cstddef>
29 #include <cstdlib>
30 #include <stdint.h>
31 #include "../log.h"
32 #include "../util.h"
33 
34 
35 
36 
37 namespace remotemono
38 {
39 
40 
41 template <typename ABI>
42 RMonoAPIBackend<ABI>::RMonoAPIBackend(ABI* abi)
43  : abi(abi), process(nullptr), worker(nullptr), injected(false)
44 {
45 }
46 
47 
48 template <typename ABI>
49 RMonoAPIBackend<ABI>::~RMonoAPIBackend()
50 {
51 }
52 
53 
54 template <typename ABI>
55 void RMonoAPIBackend<ABI>::injectAPI(RMonoAPI* mono, blackbone::Process& process, blackbone::ThreadPtr workerThread)
56 {
57  using namespace blackbone;
58 
59  if (injected) {
60  return;
61  }
62 
63  this->process = &process;
64  this->worker = workerThread;
65 
66  ABI* abi = getABI();
67 
68  bool x64 = (sizeof(irmono_voidp) == 8);
69 
70  RemoteExec& rem = process.remote();
71  ProcessMemory& mem = process.memory();
72  ProcessModules& modules = process.modules();
73 
74  ipcVec.inject(&process);
75 
76  ModuleDataPtr monoDll = modules.GetModule(L"mono.dll");
77 
78  if (!monoDll) {
79  auto loadedModules = modules.GetAllModules();
80 
81  for (auto it = loadedModules.begin() ; it != loadedModules.end() ; it++) {
82  auto& modName = it->first.first;
83  auto& mod = it->second;
84 
85  if (process.modules().GetExport(mod, "mono_get_root_domain")) {
86  monoDll = mod;
87  break;
88  }
89  }
90  }
91 
92  if (monoDll) {
93  RMonoLogInfo("Found Mono Embedded API in '%ls'", monoDll->name.data());
94  } else {
95  throw std::runtime_error("Couldn't find module containing Mono Embedded API in remote process.");
96  }
97 
98 
99 
100  // ********** PREPARE REMOTE FUNCTIONS **********
101 
102  foreachAPI(*dynamic_cast<MonoAPI*>(this), [&](const char* name, auto& func) {
103  std::string exportName("mono_");
104  exportName.append(name);
105  auto res = process.modules().GetExport(monoDll, exportName.data());
106  if (res) {
107  func.init(abi, mono, exportName, res->procAddress);
108  } else {
109  RMonoLogDebug("API function not found in remote process: %s", exportName.data());
110  func.initInvalid(exportName);
111  if constexpr (func.isRequired()) {
112  throw std::runtime_error(std::string("Required export not found in mono.dll: ").append(exportName));
113  }
114  }
115  });
116 
117  foreachAPI(*dynamic_cast<MiscAPI*>(this), [&](const char* name, auto& func) {
118  auto res = process.modules().GetExport(monoDll, name);
119  if (res) {
120  func.init(abi, mono, name, res->procAddress);
121  } else {
122  RMonoLogDebug("API function not found in remote process: %s", name);
123  func.initInvalid(name);
124  if constexpr (func.isRequired()) {
125  throw std::runtime_error(std::string("Required export not found in mono.dll: ").append(name));
126  }
127  }
128  });
129 
130 
131 
132  // ********** COMPILE REMOTE FUNCTIONS **********
133 
134  std::string monoAPIWrapperCode;
135  std::string miscAPIWrapperCode;
136 
137  struct APIWrapperInfo
138  {
139  asmjit::Label startLabel;
140  asmjit::Label endLabel;
141  intptr_t offset;
142  size_t size;
143  };
144 
145  std::map<std::string, APIWrapperInfo> monoAPIWrapperInfo;
146  std::map<std::string, APIWrapperInfo> miscAPIWrapperInfo;
147 
148  {
149  auto asmPtr = AsmFactory::GetAssembler(!x64);
150  auto& a = *asmPtr;
151 
152  foreachAPI(*dynamic_cast<MonoAPI*>(this), [&](const char* name, auto& func) {
153  if (func) {
154  APIWrapperInfo info;
155 
156  info.startLabel = func.compile(a);
157  info.endLabel = a->newLabel();
158  a->bind(info.endLabel);
159 
160  monoAPIWrapperInfo.insert(std::pair<std::string, APIWrapperInfo>(name, info));
161  }
162  });
163 
164  void* code = a->make();
165 
166  if (!code && a->getCodeSize() != 0) {
167  RMonoLogError("Error assembling MonoAPI wrapper code: %d", (int) a->getError());
168  throw RMonoException("Error assembling MonoAPI wrapper code.");
169  }
170 
171  monoAPIWrapperCode = std::string((const char*) code, a->getCodeSize());
172 
173  for (auto& p : monoAPIWrapperInfo) {
174  p.second.offset = a->getLabelOffset(p.second.startLabel);
175  p.second.size = a->getLabelOffset(p.second.endLabel) - p.second.offset;
176  }
177  }
178 
179  {
180  auto asmPtr = AsmFactory::GetAssembler(!x64);
181  auto& a = *asmPtr;
182 
183  foreachAPI(*dynamic_cast<MiscAPI*>(this), [&](const char* name, auto& func) {
184  if (func) {
185  APIWrapperInfo info;
186 
187  info.startLabel = func.compile(a);
188  info.endLabel = a->newLabel();
189  a->bind(info.endLabel);
190 
191  miscAPIWrapperInfo.insert(std::pair<std::string, APIWrapperInfo>(name, info));
192  }
193  });
194 
195  void* code = a->make();
196 
197  if (!code && a->getCodeSize() != 0) {
198  RMonoLogError("Error assembling MiscAPI wrapper code: %d", (int) a->getError());
199  throw RMonoException("Error assembling MiscAPI wrapper code.");
200  }
201 
202  miscAPIWrapperCode = std::string((const char*) code, a->getCodeSize());
203 
204  for (auto& p : miscAPIWrapperInfo) {
205  p.second.offset = a->getLabelOffset(p.second.startLabel);
206  p.second.size = a->getLabelOffset(p.second.endLabel) - p.second.offset;
207  }
208  }
209 
210 
211 
212  // ********** ASSEMBLE BOILERPLATE CODE **********
213 
214  std::string boilerplateCode = assembleBoilerplateCode();
215 
216 
217 
218  // ********** DUMP REMOTE FUNCTION SIGNATURES **********
219 
220  if (RMonoLogger::getInstance().isLogLevelActive(RMonoLogger::LOG_LEVEL_VERBOSE)) {
221  foreachAPI(*dynamic_cast<MonoAPI*>(this), [&](const char* name, auto& func) {
222  if (func) {
223  func.debugDumpSignatures();
224  }
225  });
226  foreachAPI(*dynamic_cast<MiscAPI*>(this), [&](const char* name, auto& func) {
227  if (func) {
228  func.debugDumpSignatures();
229  }
230  });
231  }
232 
233 
234 
235  // ********** ALLOCATE REMOTE DATA BLOCK **********
236 
237  this->remDataBlock = std::move(mem.Allocate(monoAPIWrapperCode.size() + boilerplateCode.size(), PAGE_EXECUTE_READWRITE).result());
238 
239  size_t monoAPIWrapperCodeOffs = 0;
240  size_t miscAPIWrapperCodeOffs = monoAPIWrapperCodeOffs + monoAPIWrapperCode.size();
241  size_t boilerplateCodeOffs = miscAPIWrapperCodeOffs + miscAPIWrapperCode.size();
242 
243  remDataBlock.Write(monoAPIWrapperCodeOffs, monoAPIWrapperCode.size(), monoAPIWrapperCode.data());
244  remDataBlock.Write(miscAPIWrapperCodeOffs, miscAPIWrapperCode.size(), miscAPIWrapperCode.data());
245  remDataBlock.Write(boilerplateCodeOffs, boilerplateCode.size(), boilerplateCode.data());
246 
247  RMonoLogDebug("Remote Data Block: %llu bytes", (long long unsigned) remDataBlock.size());
248 
249 
250 
251  // ********** LINK REMOTE FUNCTIONS **********
252 
253  foreachAPI(*dynamic_cast<MonoAPI*>(this), [&](const char* name, auto& func) {
254  if (func) {
255  const APIWrapperInfo& info = monoAPIWrapperInfo[name];
256 
257  func.link(remDataBlock.ptr() + monoAPIWrapperCodeOffs + info.offset);
258 
259  if constexpr (func.needsWrapFunc()) {
260  RMonoLogDebug("Wrapper for '%s' is at %llX (size: %llu)", func.getName().data(),
261  (long long unsigned) (remDataBlock.ptr() + monoAPIWrapperCodeOffs + info.offset), (long long unsigned) info.size);
262  } else {
263  RMonoLogVerbose("No wrapper required for '%s'", func.getName().data());
264  }
265  }
266  });
267 
268  foreachAPI(*dynamic_cast<MiscAPI*>(this), [&](const char* name, auto& func) {
269  if (func) {
270  const APIWrapperInfo& info = miscAPIWrapperInfo[name];
271 
272  func.link(remDataBlock.ptr() + miscAPIWrapperCodeOffs + info.offset);
273 
274  if constexpr (func.needsWrapFunc()) {
275  RMonoLogDebug("Wrapper for '%s' is at %llX (size: %llu)", func.getName().data(),
276  (long long unsigned) (remDataBlock.ptr() + miscAPIWrapperCodeOffs + info.offset), (long long unsigned) info.size);
277  } else {
278  RMonoLogVerbose("No wrapper required for '%s'", func.getName().data());
279  }
280  }
281  });
282 
283  foreachAPI(*dynamic_cast<BoilerplateAPI*>(this), [&](const char* name, auto& func) {
284  if (func) {
285  func.rebuild(process, remDataBlock.ptr() + boilerplateCodeOffs + func.getAddress(), worker);
286  }
287  });
288 
289 
290 
291  // ********** COLLECT VALID FUNCTIONS **********
292 
293  foreachAPI(*dynamic_cast<MonoAPI*>(this), [&](const char* name, auto& func) {
294  if (func) {
295  validAPIFuncNames.insert(func.getName());
296  }
297  });
298  foreachAPI(*dynamic_cast<MiscAPI*>(this), [&](const char* name, auto& func) {
299  if (func) {
300  validAPIFuncNames.insert(func.getName());
301  }
302  });
303 
304  injected = true;
305 }
306 
307 
308 template <typename ABI>
310 {
311  using namespace blackbone;
312 
313  if (!injected) {
314  return;
315  }
316 
317  remDataBlock.Free();
318  remDataBlock = MemBlock();
319 
320  ipcVec.vectorFree(ipcVecPtr);
321 
322  foreachAPI(*dynamic_cast<BoilerplateAPI*>(this), [&](const char* name, auto& func) {
323  func.reset();
324  });
325  foreachAPI(*dynamic_cast<MiscAPI*>(this), [&](const char* name, auto& func) {
326  func.reset();
327  });
328  foreachAPI(*dynamic_cast<MonoAPI*>(this), [&](const char* name, auto& func) {
329  func.reset();
330  });
331 
332  ipcVec.uninject();
333 
334  injected = false;
335 }
336 
337 
338 template <typename ABI>
340 {
341  using namespace asmjit;
342  using namespace asmjit::host;
343 
344  ipcVecPtr = ipcVec.vectorNew();
345 
346  bool x64 = (sizeof(irmono_voidp) == 8);
347 
348  RMonoLogVerbose("Assembling BoilerplateAPI functions for %s", x64 ? "x64" : "x86");
349 
350  auto asmPtr = AsmFactory::GetAssembler(!x64);
351  auto& a = *asmPtr;
352 
353  asmjit::Label lForeachIPCVecAdapter = a->newLabel();
354  asmjit::Label lGchandlePin = a->newLabel();
355  asmjit::Label lArraySetref = a->newLabel();
356 
357  {
358  // __cdecl void rmono_foreach_ipcvec_adapter(irmono_voidp elem, irmono_voidp vec);
359  a->bind(lForeachIPCVecAdapter);
360 
361  // IPCVector_VectorAdd(vec, elem);
362  if (x64) {
363  a->push(a->zsp); // Aligns stack to 16 bytes
364  a->xchg(a->zcx, a->zdx);
365  a->mov(a->zax, (uint64_t) ipcVec.getAPI().vectorAdd);
366  a->sub(a->zsp, 32);
367  a->call(a->zax);
368  a->add(a->zsp, 32);
369  a->pop(a->zsp);
370  } else {
371  a->mov(a->zcx, ptr(a->zsp, 8));
372  a->mov(a->zdx, ptr(a->zsp, 4));
373  a->mov(a->zax, (uint32_t) (uintptr_t) ipcVec.getAPI().vectorAdd);
374  a->call(a->zax);
375  }
376 
377  a->ret();
378  }
379 
380  {
381  // __cdecl irmono_gchandle rmono_gchandle_pin(irmono_gchandle unpinned);
382  a->bind(lGchandlePin);
383 
384  if (x64) {
385  a->push(a->zsp); // Aligns stack to 16 bytes
386 
387  // IRMonoObjectRawPtr rawObj = gchandle_get_target(unpinned);
388  a->mov(a->zax, gchandle_get_target.getRawFuncAddress());
389  a->sub(a->zsp, 32);
390  a->call(a->zax);
391  a->add(a->zsp, 32);
392 
393  // return gchandle_new(rawObj, true);
394  a->mov(a->zcx, a->zax);
395  a->mov(a->zdx, 1);
396  a->mov(a->zax, gchandle_new.getRawFuncAddress());
397  a->sub(a->zsp, 32);
398  a->call(a->zax);
399  a->add(a->zsp, 32);
400 
401  a->pop(a->zsp);
402  } else {
403  // IRMonoObjectRawPtr rawObj = gchandle_get_target(unpinned);
404  a->push(dword_ptr(a->zsp, 4));
405  a->mov(a->zax, gchandle_get_target.getRawFuncAddress());
406  a->call(a->zax);
407  a->add(a->zsp, 4);
408 
409  // return gchandle_new(rawObj, true);
410  a->push(1);
411  a->push(a->zax);
412  a->mov(a->zax, gchandle_new.getRawFuncAddress());
413  a->call(a->zax);
414  a->add(a->zsp, 8);
415  }
416 
417  a->ret();
418  }
419 
420  if (array_addr_with_size && gc_wbarrier_set_arrayref) {
421  // __cdecl void rmono_array_setref(irmono_gchandle arr, irmono_uintptr_t idx, irmono_gchandle val);
422  a->bind(lArraySetref);
423  a->push(a->zbx);
424  a->push(a->zsi);
425  a->push(a->zdi);
426 
427  if (x64) {
428  a->mov(a->zsi, a->zdx);
429  a->mov(a->zdi, r8);
430  } else {
431  a->mov(a->zbx, ptr(a->zsp, 16));
432  a->mov(a->zsi, ptr(a->zsp, 20));
433  a->mov(a->zdi, ptr(a->zsp, 24));
434  a->mov(a->zcx, a->zbx);
435  }
436 
437  // IMonoArrayPtrRaw rawArr = mono_gchandle_get_target_checked(arr);
438  AsmGenGchandleGetTargetChecked(a, gchandle_get_target.getRawFuncAddress(), x64);
439  a->mov(a->zbx, a->zax);
440 
441  // IRMonoObjectPtrRaw rawVal = mono_gchandle_get_target_checked(val);
442  a->mov(a->zcx, a->zdi);
443  AsmGenGchandleGetTargetChecked(a, gchandle_get_target.getRawFuncAddress(), x64);
444  a->mov(a->zdi, a->zax);
445 
446  // IRMonoObjectPtrRaw* p = mono_array_addr_with_size(rawArr, sizeof(IRMonoObjectPtrRaw), idx);
447  if (x64) {
448  a->mov(a->zcx, a->zbx);
449  a->mov(a->zdx, sizeof(IRMonoObjectPtrRaw));
450  a->mov(asmjit::host::r8, a->zsi);
451  a->mov(a->zax, array_addr_with_size.getRawFuncAddress());
452  a->sub(a->zsp, 32);
453  a->call(a->zax);
454  a->add(a->zsp, 32);
455  } else {
456  a->push(a->zsi);
457  a->push(sizeof(IRMonoObjectPtrRaw));
458  a->push(a->zbx);
459  a->mov(a->zax, array_addr_with_size.getRawFuncAddress());
460  a->call(a->zax);
461  a->add(a->zsp, 12);
462  }
463  a->mov(a->zsi, a->zax);
464 
465  // mono_gc_wbarrier_set_arrayref(rawArr, p, rawVal);
466  if (x64) {
467  a->mov(a->zcx, a->zbx);
468  a->mov(a->zdx, a->zsi);
469  a->mov(asmjit::host::r8, a->zdi);
470  a->mov(a->zax, gc_wbarrier_set_arrayref.getRawFuncAddress());
471  a->sub(a->zsp, 32);
472  a->call(a->zax);
473  a->add(a->zsp, 32);
474  } else {
475  a->push(a->zdi);
476  a->push(a->zsi);
477  a->push(a->zbx);
478  a->mov(a->zax, gc_wbarrier_set_arrayref.getRawFuncAddress());
479  a->call(a->zax);
480  a->add(a->zsp, 12);
481  }
482 
483  a->pop(a->zdi);
484  a->pop(a->zsi);
485  a->pop(a->zbx);
486  a->ret();
487  }
488 
489  std::string boilerplateCode((const char*) a->make(), a->getCodeSize());
490 
491  if (a->isLabelBound(lForeachIPCVecAdapter)) {
492  rmono_foreach_ipcvec_adapter.rebuild(*process, (ptr_t) a->getLabelOffset(lForeachIPCVecAdapter), worker);
493  }
494  if (a->isLabelBound(lGchandlePin)) {
495  rmono_gchandle_pin.rebuild(*process, (ptr_t) a->getLabelOffset(lGchandlePin), worker);
496  }
497  if (a->isLabelBound(lArraySetref)) {
498  rmono_array_setref.rebuild(*process, (ptr_t) a->getLabelOffset(lArraySetref), worker);
499  }
500 
501  return boilerplateCode;
502 }
503 
504 
505 
506 
507 
508 
509 
510 template <typename ABI>
511 template <typename FuncT, typename MainT, typename... PartialT>
512 void RMonoAPIBackend<ABI>::foreachAPIPacked(MainT& main, FuncT func, PackHelper<PartialT...>)
513 {
514  foreachAPIRecurse(func, *dynamic_cast<PartialT*>(&main)...);
515 }
516 
517 
518 template <typename ABI>
519 template <typename FuncT, typename FirstT, typename... OtherT>
520 void RMonoAPIBackend<ABI>::foreachAPIRecurse(FuncT func, FirstT& part, OtherT&... other)
521 {
522  visit_struct::for_each(part, func);
523 
524  if constexpr (sizeof...(other) != 0) {
525  foreachAPIRecurse(func, other...);
526  }
527 }
528 
529 
530 template <typename ABI>
531 template <typename MainAPIT, typename FuncT>
532 void RMonoAPIBackend<ABI>::foreachAPI(MainAPIT& api, FuncT func)
533 {
534  foreachAPIPacked(api, func, typename MainAPIT::internal_api_parts());
535 }
536 
537 
538 }
remotemono::RMonoAPI
Definition: RMonoAPI_Def.h:58
remotemono::RMonoAPIBackend::uninjectAPI
void uninjectAPI()
Definition: RMonoAPIBackend_Impl.h:309
remotemono::RMonoAPIBackend::injectAPI
void injectAPI(RMonoAPI *mono, blackbone::Process &process, blackbone::ThreadPtr workerThread)
Definition: RMonoAPIBackend_Impl.h:55
remotemono::RMonoAPIBackend
Definition: RMonoAPIBackend_Def.h:388