[black magic] Hacking into constant pool: altering embedded strings

Discussion in 'Resources' started by RawCode, Jun 24, 2014.

Thread Status:
Not open for further replies.
  1. Offline

    RawCode

    Unlike many(but not every) thread in this branch of forum this "tutorial" not for noobs and does not explain oracle docs of vanilla classes or javadocs shipped with bukkit.

    Thread made as extended reply to:
    http://forums.bukkit.org/threads/tut-replacing-certain-text-in-a-string.283027/
    http://forums.bukkit.org/threads/changing-the-message-of-original-bukkit-msg.282731/
    http://forums.bukkit.org/threads/ho...ou-dont-have-permission-for-this-area.282459/

    Just like that threads, i will show how to replace "text" in a normally immutable String objects embedded in some classes.

    First part of article will show "lab" version of code, without actual hacking into constant pool, second part is "live" - stub plugin that will alter buildin "world guard" message of "you dont have permission".
    Second part provided only as reference, no actual code spoiled (but you can download plugin and view source).

    A bit of theory:

    String is special java class, unlike random bukkit "Player" or "super secure Enum" it features special handling by JVM: intern method implemented by native "SymbolTable" made on c++.
    equals method baked by native implementation "behind the scene".

    Due native component, altering char[] that store actual content of string is harmful and do have side effects.

    After string is altered by explained method, intern method will fail (also will fail equals() method) to return valid reference to target string, string still can be extracted from constant pool, but anything that rely on intern() or equals() will fail with unpredictable results.
    So this method viable only for strings that have no real affect to codeflow, in other case (like literal constants) such actions require some additional work to counter side effects, like regenerating references by altering all strings at play.

    For research you can use vanilla reflection and identityHashCode or misc.Unsafe (my implementation of unsafe:
    https://github.com/RawCode/UBT/blob/master/src/rc/ubt/impl/UnsafeImpl.java)

    You will need this to output object ID, without this feature you can't "see" is tested object "same" or "different" object.
    Ofc you can compare everything to everything, but list of IDs much better option.

    First step of reseach shoud looks like :
    Code:
    		char[] RawDataNotInterned = {'t','e','s','t'};
    		String InternedString = "test";
    		
    		System.out.println(Long.toHexString(Object2ID(InternedString)));
    		System.out.println(Long.toHexString(Object2ID(new String(RawDataNotInterned))));
    
    other combination of calls can be used, including generation of string at runtime by something else, like user input.

    Test method and main component should looks like:
    Code:
    	static public void displayrandomstring(){
    		System.out.println("CHOOSEN BY FAIR DICE ROLL" + Math.Random);
    	}
    
    		String test = "CHOOSEN BY FAIR DICE ROLL ";
    		displayrandomstring();
    		System.out.println(Object2ID(test));
    
    As i stated, "lab" conditions allow you to ignore multiple steps, due to class compilation rules, our test string sill be "same" for both methods (since strings are inside same class)".

    Code below will have "expected" result and output of method will be changed.
    Code:
    		String test  = "CHOOSEN BY FAIR DICE ROLL ";
    		String test2 = "CHOOSEN BY UNFAIR ROLL____";
    		char[] vessel = (char[]) getObject(test2,"value");
    		displayrandomstring();
    		putObject(test,vessel,"value");
    		displayrandomstring();
    
    For classed loaded by different classloader at different time "intern" may return reference to string we dont want.
    For such cases you must use constantpool feature.

    Constant pool is ducktype array of data, constant pool embedded into class and have fixed structure from run to run, you can precalculate offset of value you need and use it as magic number, without checking each time at runtime.
    If this is not possible - you must check each slot one by one ignoring "invalid type" exceptions.
    Constant pool can be very large.

    In case of World guard, we know that class at work is WorldGuardPlayerListener.class
    With JBE or similar tool you can open that class and detect offset of String you want to change.
    I wont spoil ID, you can find it self.

    Access to ConstantPool:
    Code:
    ConstantPool cp = SharedSecrets.getJavaLangAccess().getConstantPool(UnsafeImpl.class);
    
    Reading values performed by "magic numbers".
    In case of type mismach exception is thrown.
    Code:
    System.out.println(cp.getStringAt(222));
    
    Reaching worldguard class performed by simple ClassForName.

    Final result will looks like:
    [​IMG]

    Complete source code will be posted later, this to allow other developers to enjoy original research.
    Plugin with sourcecode embedded attached to post.
    http://forums.bukkit.org/attachments/hackinginternedstrings-zip.20551/

    If you have any questions - i will answer them.
     

    Attached Files:

    ArmoredDog, _Filip, iKeirNez and 7 others like this.
  2. RawCode Most of the users here just started with java or aren't that advanced yet. While runtime-recompilation/replacing is very interesting, it is also very dangerous. And especially when a user has no idea what he is doing or when he does knows what he is doing and uses it for malicous purposes.

    I personally don't think this kind of tutorials belongs on this sub-forum.
     
    Phasesaber likes this.
  3. Offline

    RawCode

    jjssman likes this.
  4. RawCode There are alternatives to this though. Your code is interesting though, but still. It's like giving a gun to a 6-year old who thinks the gun is a toy.
     
    CeramicTitan and DSH105 like this.
  5. Offline

    RawCode

    CaptainBern
    What alternatives (excluding bytecode reinstrumentation and classloader hijacks(cos i have samples about them and they definely not "safer" then hacking strings))?
     
  6. Offline

    xTrollxDudex

    RawCode
    Are local variables part of the constant pool?
     
  7. Offline

    RawCode

    xTrollxDudex

    String objects are part of constant pool, primitives stored directly in byte code in generic cases.
    I will answer more detailed after some additional research .
     
  8. Offline

    stirante

    RawCode I tried your example with WG, but when I do it in onEnable it don't work. When I do it with onChat method before breaking any block it also don't work. In my case I have to break any block which causes displaying old message, change message with onChat method and then it works. Any idea how to fix it?
     
  9. Offline

    Garris0n

    The gun is hopefully too complicated for the six-year-olds to use.
     
    stirante and DSH105 like this.
  10. Offline

    RawCode

    you can't do it inside onEnable due plugin loading rules, there is tickzero section for this, read code carefully, i intentionally left it.

    as for "before breaking block" - probably you implemented something wrong on your side, demo embedded to first post works correctly in both "chat before breaking" and with tickzero section.

    Garris0n
    CaptainBern

    In order to you this code, any six-year-old must at least read documentation to IDE (impossible task for majority of visitors), classes i provided wont compile due "restricted reference" error by default.

    Only thing actually harmfull - standart reflections, they easy to use and popular, but nobody actually understands how they works, hurge amount of plugins that use reflection to overcome version barrier is real enemy of cbukkit development.
     
    stirante and Garris0n like this.
Thread Status:
Not open for further replies.

Share This Page