PatchSolution3.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765
  1. #include "PatchSolution.hpp"
  2. #include <tchar.h>
  3. #include "Helper.hpp"
  4. #undef __BASE_FILE__
  5. #define __BASE_FILE__ "PatchSolution3.cpp"
  6. // ----------- avoid link error caused by capstone_static.lib
  7. #define stdin (__acrt_iob_func(0))
  8. #define stdout (__acrt_iob_func(1))
  9. #define stderr (__acrt_iob_func(2))
  10. FILE _iob[] = { *stdin, *stdout, *stderr };
  11. extern "C" FILE * __cdecl __iob_func(void) { return _iob; }
  12. // ------------
  13. #if defined(_M_AMD64)
  14. #define REG_IP Rip
  15. #else
  16. #define REG_IP Eip
  17. #endif
  18. const PatchSolution3::KeywordType PatchSolution3::Keywords[KeywordsCount] = {
  19. { { 0x4d, 0x49, 0x49 }, 3, STRING_DATA, false },
  20. { { 0x42, 0x49 }, 2, IMM_DATA, false },
  21. { { 0x6a }, 1, IMM_DATA, false },
  22. { { 0x41 }, 1, IMM_DATA, false },
  23. { { 0x4e, 0x42, 0x67, 0x6b }, 4, IMM_DATA, false },
  24. { { 0x71 }, 1, IMM_DATA, false },
  25. { { 0x68, 0x6b, 0x69, 0x47, 0x39, 0x77 }, 6, STRING_DATA, false },
  26. { { 0x30 }, 1, STRING_DATA, true },
  27. { { 0x42 }, 1, IMM_DATA, false },
  28. { { 0x41 }, 1, IMM_DATA, false },
  29. { { 0x51, 0x45, 0x46, 0x41, 0x41, 0x4f, 0x43 }, 7, STRING_DATA, false },
  30. { { 0x41, 0x51, 0x38, 0x41, 0x4d, 0x49 }, 6, STRING_DATA, false },
  31. { { 0x49, 0x42 }, 2, STRING_DATA, false },
  32. { { 0x43, 0x67, 0x4b, 0x43 }, 4, IMM_DATA, false },
  33. { { 0x41, 0x51 }, 2, STRING_DATA, false },
  34. { { 0x45, 0x41, 0x77, 0x31 }, 4, IMM_DATA, false },
  35. { { 0x64, 0x71, 0x46, 0x33 }, 4, IMM_DATA, false },
  36. { { 0x53 }, 1, STRING_DATA, true },
  37. { { 0x6b, 0x43, 0x61, 0x41, 0x41, 0x6d }, 6, STRING_DATA, false },
  38. { { 0x4d, 0x7a, 0x73, 0x38 }, 4, IMM_DATA, false },
  39. { { 0x38, 0x39, 0x49, 0x71 }, 4, IMM_DATA, false },
  40. { { 0x64 }, 1, IMM_DATA, false },
  41. { { 0x57 }, 1, IMM_DATA, false },
  42. { { 0x39, 0x4d, 0x32, 0x64 }, 4, IMM_DATA, false },
  43. { { 0x49, 0x64, 0x68 }, 3, STRING_DATA, false },
  44. { { 0x33, 0x6a }, 2, IMM_DATA, false },
  45. { { 0x47, 0x39, 0x79, 0x50 }, 4, IMM_DATA, false },
  46. { { 0x63, 0x6d }, 2, IMM_DATA, false },
  47. { { 0x4c }, 1, IMM_DATA, false },
  48. { { 0x6e, 0x6d, 0x4a }, 3, STRING_DATA, false },
  49. { { 0x69, 0x47, 0x70, 0x42, 0x46, 0x34, 0x45 }, 7, STRING_DATA, false },
  50. { { 0x39, 0x56, 0x48, 0x53, 0x4d, 0x47 }, 6, STRING_DATA, false },
  51. { { 0x65, 0x38, 0x6f, 0x50, 0x41, 0x79, 0x32, 0x6b }, 8, STRING_DATA, false },
  52. { { 0x4a, 0x44 }, 2, STRING_DATA, false },
  53. { { 0x6d, 0x64 }, 2, IMM_DATA, false },
  54. { { 0x4e, 0x74, 0x34 }, 3, STRING_DATA, false },
  55. { { 0x42, 0x63, 0x45, 0x79, 0x67, 0x76 }, 6, STRING_DATA, false },
  56. { { 0x73, 0x73, 0x45, 0x66, 0x67, 0x69 }, 6, STRING_DATA, false },
  57. { { 0x6e, 0x76, 0x61, 0x35, 0x74 }, 5, STRING_DATA, false },
  58. { { 0x35, 0x6a, 0x6d, 0x33, 0x35, 0x32 }, 6, STRING_DATA, false },
  59. { { 0x55, 0x41 }, 2, IMM_DATA, false },
  60. { { 0x6f, 0x44, 0x6f, 0x73 }, 4, IMM_DATA, false },
  61. { { 0x55, 0x4a }, 2, IMM_DATA, false },
  62. { { 0x6b, 0x54, 0x58, 0x47, 0x51 }, 5, STRING_DATA, false },
  63. { { 0x68, 0x70, 0x41, 0x57, 0x4d, 0x46 }, 6, STRING_DATA, false },
  64. { { 0x34, 0x66, 0x42, 0x6d, 0x42 }, 5, STRING_DATA, false },
  65. { { 0x70, 0x4f, 0x33, 0x45 }, 4, IMM_DATA, false },
  66. { { 0x65, 0x64 }, 2, IMM_DATA, false },
  67. { { 0x47 }, 1, IMM_DATA, false },
  68. { { 0x36, 0x32, 0x72, 0x4f, 0x73, 0x71 }, 6, STRING_DATA, false },
  69. { { 0x4d }, 1, IMM_DATA, false },
  70. { { 0x42, 0x67, 0x6d, 0x53 }, 4, STRING_DATA, false },
  71. { { 0x64 }, 1, IMM_DATA, false },
  72. { { 0x41, 0x79, 0x78, 0x43, 0x53 }, 5, STRING_DATA, false },
  73. { { 0x50 }, 1, IMM_DATA, false },
  74. { { 0x42, 0x52, 0x4a, 0x49, 0x4f }, 5, STRING_DATA, false },
  75. { { 0x46, 0x52, 0x30, 0x51, 0x67, 0x5a, 0x46, 0x62 }, 8, STRING_DATA, false },
  76. { { 0x52 }, 1, IMM_DATA, false },
  77. { { 0x6e, 0x55, 0x30, 0x66 }, 4, STRING_DATA, false },
  78. { { 0x72, 0x6a, 0x33, 0x34 }, 4, IMM_DATA, false },
  79. { { 0x66 }, 1, STRING_DATA, true },
  80. { { 0x69, 0x56, 0x6d, 0x67 }, 4, IMM_DATA, false },
  81. { { 0x59, 0x69, 0x4c, 0x75 }, 4, STRING_DATA, false },
  82. { { 0x5a, 0x53, 0x41, 0x6d }, 4, IMM_DATA, false },
  83. { { 0x49, 0x62 }, 2, IMM_DATA, false },
  84. { { 0x73 }, 1, IMM_DATA, false },
  85. { { 0x38, 0x5a, 0x78, 0x69 }, 4, IMM_DATA, false },
  86. { { 0x48 }, 1, IMM_DATA, false },
  87. { { 0x50, 0x64, 0x70, 0x31 }, 4, IMM_DATA, false },
  88. { { 0x6f, 0x44 }, 2, IMM_DATA, false },
  89. { { 0x34 }, 1, IMM_DATA, false },
  90. { { 0x74, 0x55, 0x70, 0x76, 0x73, 0x46 }, 6, STRING_DATA, false },
  91. { { 0x63, 0x69, 0x34, 0x51, 0x4a, 0x74 }, 6, STRING_DATA, false },
  92. { { 0x59, 0x4e, 0x6a, 0x4e, 0x6e, 0x47, 0x55 }, 7, STRING_DATA, false },
  93. { { 0x32, 0x57, 0x50, 0x48 }, 4, STRING_DATA, false },
  94. { { 0x36, 0x72, 0x76, 0x43, 0x68, 0x47, 0x6c }, 7, STRING_DATA, false },
  95. { { 0x31, 0x49, 0x52, 0x4b, 0x72, 0x78, 0x4d, 0x74 }, 8, STRING_DATA, false },
  96. { { 0x71, 0x4c, 0x69, 0x65, 0x6c }, 5, STRING_DATA, false },
  97. { { 0x73, 0x76, 0x61, 0x6a, 0x55, 0x6a, 0x79, 0x72 }, 8, STRING_DATA, false },
  98. { { 0x67 }, 1, STRING_DATA, true },
  99. { { 0x4f, 0x43, 0x36, 0x4e, 0x6d, 0x79, 0x6d, 0x59 }, 8, STRING_DATA, false },
  100. { { 0x4d }, 1, IMM_DATA, false },
  101. { { 0x76, 0x5a, 0x4e }, 3, STRING_DATA, false },
  102. { { 0x45, 0x52, 0x33, 0x68, 0x74 }, 5, STRING_DATA, false },
  103. { { 0x46 }, 1, IMM_DATA, false },
  104. { { 0x45, 0x74, 0x4c, 0x31 }, 4, STRING_DATA, false },
  105. { { 0x65, 0x51, 0x62, 0x43, 0x79 }, 5, STRING_DATA, false },
  106. { { 0x54, 0x66, 0x44, 0x6d, 0x74, 0x59, 0x79, 0x51 }, 8, STRING_DATA, false },
  107. { { 0x31, 0x57, 0x74, 0x34 }, 4, STRING_DATA, false },
  108. { { 0x4f }, 1, IMM_DATA, false },
  109. { { 0x74, 0x31, 0x32, 0x6c, 0x78, 0x66 }, 6, STRING_DATA, false },
  110. { { 0x30 }, 1, IMM_DATA, false },
  111. { { 0x77, 0x56, 0x49, 0x52, 0x35 }, 5, STRING_DATA, false },
  112. { { 0x6d }, 1, IMM_DATA, false },
  113. { { 0x63, 0x47, 0x4e, 0x37 }, 4, STRING_DATA, false },
  114. { { 0x58, 0x43, 0x58, 0x4a }, 4, STRING_DATA, false },
  115. { { 0x52, 0x48, 0x4f, 0x46 }, 4, IMM_DATA, false },
  116. { { 0x48, 0x53 }, 2, IMM_DATA, false },
  117. { { 0x66 }, 1, IMM_DATA, false },
  118. { { 0x31, 0x67, 0x7a, 0x58, 0x57 }, 5, STRING_DATA, false },
  119. { { 0x61 }, 1, IMM_DATA, false },
  120. { { 0x62 }, 1, IMM_DATA, false },
  121. { { 0x52, 0x53 }, 2, STRING_DATA, false },
  122. { { 0x76, 0x6d, 0x74, 0x31, 0x6e }, 5, STRING_DATA, false },
  123. { { 0x72, 0x6c }, 2, STRING_DATA, true },
  124. { { 0x37, 0x73, 0x57 }, 3, STRING_DATA, false },
  125. { { 0x36, 0x63, 0x6a }, 3, STRING_DATA, false },
  126. { { 0x78, 0x6c, 0x6a, 0x75, 0x75, 0x51, 0x61 }, 7, STRING_DATA, false },
  127. { { 0x77, 0x49, 0x44, 0x41 }, 4, STRING_DATA, false },
  128. { { 0x51, 0x41 }, 2, IMM_DATA, false },
  129. { { 0x42 }, 1, IMM_DATA, false },
  130. };
  131. PatchSolution3::PatchSolution3() {
  132. cs_err cs_status;
  133. csh cs_handle;
  134. #if defined(_M_IX86)
  135. cs_status = cs_open(CS_ARCH_X86, CS_MODE_32, &cs_handle);
  136. #else
  137. cs_status = cs_open(CS_ARCH_X86, CS_MODE_64, &cs_handle);
  138. #endif
  139. if (cs_status != CS_ERR_OK)
  140. throw CapstoneError(__BASE_FILE__, __LINE__, cs_status,
  141. "cs_open fails.");
  142. cs_status = cs_option(cs_handle, CS_OPT_DETAIL, CS_OPT_ON);
  143. if (cs_status != CS_ERR_OK)
  144. throw CapstoneError(__BASE_FILE__, __LINE__, cs_status,
  145. "cs_option fails.");
  146. _CapstoneHandle.TakeHoldOf(cs_handle);
  147. memset(_Patches, 0, sizeof(_Patches));
  148. }
  149. #if defined(_M_AMD64)
  150. bool PatchSolution3::CheckIfMatchPattern(cs_insn* PtrToInstruction) const {
  151. // the instruction we're interested in has one of the following patterns:
  152. // 1. mov PTR [MEM], IMM (IMM must consist of printable chars) // for IMM_DATA
  153. // 2. lea REG, PTR [MEM] (MEM must point to a non-empty printable string) // for STRING_DATA
  154. if (_stricmp(PtrToInstruction->mnemonic, "mov") == 0) {
  155. if (PtrToInstruction->detail->x86.operands[1].type != X86_OP_IMM)
  156. return false;
  157. uint8_t* PtrToImm =
  158. PtrToInstruction->bytes +
  159. PtrToInstruction->detail->x86.encoding.imm_offset;
  160. uint8_t ImmSize =
  161. PtrToInstruction->detail->x86.encoding.imm_size;
  162. return Helper::IsPrintable(PtrToImm, ImmSize);
  163. } else if (_stricmp(PtrToInstruction->mnemonic, "lea") == 0) {
  164. // as far as I know, all strings are loaded by "lea REG, QWORD PTR[RIP + disp]"
  165. // so it must be "[RIP + disp]"
  166. if (PtrToInstruction->detail->x86.operands[1].mem.base != X86_REG_RIP)
  167. return false;
  168. // scale must 1, otherwise pattern mismatches
  169. if (PtrToInstruction->detail->x86.operands[1].mem.scale != 1)
  170. return false;
  171. auto StringRva = static_cast<uintptr_t>(
  172. PtrToInstruction->address + PtrToInstruction->size + // RIP
  173. PtrToInstruction->detail->x86.operands[1].mem.disp
  174. );
  175. auto PtrToString = _TargetFile.RvaToPointer<uint8_t>(StringRva);
  176. // If not found, pattern mismatches
  177. if (PtrToString == nullptr)
  178. return false;
  179. // PtrToString must have at least one char
  180. // every char in PtrToString must be printable, otherwise pattern mismatches
  181. auto StringLength = strlen(reinterpret_cast<char*>(PtrToString));
  182. if (StringLength && Helper::IsPrintable(PtrToString, StringLength))
  183. return true;
  184. return false;
  185. } else {
  186. return false;
  187. }
  188. }
  189. #else // for i386
  190. bool PatchSolution3::CheckIfMatchPattern(cs_insn* PtrToInstruction) const {
  191. // the instruction we're interested in has one of the following patterns:
  192. // 1. mov PTR [MEM], IMM (IMM must consist of printable chars) // for KeywordType::IMM_DATA
  193. // except pattern "mov [ebp - 0x4], IMM"
  194. // 2. push IMM (IMM must consist of printable chars) // for KeywordType::IMM_DATA
  195. // 3. push offset MEM (MEM must point to a non-empty printable string) // for KeywordType::STRING_DATA
  196. //
  197. if (_stricmp(PtrToInstruction->mnemonic, "mov") == 0) {
  198. // filter the case "mov [ebp - 0x4], IMM"
  199. // because IMM may consist of printable chars in that case, which can mislead us.
  200. //
  201. // Here I use "> -0x30" to intensify condition, instead of "== -0x4"
  202. if (PtrToInstruction->detail->x86.operands[0].type == X86_OP_MEM &&
  203. PtrToInstruction->detail->x86.operands[0].mem.base == X86_REG_EBP &&
  204. PtrToInstruction->detail->x86.operands[0].mem.disp > -0x30)
  205. return false;
  206. if (PtrToInstruction->detail->x86.operands[1].type != X86_OP_IMM)
  207. return false;
  208. uint8_t* PtrToImm =
  209. PtrToInstruction->bytes +
  210. PtrToInstruction->detail->x86.encoding.imm_offset;
  211. uint8_t ImmSize =
  212. PtrToInstruction->detail->x86.encoding.imm_size;
  213. // each bytes of ImmValue must be printable;
  214. return Helper::IsPrintable(PtrToImm, ImmSize);
  215. } else if (_stricmp(PtrToInstruction->mnemonic, "push") == 0) {
  216. if (PtrToInstruction->detail->x86.operands[0].type != X86_OP_IMM)
  217. return false;
  218. { // test if match pattern 2
  219. if (Helper::IsPrintable(PtrToInstruction->bytes + PtrToInstruction->detail->x86.encoding.imm_offset,
  220. PtrToInstruction->detail->x86.encoding.imm_size))
  221. return true;
  222. }
  223. { // test if match pattern 3
  224. auto StringRva = static_cast<uintptr_t>(
  225. PtrToInstruction->detail->x86.operands[0].imm -
  226. _TargetFile.GetImageNTHeaders()->OptionalHeader.ImageBase
  227. );
  228. auto PtrToString = _TargetFile.RvaToPointer<uint8_t>(StringRva);
  229. if (PtrToString) {
  230. auto StringLength = strlen(reinterpret_cast<char*>(PtrToString));
  231. if (StringLength && Helper::IsPrintable(PtrToString, StringLength))
  232. return true;
  233. }
  234. }
  235. return false;
  236. } else {
  237. return false;
  238. }
  239. }
  240. #endif
  241. #if defined(_M_AMD64)
  242. bool PatchSolution3::CheckIfFound(cs_insn* PtrToInstruction, size_t i) const {
  243. auto& op_count = PtrToInstruction->detail->x86.op_count;
  244. auto& operands = PtrToInstruction->detail->x86.operands;
  245. if (PtrToInstruction->detail->x86.op_count != 2)
  246. return false;
  247. if (Keywords[i].Type == IMM_DATA && operands[1].type == X86_OP_IMM) {
  248. uint64_t ImmValue = operands[1].imm;
  249. uint8_t ImmSize = PtrToInstruction->detail->x86.encoding.imm_size;
  250. return
  251. memcmp(&ImmValue, Keywords[i].Data, sizeof(uint64_t)) == 0 &&
  252. ImmSize == Keywords[i].Length;
  253. } else if (Keywords[i].Type == STRING_DATA && operands[1].type == X86_OP_MEM) {
  254. auto StringRva = static_cast<uintptr_t>(
  255. PtrToInstruction->address + PtrToInstruction->size + // RIP
  256. PtrToInstruction->detail->x86.operands[1].mem.disp
  257. );
  258. auto PtrToString = _TargetFile.RvaToPointer<char>(StringRva);
  259. return
  260. PtrToString &&
  261. strncmp(PtrToString, reinterpret_cast<const char*>(Keywords[i].Data), Keywords[i].Length) == 0 &&
  262. PtrToString[Keywords[i].Length] == '\x00';
  263. } else {
  264. return false;
  265. }
  266. }
  267. #else
  268. bool PatchSolution3::CheckIfFound(cs_insn* PtrToInstruction, size_t i) const {
  269. auto& op_count = PtrToInstruction->detail->x86.op_count;
  270. auto& operands = PtrToInstruction->detail->x86.operands;
  271. if (op_count < 1 || operands[op_count - 1].type != X86_OP_IMM)
  272. return false;
  273. if (Keywords[i].Type == IMM_DATA) {
  274. uint64_t ImmValue = operands[op_count - 1].imm;
  275. uint8_t ImmSize = PtrToInstruction->detail->x86.encoding.imm_size;
  276. return
  277. memcmp(&ImmValue, Keywords[i].Data, sizeof(uint64_t)) == 0 &&
  278. ImmSize == Keywords[i].Length;
  279. } else if (Keywords[i].Type == STRING_DATA) {
  280. auto StringRva = static_cast<uintptr_t>(
  281. operands[op_count - 1].imm -
  282. _TargetFile.GetImageNTHeaders()->OptionalHeader.ImageBase
  283. );
  284. auto PtrToString = _TargetFile.RvaToPointer<char>(StringRva);
  285. return
  286. PtrToString &&
  287. strncmp(PtrToString, reinterpret_cast<const char*>(Keywords[i].Data), Keywords[i].Length) == 0 &&
  288. PtrToString[Keywords[i].Length] == '\x00';
  289. } else {
  290. return false;
  291. }
  292. }
  293. #endif
  294. #if defined(_M_AMD64)
  295. PatchSolution3::PatchPointInfo
  296. PatchSolution3::CreatePatchPoint(const uint8_t* PtrToCode,
  297. cs_insn* PtrToInstruction,
  298. size_t i) const {
  299. PatchPointInfo result;
  300. result.PtrToRelativeCode = const_cast<uint8_t*>(PtrToCode);
  301. result.RelativeCodeRVA = PtrToInstruction->address;
  302. if (PtrToInstruction->detail->x86.operands[1].type == X86_OP_MEM) {
  303. auto StringRva = static_cast<uintptr_t>(
  304. PtrToInstruction->address + PtrToInstruction->size + // RIP
  305. PtrToInstruction->detail->x86.operands[1].mem.disp
  306. );
  307. auto PtrToString = _TargetFile.RvaToPointer<char>(StringRva);
  308. result.PtrToOriginalString = PtrToString;
  309. if (Keywords[i].NotRecommendedToModify == false) {
  310. result.PtrToPatch = reinterpret_cast<uint8_t*>(result.PtrToOriginalString);
  311. result.PatchSize = Keywords[i].Length;
  312. } else {
  313. result.PtrToPatch =
  314. result.PtrToRelativeCode + PtrToInstruction->detail->x86.encoding.disp_offset;
  315. result.PatchSize =
  316. PtrToInstruction->detail->x86.encoding.disp_size;
  317. }
  318. } else { // X86_OP_IMM
  319. result.PtrToPatch = result.PtrToRelativeCode + PtrToInstruction->detail->x86.encoding.imm_offset;
  320. result.PatchSize = PtrToInstruction->detail->x86.encoding.imm_size;
  321. result.PtrToOriginalString = nullptr;
  322. }
  323. result.PtrToReplaceString = nullptr;
  324. return result;
  325. }
  326. #else // for i386
  327. PatchSolution3::PatchPointInfo
  328. PatchSolution3::CreatePatchPoint(const uint8_t* PtrToCode,
  329. cs_insn* PtrToInstruction,
  330. size_t i) const {
  331. PatchPointInfo result;
  332. result.PtrToRelativeCode = const_cast<uint8_t*>(PtrToCode);
  333. result.RelativeCodeRVA = PtrToInstruction->address;
  334. if (Keywords[i].Type == IMM_DATA) {
  335. result.PtrToPatch = result.PtrToRelativeCode + PtrToInstruction->detail->x86.encoding.imm_offset;
  336. result.PatchSize = PtrToInstruction->detail->x86.encoding.imm_size;
  337. result.PtrToOriginalString = nullptr;
  338. } else {
  339. auto StringRva = static_cast<uintptr_t>(
  340. PtrToInstruction->detail->x86.operands[0].imm -
  341. _TargetFile.GetImageNTHeaders()->OptionalHeader.ImageBase
  342. );
  343. auto PtrToString = _TargetFile.RvaToPointer<char>(StringRva);
  344. result.PtrToOriginalString = PtrToString;
  345. if (Keywords[i].NotRecommendedToModify == false) {
  346. result.PtrToPatch = reinterpret_cast<uint8_t*>(result.PtrToOriginalString);
  347. result.PatchSize = Keywords[i].Length;
  348. } else {
  349. result.PtrToPatch =
  350. result.PtrToRelativeCode + PtrToInstruction->detail->x86.encoding.imm_offset;
  351. result.PatchSize =
  352. PtrToInstruction->detail->x86.encoding.imm_size;
  353. }
  354. }
  355. result.PtrToReplaceString = nullptr;
  356. return result;
  357. }
  358. #endif
  359. PatchSolution3::BranchContext
  360. PatchSolution3::GetJumpedBranch(const BranchContext& NotJumpedBranch,
  361. cs_insn* PtrToJmpInstruction) const {
  362. BranchContext JumpedBranch;
  363. const BranchContext InvalidBranch = {};
  364. JumpedBranch.PtrOfCode =
  365. _TargetFile.RvaToPointer<uint8_t>(
  366. static_cast<uintptr_t>(PtrToJmpInstruction->detail->x86.operands[0].imm)
  367. );
  368. JumpedBranch.SizeOfCode =
  369. NotJumpedBranch.SizeOfCode - (JumpedBranch.PtrOfCode - NotJumpedBranch.PtrOfCode);
  370. JumpedBranch.REG_IP = PtrToJmpInstruction->detail->x86.operands[0].imm;
  371. if (JumpedBranch.PtrOfCode)
  372. return JumpedBranch;
  373. else
  374. return InvalidBranch;
  375. }
  376. PatchSolution3::BranchContext
  377. PatchSolution3::HandleJcc(const BranchContext& A,
  378. const BranchContext& B,
  379. size_t i) const {
  380. const BranchContext InvalidBranch = {};
  381. BranchContext BranchA = A;
  382. BranchContext BranchB = B;
  383. int WeightA = 0;
  384. int WeightB = 0;
  385. ResourceGuard<CapstoneMallocTraits<cs_insn>> InstructionObjGuard(
  386. cs_malloc(_CapstoneHandle)
  387. );
  388. cs_insn* PtrToInstruction = InstructionObjGuard.GetHandle();
  389. if (PtrToInstruction == nullptr)
  390. return InvalidBranch;
  391. while (true) {
  392. int WeightAPrev = WeightA;
  393. int WeightBPrev = WeightB;
  394. // process branch A
  395. while (cs_disasm_iter(_CapstoneHandle, &BranchA.PtrOfCode, &BranchA.SizeOfCode, &BranchA.REG_IP, PtrToInstruction)) {
  396. // For all x86 mnemonics, only 'jcc' or 'jmp' starts with 'j' or 'J'.
  397. // So it should be a new branch if we meet them.
  398. if (PtrToInstruction->mnemonic[0] == 'j' || PtrToInstruction->mnemonic[0] == 'J') {
  399. BranchContext JumpedBranch = GetJumpedBranch(BranchA, PtrToInstruction);
  400. if (JumpedBranch.PtrOfCode == nullptr)
  401. return InvalidBranch;
  402. if (_stricmp(PtrToInstruction->mnemonic, "jmp") == 0) {
  403. BranchA = JumpedBranch;
  404. } else {
  405. BranchA = HandleJcc(BranchA, JumpedBranch, i);
  406. if (BranchA.PtrOfCode == nullptr)
  407. break;
  408. }
  409. } else if (_stricmp(PtrToInstruction->mnemonic, "ret") == 0) {
  410. return B;
  411. } else {
  412. if (CheckIfMatchPattern(PtrToInstruction) == false)
  413. continue;
  414. // if match pattern, but keyword doesn't match,
  415. // branch A must not be what we want
  416. if (CheckIfFound(PtrToInstruction, i) == false)
  417. return B;
  418. // If keyword is succeeded to match
  419. // Add WeightA and stop processing branch A
  420. WeightA++;
  421. break;
  422. }
  423. }
  424. // process B branch
  425. while (cs_disasm_iter(_CapstoneHandle, &BranchB.PtrOfCode, &BranchB.SizeOfCode, &BranchB.REG_IP, PtrToInstruction)) {
  426. // For all x86 mnemonics, only 'jcc' or 'jmp' starts with 'j' or 'J'.
  427. // So it should be a new branch if we meet them.
  428. if (PtrToInstruction->mnemonic[0] == 'j' || PtrToInstruction->mnemonic[0] == 'J') {
  429. BranchContext JumpedBranch = GetJumpedBranch(BranchA, PtrToInstruction);
  430. if (JumpedBranch.PtrOfCode == nullptr)
  431. return InvalidBranch;
  432. if (_stricmp(PtrToInstruction->mnemonic, "jmp") == 0) {
  433. BranchB = JumpedBranch;
  434. } else {
  435. BranchB = HandleJcc(BranchB, JumpedBranch, i);
  436. if (BranchB.PtrOfCode == nullptr)
  437. break;
  438. }
  439. } else if (_stricmp(PtrToInstruction->mnemonic, "ret") == 0) {
  440. return A;
  441. } else {
  442. if (CheckIfMatchPattern(PtrToInstruction) == false)
  443. continue;
  444. if (CheckIfFound(PtrToInstruction, i) == false)
  445. return A;
  446. WeightB++;
  447. break;
  448. }
  449. }
  450. if (WeightAPrev == WeightA && WeightBPrev == WeightB)
  451. return InvalidBranch;
  452. if (WeightA != WeightB)
  453. return WeightA > WeightB ? A : B;
  454. else
  455. i++;
  456. }
  457. }
  458. bool PatchSolution3::FindPatchOffset() noexcept {
  459. memset(_Patches, 0, sizeof(_Patches));
  460. uint8_t* pFileView = _TargetFile.GetImageBaseView<uint8_t>();
  461. PIMAGE_SECTION_HEADER pSectionHdrOftext = _TargetFile.GetSectionHeader(".text");
  462. uint8_t* pSectionOftext = pFileView + pSectionHdrOftext->PointerToRawData;
  463. off_t TargetFunctionOffset = -1;
  464. if (pSectionHdrOftext == nullptr)
  465. return false;
  466. for (DWORD i = 0; i < pSectionHdrOftext->SizeOfRawData; ++i) {
  467. const uint32_t Hint = 0x6b67424e;
  468. if (*reinterpret_cast<uint32_t*>(pSectionOftext + i) == Hint) {
  469. #if defined(_M_AMD64)
  470. static const uint8_t BeginBytesOfTargetFunction[] = {
  471. 0x40, 0x55, // push rbp
  472. 0x48, 0x8D, 0xAC, 0x24, 0x70, 0xBC, 0xFF, 0xFF, // lea rbp, [rsp-4390h]
  473. 0xB8, 0x90, 0x44, 0x00, 0x00 // mov eax, 4490h
  474. };
  475. for (DWORD j = i - 0x250; j < i; ++j) {
  476. #else
  477. static const uint8_t BeginBytesOfTargetFunction[] = {
  478. 0x55, // push ebp
  479. 0x8B, 0xEC, // mov ebp, esp
  480. 0x6A, 0xFF // push 0xffffffff
  481. };
  482. for (DWORD j = i - 0x1B0; j < i; ++j) {
  483. #endif
  484. if (memcmp(pSectionOftext + j,
  485. BeginBytesOfTargetFunction,
  486. sizeof(BeginBytesOfTargetFunction)) == 0) {
  487. TargetFunctionOffset = j;
  488. break;
  489. }
  490. }
  491. break;
  492. }
  493. }
  494. if (TargetFunctionOffset == -1)
  495. return false;
  496. size_t KeywordIndex = 0;
  497. {
  498. BranchContext CurrentBranchPrevInstruction;
  499. BranchContext CurrentBranch;
  500. CurrentBranch.PtrOfCode = pSectionOftext + TargetFunctionOffset;
  501. #if defined(_M_AMD64)
  502. CurrentBranch.SizeOfCode = 0xcd03;
  503. #else
  504. CurrentBranch.SizeOfCode = 0x9014;
  505. #endif
  506. CurrentBranch.REG_IP = pSectionHdrOftext->VirtualAddress + TargetFunctionOffset;
  507. ResourceGuard<CapstoneMallocTraits<cs_insn>> InstructionObj(cs_malloc(_CapstoneHandle));
  508. cs_insn* PtrToInstruction = InstructionObj.GetHandle();
  509. while ((CurrentBranchPrevInstruction = CurrentBranch,
  510. cs_disasm_iter(_CapstoneHandle,
  511. &CurrentBranch.PtrOfCode,
  512. &CurrentBranch.SizeOfCode,
  513. &CurrentBranch.REG_IP, PtrToInstruction))) {
  514. if (PtrToInstruction->mnemonic[0] == 'j' || PtrToInstruction->mnemonic[0] == 'J') {
  515. BranchContext JumpedBranch = GetJumpedBranch(CurrentBranch, PtrToInstruction);
  516. if (JumpedBranch.PtrOfCode == nullptr)
  517. return false;
  518. if (_stricmp(PtrToInstruction->mnemonic, "jmp") == 0) {
  519. CurrentBranch = JumpedBranch;
  520. } else {
  521. CurrentBranch = HandleJcc(CurrentBranch, JumpedBranch, KeywordIndex);
  522. if (CurrentBranch.PtrOfCode == nullptr)
  523. return false;
  524. }
  525. } else if (_stricmp(PtrToInstruction->mnemonic, "ret") == 0) {
  526. return false;
  527. } else {
  528. if (CheckIfMatchPattern(PtrToInstruction) == false)
  529. continue;
  530. if (CheckIfFound(PtrToInstruction, KeywordIndex) == false)
  531. return false;
  532. _Patches[KeywordIndex] =
  533. CreatePatchPoint(CurrentBranchPrevInstruction.PtrOfCode,
  534. PtrToInstruction,
  535. KeywordIndex);
  536. KeywordIndex++;
  537. }
  538. if (KeywordIndex == KeywordsCount)
  539. break;
  540. }
  541. }
  542. if (KeywordIndex != KeywordsCount)
  543. return false;
  544. for (size_t i = 0; i < KeywordsCount; ++i) {
  545. _tprintf_s(TEXT("MESSAGE: [PatchSolution3] Keywords[%zu] has been found:\n"), i);
  546. _tprintf_s(TEXT(" Relative Machine Code Offset = +0x%016zx\n"), _Patches[i].PtrToRelativeCode - pFileView);
  547. _tprintf_s(TEXT(" Relative Machine Code RVA = +0x%016llx\n"), _Patches[i].RelativeCodeRVA);
  548. _tprintf_s(TEXT(" Patch Offset = +0x%016zx\n"), _Patches[i].PtrToPatch - pFileView);
  549. _tprintf_s(TEXT(" Patch Size = %zu byte(s)\n"), _Patches[i].PatchSize);
  550. }
  551. return true;
  552. }
  553. // Brute-force search, str_s should be 1 or 2
  554. static off_t SearchString(const void* p, size_t s, const char* str, size_t str_s) {
  555. const char* char_ptr = reinterpret_cast<const char*>(p);
  556. for (size_t i = 0; i < s; ++i) {
  557. if (char_ptr[i] == str[0]) {
  558. bool match = true;
  559. for (size_t j = 1; j < str_s; ++j) {
  560. if (char_ptr[i + j] != str[j]) {
  561. match = false;
  562. break;
  563. }
  564. }
  565. if (match && char_ptr[i + str_s] == '\x00')
  566. return static_cast<off_t>(i);
  567. }
  568. }
  569. return -1;
  570. }
  571. bool PatchSolution3::CheckKey(RSACipher* pCipher) const {
  572. std::string PublicKeyPem = pCipher->ExportKeyString<RSACipher::KeyType::PublicKey,
  573. RSACipher::KeyFormat::PEM>();
  574. PublicKeyPem.erase(PublicKeyPem.find("-----BEGIN PUBLIC KEY-----"), 26);
  575. PublicKeyPem.erase(PublicKeyPem.find("-----END PUBLIC KEY-----"), 24);
  576. {
  577. std::string::size_type pos = 0;
  578. while ((pos = PublicKeyPem.find("\n", pos)) != std::string::npos) {
  579. PublicKeyPem.erase(pos, 1);
  580. }
  581. }
  582. if(PublicKeyPem.length() != 0x188)
  583. return false;
  584. char* pFileView = _TargetFile.GetImageBaseView<char>();
  585. PIMAGE_SECTION_HEADER pSectionHdrOfrdata = _TargetFile.GetSectionHeader(".rdata");
  586. char* pSectionOfrdata = pFileView + pSectionHdrOfrdata->PointerToRawData;
  587. size_t ptr = 0;
  588. for (size_t i = 0; i < KeywordsCount; ++i) {
  589. if (Keywords[i].NotRecommendedToModify) {
  590. off_t offset = 0;
  591. while (true) {
  592. off_t off = SearchString(_Patches[i].PtrToOriginalString + offset,
  593. pSectionHdrOfrdata->SizeOfRawData - (_Patches[i].PtrToOriginalString - pSectionOfrdata) - offset,
  594. PublicKeyPem.data() + ptr,
  595. Keywords[i].Length);
  596. if (off == -1)
  597. return false;
  598. else
  599. offset += off;
  600. uintptr_t Rva =
  601. pSectionHdrOfrdata->VirtualAddress +
  602. (_Patches[i].PtrToOriginalString - pSectionOfrdata) + // OriginalString Rva
  603. offset;
  604. if (_TargetFile.IsRvaRangeInRelocationTable(Rva, Keywords[i].Length + 1) == false)
  605. break;
  606. else
  607. ++offset;
  608. }
  609. _Patches[i].PtrToReplaceString = _Patches[i].PtrToOriginalString + offset;
  610. }
  611. ptr += Keywords[i].Length;
  612. }
  613. return true;
  614. }
  615. void PatchSolution3::MakePatch(RSACipher* pCipher) const {
  616. std::string PublicKeyPem = pCipher->ExportKeyString<RSACipher::KeyType::PublicKey,
  617. RSACipher::KeyFormat::PEM>();
  618. PublicKeyPem.erase(PublicKeyPem.find("-----BEGIN PUBLIC KEY-----"), 26);
  619. PublicKeyPem.erase(PublicKeyPem.find("-----END PUBLIC KEY-----"), 24);
  620. {
  621. std::string::size_type pos = 0;
  622. while ((pos = PublicKeyPem.find("\n", pos)) != std::string::npos) {
  623. PublicKeyPem.erase(pos, 1);
  624. }
  625. }
  626. uint8_t* pFileView = _TargetFile.GetImageBaseView<uint8_t>();
  627. size_t ptr = 0;
  628. for (size_t i = 0; i < KeywordsCount; ++i) {
  629. _tprintf_s(TEXT("@ +%08zx: "), _Patches[i].PtrToPatch - pFileView);
  630. Helper::PrintSomeBytes(_Patches[i].PtrToPatch, _Patches[i].PatchSize);
  631. _tprintf_s(TEXT(" ---> "));
  632. if (Keywords[i].NotRecommendedToModify == false) {
  633. memcpy(_Patches[i].PtrToPatch, PublicKeyPem.data() + ptr, Keywords[i].Length);
  634. } else {
  635. auto offset = _Patches[i].PtrToReplaceString - _Patches[i].PtrToOriginalString;
  636. union {
  637. uint8_t bytes[8];
  638. uint64_t qword;
  639. } disp = {};
  640. memcpy(disp.bytes, _Patches[i].PtrToPatch, _Patches[i].PatchSize);
  641. disp.qword += offset;
  642. memcpy(_Patches[i].PtrToPatch, disp.bytes, _Patches[i].PatchSize);
  643. }
  644. ptr += Keywords[i].Length;
  645. Helper::PrintSomeBytes(_Patches[i].PtrToPatch, _Patches[i].PatchSize);
  646. _tprintf_s(TEXT("\n"));
  647. }
  648. return;
  649. }