【问题标题】:Decoding a hex string解码十六进制字符串
【发布时间】:2018-08-21 16:39:34
【问题描述】:

这是一个更大问题的一部分,我们需要将旧应用程序移植到新设备(当然还有另一种编程语言/框架)。经过大量努力(嗅探通讯线路,对传输的数据进行逆向工程大约 2-3 周),我设法缩小了我认为包含 2 个数字的 4 个字节的范围。其中一个数字是温度读数,如 30.51、30.46 等,如第一列(表 01)所示。另一个值是浮点数,可以在 3.9 到 3.6 的范围内(甚至更低,小数点后 4 位)。 4 个十六进制字节(表 01 的第 2 列)应包含这两个值。我不得不做一些逆向工程,因为没有可用的文档也没有源代码。我设法缩小了我认为将十六进制字符串解码为2个数字的java代码部分。有人能够检查代码是否是我认为的那样吗?我不是 Java 程序员,我主要处理其他编程语言。所以我需要一些东西

  1. 附加的代码是否负责将十六进制解码为 3 个浮点数?这是最重要的
  2. 如果可能的话,重构一些该代码,以便它可以在 (https://www.compilejava.net/) 上运行。这样我就可以尝试使用不同的十六进制数字集来测试算法。
  3. “如果可能”添加一些 cmets

表01

30.51 => 01:53:4e:98
30.46 => 01:53:8e:94
30.43 => 01:53:8e:91
30.39 => 01:53:8e:8e
30.39 => 01:53:4e:8e

12.36 => 01:52:88:b1
16.01 => 01:52:c9:cf
18.65 => 01:52:ca:a5
21.14 => 01:52:8b:74

如果需要任何信息,请告诉我,因为我花了很多时间试图弄清楚这一点。如果需要,我可以记录更多数字。

顺便说一句,左边的数字是温度读数(以摄氏度为单位)。那么“可能”最终会涉及乘法以得出数字吗?我不太确定,但我想我会提到我对此的了解。

我没有时间学习 java 来解决这个问题(我们处理 java 是非常罕见的)而且我已经花了将近一个月的时间来解决这个问题。非常感谢任何帮助清除这个障碍。这是将十六进制解码为 2 个数字的 java 代码。我通过对遗留应用程序进行逆向工程得到了这个。请注意,我删除了之前发布的反编译代码,因为我刚刚知道它包含在 NDA 中。

糟糕,我犯了一个错误,没有提到最终需要将其插入 Python 程序中——正如我提到的那样,它是一个更大的 Python 项目的一部分,几乎所有这些项目我都已经编码并且运行良好。并为没有提及这一点(并忘记添加 Python 标签)深表歉意。我将不得不用 Python 重写它。

【问题讨论】:

  • 您还有表 1 的行吗?接近最大和最小相关值的蛋糕会有所帮助。此外,前两个字节的类似映射将有助于解决这些问题。
  • 您在此处提供的代码没有帮助。方法a() 看起来会返回一个温度,但它在 four 四个字节的数组上运行。方法m() 对四个字节的单个数组进行操作,但它为提供的示例输入返回大约 4.16 的值。并且考虑到该方法中的创造性部分,猜测这些值是如何计算的是没有意义的。这看起来不像是处理原始输入,而是故意混淆。
  • 反编译的代码对我来说有点可疑。特别是,我正在查看的部分如下所示:for (;;) { return d3 + d1; if ((d3 < 34.1D) && (d3 >= 34.0D)) {...。换句话说,for 循环是完全没用的,因为第一个语句是 return 语句,return 语句下面的所有内容都永远不会运行。这实际上甚至不应该编译 (see example)。无论如何,反编译器似乎无法正常工作,或代码已通过能够跳过编译检查的混淆器运行。
  • @Radiodef 肯定反编译的源代码看起来很奇怪,但我唯一的希望是它有一个小线索。任何我可以建立的东西 - 因为目前它正在走向死胡同。但鲍勃·雅各布森的回答令人兴奋……非常接近。但我需要它在整个范围内都是准确的。它必须有 +/- 0.05 deg c max
  • 你能在你的代码上运行这个 javap 程序并添加结果吗? “javap -c YourClassNameHere.class”docs.oracle.com/javase/7/docs/technotes/tools/windows/…

标签: java python hex byte decode


【解决方案1】:

反编译代码会计算温度:

正如您所怀疑的,反编译代码class a 能够计算温度。方法double a() 有算法。反编译的代码无法编译,但经过相当多的工作后,它已得到纠正(见下文),并根据您的输入和预期值准确计算温度。

结果:(使用 mapKey=77)

30.506 => 01:53:4e:98
30.460 => 01:53:8e:94
30.425 => 01:53:8e:91
30.391 => 01:53:8e:8e
30.391 => 01:53:4e:8e
12.338 => 01:52:88:b1
15.990 => 01:52:c9:cf
18.636 => 01:52:ca:a5
21.127 => 01:52:8b:74

校准:

方法a() - 现在称为calculateTemperature(),如果您选择使用它,它似乎内置了校准功能。在 4 个参数中,第一个是十六进制字符串(算法只使用了两个八位字节),其他三个可以校准结果。很难知道如何使用这些值。但是,如果您将它们保留为我在main() 中显示的默认值,那么结果温度将被正确计算。也许您知道为什么要校准结果。其中一项校准需要传递一个介于 65 和 85 之间的值(我使用的是 77)。还有另一个类似的参数会导致结果相乘(零将忽略该参数)。我发现使用 mapKey=77 产生的结果略低(但 78 略高)。我发现使用 100175 的比例使结果更接近预期值。第三个校准是 +/- 类型的校准(零会忽略参数)。

可能的电压计算

计算温度后,当温度在 33.2 和 36.0 之间时,有一些代码会分配大约 0.2-2.0 的值。我把这个电压称为电压,但不知道它是什么。对于您的输入值,温度始终低于 33.2,因此这部分代码实际上是无用的。

方法 c() 的未知含义

有一个小方法c()。对于您提供的值,它会返回一个大约 4.16 - 4.18 的值。这对你来说意味着什么吗?

工作代码:

main() 方法演示了如何从十六进制字符串中获取温度,并打印输入数据的结果。该代码在您在问题中提到的网站上运行。值得注意的是,3个校准参数不必是byte[]数组,可以更简单地理解并用作int类型。如果您对代码有任何疑问,请在 cmets(或聊天)中工作,我会向您解释。

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Meter {

private List<Double> parameterList = new ArrayList<>();     // Used by the temperature algorithm
private Map<Integer, Double> scaleMap = new HashMap<>(21);  // Used to scale (not necessarily required)

public static void main(String[] args) {
    Meter meter = new Meter();
    meter.initData();

    //30.51 => 01:53:4e:98
    //30.46 => 01:53:8e:94
    //30.43 => 01:53:8e:91
    //30.39 => 01:53:8e:8e
    //30.39 => 01:53:4e:8e

    //12.36 => 01:52:88:b1
    //16.01 => 01:52:c9:cf
    //18.65 => 01:52:ca:a5
    //21.14 => 01:52:8b:74

    // Test each of the provided hex values

    int mapKey = 77;    // 77 seemed the best value; 78 was a bit too high

    String[] values = { "01:53:4e:98", "01:53:8e:94", "01:53:8e:91", "01:53:8e:8e",
            "01:53:4e:8e", "01:52:88:b1", "01:52:c9:cf", "01:52:ca:a5", "01:52:8b:74" };

    ByteBuffer key = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(mapKey);
    ByteBuffer scale = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(0);     // A number around 100175 perhaps provides better results
    ByteBuffer offset = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(0);    // No offset

    for (int i=0; i<values.length; i++) {
        double tempC = meter.calculateTemperature(hexStringToByteArray(values[i]), key.array(), scale.array(), offset.array());
        System.out.printf("%2.3f => %s\n", tempC, values[i]);
    }

}

/**
 * Convert a hex string (which may contain the `:` character) to a byte array
 * @param hexString 4 octets of the form xx:xx:xx:xx
 * @return The byte array
 */
static byte[] hexStringToByteArray(String hexString) {
    hexString = hexString.replaceAll(":", "");
    int len = hexString.length();
    byte[] data = new byte[len / 2];
    for (int i = 0; i < len; i += 2) {
        data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
                             + Character.digit(hexString.charAt(i+1), 16));
    }
    return data;
}

/**
 * Populate the algorithm parameters (required), and the scaling factors (not necessarily required)
 */
private void initData() {

    scaleMap.put(65, 29.629);
    scaleMap.put(66, 29.660);
    scaleMap.put(67, 29.691);
    scaleMap.put(68, 29.722);
    scaleMap.put(69, 29.753);
    scaleMap.put(70, 29.784);
    scaleMap.put(71, 29.815);
    scaleMap.put(72, 29.846);
    scaleMap.put(73, 29.877);
    scaleMap.put(74, 29.908);
    scaleMap.put(75, 29.939);
    scaleMap.put(76, 29.970);
    scaleMap.put(77, 30.001);
    scaleMap.put(78, 30.032);
    scaleMap.put(79, 30.063);
    scaleMap.put(80, 30.094);
    scaleMap.put(81, 30.125);
    scaleMap.put(82, 30.156);
    scaleMap.put(83, 30.187);
    scaleMap.put(84, 30.218);
    scaleMap.put(85, 30.249);

    parameterList.add(52.94);
    parameterList.add(49.61);
    parameterList.add(46.51);
    parameterList.add(43.62);
    parameterList.add(40.94);
    parameterList.add(38.44);
    parameterList.add(36.12);
    parameterList.add(33.95);
    parameterList.add(31.93);
    parameterList.add(30.05);
    parameterList.add(28.29);
    parameterList.add(26.61);
    parameterList.add(25.05);
    parameterList.add(23.59);
    parameterList.add(22.23);
    parameterList.add(20.96);
    parameterList.add(19.76);
    parameterList.add(18.65);
    parameterList.add(17.60);
    parameterList.add(16.63);
    parameterList.add(15.71);
    parameterList.add(14.84);
    parameterList.add(14.02);
    parameterList.add(13.25);
    parameterList.add(12.53);
    parameterList.add(11.86);
    parameterList.add(11.22);
    parameterList.add(10.63);
    parameterList.add(10.07);
    parameterList.add(9.541);
    parameterList.add(9.046);
    parameterList.add(8.572);
    parameterList.add(8.126);
    parameterList.add(7.706);
    parameterList.add(7.311);
    parameterList.add(6.938);
    parameterList.add(6.588);
    parameterList.add(6.257);
    parameterList.add(5.946);
    parameterList.add(5.651);
    parameterList.add(5.374);
    parameterList.add(5.109);
    parameterList.add(4.859);
    parameterList.add(4.623);
    parameterList.add(4.400);
    parameterList.add(4.189);
    parameterList.add(3.990);
    parameterList.add(3.801);
    parameterList.add(3.623);
    parameterList.add(3.454);
    parameterList.add(3.294);
    parameterList.add(3.141);
    parameterList.add(2.996);
    parameterList.add(2.858);
    parameterList.add(2.728);
    parameterList.add(2.604);
    parameterList.add(2.487);
    parameterList.add(2.376);
    parameterList.add(2.270);
    parameterList.add(2.170);
    parameterList.add(2.075);
    parameterList.add(1.984);
    parameterList.add(1.897);
    parameterList.add(1.815);
    parameterList.add(1.737);
    parameterList.add(1.662);
    parameterList.add(1.591);
    parameterList.add(1.524);
    parameterList.add(1.459);
    parameterList.add(1.398);
    parameterList.add(1.340);
    parameterList.add(1.284);
    parameterList.add(1.231);
    parameterList.add(1.180);
    parameterList.add(1.132);
    parameterList.add(1.086);
    parameterList.add(1.042);
    parameterList.add(1.000);
    parameterList.add(0.9599);
    parameterList.add(0.9216);
    parameterList.add(0.8851);
    parameterList.add(0.8501);
    parameterList.add(0.8168);
    parameterList.add(0.7849);
    parameterList.add(0.7545);
    parameterList.add(0.7254);
    parameterList.add(0.6974);
    parameterList.add(0.6707);
    parameterList.add(0.6451);
    parameterList.add(0.6207);
    parameterList.add(0.5973);
    parameterList.add(0.5743);
    parameterList.add(0.5523);
    parameterList.add(0.5313);
    parameterList.add(0.5112);
    parameterList.add(0.4920);
    parameterList.add(0.4736);
    parameterList.add(0.4560);
    parameterList.add(0.4392);
    parameterList.add(0.4230);
    parameterList.add(0.4076);
    parameterList.add(0.3925);
    parameterList.add(0.3781);
    parameterList.add(0.3642);
    parameterList.add(0.3510);
    parameterList.add(0.3383);
    parameterList.add(0.3261);
    parameterList.add(0.3144);
    parameterList.add(0.3032);
    parameterList.add(0.2925);
    parameterList.add(0.2822);
    parameterList.add(0.2722);
    parameterList.add(0.2626);
    parameterList.add(0.2534);
    parameterList.add(0.2445);
    parameterList.add(0.2360);
    parameterList.add(0.2279);
    parameterList.add(0.2201);
    parameterList.add(0.2126);
    parameterList.add(0.2054);
    parameterList.add(0.1984);
    parameterList.add(0.1917);
    parameterList.add(0.1852);
    parameterList.add(0.1790);
    parameterList.add(0.1731);
    parameterList.add(0.1673);
    parameterList.add(0.1618);
    parameterList.add(0.1564);
    parameterList.add(0.1513);
    parameterList.add(0.1464);
    parameterList.add(0.1416);
    parameterList.add(0.1370);
    parameterList.add(0.1326);
    parameterList.add(0.1283);
    parameterList.add(0.1242);
    parameterList.add(0.1203);
    parameterList.add(0.1164);
    parameterList.add(0.1128);
    parameterList.add(0.1092);
    parameterList.add(0.1058);
    parameterList.add(0.1026);

}

/**
 * 
 * @param b1array The hex number b1:b2:b3:b4 (as a byte array)
 *            - The only bits used are b2 (6 low bits & x3f) and b3
 *            (all 8 bits & xff)
 * @param mapKey
 *            - Value from 65 to 85; if 77 then scale is 1.0; otherwise < 77
 *            or > 77 causes scaling
 * @param byte1Scale
 *            - Equal to zero (scale=1), or units in micro (10E6) where
 *            scale=value/10E6
 * @param byte1Offset
 *            - Measured in 10E6 (micro) - offset amount
 * @return The temperature in degrees Celsius
 */
public double calculateTemperature(byte[] b1array, byte[] mapKey, byte[] byte1Scale, byte[] byte1Offset) {

    double scale;
    int scaleMicroValue = ByteBuffer.wrap(byte1Scale).order(ByteOrder.LITTLE_ENDIAN).getInt();
    if (scaleMicroValue == 0) {
        scale = 1.0D;
    } else {
        scale = scaleMicroValue / 1000000.0D;
    }

    double offsetValue = ByteBuffer.wrap(byte1Offset).order(ByteOrder.LITTLE_ENDIAN).getInt() / 1000000.0D;

    /* 14 bits: b2_5 b2_4 ... b2_0 b3_7 .. b3_0 */
    byte byte2 = b1array[2];
    byte byte3 = b1array[3];
    int bitValue = (byte3 & 0xFF | (byte2 & 0x3F) << 8);
    double scaledBitValue = bitValue * scale - offsetValue;

    int key = (byte) (mapKey[0] & 0xFF);
    double mapValue = scaleMap.containsKey(key) ? scaleMap.get(key) : scaleMap.get(77);

    double param1 = 0.0;
    double param2 = parameterList.get(0);
    double result = 33.0D / scaledBitValue * (8191.0D - scaledBitValue) / mapValue;

    int i = 0;
    int j = parameterList.size();
    double minParameter = parameterList.get(j - 1);

    if (param2 < result || minParameter > result)
        return 0;

    int index = 0;
    boolean process = true;
    while (i < j && process) {
        if (result >= parameterList.get(i)) {
            if (i == 0) {
                param1 = parameterList.get(i);
                param2 = parameterList.get(i + 1);
                index = i;
                process = false;
            }
            if (process) {
                param1 = parameterList.get(i - 1);
                param2 = parameterList.get(i);
                index = i - 1;
            }
            process = false;
        }
        if (process)
            i++;
    }
    if (process) {
        index = 0;
        param2 = 0;
    }

    double voltage = 0.0;   // I don't even know if this is voltage (but it is only calculated if temp between 33.2 and 36
    double tempC = index + (result - param1) / (param2 - param1) - 40.0D;

    if ((tempC < 34.0D) && (tempC >= 33.2D)) {
        voltage = 1.95D;
    }

    while (true) {
        if ((tempC < 34.1D) && (tempC >= 34.0D)) {
            voltage = 1.881D;
        } else if ((tempC < 34.2D) && (tempC >= 34.1D)) {
            voltage = 1.805D;
        } else if ((tempC < 34.3D) && (tempC >= 34.2D)) {
            voltage = 1.71D;
        } else if ((tempC < 34.4D) && (tempC >= 34.3D)) {
            voltage = 1.615D;
        } else if ((tempC < 34.5D) && (tempC >= 34.4D)) {
            voltage = 1.52D;
        } else if ((tempC < 34.6D) && (tempC >= 34.5D)) {
            voltage = 1.4249999999999998D;
        } else if ((tempC < 34.7D) && (tempC >= 34.6D)) {
            voltage = 1.3299999999999998D;
        } else if ((tempC < 34.8D) && (tempC >= 34.7D)) {
            voltage = 1.2349999999999999D;
        } else if ((tempC < 34.9D) && (tempC >= 34.8D)) {
            voltage = 1.14D;
        } else if ((tempC < 35.0D) && (tempC >= 34.9D)) {
            voltage = 1.045D;
        } else if ((tempC < 35.1D) && (tempC >= 35.0D)) {
            voltage = 0.95D;
        } else if ((tempC < 35.2D) && (tempC >= 35.1D)) {
            voltage = 0.855D;
        } else if ((tempC < 35.3D) && (tempC >= 35.2D)) {
            voltage = 0.76D;
        } else if ((tempC < 35.4D) && (tempC >= 35.3D)) {
            voltage = 0.6649999999999999D;
        } else if ((tempC < 35.5D) && (tempC >= 35.4D)) {
            voltage = 0.57D;
        } else if ((tempC < 35.6D) && (tempC >= 35.5D)) {
            voltage = 0.475D;
        } else if ((tempC < 35.7D) && (tempC >= 35.6D)) {
            voltage = 0.38D;
        } else if ((tempC < 35.8D) && (tempC >= 35.7D)) {
            voltage = 0.285D;
        } else if ((tempC < 35.9D) && (tempC >= 35.8D)) {
            voltage = 0.19D;
        } else {
            if (tempC >= 36.0D) {
                break;
            }
            if (tempC < 35.9D) {
                break;
            }
            voltage = 0.095D;
        }
    }
    return tempC;
}

/**
 * I don't know what this function is:  It always calculates around 4.16 - 4.18
 * @param bArray The hex number b1:b2:b3:b4 (as a byte array)
 * Uses: 
 * byte0:  Low 2 bits
 * byte1:  all bits
 * byte2:  high 2 bits
 * @return I don't know the significance of this value
 */
public double m(byte[] bArray) {
    int byte2 = bArray[2];
    int byte1 = bArray[1];
    int byte0 = bArray[0];
    return 0.003077674645823156D * (((byte0 & 0x3) << 2 | (byte1 & 0xC0) >> 6) << 8
            | (byte2 & 0xC0) >> 6 | (byte1 & 0x3F) << 2);
}
}

编辑:接受十六进制字符串作为参数的方法,并输出温度

/**
 * Arg[0] is expected to be the input (a hex string)
 * The output is the temperature (printed to the console)
 * @param args One value; The hex string
 */
public static void main(String[] args) {

    Meter meter = new Meter();
    meter.initData();
    int mapKey = 77;    // Scaling factor
    ByteBuffer key = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(mapKey);
    ByteBuffer scale = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(1000180);       // No scaling
    ByteBuffer offset = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(0);    // No offset

    byte[] hexString = hexStringToByteArray(args[0]);

    double tempC = meter.calculateTemperature(hexString, key.array(), scale.array(), offset.array());
    System.out.printf("%.2f", tempC);

}

【讨论】:

  • 哇!这是我收到的最好的答案之一。在我消化全部内容之前,我要感谢你,并对你提到的问题进行一些澄清 - 1. 为什么需要校准 A) 一开始,“解码”在整个范围内都没有顺利进行,有一个嫌疑人 - 这个嫌疑人是这些值是否是原始 adc / 来自热电偶等。 2) 至于方法 c() 和 4.16 - 4.18 左右的值,这些值有点接近电压值。我没有提供电压的上限,但“满”时它在 3.985 左右/以上
  • 我今晚要上岗,明天很乐意回答您可能有的任何问题
  • 我对校准值进行了一些调整,并找到了使发布的结果更接近预期值的空间。映射键 77 似乎是最好的(虽然 78 也很接近,但导致结果有点高)但结果略低,并且通过使用 100175 左右的比例,这会略微增加值。没有问题?您可以将此代码移植到 python 吗?
  • 关于m():我希望这是电压。如果您考虑十六进制字符串(32 位),则可以在最低 14 位中找到温度。方法m() 正在使用更高的 12 位。代码中不使用高 6 位。 m() 使用与温度不同的位范围这一事实一定意味着它是电压。
  • 哇,太棒了! :) 是的,我必须将它移植到 Python。我记得在早期阶段,我可以很容易地使用所有 4 个字节来获得电压——这可以通过非常简单的代码来实现。但我确实预计在移植时会出现一些问题。我已经有一段时间不用处理位和位运算符了,也许这些技能有点生疏了。
