Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 73 additions & 43 deletions src/main/java/emissary/util/magic/MagicNumber.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Queue;

public class MagicNumber {

Expand Down Expand Up @@ -144,7 +145,9 @@ private static String escapeBackspace(String desc) {
StringBuilder s = new StringBuilder();
for (int i = 0; i < desc.length(); i++) {
if (desc.charAt(i) == '\\' && (i + 1) < desc.length() && desc.charAt(i + 1) == 'b') {
s = new StringBuilder(s.substring(0, s.length() - 1));
if (s.length() > 0) {
s.setLength(s.length() - 1);
}
i++;
continue;
}
Expand Down Expand Up @@ -172,16 +175,16 @@ private String format(String desc, byte[] data) {
if (!substitute) {
return desc;
}
Deque<Character> chars = new ArrayDeque<>();
for (int i = desc.length() - 1; i >= 0; --i) {
chars.push(desc.charAt(i));
Queue<Character> chars = new ArrayDeque<>();
for (int i = 0; i < desc.length(); i++) {
chars.add(desc.charAt(i));
}
StringBuilder sb = new StringBuilder();

while (!chars.isEmpty()) {
Character next = chars.pop();
Character next = chars.poll();
if (!chars.isEmpty() && next == '%') {
char subType = chars.pop();
char subType = chars.poll();
if (dataType == TYPE_STRING) {
if (offset < (data.length - 2)) {
String sub = new String(Objects.requireNonNull(getElement(data, offset, 1)), DEFAULT_CHARSET);
Expand All @@ -205,11 +208,11 @@ private String format(String desc, byte[] data) {
}

if (subType == 'l' && !chars.isEmpty() && chars.peek() == 'd') {
chars.pop();
chars.poll();
}
continue;
}
sb.append(next.charValue());
sb.append(next);
}
return sb.toString();
}
Expand Down Expand Up @@ -263,11 +266,29 @@ private boolean testNumeric(byte[] data) {
if (substitute) {
return true;
}

if (mask != null && mask.length == data.length) {
for (int i = 0; i < data.length; i++) {
data[i] = (byte) (data[i] & mask[i]);
}
}

// Short-circuit instantly for strings
if (dataType == TYPE_STRING) {
return Arrays.equals(data, value);
}

byte[] mValues = value;
if (mValues == null || data.length != mValues.length) {
return false;
}

int end = mValues.length;
boolean isBigEndian = dataType == TYPE_BESHORT || dataType == TYPE_BELONG || dataType == TYPE_BEDATE ||
dataType == TYPE_SHORT || dataType == TYPE_LONG || dataType == TYPE_BYTE;

log.debug("Unary Operator: {}", unaryOperator);

int end = mValues.length;
switch (unaryOperator) {
case MAGICOPERATOR_AND:
case MAGICOPERATOR_BWAND:
Expand All @@ -277,59 +298,63 @@ private boolean testNumeric(byte[] data) {
}
}
return true;
case MAGICOPERATOR_GTHAN:
for (int i = 0; i < end; i++) {
if ((data[i] & 0xFF) < (mValues[i] & 0xFF)) {
return false;
}
if (i == end - 1 && data[i] == mValues[i]) {
return false;
}
}
return true;
case MAGICOPERATOR_LTHAN:
for (int i = 0; i < end; i++) {
if ((data[i] & 0xFF) > (mValues[i] & 0xFF)) {
return false;
}
if (i == end - 1 && data[i] == mValues[i]) {
return false;
}
}
return true;
case MAGICOPERATOR_OR:
for (int i = 0; i < end; i++) {
if (data[i] == mValues[i]) {
if ((data[i] & mValues[i]) != 0) {
return true;
}
}
return false;
case MAGICOPERATOR_BWNOT:
case MAGICOPERATOR_NOT:
case MAGICOPERATOR_BWNOT:
for (int i = 0; i < end; i++) {
if (data[i] != mValues[i]) {
return true;
}
}
return false;
case MAGICOPERATOR_GTHAN:
case MAGICOPERATOR_EQUAL_GTHAN:
for (int i = 0; i < end; i++) {
if ((data[i] & 0xFF) < (mValues[i] & 0xFF)) {
return false;
case MAGICOPERATOR_LTHAN:
case MAGICOPERATOR_EQUAL_LTHAN:
int cmp = 0;
if (isBigEndian) {
// Big Endian: MSB is at index 0
for (int i = 0; i < end; i++) {
int v1 = data[i] & 0xFF;
int v2 = mValues[i] & 0xFF;
if (v1 != v2) {
cmp = Integer.compare(v1, v2);
break;
}
}
} else {
// Little Endian: MSB is at the last index
for (int i = end - 1; i >= 0; i--) {
int v1 = data[i] & 0xFF;
int v2 = mValues[i] & 0xFF;
if (v1 != v2) {
cmp = Integer.compare(v1, v2);
break;
}
}
}
return true;
case MAGICOPERATOR_EQUAL_LTHAN:
for (int i = 0; i < end; i++) {
if ((data[i] & 0xFF) > (mValues[i] & 0xFF)) {

switch (unaryOperator) {
case MAGICOPERATOR_GTHAN:
return cmp > 0;
case MAGICOPERATOR_EQUAL_GTHAN:
return cmp >= 0;
case MAGICOPERATOR_LTHAN:
return cmp < 0;
case MAGICOPERATOR_EQUAL_LTHAN:
return cmp <= 0;
default:
return false;
}
}
return true;
default:
throw new IllegalStateException(
"This MagicNumber instance is configured incorrectly. The unary operator is set to an unknown or unconfigured value.");

}
}

Expand Down Expand Up @@ -397,7 +422,12 @@ public String toString() {
} else {
sb.append(unaryOperator);
}
sb.append(MagicMath.byteArrayToHexString(value));

if (dataType == TYPE_STRING && value != null) {
sb.append(new String(value, DEFAULT_CHARSET));
} else {
sb.append(MagicMath.byteArrayToHexString(value));
}

sb.append('\t');
sb.append(description);
Expand Down
54 changes: 17 additions & 37 deletions src/main/java/emissary/util/magic/MagicNumberFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,9 @@ public static List<MagicNumber> buildMagicNumberList(byte[] configData, @Nullabl
try {
if (depth == 0 && !extensions.isEmpty()) {
if (finger == null) {
extensions = null;
extensions = new ArrayList<>();
} else {
addExtensionsLayer(extensions, finger);
extensions = null;
extensions = new ArrayList<>();
finger = null;
}
Expand Down Expand Up @@ -119,7 +117,6 @@ public static List<MagicNumber> buildMagicNumberList(byte[] configData, @Nullabl
}
currentDepth = depth;
addExtensionsLayer(extensions, finger);
extensions = null;
extensions = new ArrayList<>();
parseAndStore(extensions, s, swallowParseException);
}
Expand All @@ -140,11 +137,7 @@ public static List<MagicNumber> buildMagicNumberList(byte[] configData, @Nullabl
if (depth > 0) {
MagicNumber mItem = magicNumberList.get(magicNumberList.size() - 1);
String signature = mItem.toString();
List<String> failedExtensions = continuationErrorMap.get(signature);
if (failedExtensions == null) {
failedExtensions = new ArrayList<>();
continuationErrorMap.put(mItem.toString(), failedExtensions);
}
List<String> failedExtensions = continuationErrorMap.computeIfAbsent(signature, k -> new ArrayList<>());
failedExtensions.add("[MAGIC LINE# " + counter + "] " + s);
} else {
// depth = 0
Expand Down Expand Up @@ -190,11 +183,7 @@ private static MagicNumber parseAndStore(List<MagicNumber> storage, String entry
* @param extensions a {@link List} of continuations which are MagicNumber instances
*/
private static void addExtensionsLayer(List<MagicNumber> extensions, MagicNumber target) {
MagicNumber[] extensionArray = new MagicNumber[extensions.size()];
int index = 0;
for (MagicNumber m : extensions) {
extensionArray[index++] = m;
}
MagicNumber[] extensionArray = extensions.toArray(new MagicNumber[0]);
target.addDependencyLayer(extensionArray);
}

Expand Down Expand Up @@ -246,7 +235,10 @@ public static MagicNumber buildMagicNumber(String entry, boolean swallowParseExc
// column C parsing
item.unaryOperator = resolveUnary(columns, item);
item.value = resolveValue(columns, item);
item.dataTypeLength = item.value.length;

if (item.dataType == MagicNumber.TYPE_STRING && item.value != null) {
item.dataTypeLength = item.value.length;
}
} catch (Exception e) {
throw new ParseException("Error on column 2:" + columns[2] + ". " + e.getMessage());
}
Expand All @@ -261,10 +253,8 @@ public static MagicNumber buildMagicNumber(String entry, boolean swallowParseExc
private static String[] tokenizeEntry(String entry) {
int index = 0;
String[] columns = new String[4];
columns[0] = EMPTYSTRING;
columns[1] = EMPTYSTRING;
columns[2] = EMPTYSTRING;
columns[3] = EMPTYSTRING;
Arrays.fill(columns, EMPTYSTRING);

for (int i = 0; i < entry.length(); i++) {
char c = entry.charAt(i);
if (c == '\\' && i != (entry.length() - 1) && entry.charAt(i + 1) == ' ') {
Expand Down Expand Up @@ -308,10 +298,7 @@ private static String[] prepareEntry(String entry) throws ParseException {

String[] columns = tokenizeEntry(subject);
for (int count = 0; count < columns.length; count++) {
if (count == 3 && columns[count].isEmpty() && columns[0].charAt(0) != '>') {
// columns[count] = NULL_DESCRIPTION;

} else if (columns[count].isEmpty() && count < 3) {
if (columns[count].isEmpty() && count < 3) {
throw new ParseException(ENTRY_4COLUMN_RULE);
}
}
Expand Down Expand Up @@ -339,7 +326,7 @@ private static int resolveOffset(String[] columns, MagicNumber item) throws Pars
}

private static char resolveOffsetUnary(String[] columns) {
if (columns[0].charAt(0) == '&') {
if (!columns[0].isEmpty() && columns[0].charAt(0) == '&') {
return '&';
}
return (char) 0;
Expand Down Expand Up @@ -388,7 +375,10 @@ private static int resolveDataType(String[] columns) throws ParseException {
}

private static int lookupDataType(String arg) {
int dataTypeIdInt = typeMap.get(arg.toUpperCase(Locale.getDefault()));
Integer dataTypeIdInt = typeMap.get(arg.toUpperCase(Locale.getDefault()));
if (dataTypeIdInt == null) {
return -1;
}
switch (dataTypeIdInt) {
case MagicNumber.TYPE_DATE:
case MagicNumber.TYPE_BEDATE:
Expand All @@ -404,7 +394,7 @@ private static byte[] resolveMask(String[] columns, MagicNumber item) {
int ix = columns[1].indexOf("&");
if (ix > 0) {
byte[] maskValues = MagicMath.stringToByteArray(columns[1].substring(ix + 1));
MagicMath.setLength(maskValues, item.dataTypeLength);
return MagicMath.setLength(maskValues, item.dataTypeLength);
}
return null;
}
Expand All @@ -416,9 +406,7 @@ private static byte[] resolveValue(String[] columns, MagicNumber item) {
String subject = columns[2];

if (item.dataType == MagicNumber.TYPE_STRING && !(subject.length() == 1 && subject.charAt(0) == 'x')) {
byte[] strVal = MagicMath.parseEscapedString(subject);
item.dataTypeLength = strVal.length;
return strVal;
return MagicMath.parseEscapedString(subject);
} else if (subject.length() == 1 && subject.charAt(0) == 'x') {
item.substitute = true;
return new byte[0];
Expand All @@ -434,9 +422,6 @@ private static byte[] resolveValue(String[] columns, MagicNumber item) {
byte[] valueArray = MagicMath.stringToByteArray(subject);
valueArray = MagicMath.setLength(valueArray, item.dataTypeLength);

if (item.mask != null) {
valueArray = MagicMath.mask(valueArray, item.mask);
}
if (item.dataType == MagicNumber.TYPE_LELONG) {
MagicMath.longEndianSwap(valueArray, 0);
} else if (item.dataType == MagicNumber.TYPE_LESHORT) {
Expand All @@ -462,7 +447,6 @@ private static int unaryPrefixLength(@Nullable String s) {
case MagicNumber.MAGICOPERATOR_NOT:
return 1;
case MagicNumber.MAGICOPERATOR_GTHAN:
return len > 1 && s.charAt(1) == MagicNumber.MAGICOPERATOR_AND ? 2 : 1;
case MagicNumber.MAGICOPERATOR_LTHAN:
return len > 1 && s.charAt(1) == MagicNumber.MAGICOPERATOR_AND ? 2 : 1;
default:
Expand All @@ -476,11 +460,7 @@ private static int getDataTypeByteLength(MagicNumber item) {
int dataTypeId = item.dataType;
switch (dataTypeId) {
case MagicNumber.TYPE_STRING:
if (item.value == null) {
return -1;
} else {
return item.value.length;
}
return (item.value == null) ? -1 : item.value.length;
case MagicNumber.TYPE_BYTE:
return 1;
case MagicNumber.TYPE_SHORT:
Expand Down
Loading
Loading