Jump to content

fix Enable 64k Hairstyle NEMO patch for 2017 clients


Kotel

Recommended Posts


  • Group:  Members
  • Topic Count:  1
  • Topics Per Day:  0.00
  • Content Count:  8
  • Reputation:   2
  • Joined:  07/05/13
  • Last Seen:  

This is fix for 2017 clients, patch Enable 64k Hairstyle

 

Problem:

client crashes with some hair style ids (31, 81 and randomly 30, 32 and others)

 

Solution:

edit Enable64kHairstyle.qs

 

after:

  //Step 4b - Remove the reg32_B * 4 from all the matches
  for (var i = 0; i < offsets.length; i++) {
    offset2 = offsets[i] + code.hexlength();
    exe.replaceWord(offset2 - 1, 0x9010 + (exe.fetchByte(offset2) & 0x7));
  }   

 

add:

  //Step X - extra for 2017 clients, again Find the string table fetchers
  code =
      " 8B AB AB AB AB AB"  //MOV reg32_B, DWORD PTR DS:[EBP - 8C]
    + " A1 AB AB AB 00"    //MOV reg32_A, DWORD PTR DS:[addr]
    + " 8B 14"             //MOV EDX, DWORD PTR DS:[reg32_B * 4 + reg32_A]
    ;
	//need find probably 2 occurences
    offsets = exe.findAll(code, PTYPE_HEX, true, "\xAB", offset-1000, assignOffset);
  

  if (offsets.length !== 0) { 

      for (var i = 0; i < offsets.length; i++) {
        offset2 = offsets[i] + code.hexlength();
        exe.replaceWord(offset2 - 1, 0x9010 + (exe.fetchByte(offset2) & 0x7));
      }
  }  

 

 

Tested on:

2017-06-14bRagexeRE 
2017-05-17aRagexeRE

 

  • Upvote 2
Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  162
  • Topics Per Day:  0.05
  • Content Count:  1546
  • Reputation:   192
  • Joined:  07/23/14
  • Last Seen:  

Im using a 2017-01-25aRagexeRE and it didnt work.

Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  1
  • Topics Per Day:  0.00
  • Content Count:  8
  • Reputation:   2
  • Joined:  07/05/13
  • Last Seen:  

I cant patch 2017-01-25aRagexeRE anyway, so there is another problem:

Failed in Step 2 - Unknown instruction before reference

Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  162
  • Topics Per Day:  0.05
  • Content Count:  1546
  • Reputation:   192
  • Joined:  07/23/14
  • Last Seen:  

12 minutes ago, Kotel said:

I cant patch 2017-01-25aRagexeRE anyway, so there is another problem:

Failed in Step 2 - Unknown instruction before reference

Yea that's exactly the problem..

Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  117
  • Topics Per Day:  0.03
  • Content Count:  312
  • Reputation:   34
  • Joined:  10/15/12
  • Last Seen:  

 

...

Its still giving errors im using hairstyle 41, client ver : 2017-06-14

tested on 2017-5-17 and it still gives errors

Edited by iubantot
upon further testing
Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  1
  • Topics Per Day:  0.00
  • Content Count:  8
  • Reputation:   2
  • Joined:  07/05/13
  • Last Seen:  

Probably bug in style. Try change grf. Some style packs are bugged. 

Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  154
  • Topics Per Day:  0.03
  • Content Count:  493
  • Reputation:   46
  • Joined:  01/24/12
  • Last Seen:  

oh boy it work for the first time but the 2nd tym i login my account my client crashes, what seems to be the problem with ur fix.. it looks like ur missing something..  i was able to change my hairstyle to 30 31 and then it crashes

Edited by joelolopez
Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  1
  • Topics Per Day:  0.00
  • Content Count:  8
  • Reputation:   2
  • Joined:  07/05/13
  • Last Seen:  

For me works perfect in 2017-05-17aRagexeRE, but 2018 clients looks better, so i no more use this client. 

Link to comment
Share on other sites

  • 3 weeks later...

  • Group:  Members
  • Topic Count:  12
  • Topics Per Day:  0.00
  • Content Count:  88
  • Reputation:   23
  • Joined:  01/30/12
  • Last Seen:  