【解决方案2】:

左起第 4 列中的 C、8 和 4 值不影响这些值。这让我认为每个值只发送 12 位信息。也许它是来自 ADC 的整数原始输出?如果是这种情况,那么您可以使用以下内容对其进行解码:

double valueOne(long input) {
    long x = input & 0xFFF;

    double m = (30.51 - 12.36) / (0xE98 - 0x8B1);
    int c = 0xE91;
    double b = 30.43;

    return m*(x-c)+b;
}

m*(x-c+b) 线性近似是为了使其尽可能准确地围绕您的操作点;您可以根据需要调整 m、c、b 值。

要处理其他值,请创建一个类似的例程来屏蔽和移位以获得正确的 12 位并提供所需的 m、c 和 b 校准值。

这是为表 1 计算的值:

3:4e:98 30.514
3:8e:94 30.466
3:8e:91 30.430
3:4e:8e 30.393
3:4e:8e 30.393

2:88:b1 12.364
2:c9:cf 15.799
2:ca:a5 18.370
2:8b:74 20.856

最低值不太正确;也许从一个与 12.36 点不同的值推断会更好(参见m 值的公式)。或者这可能是热电偶数据,它不是很线性。需要更多地了解实际电路才能对此发表评论。

【讨论】:

  • 此数据与温度传感器有关,不能排除它可能是原始输出。不幸的是,根本没有关于如何得出这些数字的文档,也没有关于传感器本身的任何文档。但是这 2 个值(电压和温度)都由这 4 个字节携带。而且我们知道大数(温度)的精度是±0.05
  • 我不熟悉 java,但如果你看到最终“if”条件的 else 部分 - 它可能包含一些线索。
  • 发布的java代码是我反汇编二进制文件后得到的......所以除非我是java专家,否则很难指出是否有一些错误/错误导致从反编译本身。
  • 同样的 4 个字节还保存了另一个值,即电压。电压应该是 3.xxxx (最大值可以是 3.985)
  • 它是什么温度传感器?我对此表示怀疑,但可能有人以前曾使用过该特定项目
