Decompiler improvement, compiler start

This commit is contained in:
Michael Wain 2025-01-03 22:00:13 +03:00
parent f75c7e0534
commit d9bfbf2bc5
11 changed files with 202 additions and 98 deletions

View File

@ -99,6 +99,10 @@ public class FlashDecompiler {
case Push: case Push:
i = parseActionPush(data, i, tag); i = parseActionPush(data, i, tag);
break; break;
case InitArray:
tag.getActions().add(new ActionInitArray());
i++;
break;
case InitObject: case InitObject:
tag.getActions().add(new ActionInitObject()); tag.getActions().add(new ActionInitObject());
i++; i++;
@ -225,6 +229,7 @@ public class FlashDecompiler {
int bytes_len = bytesToIntRev(Arrays.copyOfRange(data, i, i+2)); int bytes_len = bytesToIntRev(Arrays.copyOfRange(data, i, i+2));
i += 2; i += 2;
int count = bytesToIntRev(Arrays.copyOfRange(data, i, i+2)); int count = bytesToIntRev(Arrays.copyOfRange(data, i, i+2));
log.info("ActionPoolSize: {}", count);
i += 2; i += 2;
int u = 1; int u = 1;
StringBuilder s = new StringBuilder(); StringBuilder s = new StringBuilder();

View File

@ -3,6 +3,7 @@ package com.alterdekim.flash.decompiler;
import com.alterdekim.flash.decompiler.tag.DoAction; import com.alterdekim.flash.decompiler.tag.DoAction;
import com.alterdekim.flash.decompiler.tag.TagType; import com.alterdekim.flash.decompiler.tag.TagType;
import com.alterdekim.flash.decompiler.translator.Flash2Java; import com.alterdekim.flash.decompiler.translator.Flash2Java;
import com.alterdekim.flash.decompiler.translator.Java2Flash;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -12,7 +13,7 @@ import java.io.File;
public class Main { public class Main {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
FlashDecompiler decompiler = new FlashDecompiler(); FlashDecompiler decompiler = new FlashDecompiler();
ShockwaveFile file = decompiler.loadFromFile(new File("D:\\Documents\\FlashProjects\\Untitled-2.swf")); // D:\Documents\rtmpSpring\static\swf\cache\rus\base[17].swf ShockwaveFile file = decompiler.loadFromFile(new File("D:\\Documents\\rtmpSpring\\static\\swf\\cache\\rus\\catalogs[18].swf")); //
DoAction da = (DoAction) file.getTags() DoAction da = (DoAction) file.getTags()
.stream() .stream()
@ -23,7 +24,7 @@ public class Main {
ObjectMapper objectMapper = new ObjectMapper(); ObjectMapper objectMapper = new ObjectMapper();
File out = new File("dec.json"); File out = new File("dec.json");
if( out.exists() ) out.delete(); if( out.exists() ) out.delete();
objectMapper.writeValue(out, new Flash2Java(da).convert()); //objectMapper.writeValue(out, new Flash2Java(da).convert());
new Java2Flash().convert(new Flash2Java(da).convert());
} }
} }

View File

@ -0,0 +1,15 @@
package com.alterdekim.flash.decompiler.action;
import com.alterdekim.flash.decompiler.util.ActionField;
import lombok.NoArgsConstructor;
import lombok.ToString;
@ToString
@NoArgsConstructor
public class ActionInitArray extends ByteCodeAction {
@Override
public ActionField getType() {
return ActionField.InitArray;
}
}

View File

@ -14,10 +14,11 @@ public enum ActionPushType {
// these types are internal (used for decompiler's purposes) // these types are internal (used for decompiler's purposes)
// they are not exist // they are not exist
INIT_OBJECT(-1), INIT_OBJECT(-1),
GET_VARIABLE(-2), GET_VARIABLE(-2),
GET_MEMBER(-3); GET_MEMBER(-3),
INIT_ARRAY(-4),
CONSTANT_POOL(-5);
public final int type; 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.ArrayList;
@ToString
@AllArgsConstructor
public class ConstantPoolItem extends ByteCodeItem {
private ArrayList<String> val;
@Override
public ActionPushType getType() {
return ActionPushType.CONSTANT_POOL;
}
@Override
public Object getValue() {
return this.val;
}
}

View File

@ -0,0 +1,24 @@
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 InitArrayItem extends ByteCodeItem {
private HashMap<String, Object> val;
@Override
public ActionPushType getType() {
return ActionPushType.INIT_ARRAY;
}
@Override
public Object getValue() {
return this.val;
}
}

View File

@ -5,6 +5,7 @@ import com.alterdekim.flash.decompiler.item.*;
import com.alterdekim.flash.decompiler.action.ByteCodeAction; import com.alterdekim.flash.decompiler.action.ByteCodeAction;
import com.alterdekim.flash.decompiler.item.internal.GetMemberItem; import com.alterdekim.flash.decompiler.item.internal.GetMemberItem;
import com.alterdekim.flash.decompiler.item.internal.GetVariableItem; import com.alterdekim.flash.decompiler.item.internal.GetVariableItem;
import com.alterdekim.flash.decompiler.item.internal.InitArrayItem;
import com.alterdekim.flash.decompiler.item.internal.InitObjectItem; import com.alterdekim.flash.decompiler.item.internal.InitObjectItem;
import com.alterdekim.flash.decompiler.tag.DoAction; import com.alterdekim.flash.decompiler.tag.DoAction;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -16,7 +17,7 @@ public class Flash2Java {
private final List<String> pool; private final List<String> pool;
private final List<ByteCodeAction> actions; private final List<ByteCodeAction> actions;
private final Stack<ByteCodeItem> stack; // ByteCodeAction private final Stack<ByteCodeItem> stack;
private final Map<String, Object> vars; private final Map<String, Object> vars;
public Flash2Java(DoAction doAction) { public Flash2Java(DoAction doAction) {
@ -27,13 +28,11 @@ public class Flash2Java {
} }
public Map<String, Object> convert() { public Map<String, Object> convert() {
// abolish the activeness of variable inside a HashMap. Use stack instead.
for (ByteCodeAction action : actions) { for (ByteCodeAction action : actions) {
// try {
//log.info("Action type: " + action.getType().toString());
switch (action.getType()) { switch (action.getType()) {
case DefineLocal2: case DefineLocal2:
defineLocalWithoutAssign(); // doesn't need implementation due to HashMap's properties. (variableName)
this.stack.pop();
break; break;
case DefineLocal: case DefineLocal:
defineLocal(); defineLocal();
@ -50,6 +49,9 @@ public class Flash2Java {
case InitObject: case InitObject:
initObject(); initObject();
break; break;
case InitArray:
initArray();
break;
case Push: case Push:
this.stack.addAll(((ActionPush) action).getItems()); this.stack.addAll(((ActionPush) action).getItems());
break; break;
@ -62,15 +64,12 @@ public class Flash2Java {
default: default:
// ignore // ignore
} }
//} catch (Exception e) {
//log.error(e.getMessage());
//}
} }
return vars; return this.vars;
} }
private void initObject() { private void initObject() {
Integer numberOfEntries = (int) Double.parseDouble(this.stack.pop().getValue()+""); // harsh way int numberOfEntries = (int) Double.parseDouble(this.stack.pop().getValue()+""); // harsh way
HashMap<String, Object> obj = new HashMap<>(); HashMap<String, Object> obj = new HashMap<>();
for( int i = 0; i < numberOfEntries; i++ ) { for( int i = 0; i < numberOfEntries; i++ ) {
Object val = valConverter( this.stack.pop() ); Object val = valConverter( this.stack.pop() );
@ -80,6 +79,16 @@ public class Flash2Java {
this.stack.push(new InitObjectItem(obj)); this.stack.push(new InitObjectItem(obj));
} }
private void initArray() {
int numberOfElements = (int) Double.parseDouble(this.stack.pop().getValue()+""); // harsh way
HashMap<String, Object> obj = new HashMap<>();
for( int i = 0; i < numberOfElements; i++ ) {
Object val = valConverter( this.stack.pop() );
obj.put(String.valueOf(i), val);
}
this.stack.push(new InitArrayItem(obj));
}
private void reduceViaAdder() { private void reduceViaAdder() {
/* /*
// get last 2 elements from stack // get last 2 elements from stack
@ -100,7 +109,6 @@ public class Flash2Java {
} }
private void getMember() { private void getMember() {
log.info("GetMember: {}", this.stack);
String property = valToString( this.stack.pop() ); String property = valToString( this.stack.pop() );
ByteCodeItem val = this.stack.pop(); ByteCodeItem val = this.stack.pop();
if( val.getType() == ActionPushType.GET_MEMBER ) { if( val.getType() == ActionPushType.GET_MEMBER ) {
@ -113,56 +121,49 @@ public class Flash2Java {
this.stack.push(new GetMemberItem(new ArrayDeque<>(List.of(key, property)))); this.stack.push(new GetMemberItem(new ArrayDeque<>(List.of(key, property))));
} }
private void setMember() { // make recursive assignment. private void setMember() {
log.info("SetMember: {}", this.stack);
ByteCodeItem val = this.stack.pop(); ByteCodeItem val = this.stack.pop();
String index = valToString( this.stack.pop() ); String index = valToString( this.stack.pop() );
ByteCodeItem varOrMember = this.stack.pop(); ByteCodeItem varOrMember = this.stack.pop();
if( varOrMember.getType() == ActionPushType.GET_VARIABLE ) { if( varOrMember.getType() == ActionPushType.GET_VARIABLE ) {
Map<String, Object> m = (Map<String, Object>) this.vars.get(valToString( varOrMember )); Map<String, Object> m = (Map<String, Object>) this.vars.get(valToString( varOrMember ));
m.put(index, val.getValue()); if( val.getType() == ActionPushType.GET_VARIABLE ) {
this.vars.put(valToString(varOrMember), m); m.put(index, this.vars.get(valToString(val)));
return;
}
m.put(index, val.getValue());
return; return;
} }
// GetMemberItem recursive
Map<String, Object> m = recursiveGet((ArrayDeque<String>) varOrMember.getValue()); Map<String, Object> m = recursiveGet((ArrayDeque<String>) varOrMember.getValue());
if( val.getType() == ActionPushType.GET_VARIABLE ) {
m.put(index, this.vars.get(valToString(val)));
return;
}
m.put(index, val.getValue()); m.put(index, val.getValue());
} }
private Map<String, Object> recursiveGet(Deque<String> l) { private Map<String, Object> recursiveGet(Deque<String> l) {
Map<String, Object> m = this.vars; Map<String, Object> m = this.vars;
Iterator<String> i = l.iterator(); for (String s : l) {
while(i.hasNext()) { m = (HashMap<String, Object>) m.get(s);
m = (HashMap<String, Object>) m.get(i.next());
} }
return m; 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) { private String valToString(ByteCodeItem item) {
if ( item.getType() == ActionPushType.POOL_INDEX ) { switch (item.getType()) {
case POOL_INDEX:
return fromPool( (Integer) item.getValue() ); return fromPool( (Integer) item.getValue() );
} else if ( item.getType() == ActionPushType.STRING ) { case STRING:
return (String) item.getValue(); return (String) item.getValue();
} default:
return String.valueOf( item.getValue() ); return String.valueOf( item.getValue() );
} }
}
private Object valConverter(ByteCodeItem item) { private Object valConverter(ByteCodeItem item) {
if( item.getType() == ActionPushType.POOL_INDEX ) { if( item.getType() == ActionPushType.POOL_INDEX ) {
return fromPool((Integer) item.getValue()); return fromPool((Integer) item.getValue());
} else if ( item.getType() == ActionPushType.STRING ) {
return item.getValue();
} }
return item.getValue(); return item.getValue();
} }
@ -170,7 +171,7 @@ public class Flash2Java {
private void setVariable() { private void setVariable() {
Object val = this.stack.pop().getValue(); Object val = this.stack.pop().getValue();
String variableName = valToString( this.stack.pop() ); String variableName = valToString( this.stack.pop() );
vars.put(variableName, val); this.vars.put(variableName, val);
} }
private void getVariable() { private void getVariable() {
@ -178,15 +179,14 @@ public class Flash2Java {
this.stack.push(new GetVariableItem(variableName)); this.stack.push(new GetVariableItem(variableName));
} }
private void defineLocalWithoutAssign() {
String variableName = valToString( this.stack.pop() );
}
private void defineLocal() { private void defineLocal() {
ByteCodeItem val = this.stack.pop(); ByteCodeItem val = this.stack.pop();
String name = valToString(this.stack.pop()); String name = valToString(this.stack.pop());
// todo: implement assignment ability (from one value to another) if( val.getType() == ActionPushType.GET_VARIABLE ) {
vars.put(name, val.getValue()); this.vars.put(name, this.vars.get(valToString(val)));
return;
}
this.vars.put(name, val.getValue());
} }
private String fromPool(int index) { private String fromPool(int index) {

View File

@ -1,15 +0,0 @@
package com.alterdekim.flash.decompiler.translator;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class FlashVar {
private boolean isActive = false;
private Object val;
}

View File

@ -0,0 +1,36 @@
package com.alterdekim.flash.decompiler.translator;
import com.alterdekim.flash.decompiler.action.ByteCodeAction;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
@Slf4j
public class Java2Flash {
private Set<String> pool;
public List<ByteCodeAction> convert(Map<String, Object> map) {
this.pool = new HashSet<>(65535);
extractAllStrings(map);
log.info("poolSet: {}", this.pool);
return new ArrayList<>();
}
private void extractAllStrings(Map<String, Object> map) {
map.keySet().forEach(k -> {
if(k.matches("[^0-9].+")) {
pool.add(k);
}
});
map.values().forEach(o -> {
switch( o.getClass().getSimpleName() ) {
case "HashMap":
extractAllStrings((HashMap<String, Object>) o);
break;
case "String":
pool.add((String) o);
break;
}
});
}
}

View File

@ -4,6 +4,7 @@ public enum ActionField {
ConstantPool((byte) 0x88), ConstantPool((byte) 0x88),
Push((byte) 0x96), Push((byte) 0x96),
InitObject((byte) 0x43), InitObject((byte) 0x43),
InitArray((byte) 0x42),
DefineLocal((byte) 0x3C), DefineLocal((byte) 0x3C),
DefineLocal2((byte) 0x41), DefineLocal2((byte) 0x41),
SetMember((byte)0x4F), SetMember((byte)0x4F),

View File

@ -1,8 +1,12 @@
package com.alterdekim.flash.decompiler.util; package com.alterdekim.flash.decompiler.util;
import lombok.extern.slf4j.Slf4j;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.Arrays;
@Slf4j
public class ByteUtil { public class ByteUtil {
public static int bytesToInt(byte[] bytes) { public static int bytesToInt(byte[] bytes) {
int value = 0; int value = 0;
@ -21,6 +25,13 @@ public class ByteUtil {
} }
public static double bytesToDouble(byte[] bytes) { public static double bytesToDouble(byte[] bytes) {
return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getDouble(); byte[] b1 = new byte[8];
for( int i = bytes.length-1, u = 0; i >= 0; i--, u++ ) {
b1[u] = bytes[i];
}
byte[] d = new byte[8];
System.arraycopy(Arrays.copyOfRange(b1, 4, 8), 0, d, 0, 4);
System.arraycopy(Arrays.copyOfRange(b1, 0, 4), 0, d, 4, 4);
return ByteBuffer.wrap(d).order(ByteOrder.BIG_ENDIAN).getDouble();
} }
} }