Confirmed to work on 2016-12-28aRagexeRE. Thanks for the fix!

Oops game crashed after a while ?

Edited by Digos
Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  45
  • Topics Per Day:  0.01
  • Content Count:  715
  • Reputation:   83
  • Joined:  01/05/12
  • Last Seen:  


  • Group:  Members
  • Topic Count:  117
  • Topics Per Day:  0.03
  • Content Count:  312
  • Reputation:   34
  • Joined:  10/15/12
  • Last Seen:  

On 4/28/2018 at 3:05 PM, Lelouch vi Britannia said:

i already tried this fork here but it still gives me error im using 2017-06-14 btw. what client are you using?

Link to comment
Share on other sites

  • 2 weeks later...

  • Group:  Members
  • Topic Count:  9
  • Topics Per Day:  0.00
  • Content Count:  79
  • Reputation:   6
  • Joined:  04/20/16
  • Last Seen:  

Anyone got a fix yet? 2017 or 2018 client has the hair style crash bug.

Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  0
  • Topics Per Day:  0
  • Content Count:  1
  • Reputation:   0
  • Joined:  12/09/14
  • Last Seen:  

Got this working on my 2017-06-14b Client with Secret Nemo w/ the Patch for 64k from both people on this page.

I have 84 hairstyles working, but problem I'm having is 1-29 for Male and Female Hairstyles, about half of them work with Color Palette's 1-8 (which are the default hair colors). I'm using the 553/251 Cloth/Hair Dye pack. I figure it's somewhere in the Coding for 64k Hairstyles. With it turned off the Palette's work perfectly, but of course I can only use 29 Hairstyles then.

 

My Enable64kHairstyle.qs:

//####################################################
//# Purpose: Disable hard-coded hair style table and #
//#          generate hair style IDs ad-hoc instead  #
//####################################################