【解决方案3】:

我不得不做一些逆向工程,因为没有可用的文档和源代码。

我喜欢 Java 的地方在于它可以“轻松”被反编译。

由于您要将某些东西移植到新设备上,我假设您有权反编译和查看源代码。

有几个像this one这样的网站,可以帮到你很多。

这可能不能直接解决问题,但希望您能够弄清楚他们是如何编码的:)

【讨论】:

  • 嗨,张,是的——我们可以弄乱编译后的代码,只要它是在内部完成的。我确实尝试了所有流行的反编译器,包括你提到的那个。不过感谢您的提示:) 这越来越令人兴奋,我想不遗余力地解决这个问题,但该死的时间不在我身边……哈哈。而且因为这主要涉及解码那一堆十六进制,我什至在 Quora 上发布了这个问题 - 你猜怎么着?已经好几天了,我还没有在Quora上收到任何像样的回复! :)
  • Jacobsen 非常快地接近,但它必须在整个范围内工作,因为准确性(+/-0.05)至关重要。并且还需要在这 4 个字节中提取较小的数字(3.xxxx)。我不是一个认真的数学家,此外,这只是我正在从事的少数几个项目之一 - 所以我也不能 24/7、7 小时工作......haiz
【解决方案4】:

如果您想反编译字节码,只需使用反编译器即可。您可以使用this page 或在您的IDE 中使用JD 插件。 IntelliJ IDEA 或 Eclipse 内置了 Decompiler,打开里面的 .class 文件,读取反编译后的代码即可。

