Clear SecureJar structs

This commit is contained in:
embeddedt 2023-04-27 15:24:23 -04:00
parent c5c939df63
commit b8450b6acd
No known key found for this signature in database
GPG Key ID: A69433EC199B5613

View File

@ -1,15 +1,20 @@
package org.embeddedt.modernfix.util;
import com.google.common.collect.ImmutableMap;
import cpw.mods.jarhandling.SecureJar;
import cpw.mods.jarhandling.impl.Jar;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.forgespi.language.IModFileInfo;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.transformer.ClassInfo;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.security.CodeSigner;
import java.util.*;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
@ -53,6 +58,136 @@ public class ClassInfoManager {
}
if(numManifestsCleared > 0)
ModernFix.LOGGER.info("Cleared {} manifest attributes", numManifestsCleared);
try {
clearSecureJarStructs();
} catch(Throwable e) {
ModernFix.LOGGER.error("Couldn't clear Jar structs", e);
}
}
private static void clearSecureJarStructs() throws Throwable {
// Clear Jar signing data
Unsafe unsafe;
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
unsafe = (Unsafe)f.get(null);
Field statusDataField, pendingSignersField, verifiedSignersField;
statusDataField = Jar.class.getDeclaredField("statusData");
pendingSignersField = Jar.class.getDeclaredField("pendingSigners");
verifiedSignersField = Jar.class.getDeclaredField("verifiedSigners");
long statusDataOffset = unsafe.objectFieldOffset(statusDataField);
long pendingSignersOffset = unsafe.objectFieldOffset(pendingSignersField);
long verifiedSignersOffset = unsafe.objectFieldOffset(verifiedSignersField);
for(IModFileInfo mod : ModList.get().getModFiles()) {
SecureJar secureJar = mod.getFile().getSecureJar();
if(secureJar instanceof Jar) {
unsafe.putObject(secureJar, statusDataOffset, LyingStatusDataMap.INSTANCE);
unsafe.putObject(secureJar, pendingSignersOffset, EmptyCodeSignerTable.INSTANCE);
unsafe.putObject(secureJar, verifiedSignersOffset, EmptyCodeSignerTable.INSTANCE);
}
}
}
static class EmptyCodeSignerTable extends Hashtable<String, CodeSigner[]> {
public static final EmptyCodeSignerTable INSTANCE = new EmptyCodeSignerTable();
private static final CodeSigner[] VAL = new CodeSigner[0];
@Override
public synchronized CodeSigner[] put(String key, CodeSigner[] value) {
return null;
}
@Override
public synchronized boolean isEmpty() {
return true;
}
@Override
public synchronized boolean containsKey(Object key) {
return false;
}
@Override
public synchronized CodeSigner[] get(Object key) {
return VAL;
}
}
/**
* This map is used to replace the statusData map.
*
* The lying in containsKey is intentionally done to force certain code paths to run in Jar.
* Otherwise the security information might be recomputed many times.
*/
static class LyingStatusDataMap implements Map<String, Object> {
public static final LyingStatusDataMap INSTANCE = new LyingStatusDataMap();
@Override
public int size() {
return 0;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public boolean containsKey(Object o) {
return true;
}
@Override
public boolean containsValue(Object o) {
return false;
}
@Override
public Object get(Object o) {
return null;
}
@Nullable
@Override
public Object put(String s, Object o) {
return null;
}
@Override
public Object remove(Object o) {
return null;
}
@Override
public void putAll(@NotNull Map<? extends String, ?> map) {
}
@Override
public void clear() {
}
@NotNull
@Override
public Set<String> keySet() {
return Collections.emptySet();
}
@NotNull
@Override
public Collection<Object> values() {
return Collections.emptyList();
}
@NotNull
@Override
public Set<Entry<String, Object>> entrySet() {
return Collections.emptySet();
}
}
static class EmptyAttributes extends Attributes {