function Enable64kHairstyle() {
  //Step 1a - Find address of Format String 
  var code = "\xC0\xCE\xB0\xA3\xC1\xB7\\\xB8\xD3\xB8\xAE\xC5\xEB\\%s\\%s_%s.%s"; // "인간족\머리통\%s\%s_%s.%s"
  var doramOn = false;
  var offset = exe.findString(code, RAW);
  
  if (offset === -1) {//Doram Client 
    code = "\\\xB8\xD3\xB8\xAE\xC5\xEB\\%s\\%s_%s.%s"; // "\머리통\%s\%s_%s.%s"
    doramOn = true;
    offset = exe.findString(code, RAW);
  }
  
  if (offset === -1)
    return "Failed in Step 1 - String not found";
  
  //Step 1b - Change the 2nd %s to %u
  exe.replace(offset + code.length - 7, "75", PTYPE_HEX);
  
  //Step 1c - Find the string reference
  offset = exe.findCode("68" + exe.Raw2Rva(offset).packToHex(4), PTYPE_HEX, false);
  if (offset === -1)
    return "Failed in Step 1 - String reference missing";
  
  //Step 2a - Move offset to previous instruction which should be an LEA reg, [ESP+x] or LEA reg, [EBP-x]
  var fpEnb = HasFramePointer();
  if (!fpEnb)
    offset = offset - 4;
  else 
    offset = offset - 3;
  
  if (exe.fetchUByte(offset) !== 0x8D) // x > 0x7F => accomodating for the extra 3 bytes of x
    offset = offset - 3;
  
  if (exe.fetchUByte(offset) !== 0x8D)
    return "Failed in Step 2 - Unknown instruction before reference";
  
  //Step 2b - Extract the register code used in the second last PUSH reg32 before the LEA instruction (0x8D)
  var regNum = exe.fetchUByte(offset - 2) - 0x50;
  if (regNum < 0 || regNum > 7)
    return "Failed in Step 2 - Missing Reg PUSH";
  
  if (fpEnb)
    regc = (0x45 | (regNum << 3)).packToHex(1);
  else
    regc = (0x44 | (regNum << 3)).packToHex(1);
  
  //Step 2c - Now look for the location where it is assigned. Dont remove the AB at the end, the code size is used later.
  if (fpEnb) {//VC9-VC10
    code =
      " 83 7D AB 10"       //CMP DWORD PTR SS:[EBP-y], 10 ; y is unknown
    + " 8B" + regc + " AB" //MOV reg32, DWORD PTR SS:[EBP-z] ; z = y+5*4
    + " 73 03"             //JAE SHORT addr ; after LEA below
    + " 8D" + regc + " AB" //LEA reg32, [EBP-z]
    ;
  }
  else {
    code =
      " 83 7C 24 AB 10"       //CMP DWORD PTR SS:[ESP+y], 10 ; y is unknown
    + " 8B" + regc + " 24 AB" //MOV reg32, DWORD PTR SS:[ESP+z] ; z = y+5*4
    + " 73 04"                //JAE SHORT addr ; after LEA below
    + " 8D" + regc + " 24 AB" //LEA reg32, [ESP+z]
    ;
  }
  var offset2 = exe.find(code, PTYPE_HEX, true, "\xAB", offset - 0x50, offset);
  
  if (offset2 === -1) {//VC11
    if (fpEnb) {
      code = 
        " 83 7D AB 10"          //CMP DWORD PTR SS:[EBP-y], 10 ; y is unknown
      + " 8D" + regc + " AB"    //LEA reg32, [EBP-z] ; z = y+5*4
      + " 0F 43" + regc + " AB" //CMOVAE reg32, DWORD PTR SS:[EBP-z]
      ;
    }
    else {
      code = 
        " 83 7C 24 AB 10"          //CMP DWORD PTR SS:[ESP+y], 10 ; y is unknown
      + " 8D" + regc + " 24 AB"    //LEA reg32, [ESP+z] ; z = y+5*4
      + " 0F 43" + regc + " 24 AB" //CMOVAE reg32, DWORD PTR SS:[ESP+z]
      ;
    }
    
    offset2 = exe.find(code, PTYPE_HEX, true, "\xAB", offset - 0x50, offset);
  }
  
  if (offset2 === -1)
    return "Failed in Step 2 - Register assignment missing";
  
  //Step 2d - Save the offset2 and code size (We need to NOP out the excess)
  var assignOffset = offset2;
  var csize = code.hexlength();
  
  //Step 3a - Find the start of the function (has a common signature like many others)
  code =
    " 6A FF"             //PUSH -1
  + " 68 AB AB AB 00"    //PUSH value
  + " 64 A1 00 00 00 00" //MOV EAX, FS:[0]
  + " 50"                //PUSH EAX
  + " 83 EC"             //SUB ESP, const
  ;
  offset = exe.find(code, PTYPE_HEX, true, "\xAB", offset2 - 0x1B0, offset2);

  if (offset === -1) {//const is > 0x7F
    code =
      " 6A FF"             //PUSH -1
    + " 68 AB AB AB 00"    //PUSH value
    + " 64 A1 00 00 00 00" //MOV EAX, FS:[0]
    + " 50"                //PUSH EAX
    + " 81 EC"             //SUB ESP, const
    ;
    offset = exe.find(code, PTYPE_HEX, true, "\xAB", offset2 - 0x2A0, offset2);
  }
  
  if (offset === -1) { // 2017 +
    offset = exe.find(code, PTYPE_HEX, true, "\xAB", offset2 - 0x2A0, offset2);
  }
  
  if (offset === -1)
    return "Failed in Step 3 - Function start missing";
  
	offset += code.hexlength();

  
  //Step 3b - Get the Stack offset w.r.t. ESP/EBP for Arg.5
  var arg5Dist = 5*4; //for the 5 PUSHes of the arguments
  
  if (fpEnb) {
    arg5Dist += 4; //Account for the PUSH EBP in the beginning
  }
  else {
    arg5Dist += 7*4;//Account for PUSH -1, PUSH addr and 5 reg32 PUSHes
    
    if (exe.fetchUByte(offset - 2) === 0x81) // Add the const from SUB ESP, const
      arg5Dist += exe.fetchDWord(offset);
    else
      arg5Dist += exe.fetchByte(offset);
    
    //Step 3c - Account for an extra PUSH instruction (security related) in VC9 clients
    code =
      " A1 AB AB AB 00" //MOV EAX, DWORD PTR DS:[__security_cookie]; 
    + " 33 C4"          //XOR EAX, ESP
    + " 50"             //PUSH EAX
    ;
    if (exe.find(code, PTYPE_HEX, true, "\xAB", offset + 0x4, offset + 0x20) !== -1)
      arg5Dist += 4;
  }
  //Step 3d - Prep code to change assignment (hairstyle index instead of the string)
  if (fpEnb) {
    code = " 8B" + regc + arg5Dist.packToHex(1); //MOV reg32_A, DWORD PTR SS:[EBP + arg5Dist]; ARG.5
  }
  else if (arg5Dist > 0x7F) {
    code = " 8B" + (0x84 | (regNum << 3)).packToHex(1) + " 24" + arg5Dist.packToHex(4); //MOV reg32_A, DWORD PTR SS:[ESP + arg5Dist]; ARG.5
  }
  else {
    code = " 8B" + regc + " 24" + arg5Dist.packToHex(1); //MOV reg32_A, DWORD PTR SS:[ESP + arg5Dist]; ARG.5
  }
  
  code += " 8B" + ((regNum << 3) | regNum).packToHex(1); //MOV reg32_A, DWORD PTR DS:[reg32_A]
  code += " 90".repeat(csize - code.hexlength());//Fill rest with NOPs
  
  //Step 3e - Replace the original at assignOffset
  exe.replace(assignOffset, code, PTYPE_HEX);
  
  //Step 4a - Find the string table fetchers
  code =
    " 8B AB AB AB AB 00" //MOV reg32_A, DWORD PTR DS:[addr]
  + " 8B AB 00"          //MOV reg32_B, DWORD PTR DS:[EBP]
  + " 8B 14"             //MOV EDX, DWORD PTR DS:[reg32_B * 4 + reg32_A]
  ;
  var offsets = exe.findAll(code, PTYPE_HEX, true, "\xAB", offset, assignOffset);
  
  if (offsets.length === 0) {
    code =
      " 8B AB"             //MOV reg32_B, DWORD PTR DS:[reg32_C]
    + " 8B AB AB AB AB 00" //MOV reg32_A, DWORD PTR DS:[addr]
    + " 8B 14"             //MOV EDX, DWORD PTR DS:[reg32_B * 4 + reg32_A]
    ;
    offsets = exe.findAll(code, PTYPE_HEX, true, "\xAB", offset, assignOffset);
  }
  
  if (offsets.length === 0) {
    code =
      " 8B AB"             //MOV reg32_B, DWORD PTR DS:[reg32_C]
    + " A1 AB AB AB 00"    //MOV reg32_A, DWORD PTR DS:[addr]
    + " 8B 14"             //MOV EDX, DWORD PTR DS:[reg32_B * 4 + reg32_A]
    ;
    offsets = exe.findAll(code, PTYPE_HEX, true, "\xAB", offset, assignOffset);
  }

  if (offsets.length === 0) {  // 2017 +
    code =
      " 8B AB"             //MOV reg32_B, DWORD PTR DS:[reg32_C]
    + " A1 AB AB AB 01"    //MOV reg32_A, DWORD PTR DS:[addr]
    + " 8B 14"             //MOV EDX, DWORD PTR DS:[reg32_B * 4 + reg32_A]
    ;
    offsets = exe.findAll(code, PTYPE_HEX, true, "\xAB", offset, assignOffset);
  }
  
  if (offsets.length === 0) {  // 2017 +
    code =
      " 8B AB"             //MOV reg32_B, DWORD PTR DS:[reg32_C]
    + " A1 AB AB AB 01"    //MOV reg32_A, DWORD PTR DS:[addr]
    + " 8B 14"             //MOV EDX, DWORD PTR DS:[reg32_B * 4 + reg32_A]
    ;
    offsets = exe.findAll(code, PTYPE_HEX, true, "\xAB", offset, assignOffset);
  }

 
  if (offsets.length === 0)
    return "Failed in Step 4 - Table fetchers missing";
  
  //Step 4b - Remove the reg32_B * 4 from all the matches
  for (var i = 0; i < offsets.length; i++) {
    offset2 = offsets[i] + code.hexlength();
    exe.replaceWord(offset2 - 1, 0x9010 + (exe.fetchByte(offset2) & 0x7));
  }
  
 //Step X - extra for 2017 clients, again Find the string table fetchers
  code =
      " 8B AB AB AB AB AB"  //MOV reg32_B, DWORD PTR DS:[EBP - 8C]
    + " A1 AB AB AB 00"    //MOV reg32_A, DWORD PTR DS:[addr]
    + " 8B 14"             //MOV EDX, DWORD PTR DS:[reg32_B * 4 + reg32_A]
    ;
	//need find probably 2 occurences
    offsets = exe.findAll(code, PTYPE_HEX, true, "\xAB", offset-1000, assignOffset);
  

  if (offsets.length !== 0) { 

      for (var i = 0; i < offsets.length; i++) {
        offset2 = offsets[i] + code.hexlength();
        exe.replaceWord(offset2 - 1, 0x9010 + (exe.fetchByte(offset2) & 0x7));
      }
  }  
  
  //Step 5a - Find the Hairstyle limiting comparison within the function
  code = 
    " 7C 05"    //JL SHORT addr1; skips the next two instructions
  + " 83 AB AB" //CMP reg32_A, const; const = max hairstyle ID
  + " 7E AB"    //JLE SHORT addr2; skip the next assignment - AB should be 06 or 07
  + " C7"       //MOV DWORD PTR DS:[reg32_B], 0D
  ;
  offset2 = exe.find(code, PTYPE_HEX, true, "\xAB", offset + 4, offset + 0x50);//VC9 - VC10
  
  if (offset2 === -1) {
    code = 
      " 78 05"    //JL SHORT addr1; skips the next two instructions
    + " 83 AB AB" //CMP reg32_A, const; const = max hairstyle ID
    + " 7E AB"    //JLE SHORT addr2; skip the next assignment - AB should be 06 or 07
    + " C7"       //MOV DWORD PTR DS:[reg32_B], 0D
    ;

    offset2 = exe.find(code, PTYPE_HEX, true, "\xAB", offset + 4, offset + 0x50);//VC11
  }
  
  if (offset2 === -1 && doramOn) {//For Doram Client, its farther away since there are extra checks for Job ID within Doram Range or Human Range
    offset2 = exe.find(code, PTYPE_HEX, true, "\xAB", offset + 0x100, offset + 0x200);
  }
  
  if (offset2 === -1)
    return "Failed in Step 5 - Limit checker missing";
  
  offset2 += code.hexlength();
  
  //Step 5b - Change the JLE to JMP
  exe.replace(offset2 - 3, "EB", PTYPE_HEX);
  
  //Step 5c - Change 0D to 02 in MOV instruction
  code = exe.fetchUByte(offset2);
  if (code === 0x04 || code > 0x07)
    exe.replace(offset2 + 2, "02");
  else
    exe.replace(offset2 + 1, "02");
  
  //Remove the && 0 to enable for Doram
  if (doramOn && 0) {//Repeat 5a & 5b for Doram race which appears before offset2. 
    //Step 6a - Find the Hairstyle limiting comparison within the function for Doram race
    code = 
      " 7C 05"    //JL SHORT addr1; skips the next two instructions
    + " 83 AB AB" //CMP reg32_A, const; const = max hairstyle ID
    + " 7C AB"    //JLE SHORT addr2; skip the next assignment - AB should be 06 or 07
    + " C7"       //MOV DWORD PTR DS:[reg32_B], 06
    ;
    
    offset = exe.find(code, PTYPE_HEX, true, "\xAB", offset2 - 0x75, offset2 - 0x10);
    if (offset === -1)
      return "Failed in Step 6 - Doram Limit Checker missing";
  
    offset += code.hexlength();
    
    //Step 6b - Change the JLE to JMP
    exe.replace(offset - 3, "EB", PTYPE_HEX);
    
    //Step 6c - Change 0D to 02 in MOV instruction
    code = exe.fetchUByte(offset);
    if (code === 0x04 || code > 0x07)
      exe.replace(offset + 2, "02");
    else
      exe.replace(offset + 1, "02");
  }
  
  return true;
}

