libredex/ProguardMap.h (66 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <cstddef>
#include <fstream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include "DexClass.h"
#include "ProguardLineRange.h"
/**
* ProguardMap parses ProGuard's mapping.txt file that maps de-obfuscated class
* and member names to obfuscated names. This facility is useful if you have
* profile data that is not obfuscated and you are trying to optimize an
* obfuscated APK.
*
* The proguard map format looks like this:
* com.foo.bar -> A:\n"
* int do1 -> a\n"
* 3:3:void <init>() -> <init>\n"
* 8:929:java.util.ArrayList getCopy() -> a\n"
*
* In keeping with this format, the `translate` functions in ProguardMap take a
* de-obfuscated name and produce an obfuscated name. Since we're likely
* working on an obfuscated APK, this direction is also good for looking up the
* result with the various `DexMember::get_member` functions.
*
* The deobfuscate* methods expect as input the "complete" name for the object.
* For classes, this is the full descriptor.
* For methods, it's <class descriptor>.<name>(<args descs>)<return desc> .
* For fields, it's <class descriptor>.<name>:<type desc> .
*/
struct ProguardMap {
/**
* Construct an empty ProGuard map.
*/
explicit ProguardMap() = default;
/**
* Construct map from the given file.
*/
explicit ProguardMap(const std::string& filename,
bool use_new_rename_map = false);
/**
* Construct map from a given stream.
*/
explicit ProguardMap(std::istream& is) { parse_proguard_map(is); }
/**
* Translate un-obfuscated class name to obfuscated name.
*/
std::string translate_class(const std::string& cls) const;
/**
* Translate un-obfuscated field name to obfuscated name.
*/
std::string translate_field(const std::string& field) const;
/**
* Translate un-obfuscated method name to obfuscated name.
*/
std::string translate_method(const std::string& method) const;
/**
* Translate obfuscated class name to un-obfuscated name.
*/
std::string deobfuscate_class(const std::string& cls) const;
/**
* Translate obfuscated field name to un-obfuscated name.
*/
std::string deobfuscate_field(const std::string& field) const;
/**
* Translate obfuscated method name to un-obfuscated name.
*/
std::string deobfuscate_method(const std::string& method) const;
struct Frame {
const DexString* method;
uint32_t line;
Frame(const DexString* s, uint32_t line) : method(s), line(line) {}
};
/**
* Translate obfuscated stack frame to un-obfuscated series of frames. The
* frames should be ordered with callees preceding their callers.
*/
std::vector<Frame> deobfuscate_frame(const DexString*, uint32_t line) const;
/**
* Obtain line range vector for a given obfuscated method name.
*/
ProguardLineRangeVector& method_lines(const std::string& obfuscated_method);
bool empty() const {
return m_classMap.empty() && m_fieldMap.empty() && m_methodMap.empty();
}
bool is_special_interface(const std::string& type) const {
return m_pg_coalesced_interfaces.find(type) !=
m_pg_coalesced_interfaces.end();
}
private:
void parse_proguard_map(std::istream& fp);
void parse_full_map(std::istream& fp);
bool parse_class(const std::string& line);
bool parse_field(const std::string& line);
bool parse_method(const std::string& line);
bool parse_class_full_format(const std::string& line);
bool parse_store_full_format(const std::string& line);
bool parse_field_full_format(const std::string& line);
bool parse_method_full_format(const std::string& line);
private:
// Unobfuscated to obfuscated maps
std::unordered_map<std::string, std::string> m_classMap;
std::unordered_map<std::string, std::string> m_fieldMap;
std::unordered_map<std::string, std::string> m_methodMap;
// Obfuscated to unobfuscated maps from proguard
std::unordered_map<std::string, std::string> m_obfClassMap;
std::unordered_map<std::string, std::string> m_obfFieldMap;
std::unordered_map<std::string, std::string> m_obfMethodMap;
// Field map for reflection analysis when type is unknown
// Stores Lcom/facebook/Class;.field -> original name without class name
std::unordered_map<std::string, std::string> m_obfUntypedFieldMap;
// Method map for reflection analysis when return type is unknown
// Stores Lcom/facebook/Class;.method(II) -> original name without class name
std::unordered_map<std::string, std::string> m_obfUntypedMethodMap;
std::unordered_map<std::string, ProguardLineRangeVector> m_obfMethodLinesMap;
// Interfaces that are (most likely) coalesced by Proguard.
std::unordered_set<std::string> m_pg_coalesced_interfaces;
std::string m_currClass;
std::string m_currNewClass;
};
/**
* Deobfuscate all items in all dexes, and cache those names in the objects
* themselves, so that they're automatically carried through optimization
* passes.
*/
void apply_deobfuscated_names(const std::vector<DexClasses>&,
const ProguardMap&);
// Exposed for testing purposes.
namespace pg_impl {
const DexString* file_name_from_method_string(const DexString* method);
void apply_deobfuscated_positions(IRCode*, const ProguardMap&);
std::string lines_key(const std::string& method_name);
} // namespace pg_impl
/**
* Convert a dot-style name to a dexdump-style name, e.g.:
* com.foo.MyClass -> Lcom/foo/MyClass;
* void -> V
* java.util.ArrayList[][] -> [[Ljava/util/ArrayList;
*/
std::string convert_type(const std::string&);
std::string convert_type(std::string_view);