如果您只想解码一些十六进制字符串,而不是字节码,请使用this website

顺便说一句,如果您真的想构建自己的十六进制字符串解码器,我认为使用一堆 if else 语句不是一个好主意。十六进制编码/解码只是移动一些位的过程。使用 &lt;&lt; 或 &gt;&gt; 运算符将使您的代码更短且更易于阅读。可能会this article 会帮助你。

【讨论】:

  • 嗨,很酷的链接。非常感谢。我早些时候尝试了一堆反编译器,但没有一个做得不错。据我了解,代码并没有被混淆,但不知何故,当使用反编译器运行时,它们会在不同的地方捕捉。可能与框架有关。我会通过链接阅读,它可能会给我急需的线索。
【解决方案5】:

我在 Quora 上看到了这篇文章。

您要求提供可以运行以在 Java 中反编译十六进制的代码。我不清楚你从哪里得到这个代码。但是我拿了上面的代码,看到最后一个函数将一个字节数组转换为一个双精度数,并使用它。您可以看到它不起作用,即使始终以错误的方式获取温度。 包括方便的调试功能来比较位值。 也许需要其他位以某种方式与这个替身进行交互。

另外,你确定你的例子都是正确的吗?通常,这里的红利是相似的,但有些却大不相同。 (运行程序!)如果这段代码是合法的,我会期望同样的红利,至少在错误范围内。那么所需要的就是调整用于乘以位的常数。

