Adding data to zip file header - java

I am trying to add data to the zip file header field. Specifically into the extra field specified in the zip header using java. Is it possible ?
public class test {
public static void main(String[] args) {
String path = "finalName.zip";
RandomAccessFile file = null;
try {
file = new RandomAccessFile(path, "rw");
byte[] buf = new byte[(int) file.length() / 1024];
file.read(buf);
byte[] newHeader = "NEW DATA".getBytes();
System.out.println("Original zip : " + bytesToHex(buf));
System.out.println("newHeader : " + bytesToHex(newHeader));
int length = newHeader.length;
buf[28] = (byte) length;
byte[] firstPart = Arrays.copyOf(buf, 30);
byte[] endPart = Arrays.copyOfRange(buf, 30, buf.length );
System.out.println("firstPart zip original : " + bytesToHex(firstPart));
System.out.println("endPart zip original : " + bytesToHex(endPart));
byte[] firstPartAndHeader = new byte[30 + length];
System.arraycopy(firstPart, 0, firstPartAndHeader, 0, firstPart.length);
System.arraycopy(newHeader, 0, firstPartAndHeader, firstPart.length, newHeader.length);
System.out.println("firstPartAndHeader zip original : " + bytesToHex(firstPartAndHeader));
byte[] combined = new byte[firstPartAndHeader.length + endPart.length];
System.arraycopy(firstPartAndHeader, 0, combined, 0, firstPartAndHeader.length);
System.arraycopy(endPart, 0, combined, firstPartAndHeader.length, endPart.length);
System.out.println("combined zip original : " + bytesToHex(combined));
file.seek(0);
file.write(combined);
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private final static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for ( int j = 0; j < bytes.length; j++ ) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
}
The way I did is corrupting the file. I have added the length of the extra header in byte 28 and the extra header from byte 30. Is there something more that needs to be done ?

Related

Same code and same data but output different contents?

I have below code which is used to write the list array into a dat file , I ran on OnePlus2 and the IDE is Android Studio 1.3.
private void writeToFile(List<Short> list) throws IOException {
String stringTransform = transform(list);
String str = new String(stringTransform.getBytes(), "ascii");
byte[] bytes = new byte[str.length() / 8];
char chatAt;
for (int i = 0; i < str.length() / 8; i++) {
for (int j = 0; j < 8; j++) {
chatAt = str.charAt(i * 8 + j);
if (chatAt == '1') {
byte b = (byte) (0x80 >> j);
bytes[i] = (byte) (bytes[i] | b);
}
}
}
FileOutputStream fos = new FileOutputStream("/sdcard/1.dat");
fos.write(bytes);
fos.close();
fos.flush();
}
private String transform(List<Short> list) {
StringBuilder sb = new StringBuilder(list.size());
for (Short integer : list) {
sb.append(integer);
}
return sb.toString();
}
However , I input the same data in different time , and the dat file which is generated will show different content , as the pictures show:
This is not about your code. This is about program and encoding you using. Try to change encoding in your editor. If it is binary file I would recommend sublimetext 3 with HexViewer plugin:

Strong changes a WAV file when you change one bit. Steganography

encode_text() function implements the LSB coding method. First recorded message length, then the message itself.
numberOfBits number of bits which are allocated for writing messages. Write to one bit of a byte or two or three ...
This method of encoding using one bit for the record, the sound changes should not be noticeable to the ear. As well as changes in the image to the eye. But this is not the case. For some reason, at the beginning of the sound is changed to "noise". That should not be.
read() and save() OK, if you read the data file and then burn them there are no changes.
The question is what is wrong with encode_text() function. Or maybe I did something wrong?
import java.io.*;
import java.util.Arrays;
public class wavIO
{
private String myPath;
private long myChunkSize;
private long mySubChunk1Size;
private int myFormat;
private long myChannels;
private long mySampleRate;
private long myByteRate;
private int myBlockAlign;
private int myBitsPerSample;
private long myDataSize;
public byte[] myData;
public String getPath()
{
return myPath;
}
public void setPath(String newPath)
{
myPath = newPath;
}
public wavIO()
{
myPath = "";
}
public wavIO(String tmpPath)
{
myPath = tmpPath;
}
// read a wav file into this class
public boolean read()
{
DataInputStream inFile = null;
myData = null;
byte[] tmpLong = new byte[4];
byte[] tmpInt = new byte[2];
try
{
inFile = new DataInputStream(new FileInputStream(myPath));
//System.out.println("Reading wav file...\n"); // for debugging only
String chunkID = "" + (char)inFile.readByte() + (char)inFile.readByte() + (char)inFile.readByte() + (char)inFile.readByte();
inFile.read(tmpLong); // read the ChunkSize
myChunkSize = byteArrayToLong(tmpLong);
String format = "" + (char)inFile.readByte() + (char)inFile.readByte() + (char)inFile.readByte() + (char)inFile.readByte();
// print what we've read so far
//System.out.println("chunkID:" + chunkID + " chunk1Size:" + myChunkSize + " format:" + format); // for debugging only
String subChunk1ID = "" + (char)inFile.readByte() + (char)inFile.readByte() + (char)inFile.readByte() + (char)inFile.readByte();
inFile.read(tmpLong); // read the SubChunk1Size
mySubChunk1Size = byteArrayToLong(tmpLong);
inFile.read(tmpInt); // read the audio format. This should be 1 for PCM
myFormat = byteArrayToInt(tmpInt);
inFile.read(tmpInt); // read the # of channels (1 or 2)
myChannels = byteArrayToInt(tmpInt);
inFile.read(tmpLong); // read the samplerate
mySampleRate = byteArrayToLong(tmpLong);
inFile.read(tmpLong); // read the byterate
myByteRate = byteArrayToLong(tmpLong);
inFile.read(tmpInt); // read the blockalign
myBlockAlign = byteArrayToInt(tmpInt);
inFile.read(tmpInt); // read the bitspersample
myBitsPerSample = byteArrayToInt(tmpInt);
// print what we've read so far
//System.out.println("SubChunk1ID:" + subChunk1ID + " SubChunk1Size:" + mySubChunk1Size + " AudioFormat:" + myFormat + " Channels:" + myChannels + " SampleRate:" + mySampleRate);
// read the data chunk header - reading this IS necessary, because not all wav files will have the data chunk here - for now, we're just assuming that the data chunk is here
String dataChunkID = "" + (char)inFile.readByte() + (char)inFile.readByte() + (char)inFile.readByte() + (char)inFile.readByte();
inFile.read(tmpLong); // read the size of the data
myDataSize = byteArrayToLong(tmpLong);
// read the data chunk
myData = new byte[(int)myDataSize];
inFile.read(myData);
// close the input stream
inFile.close();
}
catch(Exception e)
{
return false;
}
return true; // this should probably be something more descriptive
}
// write out the wav file
public boolean save(String outputPath)
{
try
{
//DataOutputStream outFile = new DataOutputStream(new FileOutputStream(myPath));
DataOutputStream outFile = new DataOutputStream(new FileOutputStream(outputPath));
// write the wav file per the wav file format
outFile.writeBytes("RIFF"); // 00 - RIFF
outFile.write(intToByteArray((int)myChunkSize), 0, 4); // 04 - how big is the rest of this file?
outFile.writeBytes("WAVE"); // 08 - WAVE
outFile.writeBytes("fmt "); // 12 - fmt
outFile.write(intToByteArray((int)mySubChunk1Size), 0, 4); // 16 - size of this chunk
outFile.write(shortToByteArray((short)myFormat), 0, 2); // 20 - what is the audio format? 1 for PCM = Pulse Code Modulation
outFile.write(shortToByteArray((short)myChannels), 0, 2); // 22 - mono or stereo? 1 or 2? (or 5 or ???)
outFile.write(intToByteArray((int)mySampleRate), 0, 4); // 24 - samples per second (numbers per second)
outFile.write(intToByteArray((int)myByteRate), 0, 4); // 28 - bytes per second
outFile.write(shortToByteArray((short)myBlockAlign), 0, 2); // 32 - # of bytes in one sample, for all channels
outFile.write(shortToByteArray((short)myBitsPerSample), 0, 2); // 34 - how many bits in a sample(number)? usually 16 or 24
outFile.writeBytes("data"); // 36 - data
outFile.write(intToByteArray((int)myDataSize), 0, 4); // 40 - how big is this data chunk
outFile.write(myData); // 44 - the actual data itself - just a long string of numbers
}
catch(Exception e)
{
System.out.println(e.getMessage());
return false;
}
return true;
}
// return a printable summary of the wav file
public String getSummary()
{
//String newline = System.getProperty("line.separator");
String newline = "<br>";
String summary = "<html>Format: " + myFormat + newline + "Channels: " + myChannels + newline + "SampleRate: " + mySampleRate + newline + "ByteRate: " + myByteRate + newline + "BlockAlign: " + myBlockAlign + newline + "BitsPerSample: " + myBitsPerSample + newline + "DataSize: " + myDataSize + "</html>";
return summary;
}
// ===========================
// CONVERT BYTES TO JAVA TYPES
// ===========================
// these two routines convert a byte array to a unsigned short
public static int byteArrayToInt(byte[] b)
{
int start = 0;
int low = b[start] & 0xff;
int high = b[start+1] & 0xff;
return (int)( high << 8 | low );
}
// these two routines convert a byte array to an unsigned integer
public static long byteArrayToLong(byte[] b)
{
int start = 0;
int i = 0;
int len = 4;
int cnt = 0;
byte[] tmp = new byte[len];
for (i = start; i < (start + len); i++)
{
tmp[cnt] = b[i];
cnt++;
}
long accum = 0;
i = 0;
for ( int shiftBy = 0; shiftBy < 32; shiftBy += 8 )
{
accum |= ( (long)( tmp[i] & 0xff ) ) << shiftBy;
i++;
}
return accum;
}
// ===========================
// CONVERT JAVA TYPES TO BYTES
// ===========================
// returns a byte array of length 4
private static byte[] intToByteArray(int i)
{
byte[] b = new byte[4];
b[0] = (byte) (i & 0x00FF);
b[1] = (byte) ((i >> 8) & 0x000000FF);
b[2] = (byte) ((i >> 16) & 0x000000FF);
b[3] = (byte) ((i >> 24) & 0x000000FF);
return b;
}
// convert a short to a byte array
public static byte[] shortToByteArray(short data)
{
return new byte[]{(byte)(data & 0xff),(byte)((data >>> 8) & 0xff)};
}
public void encode(String text, int numberOfBits)
{
byte[] byteMessage = text.getBytes();
byte[] messageLength = bit_conversion(byteMessage.length);
encodeText(messageLength, myData, 0, numberOfBits);
encodeText(byteMessage, myData, 32, numberOfBits);
}
private void encodeText(byte[] addition, byte[] byteDataInputWav, int offset, int numberOfBits)
{
if(addition.length + offset > byteDataInputWav.length)
{
System.out.println("File not long enough!");
}
else
{
for(int i=0; i<addition.length; ++i)
{
int add = addition[i];
for(int bit=7; bit>=0; --bit, ++offset)
{
int b = (add >>> bit) & 1;
byteDataInputWav[offset] = (byte)((byteDataInputWav[offset] & numberOfBits) | b );
}
}
}
}
private byte[] bit_conversion(int i)
{
byte byte3 = (byte)((i & 0xFF000000) >>> 24);
byte byte2 = (byte)((i & 0x00FF0000) >>> 16);
byte byte1 = (byte)((i & 0x0000FF00) >>> 8 );
byte byte0 = (byte)((i & 0x000000FF) );
return(new byte[]{byte3,byte2,byte1,byte0});
}
public String decode(String inputPath)
{
byte[] byteDataOutputWav = myData;
int length = 0;
int offset2 = 32;
for(int i=0; i<32; ++i)
{
length = (length << 1) | (byteDataOutputWav[i] & 1);
}
byte[] result = new byte[length];
for(int b=0; b<result.length; ++b )
{
for(int i=0; i<8; ++i, ++offset2)
{
result[b] = (byte)((result[b] << 1) | (byteDataOutputWav[offset2] & 1));
}
}
return new String(result);
}
}
You're modifying the least significant bit of every byte. So you are introducing noise of only ~ -48dB re Full Scale, regardless of how many bits one sample has in your sound file. This would be clearly Audible in silence, which I guess is what you mean with "At the beginning of the sound".
You probably meant to modify only the least significant bit of the usual 16 bit which would make only noise of ~ -96dB re Full Scale.
If you modifyied only the LSB of 16 bit samples, then your noise would be much much softer. But be aware that it would still be perceptible to normal hearing people in silent parts of the sound file on good equipment at loud playback volumes. It would also be fairly obvious to anyone looking at your sound file with an audio editor or similar. For effective steganography, you would have to (at least) adapt your coding to the time-dependent sound levels.

File Hash Generator in Command Prompt using File Path

I have the following Java code to generate hashes based on input text.
package main;
import java.util.Scanner;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class String_Hash_Generator {
public static void main(String args[]) throws NoSuchAlgorithmException {
Scanner inputScanner = new Scanner(System.in);
System.out.print("Input: ");
String input = inputScanner.next();
/* MD2 */
MessageDigest objMD2 = MessageDigest.getInstance("MD2");
byte[] bytMD2 = objMD2.digest(input.getBytes());
BigInteger intNumMD2 = new BigInteger(1, bytMD2);
String hcMD2 = intNumMD2.toString(16);
while (hcMD2.length() < 32) {
hcMD2 = "0" + hcMD2;
}
/* MD5 */
MessageDigest objMD5 = MessageDigest.getInstance("MD5");
byte[] bytMD5 = objMD5.digest(input.getBytes());
BigInteger intNumMD5 = new BigInteger(1, bytMD5);
String hcMD5 = intNumMD5.toString(16);
while (hcMD5.length() < 32) {
hcMD5 = "0" + hcMD5;
}
/* SHA-1 */
MessageDigest objSHA1 = MessageDigest.getInstance("SHA-1");
byte[] bytSHA1 = objSHA1.digest(input.getBytes());
BigInteger intNumSHA1 = new BigInteger(1, bytSHA1);
String hcSHA1 = intNumSHA1.toString(16);
while (hcSHA1.length() < 40) {
hcSHA1 = "0" + hcSHA1;
}
/* SHA-256 */
MessageDigest objSHA256 = MessageDigest.getInstance("SHA-256");
byte[] bytSHA256 = objSHA256.digest(input.getBytes());
BigInteger intNumSHA256 = new BigInteger(1, bytSHA256);
String hcSHA256 = intNumSHA256.toString(16);
while (hcSHA256.length() < 64) {
hcSHA256 = "0" + hcSHA256;
}
/* SHA-384 */
MessageDigest objSHA384 = MessageDigest.getInstance("SHA-384");
byte[] bytSHA384 = objSHA384.digest(input.getBytes());
BigInteger intNumSHA384 = new BigInteger(1, bytSHA384);
String hcSHA384 = intNumSHA384.toString(16);
while (hcSHA384.length() < 96) {
hcSHA384 = "0" + hcSHA384;
}
/* SHA-512 */
MessageDigest objSHA512 = MessageDigest.getInstance("SHA-512");
byte[] bytSHA512 = objSHA512.digest(input.getBytes());
BigInteger intNumSHA512 = new BigInteger(1, bytSHA512);
String hcSHA512 = intNumSHA512.toString(16);
while (hcSHA512.length() < 128) {
hcSHA512 = "0" + hcSHA512;
}
System.out.println("\nMD2: " + hcMD2
+ "\nMD5: " + hcMD5
+ "\nSHA-1: " + hcSHA1
+ "\nSHA-256: " + hcSHA256
+ "\nSHA-384: " + hcSHA384
+ "\nSHA-512: " + hcSHA512);
}
}
The input needs to be Scanner, because it is essential that it is run in a Command Prompt.
How could a file hash generator be created that takes the file path, such as C:\Program Files\WinRAR\Rar.exe and generates the hashes (MD2, MD5, SHA-1, SHA-256, SHA-384, SHA-512)?
Edit: The only solutions I have been able to find only use the file's name, not the entire path.
First, you need a mechanism to print a byte[] as hex. From the top answer to How to convert a byte array to a hex string in Java? you can do,
final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
Then, you might use Files.readAllBytes(Path) to read your file(s). Finally, iterate an array of hash algorithms to calculate each hash. Something like,
public static void main(String args[]) {
Scanner inputScanner = new Scanner(System.in);
String[] hashAlgos = { "MD2", "MD5", "SHA-1", "SHA-256", "SHA-384", "SHA-512" };
System.out.print("Input: ");
String input = inputScanner.next();
try {
byte[] fileContents = Files.readAllBytes(new File(input).toPath());
for (String algo : hashAlgos) {
MessageDigest md = MessageDigest.getInstance(algo);
byte[] hash = md.digest(fileContents);
System.out.printf("%s %s%n", algo, bytesToHex(hash));
}
} catch (NoSuchAlgorithmException | IOException e) {
e.printStackTrace();
}
}

Potential use for data that contains its own hash?

I was writing some code to simulate the proof-of-work done by the Bitcoin network while generating blocks when I suddenly became curious: How can you create a datum that contains it's own hash?
Just for fun, I wrote a program that attempts to create data that contains it's own hash. 4 random bytes are generated, then a nonce is added at the end and the whole value is hashed with CRC32. The nonce in incremented and the process repeats until the program finds a hash that matches the original 4 bytes.
Note: The nonce can increase indefinitely.
Here's an example of the output after approx 1,980,000,000 attempts:
Found a match!
Data: 7a73a2d4ab833876
Original hash: 7a73a2d4 new hash: 7a73a2d4
Is there a potential use for this?
package selfhash;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.zip.CRC32;
import java.util.zip.Checksum;
/**
*
* #author dylan
*/
public class SelfHash {
static byte[] data;
static byte[] hash = new byte[4];
public static void main(String[] args) {
// TODO code application logic here
SecureRandom random = new SecureRandom();
random.nextBytes(hash);
data = new byte[hash.length + 1];
System.arraycopy(hash, 0, data, 0, hash.length);
long c = 0;
while (true) {
recalculateData();
byte[] dataHash = crc32AsByteArray(data);
if (c % 10000000 == 0) {
System.out.println("Calculated " + c + " hashes");
System.out.println("Data: " + byteArrayToHex(data));
System.out.println("Original hash: " + byteArrayToHex(hash) + " new hash: " + byteArrayToHex(dataHash));
}
if (Arrays.equals(hash, dataHash)) {
System.out.println("Found a match!");
System.out.println("Data: " + byteArrayToHex(data));
System.out.println("Original hash: " + byteArrayToHex(hash) + " new hash: " + byteArrayToHex(dataHash));
break;
}
c++;
}
}
public static void recalculateData() {
int position = hash.length;
while (true) {
int valueAtPosition = unsignedToBytes(data[position]);
if (valueAtPosition == 255) {
//increase size of data
if (position == data.length-1) {
byte[] newData = new byte[data.length + 1];
System.arraycopy(data, 0, newData, 0, data.length);
data = newData;
}
data[position] = (byte) (0);
position++;
} else {
data[position] = (byte) (valueAtPosition + 1);
break;
}
}
}
public static byte[] hexToByteArray(String hexString) {
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;
}
private static final char[] BYTE2HEX = ("000102030405060708090A0B0C0D0E0F"
+ "101112131415161718191A1B1C1D1E1F"
+ "202122232425262728292A2B2C2D2E2F"
+ "303132333435363738393A3B3C3D3E3F"
+ "404142434445464748494A4B4C4D4E4F"
+ "505152535455565758595A5B5C5D5E5F"
+ "606162636465666768696A6B6C6D6E6F"
+ "707172737475767778797A7B7C7D7E7F"
+ "808182838485868788898A8B8C8D8E8F"
+ "909192939495969798999A9B9C9D9E9F"
+ "A0A1A2A3A4A5A6A7A8A9AAABACADAEAF"
+ "B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF"
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"
+ "D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF"
+ "E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF"
+ "F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF").toLowerCase().toCharArray();
;
public static String byteArrayToHex(byte[] bytes) {
final int len = bytes.length;
final char[] chars = new char[len << 1];
int hexIndex;
int idx = 0;
int ofs = 0;
while (ofs < len) {
hexIndex = (bytes[ofs++] & 0xFF) << 1;
chars[idx++] = BYTE2HEX[hexIndex++];
chars[idx++] = BYTE2HEX[hexIndex];
}
return new String(chars);
}
public static String sha256AsHexString(byte[] bytes) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
return byteArrayToHex(digest.digest(bytes));
} catch (Exception e) {
throw new Error(e);
}
}
public static byte[] sha256AsByteArray(byte[] bytes) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
return digest.digest(bytes);
} catch (Exception e) {
throw new Error(e);
}
}
public static byte[] crc32AsByteArray(byte[] bytes) {
Checksum checksum = new CRC32();
checksum.update(bytes, 0, bytes.length);
long value = checksum.getValue();
byte[] resultExcess = ByteBuffer.allocate(8).putLong(value).array();
byte[] result = new byte[4];
System.arraycopy(resultExcess, 4, result, 0, 4);
return result;
}
public static int unsignedToBytes(byte b) {
return b & 0xFF;
}
}
I can't think of a use.
CRC's are linear, and so the equations can be solved very fast to get the second four bytes. You don't need two billion trials to find it. See spoof.c.

