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:
i = parseActionPush(data, i, tag);
break;
case InitArray:
tag.getActions().add(new ActionInitArray());
i++;
break;
case InitObject:
tag.getActions().add(new ActionInitObject());
i++;
@ -225,6 +229,7 @@ public class FlashDecompiler {
int bytes_len = bytesToIntRev(Arrays.copyOfRange(data, i, i+2));
i += 2;
int count = bytesToIntRev(Arrays.copyOfRange(data, i, i+2));
log.info("ActionPoolSize: {}", count);
i += 2;
int u = 1;
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.TagType;
import com.alterdekim.flash.decompiler.translator.Flash2Java;
import com.alterdekim.flash.decompiler.translator.Java2Flash;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
@ -12,7 +13,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\\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()
.stream()
@ -23,7 +24,7 @@ public class Main {
ObjectMapper objectMapper = new ObjectMapper();
File out = new File("dec.json");
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)
// they are not exist
INIT_OBJECT(-1),
GET_VARIABLE(-2),
GET_MEMBER(-3);
GET_MEMBER(-3),
INIT_ARRAY(-4),
CONSTANT_POOL(-5);
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.item.internal.GetMemberItem;
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.tag.DoAction;
import lombok.extern.slf4j.Slf4j;
@ -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 Stack<ByteCodeItem> stack;
private final Map<String, Object> vars;
public Flash2Java(DoAction doAction) {
@ -27,50 +28,48 @@ public class Flash2Java {
}
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;
case GetVariable:
getVariable();
break;
case SetMember:
setMember();
break;
case Add:
reduceViaAdder();
break;
case InitObject:
initObject();
break;
case Push:
this.stack.addAll(((ActionPush) action).getItems());
break;
case SetVariable:
setVariable();
break;
case GetMember:
getMember();
break;
default:
// ignore
}
//} catch (Exception e) {
//log.error(e.getMessage());
//}
switch (action.getType()) {
case DefineLocal2:
// doesn't need implementation due to HashMap's properties. (variableName)
this.stack.pop();
break;
case DefineLocal:
defineLocal();
break;
case GetVariable:
getVariable();
break;
case SetMember:
setMember();
break;
case Add:
reduceViaAdder();
break;
case InitObject:
initObject();
break;
case InitArray:
initArray();
break;
case Push:
this.stack.addAll(((ActionPush) action).getItems());
break;
case SetVariable:
setVariable();
break;
case GetMember:
getMember();
break;
default:
// ignore
}
}
return vars;
return this.vars;
}
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<>();
for( int i = 0; i < numberOfEntries; i++ ) {
Object val = valConverter( this.stack.pop() );
@ -80,6 +79,16 @@ public class Flash2Java {
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() {
/*
// get last 2 elements from stack
@ -100,7 +109,6 @@ public class Flash2Java {
}
private void getMember() {
log.info("GetMember: {}", this.stack);
String property = valToString( this.stack.pop() );
ByteCodeItem val = this.stack.pop();
if( val.getType() == ActionPushType.GET_MEMBER ) {
@ -113,56 +121,49 @@ public class Flash2Java {
this.stack.push(new GetMemberItem(new ArrayDeque<>(List.of(key, property))));
}
private void setMember() { // make recursive assignment.
log.info("SetMember: {}", this.stack);
private void setMember() {
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 ));
if( val.getType() == ActionPushType.GET_VARIABLE ) {
m.put(index, this.vars.get(valToString(val)));
return;
}
m.put(index, val.getValue());
this.vars.put(valToString(varOrMember), m);
return;
}
// GetMemberItem recursive
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());
}
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());
for (String s : l) {
m = (HashMap<String, Object>) m.get(s);
}
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();
switch (item.getType()) {
case POOL_INDEX:
return fromPool( (Integer) item.getValue() );
case STRING:
return (String) item.getValue();
default:
return String.valueOf( item.getValue() );
}
return String.valueOf( item.getValue() );
}
private Object valConverter(ByteCodeItem item) {
if ( item.getType() == ActionPushType.POOL_INDEX ) {
return fromPool( (Integer) item.getValue() );
} else if ( item.getType() == ActionPushType.STRING ) {
return item.getValue();
if( item.getType() == ActionPushType.POOL_INDEX ) {
return fromPool((Integer) item.getValue());
}
return item.getValue();
}
@ -170,7 +171,7 @@ public class Flash2Java {
private void setVariable() {
Object val = this.stack.pop().getValue();
String variableName = valToString( this.stack.pop() );
vars.put(variableName, val);
this.vars.put(variableName, val);
}
private void getVariable() {
@ -178,18 +179,17 @@ public class Flash2Java {
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 = valToString( this.stack.pop() );
// todo: implement assignment ability (from one value to another)
vars.put(name, val.getValue());
String name = valToString(this.stack.pop());
if( val.getType() == ActionPushType.GET_VARIABLE ) {
this.vars.put(name, this.vars.get(valToString(val)));
return;
}
this.vars.put(name, val.getValue());
}
private String fromPool(int index) {
return pool.get(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),
Push((byte) 0x96),
InitObject((byte) 0x43),
InitArray((byte) 0x42),
DefineLocal((byte) 0x3C),
DefineLocal2((byte) 0x41),
SetMember((byte)0x4F),

View File

@ -1,8 +1,12 @@
package com.alterdekim.flash.decompiler.util;
import lombok.extern.slf4j.Slf4j;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
@Slf4j
public class ByteUtil {
public static int bytesToInt(byte[] bytes) {
int value = 0;
@ -21,6 +25,13 @@ public class ByteUtil {
}
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();
}
}