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