请注意,这些操作在 C++ 中是相同的。主要区别在于类型转换在 C++ 中具有更简单的语法(即没有 ByteBuffer 语法或类库)。在 Java 中,一个字节只是一个 8 位有符号整数。因为它是一个整数,所以几种隐式类型转换很方便。

这在https://www.compilejava.net/ 上运行。 要进行其他测试,只需将代码添加到 main(...) 方法即可。

对不起,我不知道为什么要粘贴双空格。

import java.nio.ByteBuffer;



public class CrazyConvertor {



//        int t = Integer.decode("0x01");

//        System.out.println("test is "+t+"\t01");

//        t = Integer.decode("0x24");

//        System.out.println("test is "+t+"\t36");



    //30.51 => 01:53:4e:98

    //30.46 => 01:53:8e:94

    //30.43 => 01:53:8e:91

    //30.39 => 01:53:8e:8e

    //30.39 => 01:53:4e:8e



    //12.36 => 01:52:88:b1

    //16.01 => 01:52:c9:cf

    //18.65 => 01:52:ca:a5

    //21.14 => 01:52:8b:74

    public static double[] temperatures;

    public static int[] encodedBytes;

    static {

        temperatures = new double[9];

        encodedBytes = new int[9];



        encodedBytes[0] = Integer.decode("0x01534e98");

        encodedBytes[1] = Integer.decode("0x01538e94");

        encodedBytes[2] = Integer.decode("0x01538e91");

        encodedBytes[3] = Integer.decode("0x01538e8e");

        encodedBytes[4] = Integer.decode("0x01534e8e");

        encodedBytes[5] = Integer.decode("0x015288b1");

        encodedBytes[6] = Integer.decode("0x0152c9cf");

        encodedBytes[7] = Integer.decode("0x0152caa5");

        encodedBytes[8] = Integer.decode("0x01528b74");



        temperatures[0] = 30.51;

        temperatures[1] = 30.46;

        temperatures[2] = 30.43;

        temperatures[3] = 30.39;

        temperatures[4] = 30.39;

        temperatures[5] = 12.36;

        temperatures[6] = 16.01;

        temperatures[7] = 18.65;

        temperatures[8] = 21.14;

    }



