Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Top Posters

Who's Online (1)

Powered by Vanilla. Made with Bootstrap.
[Java] EXIFRemover 0.1
  • Deque
    Posts: 78
    Hello iExploit,

    this is a command line program for removing EXIF data from images. EXIF data is saved into your pictures by your digital camera, but also image editing software. With the EXIFRemover you can prepare images before publishing them, so they won't contain any information you don't want to publish.

    Download jar:

    http://www.mediafire.com/?xycwdanv9ia1va3

    Usage:

    usage: exifremover -d <filename/folder> [-dest <filename/folder>]
    -d,--delete-exif <filename> delete all exif data of given file(s)
    -dest <filename> save changed image(s) into file
    -h,--help print help message
    -s,--show-exif <filename> show exif data
    -v,--version show version

    show-exif example: java -jar exifremover -s B:\Sonstiges\test.jpg

    [spoiler]

    Exif metadata:
    Root:
    Image Description: 'JENOPTIK DIGITAL CAMERA'
    Make: 'JENOPTIK OPTICAL CO,LTD'
    Model: 'JD5200Z'
    Orientation: 1
    XResolution: 72
    YResolution: 72
    Resolution Unit: 2
    Software: 'VJ305060'
    Modify Date: '2006:08:16 11:56:11'
    YCbCr Positioning: 2
    Exif Offset: 254

    Exif:
    ISO: 100
    Exif Version: 48, 50, 49, 48
    Date Time Original: '2006:08:16 11:56:11'
    Create Date: '2006:08:16 11:56:14'
    Components Configuration: 1, 2, 3, 0
    Compressed Bits Per Pixel: 1100/9830 (0,112)
    Shutter Speed Value: 101/15 (6,733)
    Aperture Value: 35055/10000 (3,506)
    Exposure Compensation: 7/10 (0,7)
    Subject Distance: Invalid rational (0/0)
    Metering Mode: 2
    Flash: 1
    Focal Length: 21860/1000 (21,86)
    Maker Note: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 50... (264)
    Flashpix Version: 48, 49, 48, 48
    Color Space: 1
    Exif Image Width: 2560
    Exif Image Length: 1920
    Interop Offset: 840

    Interoperability:
    Interop Index: 'R98'
    Interop Version: 48, 49, 48, 48

    Sub: (jpegImageData)
    Compression: 6
    XResolution: 72
    YResolution: 72
    Resolution Unit: 2
    Jpg From Raw Start: 964
    Jpg From Raw Length: 12117

    Photoshop (IPTC) metadata:
    Keywords: SampleKeyword

    [/spoiler]


    delete-exif example for a single file: java -jar exifremover -d B:\Sonstiges\test.jpg -dest B:\Sonstiges\temp.jpg

    [spoiler]

    All EXIF data successfully removed from B:\Sonstiges\test.jpg
    Result saved in B:\Sonstiges\temp.jpg

    [/spoiler]


    delete-exif example for all files in a given folder: java -jar exifremover -d B:\Sonstiges\
    All files are saved into a temp folder within the given folder, unless you define a destination folder with -dest.

    [spoiler]

    created file B:\Sonstiges\temp\100_1788.JPG
    created file B:\Sonstiges\temp\100_1789.JPG
    created file B:\Sonstiges\temp\100_1790.JPG
    created file B:\Sonstiges\temp\100_1791.JPG
    created file B:\Sonstiges\temp\100_1792.JPG
    created file B:\Sonstiges\temp\100_1793.JPG
    created file B:\Sonstiges\temp\100_1796.JPG
    created file B:\Sonstiges\temp\100_1797.JPG
    created file B:\Sonstiges\temp\100_1799.JPG
    created file B:\Sonstiges\temp\100_1800.JPG
    created file B:\Sonstiges\temp\100_1801.JPG
    created file B:\Sonstiges\temp\100_1802.JPG
    created file B:\Sonstiges\temp\100_1939.JPG
    Unable to remove EXIF data from B:\Sonstiges\Thumbs.db

    [/spoiler]



    Source code:

    I included Java docs for every class and method for a better understanding.

    CLI.java

    [spoiler]
    import java&#46;io&#46;File;
    import java&#46;io&#46;IOException;

    import org&#46;apache&#46;commons&#46;cli&#46;CommandLine;
    import org&#46;apache&#46;commons&#46;cli&#46;CommandLineParser;
    import org&#46;apache&#46;commons&#46;cli&#46;HelpFormatter;
    import org&#46;apache&#46;commons&#46;cli&#46;Options;
    import org&#46;apache&#46;commons&#46;cli&#46;ParseException;
    import org&#46;apache&#46;commons&#46;cli&#46;PosixParser;
    import org&#46;apache&#46;sanselan&#46;ImageReadException;
    import org&#46;apache&#46;sanselan&#46;ImageWriteException;

    import exifremover&#46;exifremover&#46;model&#46;EXIFRemover;

    /**
    * Command line interface for exifremover&#46;
    *
    * @author deque
    *
    */
    public class CLI {

    private static final String TEMP_DIR = \"temp\";
    private static final String VERSION = \"EXIFRemover 0&#46;1\";
    private static final String USAGE = \"exifremover -d &lt;filename/folder&gt;\"
    + \" &#91;-dest &lt;filename/folder&gt;&#93;\";

    private static final String FILE_SEPARATOR = System
    &#46;getProperty(\"file&#46;separator\");

    public static void main(String&#91;&#93; args) {
    new CLI(args);
    }

    /**
    * Initializes options, invokes parsing of command line arguments&#46;
    *
    * @param args
    * the command line arguments
    */
    public CLI(String&#91;&#93; args) {
    Options options = initOptions();
    parseCommandLine(args, options);
    }

    /**
    * Parses arguments for available options&#46;
    *
    * @param args
    * the command line arguments
    * @param options
    * the command line options for which the arguments are parsed
    */
    private void parseCommandLine(String&#91;&#93; args, Options options) {
    CommandLineParser parser = new PosixParser();
    try {
    CommandLine line = parser&#46;parse(options, args);

    if (line&#46;hasOption(\"version\")) {
    System&#46;out&#46;println(VERSION);
    }
    if (line&#46;hasOption(\"help\") || args&#46;length == 0) {
    showHelp(options);
    }
    if (line&#46;hasOption(\"show-exif\")) {
    showExif(line);
    }
    if (line&#46;hasOption(\"delete-exif\")) {
    deleteEXIF(line);
    }
    } catch (ParseException e) {
    System&#46;err&#46;println(e&#46;getMessage());
    } catch (ImageWriteException e) {
    System&#46;err&#46;println(e&#46;getMessage());
    } catch (IOException e) {
    System&#46;err&#46;println(e&#46;getMessage());
    } catch (ImageReadException e) {
    System&#46;err&#46;println(e&#46;getMessage());
    }
    }

    /**
    * Prints out all EXIF data found in given file&#46;
    *
    * @param line
    * command line that contains the image path
    * @throws IOException
    * @throws ImageReadException
    */
    private void showExif(CommandLine line) throws IOException,
    ImageReadException {
    File file = new File(line&#46;getOptionValue(\"show-exif\"));
    EXIFRemover remover = new EXIFRemover(file);
    System&#46;out&#46;println(remover&#46;getExifData());
    }

    /**
    * Prints out help information like usage and available options&#46;
    *
    * @param options
    * command line options to be shown
    */
    private void showHelp(Options options) {
    HelpFormatter formatter = new HelpFormatter();
    formatter&#46;printHelp(USAGE, options);
    }

    /**
    * Removes EXIF data from a single image file or all image files in a folder
    * given as command line argument&#46;
    *
    * If a folder is given, it saves the resulting images into destination
    * folder&#46; A TEMP_DIR is created if no destination is given (this is mainly
    * for security reasons, so you are not able to overwrite a whole folder of
    * images by mistake)&#46;
    *
    * If a single image file is given, it saves the resulting image into a
    * given destination&#46; If there is no destination, the image file is
    * overwritten, which means EXIF data of the original file is lost unless
    * you have a copy&#46;
    *
    * @param line
    * command line that contains the paths for source and maybe
    * destination
    * @throws IOException
    * @throws ImageReadException
    * @throws ImageWriteException
    */
    private void deleteEXIF(CommandLine line) throws IOException,
    ImageReadException, ImageWriteException {
    File source = new File(line&#46;getOptionValue(\"delete-exif\"));
    // if no destination folder given, assign source folder to dest
    File dest = line&#46;hasOption(\"dest\") ? new File(
    line&#46;getOptionValue(\"dest\")) &#58; source;

    if (source&#46;isDirectory()) {
    deleteEXIFFromDir(source, dest);
    } else {
    deleteEXIFForSingleFile(source, dest);
    }
    }

    /**
    * Removes EXIF data from all files in a given source directory and saves
    * new images into the destination directory&#46; Destination directory is
    * created if it doesn't exist&#46;
    *
    * @param source
    * source folder that contains the image files
    * @param dest
    * destination folder where the new images are saved to
    */
    private void deleteEXIFFromDir(File source, File dest) {
    assert source&#46;isDirectory();
    if (!dest&#46;isDirectory()) {
    if (dest&#46;exists()) {
    System&#46;err&#46;println(\"destination file is no directory\");
    return;
    } else {
    dest&#46;mkdir();
    }

    }
    // create temp folder if destination folder equals source folder
    if (dest&#46;equals(source)) {
    removeAllEXIFFromFolder(source, new File(dest&#46;getAbsolutePath()
    + FILE_SEPARATOR + TEMP_DIR));
    } else {
    removeAllEXIFFromFolder(source, dest);
    }
    }

    /**
    * Removes EXIF data from a single image file which is the source file&#46;
    * Resulting images is saved into the destination file which may be the same
    * as the source file (in that case the image is overwritten)&#46;
    *
    * @param source
    * source folder that contains the image files
    * @param dest
    * destination folder where the new images are saved to
    * @throws IOException
    * @throws ImageReadException
    * @throws ImageWriteException
    */
    private void deleteEXIFForSingleFile(File source, File dest)
    throws IOException, ImageReadException, ImageWriteException {
    EXIFRemover remover = new EXIFRemover(source);
    remover&#46;removeEXIFData(dest);
    System&#46;out&#46;println(\"All EXIF data successfully removed from \"
    + source&#46;getAbsolutePath());
    System&#46;out&#46;println(\"Result saved in \" + dest&#46;getAbsolutePath());
    }

    /**
    * Deletes EXIF information from every file in a given source folder&#46;
    * Resulting images are saved the destination folder&#46; The given arguments
    * files must be directories&#46;
    *
    * Directories within the source folder are ignored&#46; Everything else will be
    * treated as an image&#46;
    *
    * @param srcFolder
    * source folder that contains the image files
    * @param destFolder
    * destination folder where the new images are saved to
    */
    private void removeAllEXIFFromFolder(File srcFolder, File destFolder) {
    assert srcFolder&#46;isDirectory() && destFolder&#46;isDirectory();
    File&#91;&#93; files = srcFolder&#46;listFiles();

    if (!destFolder&#46;exists()) {
    destFolder&#46;mkdir();
    }
    for (File file &#58; files) {
    if (file&#46;isFile()) { // make sure that file is no directory

    try {
    EXIFRemover remover = new EXIFRemover(file);
    File destFile = new File(destFolder&#46;getAbsolutePath()
    + FILE_SEPARATOR + file&#46;getName());
    remover&#46;removeEXIFData(destFile);
    System&#46;out&#46;println(\"created file \"
    + destFile&#46;getAbsolutePath());
    } catch (IOException e) {
    System&#46;err&#46;println(\"Can not convert or read file \"
    + file&#46;getAbsolutePath());
    } catch (ImageReadException e) {
    System&#46;err&#46;println(\"Unable to remove EXIF data from \"
    + file&#46;getAbsolutePath());
    } catch (ImageWriteException e) {
    System&#46;err&#46;println(\"Unable to remove EXIF data from \"
    + file&#46;getAbsolutePath());
    }
    }
    }
    }

    /**
    * Initializes command line options like d, s, dest, help&#46;
    *
    * @return initialized options
    */
    private Options initOptions() {
    Options options = new Options();

    options&#46;addOption(\"v\", \"version\", false, \"show version\");
    options&#46;addOption(\"h\", \"help\", false, \"print help message\");

    options&#46;addOption(\"s\", \"show-exif\", true, \"show exif data\");
    options&#46;getOption(\"s\")&#46;setArgName(\"filename\");

    options&#46;addOption(\"d\", \"delete-exif\", true,
    \"delete all exif data of given file(s)\");
    options&#46;getOption(\"d\")&#46;setArgName(\"filename\");

    options&#46;addOption(\"dest\", true, \"save changed image(s) into file\");
    options&#46;getOption(\"dest\")&#46;setArgName(\"filename\");

    return options;
    }

    }
    [/spoiler]


    FileIO.java

    [spoiler]

    import java&#46;io&#46;File;
    import java&#46;io&#46;FileInputStream;
    import java&#46;io&#46;IOException;

    /**
    * A helper class for file input and output&#46;
    *
    * @author deque
    *
    */
    public class FileIO {

    /**
    * @param file
    * file to get the bytes from
    * @return byte array that represents the given file
    * @throws IOException
    */
    public static byte&#91;&#93; getBytesFromFile(File file) throws IOException {
    byte&#91;&#93; data = null;
    FileInputStream fileInputStream = null;
    try {
    fileInputStream = new FileInputStream(file);
    data = new byte&#91;(int) file&#46;length()&#93;;
    fileInputStream&#46;read(data);
    } finally {
    if (fileInputStream != null) {
    try {
    fileInputStream&#46;close();
    } catch (IOException e) {
    e&#46;printStackTrace();
    }
    }
    }
    return data;
    }

    }
    [/spoiler]


    EXIFRemover.java

    [spoiler]
    import java&#46;io&#46;File;
    import java&#46;io&#46;FileOutputStream;
    import java&#46;io&#46;IOException;
    import java&#46;io&#46;OutputStream;

    import org&#46;apache&#46;sanselan&#46;ImageReadException;
    import org&#46;apache&#46;sanselan&#46;ImageWriteException;
    import org&#46;apache&#46;sanselan&#46;Sanselan;
    import org&#46;apache&#46;sanselan&#46;common&#46;IImageMetadata;
    import org&#46;apache&#46;sanselan&#46;formats&#46;jpeg&#46;exifRewrite&#46;ExifRewriter;

    import exifremover&#46;exifremover&#46;FileIO;

    /**
    * A class that utilizes the sanselan library to show or remove EXIF data
    *
    * @author deque
    *
    */
    public class EXIFRemover {

    private final File image;
    private final byte&#91;&#93; imageBytes;

    /**
    * Constructor&#46; Retrieves image bytes from given image file&#46;
    *
    * @param image
    * file that shall be treated
    * @throws IOException
    */
    public EXIFRemover(File image) throws IOException {
    this&#46;image = image;
    imageBytes = FileIO&#46;getBytesFromFile(image);
    }

    /**
    * @return String that describes the meta data of the given image file&#46;
    * @throws ImageReadException
    * @throws IOException
    */
    public String getExifData() throws ImageReadException, IOException {
    IImageMetadata metdata = Sanselan&#46;getMetadata(imageBytes);
    return metdata&#46;toString();
    }

    /**
    * @return image file
    */
    public File getImageFile() {
    return image;
    }

    /**
    * Completely deletes EXIF data from the extracted image bytes&#46; Saves the
    * resulting images bytes into the destination file&#46;
    *
    * @param dest
    * the destination file
    * @throws ImageReadException
    * @throws ImageWriteException
    * @throws IOException
    */
    public void removeEXIFData(File dest) throws ImageReadException,
    ImageWriteException, IOException {
    OutputStream os = null;
    try {
    os = new FileOutputStream(dest);
    ExifRewriter exifWriter = new ExifRewriter();
    exifWriter&#46;removeExifMetadata(imageBytes, os);
    } finally {
    if (os != null) {
    try {
    os&#46;close();
    } catch (IOException e) {
    e&#46;printStackTrace();
    }
    }
    }
    }

    }
    [/spoiler]


    LOC: 213 lines (without comments and empty lines)

    License:

    [spoiler]

    Copyright 2012 Deque at http://iexploit.org All rights reserved.

    Redistribution and use in source and binary forms, with or without modification, are
    permitted provided that the following conditions are met:

    1. Redistributions of source code must retain the above copyright notice, this list of
    conditions and the following disclaimer.

    2. Redistributions in binary form must reproduce the above copyright notice, this list
    of conditions and the following disclaimer in the documentation and/or other materials
    provided with the distribution.

    THIS SOFTWARE IS PROVIDED BY Deque at http://iexploit.org ``AS IS'' AND ANY EXPRESS OR IMPLIED
    WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Deque at http://iexploit.org OR
    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
    ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
    ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

    The views and conclusions contained in the software and documentation are those of the
    authors and should not be interpreted as representing official policies, either expressed
    or implied, of Deque at http://iexploit.org

    [/spoiler]


    If you encounter any bugs or problems, please tell me.
    If you want to improve this software, you are free to do so.

    Features for future versions could be: GUI, filters for removal, EXIF editing

    Deque
  • Sh3llc0d3
    Posts: 1,910
    Does EXIF data include geo-tagging info? That would be awesome if you came up with a geo-tag remover.

    Sorry my bad for not knowing this area lol. Also have you done any android dev? Just curious to know the amount of java dev experience needed.
  • Deque
    Posts: 78
    said:


    Does EXIF data include geo-tagging info? That would be awesome if you came up with a geo-tag remover.



    Here is an example of a geotagged picture I got from the internet:

    [spoiler]
     java -jar exifremover&#46;jar -s img_5546&#46;jpg 
    Exif metadata&#58;
    Root&#58;
    Make&#58; 'Canon'
    Model&#58; 'Canon EOS 400D DIGITAL'
    XResolution&#58; 300
    YResolution&#58; 300
    Resolution Unit&#58; 2
    Modify Date&#58; '2008&#58;08&#58;05 23&#58;48&#58;04'
    Artist&#58; 'unknown'
    Copyright&#58; 'Eirik Solheim - www&#46;eirikso&#46;com'
    Exif Offset&#58; 240
    GPSInfo&#58; 610

    Exif&#58;
    Exposure Time&#58; 1/100 (0&#46;01)
    FNumber&#58; 22/10 (2&#46;2)
    Exposure Program&#58; 2
    ISO&#58; 400
    Exif Version&#58; 48, 50, 50, 49
    Date Time Original&#58; '2008&#58;08&#58;05 20&#58;59&#58;32'
    Create Date&#58; '2008&#58;08&#58;05 20&#58;59&#58;32'
    Shutter Speed Value&#58; 6643856/1000000 (6&#46;644)
    Aperture Value&#58; 2275007/1000000 (2&#46;275)
    Exposure Compensation&#58; 0
    Max Aperture Value&#58; 96875/100000 (0&#46;969)
    Metering Mode&#58; 1
    Flash&#58; 16
    Focal Length&#58; 50
    Focal Plane XResolution&#58; 3888000/877 (4,433&#46;295)
    Focal Plane YResolution&#58; 2592000/582 (4,453&#46;608)
    Focal Plane Resolution Unit&#58; 2
    Custom Rendered&#58; 0
    Exposure Mode&#58; 0
    White Balance&#58; 0
    Scene Capture Type&#58; 0

    Gps&#58;
    Interop Index&#58; 'N'
    Interop Version&#58; 59, 55, 37417/1285 (29&#46;118)
    Unknown Tag (0x3)&#58; 'E'
    Unknown Tag (0x4)&#58; 10, 41, 55324/1253 (44&#46;153)
    Unknown Tag (0x5)&#58; 0
    Unknown Tag (0x6)&#58; 81

    Photoshop (IPTC) metadata&#58;
    Keywords&#58; Marienlyst
    Keywords&#58; Norway
    Keywords&#58; 1&#46;71 Km to Marienlyst in Norway
    Keywords&#58; geotagged
    Keywords&#58; geo&#58;lat=59&#46;924755
    Keywords&#58; geo&#58;lon=10&#46;695598
    By-line&#58; unknown
    Copyright Notice&#58; Eirik Solheim - www&#46;eirikso&#46;com
    &#91;deque@desolate ~&#93;$ java -jar exifremover&#46;jar -d img_5546&#46;jpg -dest img-deleted&#46;jpg
    All EXIF data successfully removed from /home/deque/img_5546&#46;jpg
    Result saved in /home/deque/img-deleted&#46;jpg
    &#91;deque@desolate ~&#93;$ java -jar exifremover&#46;jar -s img-deleted&#46;jpg
    No Exif metadata&#46;
    Photoshop (IPTC) metadata&#58;
    Keywords&#58; Marienlyst
    Keywords&#58; Norway
    Keywords&#58; 1&#46;71 Km to Marienlyst in Norway
    Keywords&#58; geotagged
    Keywords&#58; geo&#58;lat=59&#46;924755
    Keywords&#58; geo&#58;lon=10&#46;695598
    By-line&#58; unknown
    Copyright Notice&#58; Eirik Solheim - www&#46;eirikso&#46;com
    [/spoiler]

    As you can see, Photoshop metadata removal isn't supported yet. But the EXIF geotag data is gone.

    Also have you done any android dev?



    Since I don't have an android device, I also didn't write any software for it.
  • Praxis
    Posts: 20
    Nice job. I usually use a Linux command line tool for EXIF removal, so I'll check this one out and see how it compares :D

    Yes, location data is removed. This is useful if you have an iPhone and want to upload an image somewhere online, as you can remove the location and all the other crap from it first.
  • Xin
    Posts: 3,251
    Nice! Never thought of the data stored in images
    Xin