Reverse Engineering - What's Kind of Algorithm In This Code? - java

Someone can help me to discover what type of algorithm is this?
public class hf1 {
public static final String[] f3650a = {"0sFU#W>Ao*BT64?[L5aONSK.'"...};
public static final String[] f3651b = {" \bB\u0017\u001e)YBN\u001eT/\u001e4V\u0001ZT/6VV"...};
public static final String[] f3652c = {"\u0000\u0000\u0013\u00006\u0000\\\u0000\u0000¢\u0000¹\u0000¿\u0000"...};
public static String m5192a(int i) {
int i2 = i / 4096;
int i3 = i % 4096;
int i4 = i + 1;
int i5 = i4 / 4096;
int i6 = i4 % 4096;
String[] strArr = f3652c;
String str = strArr[i2];
String str2 = strArr[i5];
int i7 = i3 * 2;
int charAt = ((str.charAt(i7 + 1) & 65535) << 16) | (str.charAt(i7) & 65535);
int i8 = i6 * 2;
int charAt2 = ((str2.charAt(i8 + 1) << 16) | str2.charAt(i8)) - charAt;
char[] cArr = new char[charAt2];
for (int i9 = 0; i9 < charAt2; i9++) {
int i10 = charAt + i9;
int charAt3 = f3651b[i10 / 8192].charAt(i10 % 8192) & 65535;
cArr[i9] = f3650a[charAt3 / 8192].charAt(charAt3 % 8192);
}
return new String(cArr);
}
}
If i call m5192a(1) passing any int index as parameter, the code returns a String. It's like some kind of hiding plain strings on the source code.
Someone have any idea of a possible reverse code? To transform plain string in to this? Is this a known technique with a name?

I'm not sure about a "known technique with a name" - perhaps keywords like "inverse function" or "bidirectionalization" might help, e.g. In pure functional languages, is there an algorithm to get the inverse function?
For this particular function, luckily it seems pretty straightforward to invert as long as the f365xx string constants abide by some certain properties. Notice that cArr is filled one-by-one and each character is independent of each other. We can try to use both the characters and the length of the string to try to decode the input i. Specifically,
A single character will give us candidates for i10, which will give us candidates for charAt. Repeating over all the characters, hopefully only one candidate will work for every character, and that candidate will give us i7/i2
-> i2/i3 -> i if the strings are "benevolent".
If the strings are not benevolent, then the length of the string will give us candidates for charAt2 which, together with the candidates for charAt, will give us candidates for i8/i6. Hopefully there is only one candidate pair i6/i8 -> i5/i6 -> -> i4 -> i if the string constants are benevolent.
If the strings are still not benevolent, then it is unsolvable since the function is not one-to-one.
I'll outline the algorithm for the 2 bullet points now, but as a disclaimer, I haven't tested this and it's just pseudocode.
From a character
Let's say you start with the first character, cArr[0]. If we focus on the 3 lines inside the for loop,
int i10 = charAt + i9;
int charAt3 = f3651b[i10 / 8192].charAt(i10 % 8192) & 65535;
cArr[i9] = f3650a[charAt3 / 8192].charAt(charAt3 % 8192);
Then we can get charAt3 by finding where cArr[0] appears in f3650a. For example,
charAt3candidates = []
for i = 0 to length(f3650a)-1:
indexCandidates = f3650[i].indexOf(cArr[0])
for indexCandidate in indexCandidates:
charAt3candidate = i * 8192 + indexCandidate
if indexCandidate >= 8192 or charAt3candidate >= 65536:
continue
charAt3candidates.append(charAt3candidate)
Note that we filter out indexCandidate >= 8192 and charAt3candidate >= 65536 due to modulo's and bitwise and's that make greater values impossible (x & 65535 == x % 65536 since 65536 = 2^16).
Do a similar process to find the candidates for i10 and subtract i9 to get candidates for charAt. If you have more than one candidate, repeat this process for every character in cArr and keep only the candidates of charAt which are valid for every character in cArr. Even after repeating for every character in cArr, you may still have many candidates.
For each candidate of charAt, use the right-half of the expression for charAt:
charAt % 65536 = (str.charAt(i7) & 65535)
combined with str = f3652c[i2] to try to find i2/i7 the same way we tried to find charAt3 and i10. Note that the left half of the expression, ((str.charAt(i7 + 1) & 65535) << 16), doesn't matter (for now). Hope and pray that this only gives you one possible choice of i2/i7. If so, use it to find i2/i3 -> i. If there's more than one option, then we have to try using the length of the string.
From the length of the string
The length of the string gives us charAt2, and we can use a similar process as before to find all the candidates for i8 and str2 (i5). Note in this process we have to try with every possible candidate for charAt. Hopefully there is only one candidate pair for i8/i5 remaining. If so, use it to find i5/i6 -> i4 -> i. If there are more than 1 candidate, then the function is not one-to-one and it's impossible to invert.
Good luck!

