View Javadoc
1   /*******************************************************************************
2    * Copyhacked (H) 2012-2025.
3    * This program and the accompanying materials
4    * are made available under no term at all, use it like
5    * you want, but share and discuss it
6    * every time possible with every body.
7    * 
8    * Contributors:
9    *      ron190 at ymail dot com - initial implementation
10   ******************************************************************************/
11  package com.jsql.util;
12  
13  import com.jsql.util.bruter.Base16;
14  import com.jsql.util.bruter.Base58;
15  import org.apache.commons.codec.binary.Base32;
16  import org.apache.commons.lang3.ArrayUtils;
17  import org.apache.commons.lang3.StringUtils;
18  import org.apache.commons.text.StringEscapeUtils;
19  import org.apache.logging.log4j.LogManager;
20  import org.apache.logging.log4j.Logger;
21  import org.mozilla.universalchardet.UniversalDetector;
22  
23  import java.awt.*;
24  import java.io.ByteArrayOutputStream;
25  import java.io.IOException;
26  import java.io.OutputStream;
27  import java.net.URLDecoder;
28  import java.net.URLEncoder;
29  import java.nio.charset.StandardCharsets;
30  import java.util.ArrayList;
31  import java.util.Base64;
32  import java.util.List;
33  import java.util.zip.DeflaterOutputStream;
34  import java.util.zip.InflaterOutputStream;
35  
36  /**
37   * Utility class adding String operations like join() which are not
38   * part of standard JVM.
39   */
40  public final class StringUtil {
41      
42      /**
43       * Log4j logger sent to view.
44       */
45      private static final Logger LOGGER = LogManager.getRootLogger();
46      
47      // Define the schema of conversion to html entities
48      private static final CharEncoder DECIMAL_HTML_ENCODER = new CharEncoder("&#", ";", 10);
49      public static final String GET = "GET";
50      public static final String POST = "POST";
51      public static final String INFORMATION_SCHEMA = "information_schema";
52      public static final String APP_NAME = "jSQL Injection";
53  
54      /**
55       * This utility class defines a schema used to encode a text into a specialized
56       * representation
57       */
58      private static class CharEncoder {
59          
60          private final String prefix;
61          private final String suffix;
62          private final int radix;
63          
64          public CharEncoder(String prefix, String suffix, int radix) {
65              this.prefix = prefix;
66              this.suffix = suffix;
67              this.radix = radix;
68          }
69          
70          protected void encode(char c, StringBuilder buff) {
71              buff
72              .append(this.prefix)
73              .append(Integer.toString(c, this.radix))
74              .append(this.suffix);
75          }
76      }
77  
78      private StringUtil() {
79          // Utility class
80      }
81      
82      /**
83       * Convert special characters like Chinese and Arabic letters to the corresponding html entities.
84       * @param text string to encode
85       * @return string encoded in html entities
86       */
87      public static String toHtmlDecimal(String text) {
88          return StringUtil.encode(text);
89      }
90      
91      /**
92       * Non-trivial methods to convert unicode characters to html entities.
93       * @param text string to encode
94       * @return string representation using the encoder schema
95       */
96      private static String encode(String text) {
97          var buff = new StringBuilder();
98          for (var i = 0 ; i < text.length() ; i++) {
99              if (text.charAt(i) > 128) {
100                 StringUtil.DECIMAL_HTML_ENCODER.encode(text.charAt(i), buff);
101             } else {
102                 buff.append(text.charAt(i));
103             }
104         }
105         return buff.toString();
106     }
107 
108     /**
109      * Convert a hexadecimal String to String.
110      * @param hex Hexadecimal String to convert
111      * @return The string converted from hex
112      */
113     public static String hexstr(String hex) {
114         var bytes = new byte[hex.length() / 2];
115         for (var i = 0 ; i < bytes.length ; i++) {
116             bytes[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16);
117         }
118         return new String(bytes, StandardCharsets.UTF_8);
119     }
120     
121     public static boolean isUtf8(String text) {
122         if (text == null) {
123             return false;
124         }
125         
126         var detector = new UniversalDetector(null);
127         detector.handleData(text.getBytes(StandardCharsets.UTF_8), 0, text.length() - 1);
128         detector.dataEnd();
129         String encoding = detector.getDetectedCharset();
130         return encoding != null;
131     }
132     
133     public static String detectUtf8(String text) {
134         if (text == null) {
135             return StringUtils.EMPTY;
136         }
137         
138         String encoding = null;
139         
140         // ArrayIndexOutOfBoundsException on handleData()
141         try {
142             var detector = new UniversalDetector(null);
143             detector.handleData(text.getBytes(StandardCharsets.UTF_8), 0, text.length() - 1);
144             detector.dataEnd();
145             encoding = detector.getDetectedCharset();
146             
147         } catch (ArrayIndexOutOfBoundsException e) {
148             LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
149         }
150         
151         String result = text;
152         if (encoding != null) {
153             result = new String(text.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
154         }
155         return result;
156     }
157     
158     public static String base32Encode(String s) {
159         var base32 = new Base32();
160         return base32.encodeToString(StringUtil.getBytesUtf8(s));
161     }
162     
163     public static String base32Decode(String s) {
164         var base32 = new Base32();
165         return StringUtil.newStringUtf8(base32.decode(s));
166     }
167     
168     public static String base58Encode(String s) {
169         return Base58.encode(StringUtil.getBytesUtf8(s));
170     }
171     
172     public static String base58Decode(String s) {
173         return StringUtil.newStringUtf8(Base58.decode(s));
174     }
175     
176     public static String base16Encode(String s) {
177         var base16 = new Base16();
178         return base16.encodeToString(StringUtil.getBytesUtf8(s));
179     }
180     
181     public static String base16Decode(String s) {
182         var base16 = new Base16();
183         return StringUtil.newStringUtf8(base16.decode(s));
184     }
185 
186     public static String base64Decode(String s) {
187         return StringUtil.newStringUtf8(Base64.getDecoder().decode(s));
188     }
189 
190     public static String base64Encode(String s) {
191         return Base64.getEncoder().encodeToString(StringUtil.getBytesUtf8(s));
192     }
193 
194     public static String toHex(String text) {
195         return StringUtil.encodeHexString(text.getBytes(StandardCharsets.UTF_8));
196     }
197     
198     public static String fromHex(String text) {
199         byte[] hex = StringUtil.decodeHexString(text);
200         return new String(hex, StandardCharsets.UTF_8);
201     }
202     
203     public static String toHexZip(String text) throws IOException {
204         byte[] zip = StringUtil.compress(text);
205         return StringUtil.encodeHexString(zip);
206     }
207 
208     public static String fromHexZip(String text) throws IOException {
209         return new String(StringUtil.decompress(StringUtil.decodeHexString(text)), StandardCharsets.UTF_8);
210     }
211     
212     public static String toBase64Zip(String text) throws IOException {
213         return new String(Base64.getEncoder().encode(StringUtil.compress(text)));
214     }
215     
216     public static String fromBase64Zip(String text) throws IOException {
217         byte[] decompressedBArray = StringUtil.decompress(Base64.getDecoder().decode(text));
218         return new String(decompressedBArray, StandardCharsets.UTF_8);
219     }
220     
221     public static String toHtml(String text) {
222         return StringEscapeUtils.escapeHtml4(text);
223     }
224     
225     public static String fromHtml(String text) {
226         return StringEscapeUtils.unescapeHtml4(text);
227     }
228     
229     public static String toUrl(String text) {
230         return URLEncoder.encode(text, StandardCharsets.UTF_8);
231     }
232     
233     public static String fromUrl(String text) {
234         return URLDecoder.decode(text, StandardCharsets.UTF_8);
235     }
236     
237     public static String cleanSql(String query) {
238         return StringUtil.removeSqlComment(query)
239             .replaceAll("(?s)([^\\s\\w])(\\s+)", "$1")  // Remove spaces after a word
240             .replaceAll("(?s)(\\s+)([^\\s\\w])", "$2")  // Remove spaces before a word
241             .replaceAll("(?s)\\s+", " ")  // Replace spaces
242             .trim();
243     }
244 
245     /**
246      * Remove SQL comments except tamper /**\/ /*!...*\/
247      * Negative lookahead: don't match tamper empty comment /**\/ or version comment /*!...*\/
248      * JavaScript: (?!\/\*!.*\*\/|\/\*\*\/)\/\*.*\*\/
249      */
250     public static String removeSqlComment(String query) {
251         return query.replaceAll(
252             "(?s)(?!/\\*\\*/|/\\*!.*\\*/)/\\*.*?\\*/",
253             StringUtils.EMPTY
254         );
255     }
256 
257     public static String formatReport(Color color, String text) {
258         return String.format(
259             "<span style=color:rgb(%s,%s,%s)>%s</span>",
260             color.getRed(),
261             color.getGreen(),
262             color.getBlue(),
263             text
264         );
265     }
266 
267 
268     // Utils
269 
270     private static byte[] compress(String text) throws IOException {
271         ByteArrayOutputStream os = new ByteArrayOutputStream();
272         try (DeflaterOutputStream dos = new DeflaterOutputStream(os)) {
273             dos.write(text.getBytes());
274         }
275         return os.toByteArray();
276     }
277 
278     private static byte[] decompress(byte[] compressedTxt) throws IOException {
279         ByteArrayOutputStream os = new ByteArrayOutputStream();
280         try (OutputStream ios = new InflaterOutputStream(os)) {
281             ios.write(compressedTxt);
282         }
283         return os.toByteArray();
284     }
285 
286     private static byte hexToByte(String hexString) {
287         int firstDigit = StringUtil.toDigit(hexString.charAt(0));
288         int secondDigit = StringUtil.toDigit(hexString.charAt(1));
289         return (byte) ((firstDigit << 4) + secondDigit);
290     }
291 
292     private static int toDigit(char hexChar) {
293         int digit = Character.digit(hexChar, 16);
294         if (digit == -1) {
295             throw new IllegalArgumentException("Invalid Hexadecimal Character: "+ hexChar);
296         }
297         return digit;
298     }
299 
300     private static String byteToHex(byte num) {
301         char[] hexDigits = new char[2];
302         hexDigits[0] = Character.forDigit((num >> 4) & 0xF, 16);
303         hexDigits[1] = Character.forDigit((num & 0xF), 16);
304         return new String(hexDigits);
305     }
306 
307     private static String encodeHexString(byte[] byteArray) {
308         StringBuilder hexStringBuffer = new StringBuilder();
309         for (byte b : byteArray) {
310             hexStringBuffer.append(StringUtil.byteToHex(b));
311         }
312         return hexStringBuffer.toString();
313     }
314 
315     private static byte[] decodeHexString(String hexString) {
316         if (hexString.length() % 2 == 1) {
317             throw new IllegalArgumentException("Invalid hexadecimal String supplied.");
318         }
319         byte[] bytes = new byte[hexString.length() / 2];
320         for (int i = 0; i < hexString.length(); i += 2) {
321             bytes[i / 2] = StringUtil.hexToByte(hexString.substring(i, i + 2));
322         }
323         return bytes;
324     }
325 
326     private static byte[] getBytesUtf8(String string) {
327         return string == null ? null : string.getBytes(StandardCharsets.UTF_8);
328     }
329 
330     private static String newStringUtf8(byte[] bytes) {
331         return bytes == null ? null : new String(bytes, StandardCharsets.UTF_8);
332     }
333 
334     public static byte[] xor(byte[] plaintext, int key) {
335         var ciphertext = new byte[plaintext.length];
336         for (var i = 0; i < plaintext.length; i++) {
337             ciphertext[i] = (byte) (plaintext[i] ^ (key >>> (8 * (i % 4))));
338         }
339         return ciphertext;
340     }
341 
342     public static List<String> toHexChunks(byte[] fileData) {
343         StringBuilder hexString = new StringBuilder();
344         for (byte b : fileData) {
345             hexString.append(String.format("%02X", b));
346         }
347         int chunkSize = 900; // 450 bytes = 900 hex characters
348         List<String> chunks = new ArrayList<>();
349         for (int i = 0; i < hexString.length(); i += chunkSize) {
350             int endIndex = Math.min(i + chunkSize, hexString.length());
351             chunks.add(hexString.substring(i, endIndex));
352         }
353         return chunks;
354     }
355 
356     public static byte[] uncloak(byte[] fileData) {
357         fileData = StringUtil.xor(fileData, 353837730);
358         ArrayUtils.reverse(fileData);
359         return fileData;
360     }
361 }