PatchSolution3.cpp 29 KB

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