1. //######################################################################
  2. //# Purpose: Modify the Cancel Button Case in UISelectCharWnd::SendMsg #
  3. //# to disconnect and show the Login Window #
  4. //######################################################################
  5. function CancelToLoginWindow() {
  6. //Step 1a - Sanity Check. Make Sure Restore Login Window is enabled.
  7. if (getActivePatches().indexOf(40) === -1)
  8. return "Patch Cancelled - Restore Login Window patch is necessary but not enabled";
  9. //Step 1b - Find the case branch that occurs before the Cancel Button case.
  10. // The pattern will match multiple locations of which 1 (or recently 2) is the one we need
  11. var code =
  12. " 8D AB AB AB AB AB 00" //LEA reg32_B, [reg32_A*8 + refAddr]
  13. + " AB" //PUSH reg32_B
  14. + " 68 37 03 00 00" //PUSH 337
  15. + " E8" //CALL addr
  16. ;
  17. var offsets = exe.findCodes(code, PTYPE_HEX, true, "\xAB");
  18. if (offsets.length === 0)
  19. {
  20. code = code.replace(" 8D AB AB AB AB AB 00", " 8D AB AB AB AB AB 01");
  21. offsets = exe.findCodes(code, PTYPE_HEX, true, "\xAB");
  22. }
  23. if (offsets.length === 0)
  24. return "Failed in Step 1 - Reference case missing";
  25. var csize = code.hexlength() + 4;
  26. //==============================//
  27. // Get all required common data //
  28. //==============================//
  29. //Step 2a - Find CConnection::Disconnect & CRagConnection::instanceR calls
  30. code =
  31. " 83 C4 08" //ADD ESP, 8
  32. + " E8 AB AB AB 00" //CALL CRagConnection::instanceR
  33. + " 8B C8" //MOV ECX, EAX
  34. + " E8 AB AB AB 00" //CALL CConnection::Disconnect
  35. + " B9 AB AB AB 00" //MOV ECX, OFFSET addr
  36. ;
  37. var offset = exe.findCode(code, PTYPE_HEX, true, "\xAB");
  38. if (offset === -1) {
  39. code = code.replace(/ E8 AB AB AB 00/g, " E8 AB AB AB FF");
  40. offset = exe.findCode(code, PTYPE_HEX, true, "\xAB");
  41. }
  42. if (offset === -1)
  43. return "Failed in Step 2 - connection functions missing";
  44. //Step 2b - Extract the RAW addresses. Not much point in converting to RVA (same section -_-)
  45. var crag = (offset + 8) + exe.fetchDWord(offset + 4);
  46. var ccon = (offset + 15) + exe.fetchDWord(offset + 11);
  47. //Step 2c - Find address of 메시지 => Korean version of "Message"
  48. offset = exe.findString("\xB8\xDE\xBD\xC3\xC1\xF6", RVA);
  49. if (offset === -1)
  50. return "Failed in Step 2 - Message not found";
  51. //Step 2d - Prep Cancel case pattern to look for
  52. var canceller =
  53. " 68" + offset.packToHex(4) //PUSH addr ; "메시지"
  54. + " AB" //PUSH reg32_A ; contains 0
  55. + " AB" //PUSH reg32_A
  56. + " 6A 01" //PUSH 1
  57. + " 6A 02" //PUSH 2
  58. + " 6A 11" //PUSH 11
  59. ;
  60. var cansize = canceller.hexlength();
  61. var matchcount = 0;
  62. for (var i = 0; i < offsets.length; i++) {
  63. //======================================//
  64. // First we find all required addresses //
  65. //======================================//
  66. //Step 3a - Find the cancel case after offsets[i] using the 'canceller' pattern
  67. // We are looking for the msgBox creator that shows the quit message
  68. offsets[i] += csize;
  69. offset = exe.find(canceller, PTYPE_HEX, true, "\xAB", offsets[i], offsets[i] + 0x80);
  70. if (offset === -1) {
  71. var zeroPush = " 6A 00";
  72. offset = exe.find(canceller.replace(" AB AB", " 6A 00 6A 00"), PTYPE_HEX, true, "\xAB", offsets[i], offsets[i] + 0x80);
  73. }
  74. else {
  75. var zeroPush = exe.fetchHex(offset + 5, 1);
  76. }
  77. if (offset === -1)
  78. continue;
  79. //Step 3b - Check for PUSH 118 before offset (only 2013+ clients have that for msgBox creation)
  80. if (exe.fetchHex(offset - 5, 5) === " 68 18 01 00 00")
  81. offset -= 7;
  82. //Step 3c - Find the end point of the msgBox call.
  83. // There will be a comparison for the return code
  84. code =
  85. " 3D AB 00 00 00" //CMP EAX, const
  86. + " 0F 85 AB AB 00 00" //JNE addr; skip quitting.
  87. ;
  88. var offset2 = exe.find(code, PTYPE_HEX, true, "\xAB", offset + cansize, offset + cansize + 40);
  89. if (offset2 === -1) {
  90. code = code.replace(" 3D AB 00 00 00", " 83 F8 AB");
  91. offset2 = exe.find(code, PTYPE_HEX, true, "\xAB", offset + cansize, offset + cansize + 40);
  92. }
  93. if (offset2 === -1)
  94. continue;
  95. offset2 += code.hexlength();
  96. //Step 3d - Lastly we find PUSH 2 below offset2 which serves as argument to the register call (CALL reg32 / CALL DWORD PTR DS:[reg32+18]) - Window Maker?.
  97. // What we need to do is to substitute the 2 with 2723 for it to show Login Window instead of quitting.
  98. code =
  99. zeroPush.repeat(3) //PUSH reg32 x3 or PUSH 0 x3
  100. + " 6A 02";
  101. var offset3 = exe.find(code, PTYPE_HEX, false, "", offset2, offset2 + 0x20);
  102. if (offset3 === -1)
  103. continue;
  104. offset3 += zeroPush.hexlength() * 3;
  105. //===================================//
  106. // Now to construct the replace code //
  107. //===================================//
  108. //Step 4a - First Disconnect from Char Server
  109. code =
  110. " E8" + GenVarHex(1) //CALL CRagConnection::instanceR
  111. + " 8B C8" //MOV ECX, EAX
  112. + " E8" + GenVarHex(2) //CALL CConnection::disconnect
  113. ;
  114. //Step 4b - Extract and paste all the code between offset2 and offset3 to prep the register call (Window Maker)
  115. code += exe.fetchHex(offset2, offset3 - offset2);
  116. //Step 4c - PUSH 2723 and go to the location after the original PUSH 2 => offset3 + 2
  117. code +=
  118. " 68 23 27 00 00" //PUSH 2723
  119. + " EB XX" //JMP addr; after PUSH 2.
  120. ;
  121. //Step 4d - Fill in the blanks
  122. code = ReplaceVarHex(code, 1, crag - (offset + 5));
  123. code = ReplaceVarHex(code, 2, ccon - (offset + 12));
  124. code = code.replace(" XX", ((offset3 + 2) - (offset + code.hexlength())).packToHex(1));
  125. //Step 4e - Replace with prepared code
  126. exe.replace(offset, code, PTYPE_HEX);
  127. matchcount++;
  128. }
  129. if (matchcount === 0)
  130. return "Failed in Step 3 - No references matched";
  131. return true;
  132. }
  133. //==========================================================================//
  134. // Disable for Unneeded Clients - Only Certain Client onwards tries to quit //
  135. //==========================================================================//
  136. function CancelToLoginWindow_() {
  137. return (exe.getClientDate() > 20100803);
  138. }

CancelToLoginWindow.qs