//=================================//
// Disable for Unsupported Clients //
//=================================//
function Enable64kHairstyle_() {
  return (exe.getClientDate() > 20111102);
}

 

Edited by Sukiyomi
added code
Link to comment
Share on other sites

  • 2 months later...

  • Group:  Members
  • Topic Count:  1
  • Topics Per Day:  0.00
  • Content Count:  8
  • Reputation:   2
  • Joined:  07/05/13
  • Last Seen:  

Some part is individual for every client.

more universal code (AB can be everythink):

 

//Step X - extra for 2017/2018 clients, again Find the string table fetchers and remove reg32_B * 4
  code =
      " 8B AB AB AB AB AB"  //MOV reg32_B, DWORD PTR DS:[EBP - 8C]
    + " A1 AB AB AB AB"    //MOV reg32_A, DWORD PTR DS:[addr]
    + " 8B 14"             //MOV EDX, DWORD PTR DS:[reg32_B * 4 + reg32_A]
    ;
	//need find probably 2 occurences (male, female)
    offsets = exe.findAll(code, PTYPE_HEX, true, "\xAB", offset-1000, assignOffset);
  

  if (offsets.length !== 0) { 

      for (var i = 0; i < offsets.length; i++) {
        offset2 = offsets[i] + code.hexlength();
        exe.replaceWord(offset2 - 1, 0x9010 + (exe.fetchByte(offset2) & 0x7));
      }
  } 

 