    static int MASK = 0x0003ffc0;



    public static void main(String[] args)

    {





        for (int k=0; k<9; k++){

            double f = fourBytesToDouble(encodedBytes[k]);

            int x = integerElegantConversion(encodedBytes[k]);

            System.out.println("Converted double            \t"+f);

            System.out.println("Converted double elegant    \t"+fourBytesElegantConversion(encodedBytes[k]));

            System.out.println("Target value\t"+temperatures[k]);

            System.out.println("Dividend\t"+temperatures[k]/f);

            System.out.println("Dividend temp by integer\t"+temperatures[k]/x);

            System.out.println();

        }



    }



    /**

     * takes four bytes

     * function identical to m below, but easier to read

     *

     * Key programmer's note: in java, a byte is a 8-bit integer

     * So this is basically dynamic type conversion from encoded bits to a double.

     *

     * @param z  and integer representing 4 bytes

     * @return

     */



    public static double fourBytesToDouble(int z){

        //read 12 bits from middle of 3 byte array

        byte[] bytes = ByteBuffer.allocate(4).putInt(z).array();



        //grab 4 bits from first byte from bits 0 thru 1 and bits 6 thru 7 of byte 2

        int x = ((bytes[1] & 0x3) << 2 | (bytes[2] & 0xC0) >> 6);

        x = x << 8;



        //grab second byte from bits  2 thru 7 of byte 1 and bits 0 thru 1 of byte 2

        int y = (bytes[2] & 0x3F) << 2 | (bytes[3] & 0xC0) >> 6;

        int r = x | y;   //or the bytes, so you obtain 12 bits; four from byte 1 and eight from byte 2

        System.out.println("  fourBytesToDouble (int)         \t"+r);

        return 0.003077674645823156D *  new Double(r);

    }



