Skip to content

Commit 8281bff

Browse files
emyfopsAvanatiker
andauthored
[1.20.4] Feat: Dynamic qualifier deobfuscation (#117)
This pull requests implements a brand new dynamic class remapping that uses the existing dynamic reflection serializer It allows reflection of obfuscated minecraft members for the packet logger module Mappings are downloaded from our cloudflare bucket at `mappings.lambda-client.org` and are stored loosely in the cache folder Before: ``` net.minecraft.class_2672 field_12236: -10 field_12235: -4 field_34870: net.minecraft.class_6603@721373fb net.minecraft.class_6603 field_34862: 2097152 field_34863: {MOTION_BLOCKING:[L;3086490446775277226L,3122730762615747244L,3104504914273194157L,3339185846487446187L,3104540029253932717L,3519681675303147692L,3104399154733218493L,3519470292041816236L,3050427429879123655L,3411242338997591207L,3411595972921364163L,3140639401982644921L,3591915948460571321L,3140639607472093891L,3591739268801584302L,3266845471266932423L,3411242545290239149L,3086984267357324989L,3194753034973702833L,3339185846487446191L,3104504846089803442L,3339184675971488428L,3032729277053101234L,3086490446640796329L,3248902058093139627L,3050355889943694515L,3230605634851133098L,2996242119247620788L,3086490446640533671L,2960143917570156202L,3068440725916568229L,3230887659449177258L,3014256517618681506L,3050391143438701737L,2942093163902356648L,3050391143303957669L,22189789353L],WORLD_SURFACE:[L;3086490446775277227L,3122730762615747244L,3104504914273194157L,3339185846487446187L,3122554427763414701L,3519681675303147692L,3104399154733218493L,3519470292041816236L,3050427429879123655L,3411242338997591207L,3411595972921364163L,3140639401982644921L,3591915948460571321L,3140639607472093891L,3591739268801584303L,3266845471266932423L,3411242545290239149L,3086984267357324989L,3194753035108182705L,3339185846487446191L,3104504846089803442L,3339184676105706156L,3032729277053101234L,3086490446640796329L,3248902058093139627L,3050355889943694515L,3230605634851133098L,2996242119247620788L,3086490446640533671L,2960143917570156202L,3068440725916568229L,3230887659449177258L,3014256517618681506L,3050391143438701737L,2942093163902356648L,3050391143303957669L,22189789353L]} field_34864: [B@62e634ff field_34865: [] field_34871: net.minecraft.class_6606@703b0ecd net.minecraft.class_6606 field_34873: {2, 3, 4, 11, 12, 13, 14} field_34874: {2, 3, 4, 9, 11} field_34875: {0, 1, 5, 6, 7, 8, 9, 10} field_34876: {0, 1, 5, 6, 7, 8, 10, 12, 13, 14} field_34877: [[B@5ebc4893, [B@d2ece77, [B@92524f5, [B@21c01e7a, [B@1d843201, [B@66220ab0, [B@4680880d] field_34878: [[B@67926d69, [B@5b3a5fa0, [B@2d3ba9fd, [B@6f398cb7, [B@1bb44d1c] ``` After: ``` ChunkDataS2CPacket chunkX: -10 chunkZ: -8 chunkData: ChunkData@349c0420 ChunkData MAX_SECTIONS_DATA_SIZE: 2097152 heightmap: {MOTION_BLOCKING:[L;2797696018872349336L,2761561462444734620L,2797660766991106717L,2743582454868686491L,2942234244450431640L,2779716873588913827L,3121884685859631257L,2797696501120061101L,2761562013141382299L,2924079110605333144L,2888049830799423138L,3122730831997644960L,2941987128944969383L,3212836218723190435L,2779646918238493361L,2743547614632689824L,2779928462473126577L,2743829089607038106L,2942234246602109080L,2815745464315232416L,2942234243778031772L,2815675577414271651L,2941705447942471324L,2887979531044733603L,2815745877438445722L,2924078765392867996L,2815851223858102433L,2905993859626253983L,2815675165097411234L,2887838656116899484L,2779857886762058400L,2924078765123384474L,2887944277684468381L,2869894419118109850L,2924043374593916578L,2815675164291579554L,21382381212L],WORLD_SURFACE:[L;2797696018872349336L,2761561462444734620L,2797660766991106717L,2761596853378168475L,2942234244450431641L,2779716873723393699L,3121884685859631770L,2797696501120061101L,2761562013141382299L,2924079110605333144L,2888049830799423138L,3122730831997907104L,2941987128944969383L,3212836218723190435L,2779682102610582193L,2743547614632689824L,2779928462473126577L,2761843488116520090L,2942234246602109080L,2815745533034709152L,2942234243778032284L,2815675577414271651L,2941740632314560156L,2887979531044733603L,2815745877438445722L,2924078765392867996L,2815851223858364577L,2905993859760471711L,2815675165097411234L,2887838656116899484L,2779857886762058400L,2924078765123384986L,2887944277684468381L,2869894419118109850L,2924043374593916578L,2815745670474710690L,21382381212L]} sectionsData: [B@1acfdb99 blockEntities: [] lightData: LightData@37f330be LightData initedSky: {2, 10, 11, 12, 13} initedBlock: {2, 3, 4, 10} uninitedSky: {0, 1, 3, 4, 5, 6, 7, 8, 9} uninitedBlock: {0, 1, 5, 6, 7, 8, 9, 11, 12, 13} skyNibbles: [[B@5e55fb7b, [B@3aa2f03b, [B@37a46db0, [B@197ccd3a, [B@6803634d] blockNibbles: [[B@458d755e, [B@51d059b2, [B@2e3a4baf, [B@5dd05c2e] ``` This pull request also revamps the file utilities with a more streamlined and functional approach Examples: ```kt File("...") .downloadCompare(url = "...", 1) // Download if remote is larger than local File("...") .downloadIfNotPresent(url = "...") { header("key", "value") } // Create the file and download the content to it; Modify the http request before posting ``` --------- Co-authored-by: Constructor <fractalminds@protonmail.com>
1 parent 1a677bf commit 8281bff

File tree

17 files changed

+441
-80
lines changed

17 files changed

+441
-80
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright 2025 Lambda
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package com.lambda.mixin;
19+
20+
import com.lambda.Lambda;
21+
import com.lambda.module.Module;
22+
import com.lambda.module.ModuleRegistry;
23+
import com.lambda.util.DynamicException;
24+
import net.minecraft.client.MinecraftClient;
25+
import net.minecraft.util.Util;
26+
import net.minecraft.util.crash.CrashReport;
27+
import org.spongepowered.asm.mixin.Final;
28+
import org.spongepowered.asm.mixin.Mixin;
29+
import org.spongepowered.asm.mixin.Mutable;
30+
import org.spongepowered.asm.mixin.Shadow;
31+
import org.spongepowered.asm.mixin.injection.At;
32+
import org.spongepowered.asm.mixin.injection.Inject;
33+
import org.spongepowered.asm.mixin.injection.Redirect;
34+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
35+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
36+
37+
// Modify the crash report behavior for dynamic remapping, Easter egg and github issue link
38+
@Mixin(CrashReport.class)
39+
public class CrashReportMixin {
40+
@Mutable
41+
@Shadow @Final private Throwable cause;
42+
43+
@Inject(method = "<init>(Ljava/lang/String;Ljava/lang/Throwable;)V", at = @At("TAIL"))
44+
void injectConstructor(String message, Throwable cause, CallbackInfo ci) {
45+
if (!Lambda.INSTANCE.isDebug() && MinecraftClient.getInstance() != null) {
46+
this.cause = new DynamicException(cause);
47+
}
48+
}
49+
50+
@Redirect(method = "asString()Ljava/lang/String;", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/crash/CrashReport;addStackTrace(Ljava/lang/StringBuilder;)V"))
51+
void injectString(CrashReport instance, StringBuilder stringBuilder) {
52+
stringBuilder.append("If this issue is related to Lambda, check if other users have experienced this too, or create a new issue at https://github.com/lambda-client/lambda/issues.\n\n");
53+
54+
if (MinecraftClient.getInstance() != null) {
55+
stringBuilder.append("Enabled modules:\n");
56+
57+
ModuleRegistry.INSTANCE.getModules()
58+
.stream().filter(Module::isEnabled)
59+
.forEach(m -> stringBuilder.append("\t").append(m.getName()).append("\n"));
60+
}
61+
62+
stringBuilder.append("\n");
63+
stringBuilder.append("-".repeat(43));
64+
stringBuilder.append("\n\n");
65+
66+
instance.addStackTrace(stringBuilder);
67+
}
68+
69+
@Inject(method = "generateWittyComment()Ljava/lang/String;", at = @At("HEAD"), cancellable = true)
70+
private static void generateWittyComment(CallbackInfoReturnable<String> cir) {
71+
String[] strings = new String[]{"Who set us up the TNT?", "Everything's going to plan. No, really, that was supposed to happen.", "Uh... Did I do that?", "Oops.", "Why did you do that?", "I feel sad now :(", "My bad.", "I'm sorry, Dave.", "I let you down. Sorry :(", "On the bright side, I bought you a teddy bear!", "Daisy, daisy...", "Oh - I know what I did wrong!", "Hey, that tickles! Hehehe!", "I blame Dinnerbone.", "You should try our sister game, Minceraft!", "Don't be sad. I'll do better next time, I promise!", "Don't be sad, have a hug! <3", "I just don't know what went wrong :(", "Shall we play a game?", "Quite honestly, I wouldn't worry myself about that.", "I bet Cylons wouldn't have this problem.", "Sorry :(", "Surprise! Haha. Well, this is awkward.", "Would you like a cupcake?", "Hi. I'm Minecraft, and I'm a crashaholic.", "Ooh. Shiny.", "This doesn't make any sense!", "Why is it breaking :(", "Don't do that.", "Ouch. That hurt :(", "You're mean.", "This is a token for 1 free hug. Redeem at your nearest Mojangsta: [~~HUG~~]", "There are four lights!", "But it works on my machine.", "Popbob was here.", "The oldest anarchy server in Minecraft.", "Better luck next time..", "Fatal error occurred user is too based.", "Running premium software on a potato is not advised", "I don't know, ask that kilab guy", "Ah shit, here we go again.", "I will uhh, fix that sometime.", "Not a bug, a feature!", "You should try out Lambda on Windows XP.", "Blade did that."};
72+
73+
try {
74+
cir.setReturnValue(strings[(int)(Util.getMeasuringTimeNano() % (long)strings.length)]);
75+
} catch (Throwable var2) {
76+
cir.setReturnValue("Witty comment unavailable :(");
77+
}
78+
}
79+
}

common/src/main/kotlin/com/lambda/command/commands/ReplayCommand.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ import com.lambda.brigadier.executeWithResult
2727
import com.lambda.brigadier.required
2828
import com.lambda.command.LambdaCommand
2929
import com.lambda.module.modules.player.Replay
30+
import com.lambda.util.FileUtils.listRecursive
3031
import com.lambda.util.FolderRegister
31-
import com.lambda.util.FolderRegister.listRecursive
3232
import com.lambda.util.extension.CommandBuilder
3333
import kotlin.io.path.exists
3434

common/src/main/kotlin/com/lambda/module/modules/client/Network.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import com.lambda.network.NetworkManager.updateToken
3131
import com.lambda.network.api.v1.endpoints.login
3232
import com.lambda.util.StringUtils.hash
3333
import com.lambda.util.extension.isOffline
34+
import net.minecraft.SharedConstants
3435
import net.minecraft.client.network.AllowedAddressResolver
3536
import net.minecraft.client.network.ClientLoginNetworkHandler
3637
import net.minecraft.client.network.ServerAddress
@@ -51,6 +52,9 @@ object Network : Module(
5152
val authServer by setting("Auth Server", "auth.lambda-client.org")
5253
val apiUrl by setting("API Server", "https://api.lambda-client.org")
5354
val apiVersion by setting("API Version", ApiVersion.V1)
55+
val mappings by setting("Mappings", "https://mappings.lambda-client.org")
56+
57+
val gameVersion = SharedConstants.getGameVersion().name
5458

5559
private var hash: String? = null
5660

common/src/main/kotlin/com/lambda/module/modules/network/PacketLogger.kt

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,14 @@ import com.lambda.util.Communication
3030
import com.lambda.util.Communication.info
3131
import com.lambda.util.DynamicReflectionSerializer.dynamicString
3232
import com.lambda.util.FolderRegister
33+
import com.lambda.util.FolderRegister.relativeMCPath
3334
import com.lambda.util.Formatting.getTime
3435
import com.lambda.util.text.*
3536
import kotlinx.coroutines.channels.BufferOverflow
3637
import kotlinx.coroutines.flow.MutableSharedFlow
3738
import net.minecraft.network.packet.Packet
3839
import java.awt.Color
3940
import java.io.File
40-
import java.nio.file.Path
4141
import java.time.format.DateTimeFormatter
4242
import kotlin.io.path.pathString
4343

@@ -83,7 +83,6 @@ object PacketLogger : Module(
8383
extraBufferCapacity = 1000,
8484
onBufferOverflow = BufferOverflow.DROP_OLDEST
8585
)
86-
private val File.relativePath: Path get() = mc.runDirectory.toPath().relativize(toPath())
8786

8887
init {
8988
runIO {
@@ -105,15 +104,14 @@ object PacketLogger : Module(
105104
createNewFile()
106105
}
107106
val info = buildText {
108-
clickEvent(ClickEvents.openFile(relativePath.pathString)) {
107+
clickEvent(ClickEvents.openFile(relativeMCPath.pathString)) {
109108
literal("Packet logger started: ")
110109
color(Color.YELLOW) { literal(fileName) }
111110
literal(" (click to open)")
112111
}
113112
}
114113
this@PacketLogger.info(info)
115114
}.apply {
116-
// ToDo: Add more rich and accurate data to the header
117115
StringBuilder().apply {
118116
appendLine(Communication.ascii)
119117
appendLine("${Lambda.SYMBOL} - Lambda ${Lambda.VERSION} - Packet Log")
@@ -144,8 +142,8 @@ object PacketLogger : Module(
144142
file?.let {
145143
val info = buildText {
146144
literal("Stopped logging packets to ")
147-
clickEvent(ClickEvents.openFile(it.relativePath.pathString)) {
148-
color(Color.YELLOW) { literal(it.relativePath.pathString) }
145+
clickEvent(ClickEvents.openFile(it.relativeMCPath.pathString)) {
146+
color(Color.YELLOW) { literal(it.relativeMCPath.pathString) }
149147
literal(" (click to open)")
150148
}
151149
}

common/src/main/kotlin/com/lambda/module/modules/player/MapDownloader.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ import com.lambda.event.events.TickEvent
2121
import com.lambda.event.listener.SafeListener.Companion.listen
2222
import com.lambda.module.Module
2323
import com.lambda.module.tag.ModuleTag
24+
import com.lambda.util.FileUtils.locationBoundDirectory
2425
import com.lambda.util.FolderRegister
25-
import com.lambda.util.FolderRegister.locationBoundDirectory
2626
import com.lambda.util.StringUtils.hashString
2727
import com.lambda.util.player.SlotUtils.combined
2828
import com.lambda.util.world.entitySearch

common/src/main/kotlin/com/lambda/module/modules/player/Replay.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ import com.lambda.sound.SoundManager.playSound
3838
import com.lambda.util.Communication.info
3939
import com.lambda.util.Communication.logError
4040
import com.lambda.util.Communication.warn
41+
import com.lambda.util.FileUtils.locationBoundDirectory
4142
import com.lambda.util.FolderRegister
42-
import com.lambda.util.FolderRegister.locationBoundDirectory
4343
import com.lambda.util.Formatting.asString
4444
import com.lambda.util.Formatting.getTime
4545
import com.lambda.util.KeyCode

common/src/main/kotlin/com/lambda/network/LambdaHttp.kt

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,20 @@ val LambdaHttp = HttpClient {
3434
}
3535
}
3636

37-
suspend inline fun HttpClient.download(url: String, file: File, block: HttpRequestBuilder.() -> Unit = {}) =
38-
file.writeBytes(get(url, block).readRawBytes())
37+
suspend inline fun HttpClient.download(url: String, file: File, block: HttpRequestBuilder.() -> Unit = {}) {
38+
val response = get(url, block)
39+
check(response.status.isSuccess()) { "Download for $url failed with non 2xx status code" }
3940

40-
suspend inline fun HttpClient.download(url: String, output: OutputStream, block: HttpRequestBuilder.() -> Unit = {}) =
41-
output.write(get(url, block).readRawBytes())
41+
file.writeBytes(response.readRawBytes())
42+
}
43+
44+
suspend inline fun HttpClient.download(url: String, output: OutputStream, block: HttpRequestBuilder.() -> Unit = {}) {
45+
val response = get(url, block)
46+
check(response.status.isSuccess()) { "Download for $url failed with non 2xx status code" }
47+
48+
output.write(response.readRawBytes())
49+
}
4250

4351
suspend inline fun HttpClient.download(url: String, block: HttpRequestBuilder.() -> Unit) =
4452
get(url, block).readRawBytes()
53+

common/src/main/kotlin/com/lambda/network/api/v1/endpoints/GetCape.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import java.util.UUID
3131
* Example:
3232
* - id: ab24f5d6-dcf1-45e4-897e-b50a7c5e7422
3333
*
34-
* response: [Cape] or error
34+
* @return results of cape
3535
*/
3636
suspend fun getCape(uuid: UUID) = runCatching {
3737
LambdaHttp.get("$apiUrl/api/${apiVersion.value}/cape?id=$uuid").body<Cape>()

common/src/main/kotlin/com/lambda/network/api/v1/endpoints/LinkDiscord.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import io.ktor.http.*
3232
* Example:
3333
* - token: OTk1MTU1NzcyMzYxMTQ2NDM4
3434
*
35-
* response: [Authentication] or error
35+
* @return result of [Authentication]
3636
*/
3737
suspend fun linkDiscord(discordToken: String) = runCatching {
3838
LambdaHttp.post("${apiUrl}/api/${apiVersion.value}/link/discord") {

common/src/main/kotlin/com/lambda/network/api/v1/endpoints/Login.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import io.ktor.http.*
3232
* - username: Notch
3333
* - hash: 069a79f444e94726a5befca90e38aaf5
3434
*
35-
* response: [Authentication] or error
35+
* @return result of [Authentication]
3636
*/
3737
suspend fun login(username: String, hash: String) = runCatching {
3838
LambdaHttp.post("${apiUrl}/api/${apiVersion.value}/login") {

0 commit comments

Comments
 (0)