this works for 2018-01-24bRagexeRE and maybe 2017

 

edit: found another problem, this works only without headgear ?

Edited by Kotel
Link to comment
Share on other sites


  • Group:  Members
  • Topic Count:  1
  • Topics Per Day:  0.00
  • Content Count:  8
  • Reputation:   2
  • Joined:  07/05/13
  • Last Seen:  

Maybe final solution, but not universal (each client has probably different function start). This is for 2018-01-24bRagexeRE

 

previous post + this

 

before this :

  return true;
}

//=================================//
// Disable for Unsupported Clients //
//=================================//
function Enable64kHairstyle_() {
  return (exe.getClientDate() > 20111102);
}

 

add this:

 

      code =
      " 55"                 //PUSH ebp
    + " 8B EC"              //mov ebp, esp
    + " 6A FF"              //push 0xffffffff
    + " 68 AB AB AB AB"     //PUSH address
    + " 64 A1 00 00 00 00"  //mov eax,dword ptr [0x0] 
    + " 50"                 //push eax 
    + " 81 EC 90 00 00 00"  //sub esp,0x90
    + " A1 AB AB AB AB"    //mov eax,dword ptr address
    + " 33 C5"              //xor eax, ebp
    + " 89 AB AB"         //mov dword ptr [ebp-0x10],eax
    + " 53"              //push ebx
    + " 56"              //push esi
    + " 57"              //push edi
    + " 50"              //push eax
    ;
    offset = exe.find(code, PTYPE_HEX, true, "\xAB");
    
  
 if(offset !== -1) 
 {
    code =
        " 8B 0E"              //MOV ecx, [esi]
      + " A1 AB AB AB AB"    //MOV reg32_A, DWORD PTR DS:[addr]
      + " 8B 14"             //MOV EDX, DWORD PTR DS:[reg32_B * 4 + reg32_A]
      ;
      offsets = exe.findAll(code, PTYPE_HEX, true, "\xAB", offset, offset+1000);


    if (offsets.length == 2) { 

        for (var i = 0; i < offsets.length; i++) {
          offset2 = offsets[i] + code.hexlength();
          exe.replaceWord(offset2 - 1, 0x9010 + (exe.fetchByte(offset2) & 0x7));
        }
    } 
    else
 	    return "Failed in Step Y - offsets not found";
 }
 else
	 return "Failed in Step Y - start of the function not found";
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...