Related

Generate all subsequences of length m from a character array of length n where n >= m in Java language

I was looking for an optimal code in Java for generating all subsequences of length m from a character array of length n, where n >= m.
The meaning of subsequence is here: https://en.wikipedia.org/wiki/Subsequence
My current psuedocode/algorithm is below. But it doesn't look optimized.
if (m <= 0)
return;
for (i = 0; i < 2^n; i++) { // can use BigInteger if n > 64
j = numberOfBitsSet(i); // assuming numberOfBitsSet is implemented
if (j == m) {
s = "";
while((index = getIndexOfNextBitSet(i)) >= 0) { // assuming getIndexOfNextBitSet is implemented
s = s + charArray[index]; // bit numbering starts from zero
} // end of while
System.out.println(s);
} // end of if
} // end of for
Usually I solve algorithmic tasks in C++. There is the cool function of next_permutation that allows you to shuffle through all "permutations" of a sequence. If the sequence is say "00110100" next_permutation will give you "00111000" (in optimal way).
I found an example implementation of the same function in java here.
How this can help you solve your problem: initialize a sequence with n-m leading zeroes followed by m ones. Use your mask-base generation. After that do next_permutation. The complexity of this algorithm is n* subsequence_count. This will be significant improvement if you know that m follows certain rules, but once again will trigger huge number of results if n = 64 and m = 32 for example.

Karatsuba algorithm implementation: works for small ns, breaks for bigger ns

I'm working on an implementation of the Karatsuba algorithm of multiplying numbers, but unlike most implementations using Strings as the primary data structure instead of BigNumbers or longs. I've written a recursive solution to the problem that appears to work for all n < 6, but for some reason it fails to work for odd ns greater than 6, despite all of the base cases working. Here's the karatsuba part of the program, with a few prints left behind from debugging. All of the methods used in this should work as intended, I tested them thoroughly. For a value factor1 = "180" and factor2 = "109", the correct result is outputted. For a value factor1 = "1111" and factor2 = "1111" the correct result is outputted. For a factor1 = "2348711" and factor2 = "8579294" the program outputs "20358060808034" when it should output "20150282190034". I've tried backtracing the logic, and I can't find where exactly it goes wrong. If anyone has any insight as to where something may not work, any help is appreciated.
public static String multiply(String factor1, String factor2) {
// base case of length = 1
System.out.println("Factor1 " + factor1 + " factor2 " + factor2);
if (factor1.length() == 1 && factor2.length() == 1) {
return smallNumberMultiplication(factor1, factor2);
} else if (factor1.length() == 1 && factor2.length() == 2) { //these conditions needed for odd-size #s
return smallNumberMultiplication(factor1, factor2); // max iteration = 10
} else if (factor1.length() == 2 && factor2.length() == 1) {
return smallNumberMultiplication(factor2, factor1); // max iteration = 10
}
// check which factor is smaller, find the index at which the value is split
int numberLength = factor1.length();
int middleIndex = numberLength / 2;
// Find the power to which 10 is raised such that it follows Karatsuba's algorithm for ac
int powerValue = numberLength + numberLength % 2;
// divide both numbers into two parts bounded by middleIndex place
String[] tempSplitString = splitString(factor1, middleIndex);
String f1Large = tempSplitString[0], f1Small = tempSplitString[1];
tempSplitString = splitString(factor2, middleIndex);
String f2Large = tempSplitString[0], f2Small = tempSplitString[1];
String multiplyHighestNumbers, multiplySmallestNumbers, multiplyMiddleNumbers;
// large factor1 * large factor2
multiplyHighestNumbers = multiply(f1Large, f2Large);
// Multiply (f1Large + f1Small)*(f2Large + f2Small)
multiplyMiddleNumbers = multiply(addTwoValues(f1Large, f1Small), addTwoValues(f2Large, f2Small));
// small factor1 * small factor2
multiplySmallestNumbers = multiply(f1Small, f2Small);
// add trailing zeros to values (multiply by 10^powerValue)
String finalHighestNumber = addTrailingZeros(multiplyHighestNumbers, powerValue);
String finalMiddleNumber = addTrailingZeros(
subtractTwoValues(subtractTwoValues(multiplyMiddleNumbers, multiplyHighestNumbers),
multiplySmallestNumbers),
powerValue / 2);
String finalSmallestNumber = multiplySmallestNumbers;
// add each part together
return removeLeadingZeros(addTwoValues(addTwoValues(finalHighestNumber, finalMiddleNumber), finalSmallestNumber));
}
I noticed two problems:
using different values for splitting (middleIndex) and shifting (powerValue) (needlessly implemented by tacking on zeroes).
For productHighParts("multiplyHighestNumbers") to be closer in length to the other products, use (factor1.length() + factor2.length()) / 4 (half the average length of both factors).
this length has to be the length of the less significant part in splitString(), not the leading part.
(Note that the first two controlled statements can be combined:
if (factor1.length() <= 1 && factor2.length() <= 2).)

