Decompiler improvement, slightly recursive

This commit is contained in:
Michael Wain 2025-01-03 19:32:01 +03:00
parent a06423a6bc
commit f75c7e0534
6 changed files with 127 additions and 50 deletions

View File

@ -12,7 +12,7 @@ import java.io.File;
public class Main {
public static void main(String[] args) throws Exception {
FlashDecompiler decompiler = new FlashDecompiler();
ShockwaveFile file = decompiler.loadFromFile(new File("D:\\Documents\\rtmpSpring\\static\\swf\\cache\\rus\\base[17].swf"));
ShockwaveFile file = decompiler.loadFromFile(new File("D:\\Documents\\FlashProjects\\Untitled-2.swf")); // D:\Documents\rtmpSpring\static\swf\cache\rus\base[17].swf
DoAction da = (DoAction) file.getTags()
.stream()

View File

@ -15,7 +15,9 @@ public enum ActionPushType {
// these types are internal (used for decompiler's purposes)
// they are not exist
INIT_OBJECT(-1);
INIT_OBJECT(-1),
GET_VARIABLE(-2),
GET_MEMBER(-3);
public final int type;

View File

@ -0,0 +1,25 @@
package com.alterdekim.flash.decompiler.item.internal;
import com.alterdekim.flash.decompiler.item.ActionPushType;
import com.alterdekim.flash.decompiler.item.ByteCodeItem;
import lombok.AllArgsConstructor;
import lombok.ToString;
import java.util.ArrayDeque;
@ToString
@AllArgsConstructor
public class GetMemberItem extends ByteCodeItem {
private final ArrayDeque<String> hierarchy;
@Override
public ActionPushType getType() {
return ActionPushType.GET_MEMBER;
}
@Override
public ArrayDeque<String> getValue() {
return hierarchy;
}
}

View File

@ -0,0 +1,23 @@
package com.alterdekim.flash.decompiler.item.internal;
import com.alterdekim.flash.decompiler.item.ActionPushType;
import com.alterdekim.flash.decompiler.item.ByteCodeItem;
import lombok.AllArgsConstructor;
import lombok.ToString;
@ToString
@AllArgsConstructor
public class GetVariableItem extends ByteCodeItem {
private String name;
@Override
public ActionPushType getType() {
return ActionPushType.GET_VARIABLE;
}
@Override
public Object getValue() {
return this.name;
}
}

View File

@ -3,9 +3,11 @@ package com.alterdekim.flash.decompiler.item.internal;
import com.alterdekim.flash.decompiler.item.ActionPushType;
import com.alterdekim.flash.decompiler.item.ByteCodeItem;
import lombok.AllArgsConstructor;
import lombok.ToString;
import java.util.HashMap;
@ToString
@AllArgsConstructor
public class InitObjectItem extends ByteCodeItem {

View File

@ -3,9 +3,10 @@ package com.alterdekim.flash.decompiler.translator;
import com.alterdekim.flash.decompiler.action.ActionPush;
import com.alterdekim.flash.decompiler.item.*;
import com.alterdekim.flash.decompiler.action.ByteCodeAction;
import com.alterdekim.flash.decompiler.item.internal.GetMemberItem;
import com.alterdekim.flash.decompiler.item.internal.GetVariableItem;
import com.alterdekim.flash.decompiler.item.internal.InitObjectItem;
import com.alterdekim.flash.decompiler.tag.DoAction;
import com.alterdekim.flash.decompiler.util.ActionField;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
@ -16,7 +17,7 @@ public class Flash2Java {
private final List<String> pool;
private final List<ByteCodeAction> actions;
private final Stack<ByteCodeItem> stack; // ByteCodeAction
private final Map<String, FlashVar> vars;
private final Map<String, Object> vars;
public Flash2Java(DoAction doAction) {
this.pool = doAction.getActionPool();
@ -25,12 +26,15 @@ public class Flash2Java {
this.vars = new HashMap<>();
}
public Map<String, FlashVar> convert() {
// abolish the activeness of variable inside of an HashMap. Use stack instead.
public Map<String, Object> convert() {
// abolish the activeness of variable inside a HashMap. Use stack instead.
for (ByteCodeAction action : actions) {
// try {
//log.info("Action type: " + action.getType().toString());
switch (action.getType()) {
case DefineLocal2:
defineLocalWithoutAssign();
break;
case DefineLocal:
defineLocal();
break;
@ -66,11 +70,11 @@ public class Flash2Java {
}
private void initObject() {
Integer numberOfEntries = (Integer) this.stack.pop().getValue();
Integer numberOfEntries = (int) Double.parseDouble(this.stack.pop().getValue()+""); // harsh way
HashMap<String, Object> obj = new HashMap<>();
for( int i = 0; i < numberOfEntries; i++ ) {
Object val = valConverter( this.stack.pop() );
String key = (String) valConverter( this.stack.pop() );
String key = valToString( this.stack.pop() );
obj.put(key, val);
}
this.stack.push(new InitObjectItem(obj));
@ -96,36 +100,62 @@ public class Flash2Java {
}
private void getMember() {
Object val = this.stack.pop().getValue();
String name = (String) valConverter( this.stack.pop() );
log.info("SetMember: {}", name);
String key = this.vars.keySet().stream().filter(k -> this.vars.get(k).isActive()).findFirst().get();
HashMap<String, Object> g = (HashMap<String, Object>) this.vars.get(key).getVal();
g.put(name, val);
this.vars.put(key, new FlashVar(true, g));
}
private boolean hasActiveVariable() {
return this.vars.keySet().stream().anyMatch(k -> this.vars.get(k).isActive());
}
private String getActiveVarKey() {
return this.vars.keySet().stream().filter(k -> this.vars.get(k).isActive()).findFirst().get();
}
private void setMember() {
log.info("GetMember: {}", this.stack);
String property = valToString( this.stack.pop() );
ByteCodeItem val = this.stack.pop();
if( this.stack.isEmpty() && this.hasActiveVariable() ) {
String var = getActiveVarKey();
if( val.getType() == ActionPushType.GET_MEMBER ) {
ArrayDeque<String> l = (ArrayDeque<String>) val.getValue();
l.add(property);
this.stack.push(new GetMemberItem(l));
return;
}
Integer index = (Integer) this.stack.pop().getValue();
String var = this.vars.keySet().stream().filter(k -> this.vars.get(k).isActive()).findFirst().get();
Map<String, Object> m = (Map<String, Object>) this.vars.get(var).getVal();
m.put(String.valueOf(index), val.getValue());
this.vars.put(var, new FlashVar(false, m)); // this may cause a bug (false in FlashVar arg)
String key = valToString(val);
this.stack.push(new GetMemberItem(new ArrayDeque<>(List.of(key, property))));
}
private void setMember() { // make recursive assignment.
log.info("SetMember: {}", this.stack);
ByteCodeItem val = this.stack.pop();
String index = valToString( this.stack.pop() );
ByteCodeItem varOrMember = this.stack.pop();
if( varOrMember.getType() == ActionPushType.GET_VARIABLE ) {
Map<String, Object> m = (Map<String, Object>) this.vars.get(valToString( varOrMember ));
m.put(index, val.getValue());
this.vars.put(valToString(varOrMember), m);
return;
}
// GetMemberItem recursive
Map<String, Object> m = recursiveGet((ArrayDeque<String>) varOrMember.getValue());
m.put(index, val.getValue());
}
private Map<String, Object> recursiveGet(Deque<String> l) {
Map<String, Object> m = this.vars;
Iterator<String> i = l.iterator();
while(i.hasNext()) {
m = (HashMap<String, Object>) m.get(i.next());
}
return m;
}
private GetMemberItem toGetMember(ByteCodeItem item) {
switch (item.getType()) {
case GET_MEMBER:
return (GetMemberItem) item;
case STRING:
case POOL_INDEX:
default:
return new GetMemberItem(new ArrayDeque<>(List.of(valToString(item))));
}
}
private String valToString(ByteCodeItem item) {
if ( item.getType() == ActionPushType.POOL_INDEX ) {
return fromPool( (Integer) item.getValue() );
} else if ( item.getType() == ActionPushType.STRING ) {
return (String) item.getValue();
}
return String.valueOf( item.getValue() );
}
private Object valConverter(ByteCodeItem item) {
@ -139,29 +169,24 @@ public class Flash2Java {
private void setVariable() {
Object val = this.stack.pop().getValue();
String variableName = (String) valConverter( this.stack.pop() );
vars.put(variableName, new FlashVar(false, val));
String variableName = valToString( this.stack.pop() );
vars.put(variableName, val);
}
private void getVariable() {
String variableName = (String) valConverter( this.stack.pop() );
vars.keySet().forEach(k -> {
FlashVar fv = vars.get(k);
fv.setActive(false);
vars.put(k, fv);
});
FlashVar v = vars.get(variableName);
v.setActive(true);
vars.put(variableName, v);
String variableName = valToString( this.stack.pop() );
this.stack.push(new GetVariableItem(variableName));
}
private void defineLocalWithoutAssign() {
String variableName = valToString( this.stack.pop() );
}
private void defineLocal() {
ByteCodeItem val = this.stack.pop();
String name = (String) valConverter( this.stack.pop() );
String name = valToString( this.stack.pop() );
// todo: implement assignment ability (from one value to another)
vars.put(name,
new FlashVar(false, val.getValue())
);
vars.put(name, val.getValue());
}
private String fromPool(int index) {