解码十六进制字符串

R.W*_*R.W 7 python java hex byte decode

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

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

表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
Run Code Online (Sandbox Code Playgroud)

如果有任何需要的信息,请告诉我,因为我花了很多时间试图解决这个问题.如果需要,我可以记录更多数字.

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

我没有时间学习java来解决这个问题(我们处理java是非常罕见的)而且我已经花了将近一个月的时间来解决这个问题.真的很感激任何帮助,只是清除这个障碍.这是将十六进制解码为2个数字的java代码.我通过对传统应用进行逆向工程来实现这一点 请注意,我删除了之前发布的反编译代码,因为我刚刚意识到它已被NDA涵盖.

哎呀,我犯了一个错误,即没有提到最终需要将其插入到Python程序中 - 因为我提到它是一个更大的Python项目的一部分,几乎所有我编写并且工作得很好的项目.并且没有提及(并忘记添加Python标签)的巨大道歉.我将不得不用Python重写它.

Ian*_* Mc 3

反编译代码确实计算温度:

正如您所怀疑的,反编译的代码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
Run Code Online (Sandbox Code Playgroud)

校准:

方法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[]数组,并且作为 type 可能更容易理解和使用int。如果您对代码有任何疑问,请在评论(或聊天)中进行讨论,我会向您解释。

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);
}
}
Run Code Online (Sandbox Code Playgroud)

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

/**
 * 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);

}
Run Code Online (Sandbox Code Playgroud)