Java partition method

I'm trying to create a partition function that accepts three parameters: a text string, a pattern string, and an integer k.
The goal is to store the contents of the pattern of length m in a string array of k+1 fragments, where each fragment is of length m/k+1 (or as close to).
For instance if the string "ABCDEFGHIJKLMNOPQRSTUVWXYZ" is parsed to the method where k = 2
The array should look something like this [ABCDEFGHI, JKLMNOPQ, RSTUVWXYZ]
The program runs fine when m/k+1 is divided evenly, however when result produces a remainder the results are off. I’ve noticed that the errors seems to correspond with the remainder of m/k+1
This is the part of the code I'm having problems with:
public static String[] partition(String text, String pattern, int k) {
String[] fragment = new String[k+1];
int f = k+1;
int m = pattern.length();
int fragmentSize = (int)Math.floor(m/f);
int lastCharIndex;
// cannot partition evenly{
int i = 0;
while(i < f) {
// set the first partition as the largest
if(fragment[i] == fragment[0]) {
fragmentSize = (int)Math.ceil(m/f);
lastCharIndex = i * fragmentSize;
fragment[i] = pattern.substring(lastCharIndex, lastCharIndex+fragmentSize);
}
else {
fragmentSize = (int)Math.floor(m/f);
lastCharIndex = i * fragmentSize;
fragment[i] = pattern.substring(lastCharIndex, lastCharIndex+fragmentSize);
}
i++;
}
return fragment;
Using the example above the output I’m currently receiving is [ABCDEFGHI, IJKLMNOP, QRSTUVWX]
I have a feeling it has something to do with the explicit cast of fragmentSize, but I can't figure out a way around it.
Any help would be much appreciated.
Your logic is incorrect. Let's say you have 26 letters, and want 3 fragments. That makes a first fragment of 9 elements, a second of 9 elements, and a last one of 8 elements.
Your logic makes each fragments of length 8 (floor(26 / 3)), except the first one, which is of length 9 (ceil(26 / 3)). Not only that, but you add the additional letter of the first fragment to the second one.
Side note: the test if(fragment[i] == fragment[0]) should in fact be if (i == 0). And you should make your numbers double to avoid losing the decimal part.
All your operations are made with int, which means two things :
they also produce int, and you loose the decimal part
your calls to Math.ceiland Math.floor are useless : they need a double as argument but already get an int (as you pass the result of an operation involving only int), there is no floor or ceil to be made.
You should start use double when declaring f and m :
double f = (double) k+1;
double m = (double) pattern.length();

Porting '&' operator from Java to Javascript: Overflow issues

Consider the following JAVA statement:
System.out.println(3232235776l & 0xFFFFFFFE);
The output is: 3232235776
When I re-write the statement in JavaScript:
console.log(3232235776 & 0xFFFFFFFE);
The output is: -1062731520
Q. Is there a way to work around this overflow in JavaScript and get the right output?
For the sake of simplicity, I did not post the function I was converting from Java. Here it is. Please assume ipToLong and longToIp as working blackboxes in both Java and JavaScript (i.e. they do the right ip to long int conversion and vice-versa correctly, in both Java and JS, linted and unit tested).
Taken from here: https://stackoverflow.com/a/5032908/504674
Now, can someone help me convert the below Java line to JavaScript correctly?
Specifically: long maskedBase = start & mask;.
Full function to be converted:
public static List<String> range2cidrlist( String startIp, String endIp ) {
int[] CIDR2MASK = new int[] { 0x00000000, 0x80000000,
0xC0000000, 0xE0000000, 0xF0000000, 0xF8000000, 0xFC000000,
0xFE000000, 0xFF000000, 0xFF800000, 0xFFC00000, 0xFFE00000,
0xFFF00000, 0xFFF80000, 0xFFFC0000, 0xFFFE0000, 0xFFFF0000,
0xFFFF8000, 0xFFFFC000, 0xFFFFE000, 0xFFFFF000, 0xFFFFF800,
0xFFFFFC00, 0xFFFFFE00, 0xFFFFFF00, 0xFFFFFF80, 0xFFFFFFC0,
0xFFFFFFE0, 0xFFFFFFF0, 0xFFFFFFF8, 0xFFFFFFFC, 0xFFFFFFFE,
0xFFFFFFFF
};
long start = ipToLong(startIp);
long end = ipToLong(endIp);
ArrayList<String> pairs = new ArrayList<String>();
while ( end >= start ) {
byte maxsize = 32;
while ( maxsize > 0) {
long mask = CIDR2MASK[maxsize -1];
long maskedBase = start & mask;
if ( maskedBase != start ) {
break;
}
maxsize--;
}
double x = Math.log( end - start + 1) / Math.log( 2 );
byte maxdiff = (byte)( 32 - Math.floor( x ) );
if ( maxsize < maxdiff) {
maxsize = maxdiff;
}
String ip = longToIp(start);
pairs.add( ip + "/" + maxsize);
start += Math.pow( 2, (32 - maxsize) );
}
return pairs;
}
Instead of using & to remove the bit you want, you could subtract it.
long n = 3232235776L;
System.out.println(n - (n & 1)); // instead of 1 you can use ~0xFFFFFFFE
This shouldn't suffer from an overflow in your case.
Bitwise operators treat their operands as a sequence of 32 bits (zeros and ones)
says the Mozilla documentation.
You start out with a floating point value, it is converted to a 32 bit value. But because it's too big, it will overflow.
I suggest you try the following instead:
var number = 3232235776;
if (number % 2 == 1) {
number = number - 1;
}
Of course, you could write this more succinctly, but also more cryptic:
var number = 3232235776;
number = number - (number % 2);
That should be semantically equivalent for both positive and negative numbers.
Sign extension
In Java, 0xFFFFFFFE is a 32bit integer representing -2 when ANDing this with a long, it gets converted to a 64bit integer: 0xFFFF_FFFF_FFFF_FFFE, so all this effectively does is clear the last bit, i.e. round down (down, not towards zero).
I'm not sure if that's what you wanted. If it is intended, it's probably not something I would like in my codebase.
No sign extension
Here is the equivalent JavaScript code, if you intended this to happen without sign extension:
var number = 3232235776;
if (number % 2 == 1) {
number = number - 1;
}
number = number % 0x100000000; // That's 8 zeroes, i.e. keep the last 4 bytes

Difference between this Java and C code?

I tried to convert some C code to Java, but it is working slightly different. It is XOR encryption and for some data it returns the same results, so I know it is pretty close, but for some data it doesn't work exactly the same (different results).
C code (runs on x86 Windows, compiled with Borland Builder):
void Crypt(unsigned char *data,long len)
{
char key[] = "X02$B:";
ULONG crypt_ptr;
long x;
for(crypt_ptr=0,x=0;x<len;x++)
{
data[x] ^= key[crypt_ptr];
if(crypt_ptr < (sizeof(key) - 2))
key[crypt_ptr] += key[crypt_ptr + 1];
else
key[crypt_ptr] += key[0];
if(!key[crypt_ptr])
key[crypt_ptr] += (char) 1;
if(++crypt_ptr >= sizeof(key) - 1)
crypt_ptr = 0;
}
}
Java Code (which runs on the Android platform if it matters):
public static void Crypt(byte[] data,int offset,int len)
{
// EDIT: Changing this to byte[] instead of char[] seems to have fixed code
//char[] key = {'X','0','2','$','B',':'};
byte[] key = {'X','0','2','$','B',':'};
int size_of_key = 7;
int crypt_ptr;
int x;
for(crypt_ptr=0,x=0;x<len;x++)
{
data[x+offset] ^= key[crypt_ptr];
if(crypt_ptr < (size_of_key - 2))
key[crypt_ptr] += key[crypt_ptr + 1];
else
key[crypt_ptr] += key[0];
if(key[crypt_ptr] == 0)
key[crypt_ptr] += (char) 1;
if(++crypt_ptr >= size_of_key - 1)
crypt_ptr = 0;
}
}
I have confirmed that the data going in to each function is the same, and for the Java version I am passing in the correct offset value within the byte array. As mentioned, it works sometimes, so I don't think it is a major/obvious issue, more like some minor issue between signed vs unsigned values. If it helps at all, the first byte that was different was at byte 125 (index 124 if zero-based) in the data. I didn't see a pattern though, like every 125 bytes, it was pretty much random after that. The data is only 171 bytes, and I can figure out how to post as an attachment probably if needed, but I don't think it is.
I guess it's because char is 16-bit in java. So when you increment key key[crypt_ptr] += (char) 1 or add two chars key[crypt_ptr] += key[crypt_ptr + 1], it acts in different way from c (where char is 8-bit).
Try to use bytes everywhere instead of chars, just use symbol codes for initialization.
Your key values need to be 8-bit. Try
byte[] key = "X02$B:".getBytes();
Why don't you show us an example where the differences manifest themselves?
BTW, I would write:
char[] key = {'X','0','2','$','B',':', '\0'};

Categories