Hexdump.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. /* globals app */
  2. import Utils from "../Utils.js";
  3. /**
  4. * Hexdump operations.
  5. *
  6. * @author n1474335 [n1474335@gmail.com]
  7. * @copyright Crown Copyright 2016
  8. * @license Apache-2.0
  9. *
  10. * @namespace
  11. */
  12. const Hexdump = {
  13. /**
  14. * @constant
  15. * @default
  16. */
  17. WIDTH: 16,
  18. /**
  19. * @constant
  20. * @default
  21. */
  22. UPPER_CASE: false,
  23. /**
  24. * @constant
  25. * @default
  26. */
  27. INCLUDE_FINAL_LENGTH: false,
  28. /**
  29. * To Hexdump operation.
  30. *
  31. * @param {byteArray} input
  32. * @param {Object[]} args
  33. * @returns {string}
  34. */
  35. runTo: function(input, args) {
  36. const length = args[0] || Hexdump.WIDTH;
  37. const upperCase = args[1];
  38. const includeFinalLength = args[2];
  39. let output = "", padding = 2;
  40. for (let i = 0; i < input.length; i += length) {
  41. const buff = input.slice(i, i+length);
  42. let hexa = "";
  43. for (let j = 0; j < buff.length; j++) {
  44. hexa += Utils.hex(buff[j], padding) + " ";
  45. }
  46. let lineNo = Utils.hex(i, 8);
  47. if (upperCase) {
  48. hexa = hexa.toUpperCase();
  49. lineNo = lineNo.toUpperCase();
  50. }
  51. output += lineNo + " " +
  52. Utils.padRight(hexa, (length*(padding+1))) +
  53. " |" + Utils.padRight(Utils.printable(Utils.byteArrayToChars(buff)), buff.length) + "|\n";
  54. if (includeFinalLength && i+buff.length === input.length) {
  55. output += Utils.hex(i+buff.length, 8) + "\n";
  56. }
  57. }
  58. return output.slice(0, -1);
  59. },
  60. /**
  61. * From Hexdump operation.
  62. *
  63. * @param {string} input
  64. * @param {Object[]} args
  65. * @returns {byteArray}
  66. */
  67. runFrom: function(input, args) {
  68. let output = [],
  69. regex = /^\s*(?:[\dA-F]{4,16}:?)?\s*((?:[\dA-F]{2}\s){1,8}(?:\s|[\dA-F]{2}-)(?:[\dA-F]{2}\s){1,8}|(?:[\dA-F]{2}\s|[\dA-F]{4}\s)+)/igm,
  70. block, line;
  71. while ((block = regex.exec(input))) {
  72. line = Utils.fromHex(block[1].replace(/-/g, " "));
  73. for (let i = 0; i < line.length; i++) {
  74. output.push(line[i]);
  75. }
  76. }
  77. // Is this a CyberChef hexdump or is it from a different tool?
  78. const width = input.indexOf("\n");
  79. const w = (width - 13) / 4;
  80. // w should be the specified width of the hexdump and therefore a round number
  81. if (Math.floor(w) !== w || input.indexOf("\r") !== -1 || output.indexOf(13) !== -1) {
  82. if (app) app.options.attemptHighlight = false;
  83. }
  84. return output;
  85. },
  86. /**
  87. * Highlight to hexdump
  88. *
  89. * @param {Object[]} pos
  90. * @param {number} pos[].start
  91. * @param {number} pos[].end
  92. * @param {Object[]} args
  93. * @returns {Object[]} pos
  94. */
  95. highlightTo: function(pos, args) {
  96. // Calculate overall selection
  97. let w = args[0] || 16,
  98. width = 14 + (w*4),
  99. line = Math.floor(pos[0].start / w),
  100. offset = pos[0].start % w,
  101. start = 0,
  102. end = 0;
  103. pos[0].start = line*width + 10 + offset*3;
  104. line = Math.floor(pos[0].end / w);
  105. offset = pos[0].end % w;
  106. if (offset === 0) {
  107. line--;
  108. offset = w;
  109. }
  110. pos[0].end = line*width + 10 + offset*3 - 1;
  111. // Set up multiple selections for bytes
  112. let startLineNum = Math.floor(pos[0].start / width);
  113. const endLineNum = Math.floor(pos[0].end / width);
  114. if (startLineNum === endLineNum) {
  115. pos.push(pos[0]);
  116. } else {
  117. start = pos[0].start;
  118. end = (startLineNum+1) * width - w - 5;
  119. pos.push({ start: start, end: end });
  120. while (end < pos[0].end) {
  121. startLineNum++;
  122. start = startLineNum * width + 10;
  123. end = (startLineNum+1) * width - w - 5;
  124. if (end > pos[0].end) end = pos[0].end;
  125. pos.push({ start: start, end: end });
  126. }
  127. }
  128. // Set up multiple selections for ASCII
  129. let len = pos.length, lineNum = 0;
  130. start = 0;
  131. end = 0;
  132. for (let i = 1; i < len; i++) {
  133. lineNum = Math.floor(pos[i].start / width);
  134. start = (((pos[i].start - (lineNum * width)) - 10) / 3) + (width - w -2) + (lineNum * width);
  135. end = (((pos[i].end + 1 - (lineNum * width)) - 10) / 3) + (width - w -2) + (lineNum * width);
  136. pos.push({ start: start, end: end });
  137. }
  138. return pos;
  139. },
  140. /**
  141. * Highlight from hexdump
  142. *
  143. * @param {Object[]} pos
  144. * @param {number} pos[].start
  145. * @param {number} pos[].end
  146. * @param {Object[]} args
  147. * @returns {Object[]} pos
  148. */
  149. highlightFrom: function(pos, args) {
  150. const w = args[0] || 16;
  151. const width = 14 + (w*4);
  152. let line = Math.floor(pos[0].start / width);
  153. let offset = pos[0].start % width;
  154. if (offset < 10) { // In line number section
  155. pos[0].start = line*w;
  156. } else if (offset > 10+(w*3)) { // In ASCII section
  157. pos[0].start = (line+1)*w;
  158. } else { // In byte section
  159. pos[0].start = line*w + Math.floor((offset-10)/3);
  160. }
  161. line = Math.floor(pos[0].end / width);
  162. offset = pos[0].end % width;
  163. if (offset < 10) { // In line number section
  164. pos[0].end = line*w;
  165. } else if (offset > 10+(w*3)) { // In ASCII section
  166. pos[0].end = (line+1)*w;
  167. } else { // In byte section
  168. pos[0].end = line*w + Math.ceil((offset-10)/3);
  169. }
  170. return pos;
  171. },
  172. };
  173. export default Hexdump;