Sunday, April 24, 2016

Clean C++ Member Function Detouring

Whenever I write detour code related to member functions things tend to get messy so today I decided not to be lazy and to do it properly. In order to write a detour for a member function cleanly you can do the following:

* Stop using __fastcall and use __thiscall as MSVC has support for declaring functions with __thiscall now
* Rebuild the entire class with its members, inheritance hierarchy and even the member functions just as if it were your code
* Use static pointers-to-member functions to manage your trampoline pointers
* Add support for member function detouring to your detour code
* Simple functions that return the address for each method

Example:

As an example I'll use a portion of Skype's Socket class. The first step is reversing a method (class function) that utilizes the data structure/class that is Socket so I found the Send/Receive methods which are Socket::Send and Socket::Receive.

Socket::Send Signature -

Socket::Send Pattern -

Socket::Receive Signature -
Socket::Receive Pattern -

Inside of hex-rays right-click the signature of the pseudo-subroutine and click 'Create new struct type' and you have the members declared for you.

Hex-rays gave me this data structure:

Now there's a start for creating the rest of the class. Now add the simple functions that return the address of the two methods after using a find pattern (or a hard-coded address depending on what you do).


At this point you know the signature of the methods Socket::Send and Socket::Receive and you have the members too (probably not every member). At this point you can construct a portion of the class using what you have and come up with something like this:


My definition of Socket::Send and Socket::Receive can be used when I want to call the function manually but I can also use it as my detour function that replaces the real Socket::Send and Socket::Receive. In order detour a member function you have to be able to store a pointer to it and for this you need a special cast which kokole posted:


In your normal detour class if you already provide a templated constructor for accepting the target and detour then just create a specialization like I did:


One final issue that you'll come across is declaring the trampoline. Normally I see people declaring trampolines as global variables that are pointer-to-functions but casting to a pointer-to-member function type isn't possible so you can't do:
But what you can do is use the __thiscall calling convention. Don't make the same mistake I did and assume __thiscall will do some magic that'll just pass on ecx to the function being called. The first parameter in a __thiscall pointer-to-member function declaration is a pointer to the class type. So what I did was:


I declared the trampolines oSocketSend and oSocketReceive as static objects variables inside of the class because it'll be easier to identify it later using Socket:Socket etc. Of course you'll have to define the variable in your implementation file (.cpp).

And yeah so you can finally write code like this:

Result (some stuff I was working on today):


Thursday, January 14, 2016

Farcry 2 In-game Console Hook

I got bored again so I decided to come back to Far Cry 2 to see if it's any fun. As someone that doesn't have the energy to learn anything drawing related (dx/opengl) I decided that I'd go the usual route of CLI-based commands. The in-game console can be launched using the tilde (~) key and you can issue a limited set of commands. Using the data structures/functions below you can replace the default callback function with your custom one in order to add custom commands for your hax.

NOTE: Since there isn't any data on this game (SDK/PDB/etc.) I'm naming data structures with names I deem fit.
Code:
  1. struct __declspec(align(4)) IConsole
  2. {
  3. __int32 N00000001[0x1A];
  4. char EnableConsoleWrite;
  5. __int16 mIsOpen;
  6. char Padding1;
  7. __int32 N0000001[0X90];
  8. };
IConsole represents the in-game console. There's 1 instance of this class which is a global variable located at:
Code:
  1. .data:11555980
You can build a wrapper class that uses singleton pattern to properly rebuild this class or just use the pointer directly.

Every time a command is entered the game takes the input, formats it and sends it to the callback function for parsing, filtering etc. The address of this callback function is at:
Code:
  1. .code:0x102955E0
