Implemented deobfuscation

This commit is contained in:
F43nd1r 2017-05-20 00:53:37 +02:00
parent cf791aef06
commit 2d43278851
13 changed files with 289 additions and 52 deletions

View file

@ -30,6 +30,7 @@ dependencies {
compile 'org.apache.commons:commons-collections4:4.1'
compile 'org.ocpsoft.prettytime:prettytime:3.2.7.Final'
compile 'commons-fileupload:commons-fileupload:1.3.2'
compile 'net.sf.proguard:proguard-retrace:5.3.3'
compileOnly project(':annotation')
apt project(':annotationprocessor')

View file

@ -15,12 +15,12 @@ public class Bug {
public Bug(Report report){
reports = new ArrayList<>();
this.trace = report.getContent().getString("STACK_TRACE");
this.versionCode = report.getContent().getInt("APP_VERSION_CODE");
this.trace = report.getStacktrace();
this.versionCode = report.getVersionCode();
}
public boolean is(Report report){
return report.getContent().getString("STACK_TRACE").equals(trace) && report.getContent().getInt("APP_VERSION_CODE") == versionCode;
return report.getStacktrace().equals(trace) && report.getVersionCode() == versionCode;
}
public List<Report> getReports() {

View file

@ -0,0 +1,34 @@
package com.faendir.acra.data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @author Lukas
* @since 19.05.2017
*/
@Component
public class MappingManager {
private final MappingRepository mappingRepository;
@Autowired
public MappingManager(MappingRepository mappingRepository) {
this.mappingRepository = mappingRepository;
}
public void addMapping(String app, int version, String mappings) {
mappingRepository.save(new ProguardMapping(app, version, mappings));
}
public ProguardMapping getMapping(String app, int version) {
return mappingRepository.findOne(new ProguardMapping.MetaData(app, version));
}
public List<ProguardMapping> getMappings(String app) {
return mappingRepository.findAll(Example.of(new ProguardMapping(app, -1, null), ExampleMatcher.matchingAny()));
}
}

View file

@ -0,0 +1,10 @@
package com.faendir.acra.data;
import org.springframework.data.mongodb.repository.MongoRepository;
/**
* @author Lukas
* @since 19.05.2017
*/
public interface MappingRepository extends MongoRepository<ProguardMapping, ProguardMapping.MetaData> {
}

View file

@ -0,0 +1,45 @@
package com.faendir.acra.data;
import org.springframework.data.mongodb.core.mapping.Document;
import java.io.Serializable;
/**
* @author Lukas
* @since 19.05.2017
*/
@Document
public class ProguardMapping {
private String mappings;
private MetaData id;
public ProguardMapping(){
}
public ProguardMapping(String app, int version, String mappings) {
this.id = new MetaData(app, version);
this.mappings = mappings;
}
public String getApp() {
return id.app;
}
public int getVersion() {
return id.version;
}
public String getMappings() {
return mappings;
}
static class MetaData implements Serializable {
private String app;
private int version;
MetaData(String app, int version) {
this.app = app;
this.version = version;
}
}
}

View file

@ -1,10 +1,13 @@
package com.faendir.acra.data;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
import java.io.IOException;
import java.util.Date;
import java.util.function.Function;
/**
* @author Lukas
@ -16,14 +19,16 @@ public class Report {
@Indexed
private String app;
private JSONObject content;
private String deObfuscatedTrace;
public Report() {
}
public Report(JSONObject content, String app) {
id = content == null ? null : content.getString("REPORT_ID");
this.content = content;
this.app = app;
id = content == null ? null : getValueSafe("REPORT_ID", content::getString, "");
deObfuscatedTrace = null;
}
public JSONObject getContent() {
@ -31,10 +36,61 @@ public class Report {
}
public Date getDate() {
return ReportUtils.getDateFromString(content.getString("USER_CRASH_DATE"));
return ReportUtils.getDateFromString(getValueSafe("USER_CRASH_DATE", content::getString, ""));
}
public String getId() {
return id;
}
public String getStacktrace() {
return getValueSafe("STACK_TRACE", content::getString, "");
}
public String getDeObfuscatedStacktrace(MappingManager mappingManager){
if (deObfuscatedTrace != null) {
return deObfuscatedTrace;
}
ProguardMapping mapping = mappingManager.getMapping(app, getVersionCode());
if (mapping != null) {
try {
deObfuscatedTrace = ReportUtils.retrace(getStacktrace(), mapping);
return deObfuscatedTrace;
} catch (IOException ignored) {
}
}
return getStacktrace();
}
public int getVersionCode() {
return getValueSafe("APP_VERSION_CODE", content::getInt, -1);
}
public String getVersionName(){
return getValueSafe("APP_VERSION_NAME", content::getString, "");
}
public String getUserEmail(){
return getValueSafe("USER_EMAIL", content::getString, "");
}
public String getUserComment(){
return getValueSafe("USER_COMMENT", content::getString, "");
}
public String getAndroidVersion(){
return getValueSafe("ANDROID_VERSION", content::getString, "");
}
public String getPhoneModel(){
return getValueSafe("PHONE_MODEL", content::getString, "");
}
private <T> T getValueSafe(String key, Function<String, T> function, T defaultValue) {
try {
return function.apply(key);
} catch (JSONException e) {
return defaultValue;
}
}
}

View file

@ -1,5 +1,14 @@
package com.faendir.acra.data;
import org.apache.commons.io.FileUtils;
import proguard.retrace.ReTrace;
import java.io.File;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@ -34,4 +43,12 @@ public final class ReportUtils {
}
}
static String retrace(String stacktrace, ProguardMapping mapping) throws IOException {
File file = File.createTempFile("mapping", ".txt");
FileUtils.writeStringToFile(file, mapping.getMappings());
StringWriter writer = new StringWriter();
new ReTrace(ReTrace.STACK_TRACE_EXPRESSION, false, file).retrace(new LineNumberReader(new StringReader(stacktrace)), new PrintWriter(writer));
return writer.toString();
}
}

View file

@ -2,16 +2,13 @@ package com.faendir.acra.ui.view;
import com.faendir.acra.data.App;
import com.faendir.acra.data.AppManager;
import com.faendir.acra.data.MappingManager;
import com.faendir.acra.data.ReportManager;
import com.faendir.acra.util.Style;
import com.vaadin.data.ValueProvider;
import com.vaadin.navigator.ViewChangeListener;
import com.vaadin.shared.ui.ContentMode;
import com.vaadin.spring.annotation.UIScope;
import com.vaadin.ui.Grid;
import com.vaadin.ui.Label;
import com.vaadin.ui.TabSheet;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@ -26,18 +23,13 @@ public class AppView extends NamedView {
private final AppManager appManager;
private final ReportManager reportManager;
private final MappingManager mappingManager;
@Autowired
public AppView(AppManager appManager, ReportManager reportManager) {
public AppView(AppManager appManager, ReportManager reportManager, MappingManager mappingManager) {
this.appManager = appManager;
this.reportManager = reportManager;
}
private <T> Grid.Column addColumn(Grid<T> grid, ValueProvider<T, String> valueProvider, String caption) {
Grid.Column column = grid.addColumn(valueProvider);
column.setId(caption);
column.setCaption(caption);
return column;
this.mappingManager = mappingManager;
}
@Override
@ -51,14 +43,9 @@ public class AppView extends NamedView {
VerticalLayout statistics = new VerticalLayout(new Label("Coming soon"));
statistics.setCaption("Statistics");
statistics.setSizeFull();
String location = UI.getCurrent().getPage().getLocation().toASCIIString();
location = location.substring(0, location.indexOf('#'));
VerticalLayout properties = new VerticalLayout(new Label(String.format("Required ACRA configuration:<br><code>formUri = \"%sreport\",<br>" +
"formUriBasicAuthLogin = \"%s\",<br>formUriBasicAuthPassword = \"%s\",<br>httpMethod = HttpSender.Method.POST,<br>reportType = HttpSender.Type.JSON</code>",
location, app.getId(), app.getPassword()), ContentMode.HTML));
properties.setCaption("Properties");
properties.setSizeFull();
TabSheet tabSheet = new TabSheet(new BugTab(app.getId(), getNavigationManager(), reportManager), new ReportList(app.getId(), getNavigationManager(), reportManager), statistics, properties);
TabSheet tabSheet = new TabSheet(new BugTab(app.getId(), getNavigationManager(), reportManager),
new ReportList(app.getId(), getNavigationManager(), reportManager),
statistics, new DeObfuscationTab(app.getId(), mappingManager), new PropertiesTab(app));
tabSheet.setSizeFull();
VerticalLayout content = new VerticalLayout(tabSheet);
content.setSizeFull();

View file

@ -0,0 +1,77 @@
package com.faendir.acra.ui.view;
import com.faendir.acra.data.MappingManager;
import com.faendir.acra.data.ProguardMapping;
import com.vaadin.ui.Button;
import com.vaadin.ui.ProgressBar;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.Upload;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import java.io.ByteArrayOutputStream;
/**
* @author Lukas
* @since 19.05.2017
*/
public class DeObfuscationTab extends MyGrid<ProguardMapping> {
private final String app;
private final MappingManager mappingManager;
private boolean validNumber;
private boolean validFile;
public DeObfuscationTab(String app, MappingManager mappingManager) {
super("De-Obfuscation", mappingManager.getMappings(app));
this.app = app;
this.mappingManager = mappingManager;
Column column = addColumn(mapping -> String.valueOf(mapping.getVersion()), "Version");
setSizeFull();
Button add = new Button("Add File", e -> addFile());
add.setSizeFull();
appendFooterRow().getCell(column).setComponent(add);
}
private void addFile() {
Window window = new Window("New Mapping Configuration");
Button confirm = new Button("Confirm");
confirm.setEnabled(false);
TextField version = new TextField("Version code", e -> {
try {
//noinspection ResultOfMethodCallIgnored
Integer.parseInt(e.getValue());
validNumber = true;
confirm.setEnabled(validFile);
} catch (NumberFormatException ex) {
validNumber = false;
confirm.setEnabled(false);
}
});
ByteArrayOutputStream out = new ByteArrayOutputStream();
Upload upload = new Upload("Mapping file:", (filename, mimeType) -> out);
ProgressBar progressBar = new ProgressBar();
progressBar.setSizeFull();
upload.addProgressListener((readBytes, contentLength) -> progressBar.setValue(readBytes / contentLength));
upload.addSucceededListener(e -> {
validFile = true;
confirm.setEnabled(validNumber);
});
upload.addFailedListener(e -> {
validFile = false;
confirm.setEnabled(false);
});
upload.setSizeFull();
confirm.addClickListener(e -> {
mappingManager.addMapping(app, Integer.parseInt(version.getValue()), out.toString());
setItems(mappingManager.getMappings(app));
window.close();
});
confirm.setSizeFull();
VerticalLayout layout = new VerticalLayout(version, upload, progressBar, confirm);
window.setContent(layout);
window.center();
UI.getCurrent().addWindow(window);
}
}

View file

@ -57,9 +57,8 @@ public class Overview extends NamedView {
Grid.Column column = grid.addColumn(App::getName, "Name");
grid.addColumn(app -> String.valueOf(reportManager.getReports(app.getId()).size()), "Reports");
FooterCell footerCell = grid.appendFooterRow().getCell(column.getId());
Button add = new Button("New App");
Button add = new Button("New App", e -> addApp());
add.setSizeFull();
add.addClickListener(e -> addApp());
footerCell.setComponent(add);
VerticalLayout layout = new VerticalLayout(grid);
Style.apply(layout, Style.NO_PADDING, Style.PADDING_LEFT, Style.PADDING_RIGHT, Style.PADDING_BOTTOM);

View file

@ -0,0 +1,24 @@
package com.faendir.acra.ui.view;
import com.faendir.acra.data.App;
import com.vaadin.shared.ui.ContentMode;
import com.vaadin.ui.Label;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
/**
* @author Lukas
* @since 19.05.2017
*/
public class PropertiesTab extends VerticalLayout {
public PropertiesTab(App app) {
String location = UI.getCurrent().getPage().getLocation().toASCIIString();
location = location.substring(0, location.indexOf('#'));
addComponent(new Label(String.format("Required ACRA configuration:<br><code>formUri = \"%sreport\",<br>" +
"formUriBasicAuthLogin = \"%s\",<br>formUriBasicAuthPassword = \"%s\",<br>" +
"httpMethod = HttpSender.Method.POST,<br>reportType = HttpSender.Type.JSON</code>",
location, app.getId(), app.getPassword()), ContentMode.HTML));
setCaption("Properties");
setSizeFull();
}
}

View file

@ -20,21 +20,16 @@ public class ReportList extends MyGrid<Report> implements ReportManager.ChangeLi
this.reportManager = reportManager;
setSizeFull();
addColumn(report -> StringUtils.distanceFromNowAsString(report.getDate()), "Date");
addReportColumn("APP_VERSION_NAME", "App Version");
addReportColumn("ANDROID_VERSION", "Android Version");
addReportColumn("PHONE_MODEL", "Device");
addColumn(report -> report.getContent().getString("STACK_TRACE").split("\n", 2)[0], "Stacktrace").setExpandRatio(1);
addColumn(report -> String.valueOf(report.getVersionCode()), "App Version");
addColumn(Report::getAndroidVersion, "Android Version");
addColumn(Report::getPhoneModel, "Device");
addColumn(report -> report.getStacktrace().split("\n", 2)[0], "Stacktrace").setExpandRatio(1);
addColumn(report -> "Delete", new ButtonRenderer<>(e -> reportManager.remove(e.getItem())));
addItemClickListener(e -> navigationManager.navigateTo(ReportView.class, e.getItem().getId()));
addAttachListener(e -> reportManager.addListener(this));
addDetachListener(e -> reportManager.removeListener(this));
}
private void addReportColumn(String key, String caption) {
addColumn(report -> report.getContent().getString(key), caption);
}
@Override
public void onChange() {
setItems(reportManager.getReports(app));

View file

@ -1,6 +1,7 @@
package com.faendir.acra.ui.view;
import com.faendir.acra.data.AttachmentManager;
import com.faendir.acra.data.MappingManager;
import com.faendir.acra.data.Report;
import com.faendir.acra.data.ReportManager;
import com.faendir.acra.util.Style;
@ -36,11 +37,13 @@ public class ReportView extends NamedView {
private final ReportManager reportManager;
private final AttachmentManager attachmentManager;
private final MappingManager mappingManager;
@Autowired
public ReportView(ReportManager reportManager, AttachmentManager attachmentManager) {
public ReportView(ReportManager reportManager, AttachmentManager attachmentManager, MappingManager mappingManager) {
this.reportManager = reportManager;
this.attachmentManager = attachmentManager;
this.mappingManager = mappingManager;
}
private Stream<Component> getLayoutForEntry(String key, Object value) {
@ -86,21 +89,10 @@ public class ReportView extends NamedView {
return button;
}).toArray(Component[]::new));
GridLayout summaryGrid = new GridLayout(2, 1);
summaryGrid.addComponents(new Label("Version", ContentMode.PREFORMATTED), new Label(report.getContent().getString("APP_VERSION_NAME"), ContentMode.PREFORMATTED));
summaryGrid.addComponents(new Label("Email", ContentMode.PREFORMATTED), new Label(report.getContent().getString("USER_EMAIL"), ContentMode.PREFORMATTED));
summaryGrid.addComponents(new Label("Comment", ContentMode.PREFORMATTED), new Label(report.getContent().getString("USER_COMMENT"), ContentMode.PREFORMATTED));
String trace = report.getContent().getString("STACK_TRACE");
String[] lines = trace.split("\n");
String exception = lines[0];
String cause = "";
for(int i = lines.length -1; i >= 1; i--){
if(lines[i].charAt(0) != '\t'){
cause = lines[i];
break;
}
}
summaryGrid.addComponents(new Label("Exception", ContentMode.PREFORMATTED), new Label(exception, ContentMode.PREFORMATTED));
summaryGrid.addComponents(new Label("Root cause", ContentMode.PREFORMATTED), new Label(cause, ContentMode.PREFORMATTED));
summaryGrid.addComponents(new Label("Version", ContentMode.PREFORMATTED), new Label(report.getVersionName(), ContentMode.PREFORMATTED));
summaryGrid.addComponents(new Label("Email", ContentMode.PREFORMATTED), new Label(report.getUserEmail(), ContentMode.PREFORMATTED));
summaryGrid.addComponents(new Label("Comment", ContentMode.PREFORMATTED), new Label(report.getUserComment(), ContentMode.PREFORMATTED));
summaryGrid.addComponents(new Label("De-obfuscated Stacktrace", ContentMode.PREFORMATTED), new Label(report.getDeObfuscatedStacktrace(mappingManager), ContentMode.PREFORMATTED));
summaryGrid.addComponents(new Label("Attachments", ContentMode.PREFORMATTED), attachments);
summaryGrid.setDefaultComponentAlignment(Alignment.MIDDLE_LEFT);
summaryGrid.setSizeFull();