    /**

     * original reference function

     * @param paramArrayOfByte

     * @return

     */

    public static double m(byte[] paramArrayOfByte)

    {

        int i = paramArrayOfByte[2];

        int j = paramArrayOfByte[1];

        return 0.003077674645823156D * (((paramArrayOfByte[0] & 0x3) << 2 | (paramArrayOfByte[1] & 0xC0) >> 6) << 8 | (i & 0xC0) >> 6 | (j & 0x3F) << 2);

    }



    /**

     * Same task using integer masking.

     * Probably the original code that got obfuscated by compiler

     * @param x

     * @return

     */

    public static double fourBytesElegantConversion(int x){

////        ByteBuffer bb = ByteBuffer.allocate(4).put(b);

////        bb.position(0);

////        int x = (bb.getInt() << 10) >> 10;

////        System.out.println("   x\t"+fourBytesBinaryString(x));

////        System.out.println("mask\t"+fourBytesBinaryString(MASK));

//        x = x & MASK;

////        System.out.println("  x1\t"+fourBytesBinaryString(x));

//        x = x >> 6;



        x = (x & MASK) >> 6;

        System.out.println("  fourBytesElegantConversion (int)\t"+x);

        return 0.003077674645823156D *  new Double(x);

    }



    public static int integerElegantConversion(int x) {

        return (x & MASK) >> 6;

    }