Pseudo-code for the callback function (Hexrays):
Code:
  1. void __thiscall OnCommandEntered(IConsole *this, CommandDescriptor *commandDescriptor)
  2. {
  3. IConsole *pConsole; // ebp@1
  4. unsigned int src_; // esi@8
  5. _BYTE *v4; // ecx@8
  6. unsigned int v5; // eax@11
  7. unsigned int v6; // esi@13
  8. unsigned int v7; // eax@13
  9. unsigned int v8; // eax@13
  10. int v9; // ST10_4@15
  11. unsigned int v10; // ST0C_4@15
  12. unsigned int srcLength; // eax@18
  13. const char *v12; // eax@18
  14. const char *v13; // esi@18
  15. int v14; // eax@20
  16. int v15; // edi@22
  17. int v16; // edi@24
  18. int v17; // eax@27
  19. int v18; // eax@27
  20. const char *i; // eax@27
  21. void *v20; // eax@32
  22. int v21; // ST10_4@34
  23. void *v22; // ST0C_4@34
  24. int v23; // esi@35
  25. char *v24; // eax@35
  26. CommandDescriptor *v25; // ST14_4@35
  27. wchar_t *v26; // eax@35
  28. int v27; // eax@38
  29. wchar_t *v28; // eax@39
  30. char *v29; // eax@41
  31. const char *v30; // eax@44
  32. const char *result; // eax@45
  33. const char *v32; // eax@46
  34. _BYTE *v33; // eax@46
  35. const char *v34; // eax@49
  36. int v35; // edi@49
  37. signed int v36; // ebp@51
  38. unsigned int v37; // eax@51
  39. const char **v38; // ebx@51
  40. const char *v39; // ecx@52
  41. const void *v40; // edx@57
  42. const char *v41; // ecx@60
  43. char *v42; // ecx@64
  44. int v43; // eax@67
  45. int v44; // ecx@70
  46. int v45; // eax@70
  47. wchar_t *v46; // eax@71
  48. char *v47; // eax@74
  49. char *commandStr; // eax@80
  50. char searchCharacter; // [sp+10h] [bp-150h]@1
  51. int v50; // [sp+14h] [bp-14Ch]@21
  52. bool v51; // [sp+1Bh] [bp-145h]@48
  53. int a1; // [sp+1Ch] [bp-144h]@1
  54. unsigned int src; // [sp+20h] [bp-140h]@5
  55. int v54; // [sp+30h] [bp-130h]@6
  56. unsigned int v55; // [sp+34h] [bp-12Ch]@4
  57. void *v56; // [sp+38h] [bp-128h]@1
  58. char *userCommandStr; // [sp+3Ch] [bp-124h]@1
  59. int v58; // [sp+4Ch] [bp-114h]@1
  60. unsigned int v59; // [sp+50h] [bp-110h]@1
  61. int v60; // [sp+54h] [bp-10Ch]@48
  62. unsigned int v61; // [sp+58h] [bp-108h]@48
  63. int v62; // [sp+5Ch] [bp-104h]@48
  64. char v63; // [sp+60h] [bp-100h]@26
  65. char *v64; // [sp+64h] [bp-FCh]@32
  66. int v65; // [sp+74h] [bp-ECh]@34
  67. unsigned int v66; // [sp+78h] [bp-E8h]@32
  68. int descriptor_1; // [sp+7Ch] [bp-E4h]@20
  69. IConsole *pConsole1; // [sp+98h] [bp-C8h]@1
  70. int (__stdcall **v69)(int, int); // [sp+9Ch] [bp-C4h]@35
  71. wchar_t *fmt; // [sp+A0h] [bp-C0h]@35
  72. int v71; // [sp+A4h] [bp-BCh]@78
  73. int *v72; // [sp+A8h] [bp-B8h]@78
  74. int v73; // [sp+ACh] [bp-B4h]@78
  75. unsigned int v74; // [sp+B4h] [bp-ACh]@35
  76. int descriptor; // [sp+B8h] [bp-A8h]@8
  77. char v76; // [sp+D4h] [bp-8Ch]@21
  78. char v77; // [sp+F0h] [bp-70h]@27
  79. int v78; // [sp+10Ch] [bp-54h]@27
  80. char v79; // [sp+128h] [bp-38h]@27
  81. char v80; // [sp+144h] [bp-1Ch]@27
  82.  
  83. pConsole = this;
  84. pConsole1 = this;
  85. sub_100033D0(&a1, commandDescriptor); // parses the descriptor?
  86. v56 = &unk_10F239D1;
  87. v59 = 15;
  88. searchCharacter = 0;
  89. v58 = 0;
  90. std::char_traits<char>::assign(&userCommandStr, &searchCharacter);// ZeroMemory
  91. if ( !sub_1028F9E0(pConsole, &a1) )
  92. {
  93. if ( !v54 )
  94. {
  95. LABEL_85:
  96. sub_10096AC0(&v56);
  97. sub_10096AC0(&a1);
  98. return;
  99. }
  100. CommandDescriptor::CommandDescriptor(&descriptor, &version);// Constructor for a2
  101. sub_10002850(&descriptor, commandDescriptor, 0, 0xFFFFFFFF);
  102. sub_10292EE0(pConsole, &descriptor);
  103. src_ = src;
  104. v4 = src;
  105. if ( v55 < 0x10 )
  106. v4 = &src;
  107. if ( *v4 == 35 )
  108. {
  109. v5 = src;
  110. if ( v55 < 0x10 )
  111. v5 = &src;
  112. v6 = v5 + 1;
  113. v7 = std::char_traits<char>::length(v5 + 1);
  114. sub_10002D40(&a1, v6, v7);
  115. v8 = src;
  116. if ( v55 < 0x10 )
  117. v8 = &src;
  118. v9 = v54;
  119. v10 = v8;
  120. sub_102A7350();
  121. sub_102A7C90(v10, v9, 0);
  122. goto LABEL_84;
  123. }
  124. if ( v55 < 0x10 )
  125. src_ = &src;
  126. srcLength = std::char_traits<char>::length(src_);
  127. sub_10002D40(&a1, src_, srcLength);
  128. searchCharacter = 32;
  129. v12 = ParseCommand(&a1, &searchCharacter, 0x100000000ui64);
  130. v13 = v12;
  131. if ( v12 == -1 )
  132. {
  133. sub_10002B00(&v56, &a1, 0, 0xFFFFFFFF);
  134. }
  135. else
  136. {
  137. v14 = sub_10003110(&descriptor_1, 0, v12);
  138. sub_10002B00(&v56, v14, 0, 0xFFFFFFFF);
  139. sub_10096AC0(&descriptor_1);
  140. }
  141. sub_10742C00(&v56);
  142. sub_10002CC0(&v76);
  143. sub_10290810(&v50, &v76);
  144. if ( v50 != pConsole->N00000012 )
  145. {
  146. v15 = v50 + 40;
  147. if ( sub_1028F7D0(pConsole, v50 + 40) )
  148. {
  149. sub_10002B00(&a1, &a1, 0, 0xFFFFFFFF);
  150. sub_10294370(pConsole, v15, &a1);
  151. goto EXIT_FUNCTION;
  152. }
  153. }
  154. sub_10290190(&v50, &v56);
  155. v16 = v50;
  156. if ( v50 != pConsole->N00000018 )
  157. {
  158. v50 = *(v50 + 40);
  159. if ( sub_1028F7D0(pConsole, v50) )
  160. {
  161. sub_10047090(&v63);
  162. sub_100032C0(v16 + 12);
  163. if ( v13 != -1 )
  164. {
  165. v17 = sub_10003110(&descriptor_1, v13 + 1, -1);
  166. sub_100032C0(v17);
  167. sub_10096AC0(&descriptor_1);
  168. sub_10003300(&descriptor_1, filename);
  169. sub_10003300(&v78, "=\"");
  170. sub_100031F0(&v77, &v78);
  171. sub_100031F0(&v80, &a1);
  172. v18 = sub_100031F0(&v79, &descriptor_1);
  173. sub_100032C0(v18);
  174. sub_10096AC0(&v79);
  175. sub_10096AC0(&v80);
  176. sub_10096AC0(&v77);
  177. sub_10096AC0(&v78);
  178. sub_10096AC0(&descriptor_1);
  179. searchCharacter = 32;
  180. for ( i = ParseCommand(&a1, &searchCharacter, 0x100000000ui64);
  181. i != -1;
  182. i = ParseCommand(&a1, &searchCharacter, 0x100000000ui64) )
  183. {
  184. sub_100BC060(&a1, i, 1u);
  185. searchCharacter = 32;
  186. }
  187. if ( AreStringsEqual(&a1, "?") || AreStringsEqual(&a1, "help") )
  188. {
  189. v27 = sub_10290660(&v77);
  190. if ( *(v27 + 24) < 8u )
  191. v28 = (v27 + 4);
  192. else
  193. v28 = *(v27 + 4);
  194. CommandDescriptor::CommandDescriptor(&descriptor_1, v28);
  195. v29 = userCommandStr;
  196. if ( v59 < 0x10 )
  197. v29 = &userCommandStr;
  198. sub_10293700(pConsole, &descriptor_1, v29);
  199. sub_10002930(&descriptor_1);
  200. sub_10002930(&v77);
  201. sub_10096AC0(&v63);
  202. goto EXIT_FUNCTION;
  203. }
  204. if ( v54 )
  205. {
  206. v20 = v64;
  207. if ( v66 < 0x10 )
  208. v20 = &v64;
  209. v21 = v65;
  210. v22 = v20;
  211. sub_102A7350();
  212. sub_102A7C90(v22, v21, 0);
  213. }
  214. }
  215. v23 = v50;
  216. v24 = sub_102ADAC0(v50);
  217. sub_10003300(&v78, v24);
  218. CommandDescriptor::CommandDescriptor(&descriptor_1, L" = ");
  219. v25 = Descriptor::Descriptor(&v79, &v78);
  220. Descriptor::Descriptor(&v80, v23 + 4);
  221. sub_10191620(&v77, &descriptor_1);
  222. sub_10191620(&v69, v25);
  223. sub_10002930(&v77);
  224. sub_10002930(&v80);
  225. sub_10002930(&v79);
  226. sub_10002930(&descriptor_1);
  227. sub_10096AC0(&v78);
  228. v26 = fmt;
  229. if ( v74 < 8 )
  230. v26 = &fmt;
  231. WriteToConsole(pIConsole, 0, v26);
  232. sub_10002930(&v69);
  233. sub_10096AC0(&v63);
  234. EXIT_FUNCTION:
  235. sub_10096AC0(&v76);
  236. LABEL_84:
  237. sub_10002930(&descriptor);
  238. goto LABEL_85;
  239. }
  240. }
  241. searchCharacter = 63;
  242. v30 = ParseCommand(&a1, &searchCharacter, '\x01\0\0\0\0');
  243. searchCharacter = 0;
  244. if ( v30 + 1 == 0 )
  245. {
  246. searchCharacter = 33;
  247. result = ParseCommand(&a1, &searchCharacter, '\x01\0\0\0\0');
  248. searchCharacter = 1;
  249. if ( result + 1 == 0 )
  250. {
  251. if ( commandDescriptor->CopySize < 8u )
  252. commandStr = &commandDescriptor->UserCommand;
  253. else
  254. commandStr = commandDescriptor->UserCommand;
  255. WriteToConsole(pIConsole, '\0', L"Unknown command: %s", commandStr);
  256. goto EXIT_FUNCTION;
  257. }
  258. }
  259. v32 = sub_10AD7180(&a1, " ", '\0');
  260. sub_10003110(&v63, '\0', v32);
  261. v33 = src;
  262. if ( v55 < 0x10 )
  263. v33 = &src;
  264. v51 = *v33 == 63;
  265. v60 = '\0';
  266. v61 = '\0';
  267. v62 = 0;
  268. sub_10295300(&v60, 1);
  269. v50 = '\0';
  270. if ( v61 <= '\0' )
  271. {
  272. LABEL_78:
  273. v72 = &v61;
  274. fmt = &v60;
  275. v71 = 0;
  276. v73 = 4;
  277. v69 = &off_10DC385C;
  278. sub_10225F00(&v69);
  279. sub_10096AC0(&v63);
  280. goto EXIT_FUNCTION;
  281. }
  282. while ( 1 )
  283. {
  284. v34 = v64;
  285. v35 = *(v60 + 4 * v50) + 4;
  286. if ( v66 < 0x10 )
  287. v34 = &v64;
  288. v36 = strlen(v34);
  289. v37 = *(*(v60 + 4 * v50) + 28);
  290. v38 = (*(v60 + 4 * v50) + 8);
  291. if ( v37 < 0x10 )
  292. v39 = (*(v60 + 4 * v50) + 8);
  293. else
  294. v39 = *v38;
  295. if ( strlen(v39) < v36 )
  296. goto LABEL_77;
  297. if ( !v51 )
  298. {
  299. if ( searchCharacter )
  300. {
  301. v42 = v64;
  302. if ( v66 < 0x10 )
  303. v42 = &v64;
  304. if ( v37 < 0x10 )
  305. v43 = *(v60 + 4 * v50) + 8;
  306. else
  307. v43 = *v38;
  308. if ( !sub_1018DAD0(v43, v42) )
  309. goto LABEL_77;
  310. }
  311. else
  312. {
  313. v40 = v64;
  314. if ( v66 < 0x10 )
  315. v40 = &v64;
  316. if ( v37 < 0x10 )
  317. v41 = (*(v60 + 4 * v50) + 8);
  318. else
  319. v41 = *v38;
  320. if ( memicmp(v41, v40, v36) )
  321. goto LABEL_77;
  322. }
  323. }
  324. v44 = *(v60 + 4 * v50);
  325. v45 = sub_10290660(&v77);
  326. if ( *(v45 + 24) < 8u )
  327. v46 = (v45 + 4);
  328. else
  329. v46 = *(v45 + 4);
  330. CommandDescriptor::CommandDescriptor(&descriptor_1, v46);
  331. if ( *(v35 + 24) < 0x10u )
  332. v47 = (v35 + 4);
  333. else
  334. v47 = *v38;
  335. sub_10293700(pConsole1, &descriptor_1, v47);
  336. sub_10002930(&descriptor_1);
  337. sub_10002930(&v77);
  338. LABEL_77:
  339. if ( ++v50 >= v61 )
  340. goto LABEL_78;
  341. }
  342. }
  343. if ( v59 >= 0x10 )
  344. sub_10419130(userCommandStr);
  345. v59 = 15;
  346. searchCharacter = 0;
  347. v58 = 0;
  348. std::char_traits<char>::assign(&userCommandStr, &searchCharacter);
  349. if ( v55 >= 0x10 )
  350. sub_10419130(src);
  351. v55 = 15;
  352. searchCharacter = 0;
  353. v54 = 0;
  354. std::char_traits<char>::assign(&src, &searchCharacter);
  355. }
The function takes a pointer to a data structure I named CommandDescriptor. This contains information such as the actual string, length of the string and some other stuff that I'll act like I understand. CommandDescriptor:
Code:
  1. struct CommandDescriptor
  2. {
  3. int field_0;
  4. char *UserCommand;
  5. int field_8;
  6. int field_C;
  7. int field_10;
  8. int CommandLength;
  9. int CopySize;
  10. };
If you want to implement your own commands then just hook/redirect the callback function and add in your own parsing, filtering code. A nice way to implement this would be to use an std::map that maps strings (the command) to a second callback function to be called for that specific command.

Enjoy (:

Bonus:

The version of the console is printed in a different position inside of the console. In order to print out in any position on the screen you can reverse the way it's printed. This is at:

Code:
  1. .text:104C262F
Example Case (Command length):