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