    /**

     * helper function to compare bits

     * @param x

     * @return

     */

    public static String fourBytesBinaryString(int x){

        String r = Integer.toBinaryString(x);

        int extra = 32 - r.length();



        for (int i=0; i < extra; i++){

            r = "0"+r;

        }

        return r;

    }



    /**

     * helper function to compare bits

     * @param b

     * @return

     */

    public static String eightBytesBinaryString(byte[] b){

        String r = oneByteBinaryString(b[0]);

        for (int k=1; k < 8; k++)  r += oneByteBinaryString(b[k]);

        return r;

    }



    /**

     * helper function to compare bits

     * @param b

     * @return

     */

    public static String fourBytesBinaryString(byte[] b){

        String r = oneByteBinaryString(b[0]);

        for (int k=1; k < 4; k++) r += oneByteBinaryString(b[k]);

        return r;

    }



    /**

     * helper function to compare bits; helper function to above

     * @param b

     * @return

     */

    private static String oneByteBinaryString(byte b) {

        String r = Integer.toBinaryString(b);



        if (b < 0){

            r = r.substring(24);

        } else {

            int extra = 8 - r.length();



            for (int i = 0; i < extra; i++) {

                r = "0" + r;

            }

        }

        return r;

    }

}

编辑:由于复制/粘贴造成的拼写错误

【讨论】:

  • 我有一个想法。您提到了大约 4 的第二个电压。嗯,我需要将此数字乘以 1 到 3 范围内的数字才能得到正确答案。也许我们需要那个号码?它显然不在这里,但我敢打赌它在某个 4 字节块中。
  • 不知道传感器背后的原理。任何人都可以评论一下,根据设计将其乘以电压是否有意义?
  • 嗨@BAMF4bacon,太棒了!顺便说一句,您看到的代码是我通过反编译器运行后得到的代码,因为它没有可用的源代码。我正在用 Python 做这些事情,但感谢 Java 代码,非常感谢。我将逐步介绍您提供的所有内容。该数字(在您几小时前的帖子中)也应该在这 4 个字节中,因为在屏幕更新的那一刻,实际上没有其他数据正在传输(甚至没有!) - 所以是的本身就藏在那里:)
猜你喜欢
  • 2020-11-11
  • 1970-01-01
  • 2014-12-28
  • 1970-01-01
  • 2020-10-23
  • 1970-01-01
  • 2020-01-18
  • 2011-03-18
  • 2013-01-03
相关资源
最近更新 更多