Convert UUID to hex string and vice versa

A UUID in the form of "b2f0da40ec2c11e00000242d50cf1fbf" has been transformed (see the following code segment) into a hex string as 6232663064613430656332633131653030303030323432643530636631666266. I want to code a reverse routine and get it back to the original format as in "b2f0...", but had a hard time to do so, any help?
byte[] bytes = uuid.getBytes("UTF-8");
StringBuilder hex = new StringBuilder(bytes.length* 2);
Formatter fmt = new Formatter(hex);
for (byte b : bytes)
fmt.format("%x", b);
final String input = "6232663064613430656332633131653030303030323432643530636631666266";
System.out.println("input: " + input);
final StringBuilder result = new StringBuilder();
for (int i = 0; i < input.length(); i += 2) {
final String code = input.substring(i, i + 2);
final int code2 = Integer.parseInt(code, 16);
result.append((char)code2);
}
System.out.println("result: " + result);
It prints:
input: 6232663064613430656332633131653030303030323432643530636631666266
result: b2f0da40ec2c11e00000242d50cf1fbf
Here you go:
import java.util.Formatter;
class Test {
public static void main(String[] args) {
String uuid = "b2f0da40ec2c11e00000242d50cf1fbf";
byte[] bytes = uuid.getBytes();
StringBuilder hex = new StringBuilder(bytes.length * 2);
Formatter fmt = new Formatter(hex);
for (byte b : bytes) {
fmt.format("%x", b);
}
System.out.println(hex);
/******** reverse the process *******/
/**
* group the bytes in couples
* convert them to integers (base16)
* and store them as bytes
*/
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16);
}
/**
* build a string from the bytes
*/
String original = new String(bytes);
System.out.println(original);
}
}

Categories

Resources