NMS [Reflection] Creating Skulls with Non-Player Skins!

Discussion in 'Resources' started by dori99xd, Nov 12, 2014.

?

Useful?

  1. Yeah!

    16 vote(s)
    45.7%
  2. No!

    4 vote(s)
    11.4%
  3. Maybe...

    2 vote(s)
    5.7%
  4. Bacon.

    13 vote(s)
    37.1%
Thread Status:
Not open for further replies.
  1. Offline

    dori99xd

    Hello everyone,

    I created a little method to create skulls (Game Profiles, see down below) with non-player Skins!
    The idea is from Dragnoz, they made a YouTube Video.

    V1. NMS:
    Code:java
    1. package me.dori99xd.example;
    2.  
    3. import java.util.Random;
    4. import java.util.UUID;
    5.  
    6. import net.minecraft.server.v1_7_R4.TileEntitySkull;
    7. import net.minecraft.util.com.mojang.authlib.GameProfile;
    8. import net.minecraft.util.com.mojang.authlib.properties.Property;
    9.  
    10. import org.bukkit.Material;
    11. import org.bukkit.block.Block;
    12. import org.bukkit.craftbukkit.v1_7_R4.CraftWorld;
    13. import org.bukkit.event.EventHandler;
    14. import org.bukkit.event.Listener;
    15. import org.bukkit.event.block.Action;
    16. import org.bukkit.event.player.PlayerInteractEvent;
    17. import org.bukkit.plugin.java.JavaPlugin;
    18. import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
    19.  
    20. public class Example extends JavaPlugin implements Listener {
    21.  
    22. private static final Random random = new Random();
    23. private static final String chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    24.  
    25. // Example Usage
    26. @Override
    27. public void onEnable() {
    28. getServer().getPluginManager().registerEvents(this, this);
    29. }
    30.  
    31. // Example Usage
    32. @EventHandler(ignoreCancelled=true)
    33. public void on(PlayerInteractEvent event) {
    34. if(event.getAction() == Action.RIGHT_CLICK_BLOCK && event.getClickedBlock().getType() == Material.SKULL)
    35. setSkullWithNonPlayerProfile("[url]http://213.136.94.170/downloads/img/skin.png[/url]", true, event.getClickedBlock());
    36. }
    37.  
    38. // Real Method
    39. public static GameProfile getNonPlayerProfile(String skinURL, boolean randomName) {
    40. GameProfile newSkinProfile = new GameProfile(UUID.randomUUID(), randomName ? getRandomString(16) : null);
    41. newSkinProfile.getProperties().put("textures", new Property("textures", Base64Coder.encodeString("{textures:{SKIN:{url:\"" + skinURL + "\"}}}")));
    42. return newSkinProfile;
    43. }
    44.  
    45. // Example Usage
    46. public static void setSkullWithNonPlayerProfile(String skinURL, boolean randomName, Block skull) {
    47. if(skull.getType() != Material.SKULL)
    48. throw new IllegalArgumentException("Block must be a skull.");
    49. TileEntitySkull skullTile = (TileEntitySkull)((CraftWorld)skull.getWorld()).getHandle().getTileEntity(skull.getX(), skull.getY(), skull.getZ());
    50. skullTile.setGameProfile(getNonPlayerProfile(skinURL, randomName));
    51. skull.getWorld().refreshChunk(skull.getChunk().getX(), skull.getChunk().getZ());
    52. }
    53.  
    54. // Util
    55. public static String getRandomString(int length) {
    56. StringBuilder b = new StringBuilder(length);
    57. for(int j = 0; j < length; j++)
    58. b.append(chars.charAt(random.nextInt(chars.length())));
    59. return b.toString();
    60. }
    61. }

    V2. Reflections: (Pastebin)
    Code:java
    1. package net.dori99xd.tials.test;
    2.  
    3. import java.lang.reflect.InvocationTargetException;
    4. import java.lang.reflect.Method;
    5. import java.util.Random;
    6. import java.util.UUID;
    7.  
    8. import net.minecraft.util.com.mojang.authlib.GameProfile;
    9. import net.minecraft.util.com.mojang.authlib.properties.Property;
    10.  
    11. import org.bukkit.Bukkit;
    12. import org.bukkit.Material;
    13. import org.bukkit.block.Block;
    14. import org.bukkit.block.Skull;
    15. import org.bukkit.event.EventHandler;
    16. import org.bukkit.event.Listener;
    17. import org.bukkit.event.block.Action;
    18. import org.bukkit.event.player.PlayerInteractEvent;
    19. import org.bukkit.plugin.java.JavaPlugin;
    20. import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
    21.  
    22. /**
    23. * @author BigTeddy98
    24. * @author dori99xd
    25. * Refletions by BigTeddy98
    26. */
    27. public class Example extends JavaPlugin implements Listener {
    28.  
    29. private static final Random random = new Random();
    30. private static final String chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    31. private static Method getWorldHandle;
    32. private static Method getWorldTileEntity;
    33. private static Method setGameProfile;
    34.  
    35. // Example
    36. @Override
    37. public void onEnable() {
    38. getServer().getPluginManager().registerEvents(this, this);
    39. if (getWorldHandle == null || getWorldTileEntity == null || setGameProfile == null) {
    40. try {
    41. getWorldHandle = getCraftClass("CraftWorld").getMethod("getHandle");
    42. getWorldTileEntity = getMCClass("WorldServer").getMethod("getTileEntity", int.class, int.class, int.class);
    43. setGameProfile = getMCClass("TileEntitySkull").getMethod("setGameProfile", GameProfile.class);
    44. e.printStackTrace();
    45. }
    46. }
    47. }
    48.  
    49. // Example
    50. @EventHandler(ignoreCancelled=true)
    51. public void on(PlayerInteractEvent event) {
    52. if(event.getAction() == Action.RIGHT_CLICK_BLOCK && event.getClickedBlock().getType() == Material.SKULL)
    53. setSkullWithNonPlayerProfile("[url]http://213.136.94.170/downloads/img/skin.png[/url]", true, event.getClickedBlock());
    54. }
    55.  
    56. // Method
    57. public static void setSkullWithNonPlayerProfile(String skinURL, boolean randomName, Block skull) {
    58. if(skull.getType() != Material.SKULL)
    59. throw new IllegalArgumentException("Block must be a skull.");
    60. Skull s = (Skull) skull.getState();
    61. try {
    62. setSkullProfile(s, getNonPlayerProfile(skinURL, randomName));
    63. e.printStackTrace();
    64. e.printStackTrace();
    65. e.printStackTrace();
    66. } catch (SecurityException e) {
    67. e.printStackTrace();
    68. }
    69. skull.getWorld().refreshChunk(skull.getChunk().getX(), skull.getChunk().getZ());
    70. }
    71.  
    72. // Method
    73. private static void setSkullProfile(Skull skull, GameProfile someGameprofile) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
    74. Object world = getWorldHandle.invoke(skull.getWorld());
    75. Object tileSkull = getWorldTileEntity.invoke(world, skull.getX(), skull.getY(), skull.getZ());
    76. setGameProfile.invoke(tileSkull, someGameprofile);
    77. }
    78.  
    79. // Method
    80. public static GameProfile getNonPlayerProfile(String skinURL, boolean randomName) {
    81. GameProfile newSkinProfile = new GameProfile(UUID.randomUUID(), randomName ? getRandomString(16) : null);
    82. newSkinProfile.getProperties().put("textures", new Property("textures", Base64Coder.encodeString("{textures:{SKIN:{url:\"" + skinURL + "\"}}}")));
    83. return newSkinProfile;
    84. }
    85.  
    86. // Example
    87. public static String getRandomString(int length) {
    88. StringBuilder b = new StringBuilder(length);
    89. for(int j = 0; j < length; j++)
    90. b.append(chars.charAt(random.nextInt(chars.length())));
    91. return b.toString();
    92. }
    93.  
    94. // Refletion
    95. private static Class<?> getMCClass(String name) {
    96. String version = Bukkit.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3] + ".";
    97. String className = "net.minecraft.server." + version + name;
    98. Class<?> clazz = null;
    99. try {
    100. clazz = Class.forName(className);
    101. e.printStackTrace();
    102. }
    103. return clazz;
    104. }
    105.  
    106. // Refletion
    107. private static Class<?> getCraftClass(String name) {
    108. String version = Bukkit.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3] + ".";
    109. String className = "org.bukkit.craftbukkit." + version + name;
    110. Class<?> clazz = null;
    111. try {
    112. clazz = Class.forName(className);
    113. e.printStackTrace();
    114. }
    115. return clazz;
    116. }
    117. }

    I hope you like it :3

    Big thanks to bigteddy98 for the Refletions!
    Oh, before i forgot it: sorry for my english, i'am from germany.

    License: I don't care, so: Feel free to copy!

    ~ dori99xd
     
  2. Offline

    xTrollxDudex

    For my own mental health - please finalize the random and eager intialize, then put in all caps to make constant please.
     
    mine-care and rbrick like this.
  3. Offline

    shohouku

    Niceeee..
     
  4. Offline

    crolemol

    dori99xd
    as I expected this doesn't work. when you put the gameprofile on an npc it kicks you out with an encoder exception because the gameprofile doesn't have signature. It is impossible to to this if you don't have signature and that signature is localy encrypted on the minecraft cloud servers. the signature is an RSA key of the link so if you change hte link the signature has to change and it can't so it will crash. only client mods can preform this
     
  5. Offline

    dori99xd

    crolemol Oh, okay. Good to know, but why does skulls works without a signature?
     
  6. Offline

    RawCode

    You can download MCP, exctract minecraft client source code, navigate to skull render and check how it works.
    There are LOTS of features not included into public API but allowed with hacks.

    also it's possible to overcome RSA signatures with lots of time and computation power if you really want to.
     
    rbrick likes this.
  7. Offline

    ChipDev

    NMS?

    ;(
     
  8. Offline

    dori99xd

    No. The thing that using NMS is only the Skull Method. This thread only means this:
    Code:java
    1. // Real Method
    2. public static GameProfile getNonPlayerProfile(String skinURL, boolean randomName) {
    3. GameProfile newSkinProfile = new GameProfile(UUID.randomUUID(), randomName ? getRandomString(16) : null);
    4. newSkinProfile.getProperties().put("textures", new Property("textures", Base64Coder.encodeString("{textures:{SKIN:{url:\"" + skinURL + "\"}}}")));
    5. return newSkinProfile;
    6. }


    I'll update it to refletions in a hour.

    Edit: Why this? This message is awaiting moderator approval, and is invisible to normal visitors.
     
  9. New users, or users who haven't posted for a while, need their posts approved before they're visible.
     
  10. Offline

    dori99xd

    #Offtopic: AdamQpzm Okay thanks. I think inactive, i'am member since 2011 :D
     
    AdamQpzm likes this.
  11. Offline

    ChipDev

    Yay, reflection!
     
  12. Offline

    dori99xd

    Yep, big thanks to BigTeddy98 :D
     
  13. dori99xd bigteddy98 This is not a good way to support multiple versions. See this post. Also, you replace dots with commas and then split on commas... I assume this is because split takes regex? You can escape characters you know ;)
     
  14. Offline

    crolemol

    dori99xd
    I did spend 2 weeks figuring everything out so I now that it is not possible if it doesn't have signature. but when my newest plugin is done I will look into this and see how I can make a fake signature using MCP
     
  15. Offline

    dori99xd

    crolemol I think you mean, that "faked" gameprofiles wont work on npc's, right? On skull's it works.
    If a gameprofile need to be signatured, i think mcp dosent can signature it, without client mods.
     
  16. Offline

    crolemol

    dori99xd
    I don't mean that. I mean that I can manually fake a signature using mcp's deobfuscated source code. if i know how the client handles skins i can maybe fake a signature that works
     
    dori99xd likes this.
  17. Offline

    Force_Update1

    @dori99xd
    I have testet the code, but the code don't work
     
  18. Offline

    dori99xd

    :confused:? Do you complete copied the code? That it wont work. In the example, the skull is from http://213.136.94.170/downloads/img/skin.png and XenForo added [.url] Tags. You have to remove it.
     
  19. Offline

    Force_Update1

    @dori99xd
    i have removed the tags i cann see that the chunk are refesh, but the skin dont change
     
  20. Offline

    dori99xd

    .... What Version of minecraft client do you use? Which server version? Spigot?
     
  21. Offline

    Force_Update1

  22. Offline

    dori99xd

    I need more informations. Client Version, Server Version (1.7.10 with Protocol Hack?!) and your code. What is "spigot1656". I only know Patched Spigot #1649...

    // Edit: I'll asked a friend: spigot1656 = Spigot #1649 with Patch #8?
     
  23. Offline

    Cirno

    You can't fake the signature nor use 3rd party websites. The signature is signed with Yggdrasil's private key and is verified with it's public counterpart. I've tried this before to basically have dynamic skins, but it doesn't work purely because of the Yggdrasil system. No 3rd party builds (including Spigot) can do this either; everything is done client-side.
     
  24. Offline

    dori99xd

    If you only modify the texuture and set the gameprofile to a skull, it dons't need to be signatured.
     
  25. Offline

    Cirno

    The texture is validated with the signature...
     
  26. Offline

    Deleted user

    Cirno

    Same. I was trying just that today, and I actually saw your post from a few months back.

    Anyways, for the general public, validation on skulls isn't as strict, so a signature isn't required.
     
  27. Offline

    Gingerbreadman

  28. Offline

    dori99xd

    For me, it works.. Force_Update1 say not... Don't know.. I tested it with CB 1.7.10 and Spigot 1.8 with Protocol Hack and it works.
     
  29. Offline

    Gingerbreadman

    dori99xd likes this.
  30. Offline

    GermanCoding

    First, this is very cool!
    I have to say that I am not very experienced with those skin system, so I did not really understand what is going on there.

    I tried this a few minutes ago, first I could only the see default skins on the skulls when I use your code. I waited about 10 minutes and made multiple restarts of the server and the client. Suddenly (Maybe some cache was updated, I do not know) the skins on the skulls are shown correctly, even if I create new skulls now, they have immediatly the correct/changed skin.
    In the log of my local server, the following error occured when the skulls got the correct skin for the first time:
    Code:
    [15:01:51 WARN]: Couldn't look up profile properties for net.minecraft.util.com.
    mojang.authlib.GameProfile@71b43e5e[id=74b9d394-f585-4115-9264-721212a0373d,name
    =Nummer378,properties={},legacy=false]
    net.minecraft.util.com.mojang.authlib.exceptions.AuthenticationException: The cl
    ient has sent too many requests within a certain amount of time
    My server is in online mode (The login was successfull, even if there was an error) and I can also see my skin AND the skulls correctly. So I get that there is an authentication problem, but everything works though... Strange skin and authentication system :).
     
Thread Status:
Not open for further replies.

Share This Page