Yggdrasil Login (#229)

This commit is contained in:
Soda5601
2023-03-04 20:13:28 +08:00
committed by GitHub
parent 4fbc7e3725
commit 9c8b2a19c6
13 changed files with 519 additions and 5 deletions

View File

@@ -94,6 +94,8 @@
- `Random Teleport, Hit Chance, Random Delay` (Removed from Meteor in [8722e](https://github.com/MeteorDevelopment/meteor-client/commit/8722ef565afa02ca4b6d9710a20fc9fcfd97bf05))
- AimAssist
- `Fov filter`
- Alts
- `Yggdrasil Login`
## Commands
- `.center`

View File

@@ -0,0 +1,54 @@
package anticope.rejects.mixin;
import com.mojang.authlib.minecraft.MinecraftSessionService;
import com.mojang.authlib.minecraft.UserApiService;
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.SocialInteractionsManager;
import net.minecraft.client.report.AbuseReportContext;
import net.minecraft.client.texture.PlayerSkinProvider;
import net.minecraft.client.util.ProfileKeys;
import net.minecraft.client.util.Session;
import net.minecraft.network.encryption.SignatureVerifier;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(MinecraftClient.class)
public interface MinecraftClientAccessor {
@Mutable
@Accessor("session")
void setSession(Session session);
@Mutable
@Accessor("sessionService")
void setSessionService(MinecraftSessionService sessionService);
@Mutable
@Accessor("authenticationService")
void setAuthenticationService(YggdrasilAuthenticationService authenticationService);
@Mutable
@Accessor("servicesSignatureVerifier")
void setServicesSignatureVerifier(SignatureVerifier servicesSignatureVerifier);
@Mutable
@Accessor("skinProvider")
void setSkinProvider(PlayerSkinProvider skinProvider);
@Mutable
@Accessor("socialInteractionsManager")
void setSocialInteractionsManager(SocialInteractionsManager socialInteractionsManager);
@Mutable
@Accessor("userApiService")
void setUserApiService(UserApiService userApiService);
@Mutable
@Accessor("abuseReportContext")
void setAbuseReportContext(AbuseReportContext abuseReportContext);
@Mutable
@Accessor("profileKeys")
void setProfileKeys(ProfileKeys profileKeys);
}

View File

@@ -0,0 +1,13 @@
package anticope.rejects.mixin;
import net.minecraft.client.texture.PlayerSkinProvider;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.io.File;
@Mixin(PlayerSkinProvider.class)
public interface PlayerSkinProviderAccessor {
@Accessor("skinCacheDir")
File getSkinCacheDir();
}

View File

@@ -0,0 +1,16 @@
package anticope.rejects.mixin.meteor;
import anticope.rejects.utils.accounts.CustomYggdrasilAccount;
import meteordevelopment.meteorclient.systems.accounts.Account;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
@Mixin(value = Account.class, remap = false)
public class AccountMixin {
@ModifyArg(method = "toTag", at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/NbtCompound;putString(Ljava/lang/String;Ljava/lang/String;)V", ordinal = 0), index = 1)
private String putString(String key) {
if ((Object) this instanceof CustomYggdrasilAccount) return "Yggdrasil";
return key;
}
}

View File

@@ -0,0 +1,23 @@
package anticope.rejects.mixin.meteor;
import anticope.rejects.utils.accounts.CustomYggdrasilAccount;
import meteordevelopment.meteorclient.systems.accounts.Account;
import meteordevelopment.meteorclient.systems.accounts.Accounts;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
@Mixin(value = Accounts.class, remap = false)
public class AccountsMixin {
@Inject(method = "lambda$fromTag$0", at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/NbtCompound;getString(Ljava/lang/String;)Ljava/lang/String;"), locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true)
private static void onFromTag(NbtElement tag1, CallbackInfoReturnable<Account<?>> cir, NbtCompound t) {
if (t.getString("type").equals("Yggdrasil")) {
Account<CustomYggdrasilAccount> account = new CustomYggdrasilAccount(null, null, null).fromTag(t);
if (account.fetchInfo()) cir.setReturnValue(account);
}
}
}

View File

@@ -0,0 +1,32 @@
package anticope.rejects.mixin.meteor;
import anticope.rejects.utils.accounts.AddCustomYggdrasilAccountScreen;
import meteordevelopment.meteorclient.gui.GuiTheme;
import meteordevelopment.meteorclient.gui.WindowScreen;
import meteordevelopment.meteorclient.gui.screens.AccountsScreen;
import meteordevelopment.meteorclient.gui.widgets.WWidget;
import meteordevelopment.meteorclient.gui.widgets.containers.WContainer;
import meteordevelopment.meteorclient.gui.widgets.containers.WHorizontalList;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import static meteordevelopment.meteorclient.MeteorClient.mc;
@Mixin(value = AccountsScreen.class, remap = false)
public abstract class AccountsScreenMixin extends WindowScreen {
public AccountsScreenMixin(GuiTheme theme, WWidget icon, String title) {
super(theme, icon, title);
}
@Shadow
protected abstract void addButton(WContainer c, String text, Runnable action);
@Inject(method = "initWidgets", at = @At(value = "INVOKE", target = "Lmeteordevelopment/meteorclient/gui/screens/AccountsScreen;addButton(Lmeteordevelopment/meteorclient/gui/widgets/containers/WContainer;Ljava/lang/String;Ljava/lang/Runnable;)V", ordinal = 0), locals = LocalCapture.CAPTURE_FAILHARD)
private void afterAddCrackedButton(CallbackInfo info, WHorizontalList l) {
addButton(l, "Yggdrasil", () -> mc.setScreen(new AddCustomYggdrasilAccountScreen(theme, (AccountsScreen) (Object) this)));
}
}

View File

@@ -0,0 +1,21 @@
package anticope.rejects.mixin.meteor;
import anticope.rejects.utils.accounts.CustomYggdrasilAccount;
import meteordevelopment.meteorclient.gui.widgets.WAccount;
import meteordevelopment.meteorclient.systems.accounts.Account;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
@Mixin(value = WAccount.class, remap = false)
public class WAccountMixin {
@Shadow @Final private Account<?> account;
@ModifyArg(method = "init", at = @At(value = "INVOKE", target = "Lmeteordevelopment/meteorclient/gui/GuiTheme;label(Ljava/lang/String;)Lmeteordevelopment/meteorclient/gui/widgets/WLabel;",ordinal = 1))
private String accountName(String text) {
if (account instanceof CustomYggdrasilAccount) return "(Yggdrasil)";
return text;
}
}

View File

@@ -0,0 +1,45 @@
package anticope.rejects.utils.accounts;
import anticope.rejects.mixin.MinecraftClientAccessor;
import anticope.rejects.mixin.PlayerSkinProviderAccessor;
import com.mojang.authlib.exceptions.AuthenticationException;
import com.mojang.authlib.minecraft.MinecraftSessionService;
import com.mojang.authlib.minecraft.UserApiService;
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
import net.minecraft.client.network.SocialInteractionsManager;
import net.minecraft.client.report.AbuseReportContext;
import net.minecraft.client.report.ReporterEnvironment;
import net.minecraft.client.texture.PlayerSkinProvider;
import net.minecraft.client.util.ProfileKeys;
import net.minecraft.client.util.Session;
import net.minecraft.network.encryption.SignatureVerifier;
import java.io.File;
import static meteordevelopment.meteorclient.MeteorClient.mc;
public class AccountUtils {
public static MinecraftSessionService applyLoginEnvironment(YggdrasilAuthenticationService authService, MinecraftSessionService sessService, Session session) {
File skinDir = ((PlayerSkinProviderAccessor) mc.getSkinProvider()).getSkinCacheDir();
((MinecraftClientAccessor) mc).setSession(session);
((MinecraftClientAccessor) mc).setAuthenticationService(authService);
((MinecraftClientAccessor) mc).setSessionService(sessService);
((MinecraftClientAccessor) mc).setServicesSignatureVerifier(SignatureVerifier.create(authService.getServicesKey()));
((MinecraftClientAccessor) mc).setSkinProvider(new PlayerSkinProvider(mc.getTextureManager(), skinDir, sessService));
UserApiService apiService = createUserApiService(authService, session);
((MinecraftClientAccessor) mc).setUserApiService(apiService);
((MinecraftClientAccessor) mc).setSocialInteractionsManager(new SocialInteractionsManager(mc, apiService));
((MinecraftClientAccessor) mc).setProfileKeys(ProfileKeys.create(apiService, session, mc.runDirectory.toPath()));
((MinecraftClientAccessor) mc).setAbuseReportContext(AbuseReportContext.create(ReporterEnvironment.ofIntegratedServer(), apiService));
return sessService;
}
public static UserApiService createUserApiService(YggdrasilAuthenticationService authService, Session session) {
try {
return authService.createUserApiService(session.getAccessToken());
} catch (AuthenticationException e) {
return UserApiService.OFFLINE;
}
}
}

View File

@@ -0,0 +1,46 @@
package anticope.rejects.utils.accounts;
import meteordevelopment.meteorclient.gui.GuiTheme;
import meteordevelopment.meteorclient.gui.screens.AccountsScreen;
import meteordevelopment.meteorclient.gui.screens.AddAccountScreen;
import meteordevelopment.meteorclient.gui.widgets.containers.WTable;
import meteordevelopment.meteorclient.gui.widgets.input.WTextBox;
import meteordevelopment.meteorclient.systems.accounts.Accounts;
public class AddCustomYggdrasilAccountScreen extends AddAccountScreen {
public AddCustomYggdrasilAccountScreen(GuiTheme theme, AccountsScreen parent) {
super(theme, "Add Yggdrasil Account", parent);
}
@Override
public void initWidgets() {
WTable t = add(theme.table()).widget();
// Email
t.add(theme.label("Username / Email: "));
WTextBox username = t.add(theme.textBox("")).minWidth(400).expandX().widget();
username.setFocused(true);
t.row();
// Password
t.add(theme.label("Password: "));
WTextBox password = t.add(theme.textBox("")).minWidth(400).expandX().widget();
t.row();
// Password
t.add(theme.label("Server: "));
WTextBox server = t.add(theme.textBox("")).minWidth(400).expandX().widget();
t.row();
// Add
add = t.add(theme.button("Add")).expandX().widget();
add.action = () -> {
CustomYggdrasilAccount account = new CustomYggdrasilAccount(username.get(), password.get(), server.get());
if (!username.get().isEmpty() && !password.get().isEmpty() && !Accounts.get().exists(account)) {
AccountsScreen.addAccount(this, parent, account);
}
};
enterAction = add.action;
}
}

View File

@@ -0,0 +1,80 @@
package anticope.rejects.utils.accounts;
import com.mojang.authlib.exceptions.AuthenticationException;
import meteordevelopment.meteorclient.MeteorClient;
import meteordevelopment.meteorclient.mixin.MinecraftClientAccessor;
import meteordevelopment.meteorclient.systems.accounts.Account;
import meteordevelopment.meteorclient.systems.accounts.AccountType;
import meteordevelopment.meteorclient.utils.misc.NbtException;
import net.minecraft.client.util.Session;
import net.minecraft.nbt.NbtCompound;
import static meteordevelopment.meteorclient.MeteorClient.mc;
public class CustomYggdrasilAccount extends Account<CustomYggdrasilAccount> {
private String password, server;
public CustomYggdrasilAccount(String name, String password, String server) {
super(AccountType.Cracked, name);
this.password = password;
this.server = server;
}
@Override
public boolean fetchInfo() {
try {
Session session = CustomYggdrasilLogin.login(name, password, server);
cache.username = session.getUsername();
cache.uuid = session.getUuid();
return true;
} catch (AuthenticationException e) {
return false;
}
}
@Override
public boolean login() {
try {
Session session = CustomYggdrasilLogin.login(name, password, server);
CustomYggdrasilLogin.LocalYggdrasilAuthenticationService service = new CustomYggdrasilLogin.LocalYggdrasilAuthenticationService(((MinecraftClientAccessor) mc).getProxy(), server);
CustomYggdrasilLogin.applyYggdrasilAccount(service, session);
cache.username = session.getUsername();
cache.loadHead();
return true;
} catch (AuthenticationException e) {
if (e.getMessage().contains("Invalid username or password") || e.getMessage().contains("account migrated"))
MeteorClient.LOG.error("Wrong password.");
else MeteorClient.LOG.error("Failed to contact the authentication server.");
return false;
}
}
@Override
public NbtCompound toTag() {
NbtCompound tag = super.toTag();
tag.putString("password", password);
tag.putString("server", server);
return tag;
}
@Override
public CustomYggdrasilAccount fromTag(NbtCompound tag) {
super.fromTag(tag);
if (!tag.contains("password")) throw new NbtException();
password = tag.getString("password");
server = tag.getString("server");
return this;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof CustomYggdrasilAccount)) return false;
return ((CustomYggdrasilAccount) o).name.equals(this.name);
}
}

View File

@@ -0,0 +1,176 @@
// Credit to https://github.com/IAFEnvoy/AccountSwitcher
package anticope.rejects.utils.accounts;
import com.google.common.collect.Iterables;
import com.google.gson.*;
import com.mojang.authlib.Environment;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.exceptions.AuthenticationException;
import com.mojang.authlib.minecraft.InsecureTextureException;
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
import com.mojang.authlib.minecraft.MinecraftSessionService;
import com.mojang.authlib.properties.Property;
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
import com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService;
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
import com.mojang.util.UUIDTypeAdapter;
import meteordevelopment.meteorclient.utils.network.Http;
import net.minecraft.client.util.Session;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.net.Proxy;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;
public class CustomYggdrasilLogin {
public static Session login(String name, String password, String server) throws AuthenticationException {
try {
String url = server + "/authserver/authenticate";
JsonObject agent = new JsonObject();
agent.addProperty("name", "Minecraft");
agent.addProperty("version", 1);
JsonObject root = new JsonObject();
root.add("agent", agent);
root.addProperty("username", name);
root.addProperty("password", password);
String data = Http.post(url).bodyJson(root).sendString();
JsonObject json = JsonParser.parseString(data).getAsJsonObject();
if (json.has("error")) {
throw new AuthenticationException(json.get("errorMessage").getAsString());
}
String token = json.get("accessToken").getAsString();
String uuid = json.get("selectedProfile").getAsJsonObject().get("id").getAsString();
String username = json.get("selectedProfile").getAsJsonObject().get("name").getAsString();
return new Session(username, uuid, token, Optional.empty(), Optional.empty(), Session.AccountType.MOJANG);
} catch (Exception e) {
throw new AuthenticationException(e);
}
}
public static void applyYggdrasilAccount(LocalYggdrasilAuthenticationService authService, Session session) {
MinecraftSessionService service = new LocalYggdrasilMinecraftSessionService(authService, authService.server);
AccountUtils.applyLoginEnvironment(authService, service, session);
}
public static class LocalYggdrasilApi implements Environment {
private final String url;
public LocalYggdrasilApi(String serverUrl) {
this.url = serverUrl;
}
@Override
public String getAuthHost() {
return url + "/authserver";
}
@Override
public String getAccountsHost() {
return url + "/api";
}
@Override
public String getSessionHost() {
return url + "/sessionserver";
}
@Override
public String getServicesHost() {
return url + "/minecraftservices";
}
@Override
public String getName() {
return "Custom-Yggdrasil";
}
@Override
public String asString() {
return new StringJoiner(", ", "", "")
.add("authHost='" + getAuthHost() + "'")
.add("accountsHost='" + getAccountsHost() + "'")
.add("sessionHost='" + getSessionHost() + "'")
.add("servicesHost='" + getServicesHost() + "'")
.add("name='" + getName() + "'")
.toString();
}
}
public static class LocalYggdrasilMinecraftSessionService extends YggdrasilMinecraftSessionService {
private static final Logger LOGGER = LogManager.getLogger();
private final PublicKey publicKey;
private final Gson gson = new GsonBuilder().registerTypeAdapter(UUID.class, new UUIDTypeAdapter()).create();
public LocalYggdrasilMinecraftSessionService(YggdrasilAuthenticationService service, String serverUrl) {
super(service, new LocalYggdrasilApi(serverUrl));
String data = Http.get(serverUrl).sendString();
JsonObject json = JsonParser.parseString(data).getAsJsonObject();
this.publicKey = getPublicKey(json.get("signaturePublickey").getAsString());
}
private static PublicKey getPublicKey(String key) {
key = key.replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "");
try {
byte[] byteKey = Base64.getDecoder().decode(key.replace("\n", ""));
X509EncodedKeySpec spec = new X509EncodedKeySpec(byteKey);
KeyFactory factory = KeyFactory.getInstance("RSA");
return factory.generatePublic(spec);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
e.printStackTrace();
}
return null;
}
@Override
public Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> getTextures(final GameProfile profile, final boolean requireSecure) {
final Property textureProperty = Iterables.getFirst(profile.getProperties().get("textures"), null);
if (textureProperty == null)
return new HashMap<>();
if (requireSecure) {
if (!textureProperty.hasSignature()) {
LOGGER.error("Signature is missing from textures payload");
throw new InsecureTextureException("Signature is missing from textures payload");
}
if (!textureProperty.isSignatureValid(publicKey)) {
LOGGER.error("Textures payload has been tampered with (signature invalid)");
throw new InsecureTextureException("Textures payload has been tampered with (signature invalid)");
}
}
final MinecraftTexturesPayload result;
try {
final String json = new String(org.apache.commons.codec.binary.Base64.decodeBase64(textureProperty.getValue()), StandardCharsets.UTF_8);
result = gson.fromJson(json, MinecraftTexturesPayload.class);
} catch (final JsonParseException e) {
LOGGER.error("Could not decode textures payload", e);
return new HashMap<>();
}
if (result == null || result.getTextures() == null)
return new HashMap<>();
return result.getTextures();
}
}
public static class LocalYggdrasilAuthenticationService extends YggdrasilAuthenticationService {
public final String server;
public LocalYggdrasilAuthenticationService(Proxy proxy, String server) {
super(proxy, new LocalYggdrasilApi(server));
this.server = server;
}
}
}

View File

@@ -3,18 +3,22 @@
"package": "anticope.rejects.mixin.meteor",
"compatibilityLevel": "JAVA_16",
"client": [
"AccountMixin",
"AccountsMixin",
"AccountsScreenMixin",
"ConfigTabMixin",
"DefaultSettingsWidgetFactoryMixin",
"FontUtilsMixin",
"GuiRendererAccessor",
"HttpMixin",
"ModuleMixin",
"ModulesMixin",
"FontUtilsMixin",
"DefaultSettingsWidgetFactoryMixin",
"modules.FlightMixin",
"modules.NoRenderAccessor",
"WAccountMixin",
"modules.AimAssistMixin",
"modules.AutoSignMixin",
"modules.FlightMixin",
"modules.InventoryTweaksMixin",
"modules.KillAuraMixin",
"modules.AimAssistMixin"
"modules.NoRenderAccessor"
]
}

View File

@@ -12,9 +12,11 @@
"GameRendererMixin",
"LivingEntityMixin",
"LivingEntityRendererMixin",
"MinecraftClientAccessor",
"MultiplayerScreenAccessor",
"MultiplayerScreenMixin",
"PlayerMoveC2SPacketAccessor",
"PlayerSkinProviderAccessor",
"ServerListAccessor",
"StructureVoidBlockMixin",
"TexturedRenderLayersMixin",