Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

マッピング

bytekinは難読化または名前変更されたコードを扱うためのクラス名とメソッド名のマッピングをサポートしています。

マッピングとは?

マッピングは人間が読める名前とバイトコード名の間を変換します。これは以下の場合に便利です:

  • 難読化されたコードを扱う場合
  • 名前変更されたクラスに変換を適用する場合
  • バージョンの違いを処理する場合
  • 複数の命名規則をサポートする場合

マッピングプロバイダーの作成

IMappingProviderインターフェースを実装:

public class MyMappingProvider implements IMappingProvider {

    @Override
    public String getClassName(String name) {
        // クラス名をマップ
        if ("OriginalName".equals(name)) {
            return "MappedName";
        }
        return name;
    }

    @Override
    public String getMethodName(String className, String methodName, String descriptor) {
        // クラスとシグネチャに基づいてメソッド名をマップ
        if ("MyClass".equals(className) && "oldMethod".equals(methodName)) {
            return "newMethod";
        }
        return methodName;
    }

    @Override
    public String getFieldName(String className, String fieldName) {
        // フィールド名をマップ
        if ("MyClass".equals(className) && "oldField".equals(fieldName)) {
            return "newField";
        }
        return fieldName;
    }
}

マッピングの使用

ビルダーにマッピングプロバイダーを渡す:

BytekinTransformer transformer = new BytekinTransformer.Builder(MyHooks.class)
    .mapping(new MyMappingProvider())
    .build();

一般的なマッピングパターン

シンプルな名前変更

public String getClassName(String name) {
    return name.replace("OldPrefix", "NewPrefix");
}

ルックアップテーブル

private static final Map<String, String> classMap = new HashMap<>();

static {
    classMap.put("a", "com.example.ClassA");
    classMap.put("b", "com.example.ClassB");
}

public String getClassName(String name) {
    return classMap.getOrDefault(name, name);
}

ファイルベースのマッピング

public String getClassName(String name) {
    // 設定ファイルから読み込み
    Properties props = loadMappings("mappings.properties");
    return props.getProperty(name, name);
}

難読化されたコードのマッピング

難読化されたコードを扱う場合:

public class ObfuscationMapping implements IMappingProvider {

    @Override
    public String getClassName(String name) {
        // a.class -> com.example.MyClass
        switch (name) {
            case "a": return "com.example.MyClass";
            case "b": return "com.example.OtherClass";
            default: return name;
        }
    }

    @Override
    public String getMethodName(String className, String methodName, String descriptor) {
        // a.b() -> MyClass.process()
        if ("com.example.MyClass".equals(className)) {
            switch (methodName) {
                case "b": return "process";
                case "c": return "validate";
                default: return methodName;
            }
        }
        return methodName;
    }
}

マッピングを使用したフック設定

人間が読める名前を使用してフックを書く:

@ModifyClass("com.example.UserService")  // 読みやすい名前を使用
public class UserServiceHooks {
    @Inject(methodName = "getUser", methodDesc = "(I)Lcom/example/User;", at = At.HEAD)
    public static CallbackInfo hook() { }
}

マッピングプロバイダーがバイトコード内の実際のクラス名に変換します。

デフォルト(何もしない)マッピング

変更されない名前のために空のマッピングを使用:

public class EmptyMappingProvider implements IMappingProvider {

    @Override
    public String getClassName(String name) {
        return name;  // 変更なし
    }

    @Override
    public String getMethodName(String className, String methodName, String descriptor) {
        return methodName;  // 変更なし
    }

    @Override
    public String getFieldName(String className, String fieldName) {
        return fieldName;  // 変更なし
    }
}

応用: バージョン固有のマッピング

複数のバージョンをサポート:

public class VersionAwareMappingProvider implements IMappingProvider {
    private final String version;

    public VersionAwareMappingProvider(String version) {
        this.version = version;
    }

    @Override
    public String getClassName(String name) {
        if ("1.0".equals(version)) {
            return mapToV1(name);
        } else if ("2.0".equals(version)) {
            return mapToV2(name);
        }
        return name;
    }

    private String mapToV1(String name) {
        // バージョン1のマッピング
        return name;
    }

    private String mapToV2(String name) {
        // バージョン2のマッピング
        return name;
    }
}

次のステップ