├── stego ├── __init__.py ├── decode ├── embed ├── bitmap.h ├── stego.py ├── bitmap.c ├── jinclude.h ├── decode.c ├── cderror.h ├── cdjpeg.h └── embed.c ├── api_access ├── __init__.py ├── insta_access.py └── InstagramAPI.py ├── img_prep ├── __init__.py ├── .jpg_prepped.jpg ├── get_qtables_from_me.jpg └── img_prep.py ├── examples ├── message.txt ├── biking.jpg ├── piercing.jpg ├── biking_prepped.jpg ├── piercing_prepped.jpg ├── biking_prepped_encoded.jpg └── piercing_prepped_encoded.jpg ├── .gitignore ├── LICENSE.md ├── instegogram.py └── README.md /stego/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /api_access/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img_prep/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/message.txt: -------------------------------------------------------------------------------- 1 | test text! 2 | more text -------------------------------------------------------------------------------- /stego/decode: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endgameinc/instegogram/HEAD/stego/decode -------------------------------------------------------------------------------- /stego/embed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endgameinc/instegogram/HEAD/stego/embed -------------------------------------------------------------------------------- /examples/biking.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endgameinc/instegogram/HEAD/examples/biking.jpg -------------------------------------------------------------------------------- /examples/piercing.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endgameinc/instegogram/HEAD/examples/piercing.jpg -------------------------------------------------------------------------------- /img_prep/.jpg_prepped.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endgameinc/instegogram/HEAD/img_prep/.jpg_prepped.jpg -------------------------------------------------------------------------------- /examples/biking_prepped.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endgameinc/instegogram/HEAD/examples/biking_prepped.jpg -------------------------------------------------------------------------------- /examples/piercing_prepped.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endgameinc/instegogram/HEAD/examples/piercing_prepped.jpg -------------------------------------------------------------------------------- /img_prep/get_qtables_from_me.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endgameinc/instegogram/HEAD/img_prep/get_qtables_from_me.jpg -------------------------------------------------------------------------------- /examples/biking_prepped_encoded.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endgameinc/instegogram/HEAD/examples/biking_prepped_encoded.jpg -------------------------------------------------------------------------------- /examples/piercing_prepped_encoded.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endgameinc/instegogram/HEAD/examples/piercing_prepped_encoded.jpg -------------------------------------------------------------------------------- /stego/bitmap.h: -------------------------------------------------------------------------------- 1 | #ifndef _BITMAP_H 2 | #define _BITMAP_H 3 | 4 | #define BIT (8*sizeof(byte)) 5 | #define BITMAP_NOTFOUND -1 6 | 7 | typedef enum{false=0, true} bool; 8 | typedef unsigned char byte; 9 | 10 | bool bitmapGet (const byte *, int); 11 | void bitmapSet (byte *, int); 12 | void bitmapFlip (byte *, int); 13 | void bitmapClear (byte *, int); 14 | void bitmapAssign(byte *, int, bool); 15 | int bitmapSearch(byte *, bool, int, int); 16 | 17 | bool get (byte, byte); 18 | void set (byte *, byte); 19 | void clear(byte *, byte); 20 | void assign(byte*, byte, byte); 21 | void xor(byte *, byte); 22 | 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /stego/stego.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import os 3 | 4 | 5 | def encode(filepath, message): 6 | """ Encodes an image specified by filepath with a message in a text file """ 7 | print 'Encoded message: ' 8 | with open(message, 'r') as f: 9 | message_txt = ''.join(f.readlines()) 10 | print message_txt 11 | encoded_filepath = filepath[:-4] + '_encoded' + '.jpg' 12 | dir_path = os.path.dirname(os.path.realpath(__file__)) 13 | subprocess.call([dir_path + '/embed', filepath, message, encoded_filepath]) 14 | return encoded_filepath 15 | 16 | 17 | def decode(filepath): 18 | """ Decodes an image based on the stego used in this repo """ 19 | dir_path = os.path.dirname(os.path.realpath(__file__)) 20 | decoded_text = subprocess.check_output([dir_path + '/decode', filepath])[0:-1] 21 | print 'Decoded_text: ' 22 | print decoded_text 23 | return decoded_text 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | 59 | *.sublime-project 60 | *.sublime-workspace 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Endgame, Inc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /img_prep/img_prep.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | from PIL import Image 3 | #requires ImageMagick 4 | import array 5 | 6 | 7 | QTABLEARRAY = {0: array.array('b', [6, 4, 5, 6, 5, 4, 6, 6, 5, 6, 7, 7, 6, 8, 10, 16, 10, 10, 9, 9, 10, 20, 14, 8 | 15, 12, 16, 23, 20, 24, 24, 23, 20, 22, 22, 26, 29, 37, 31, 26, 27, 35, 28, 9 | 22, 22, 32, 44, 32, 35, 38, 39, 41, 42, 41, 25, 31, 45, 48, 45, 40, 48, 37, 10 | 40, 41, 40]), 11 | 1: array.array('b', [7, 7, 7, 10, 8, 10, 19, 10, 10, 19, 40, 26, 22, 26, 40, 40, 40, 40, 40, 40, 12 | 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 13 | 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 14 | 40, 40, 40, 40, 40, 40])} 15 | 16 | 17 | def prep_img(filepath): 18 | """ Resizes an image to at max 1080x1080 square and re-quantizes with Instagram's quantization table""" 19 | target = filepath[:-4] + '_prepped' + '.jpg' 20 | 21 | with Image.open(filepath) as img: 22 | width, height = img.size 23 | 24 | min_dim = min(width, height) 25 | if min_dim < 1080: 26 | img_dim = str(min_dim) + 'x' + str(min_dim) + '!' 27 | else: 28 | img_dim = '1080x1080!' 29 | 30 | subprocess.call(['convert', '-resize', img_dim, filepath, target]) 31 | 32 | with Image.open(target) as img: 33 | img.save(target, qtables=QTABLEARRAY, **img.info) 34 | 35 | return target 36 | -------------------------------------------------------------------------------- /api_access/insta_access.py: -------------------------------------------------------------------------------- 1 | import InstagramAPI 2 | import requests 3 | import urllib 4 | 5 | 6 | # Hat tip to the following for initial Instagram API work 7 | # https://github.com/LevPasha/Instagram-API-python (WTFPL License) 8 | # https://github.com/mgp25/Instagram-API (MIT License) 9 | 10 | 11 | def upload(filepath, uname, pword): 12 | """ 13 | Uploads a given image to the supplied user account with the supplied password 14 | Image must be a jpg and square 15 | """ 16 | print 'Uploading ' + filepath + '\n' 17 | insta = InstagramAPI.InstagramAPI(uname, pword) 18 | insta.uploadPhoto(filepath) 19 | 20 | 21 | def download(uname=None, img_url=None, save_location=None): 22 | """ 23 | Downloads an input img_url or the latest img from an Instagram account. 24 | """ 25 | if img_url: 26 | print 'Downloading input img' 27 | elif uname: 28 | print 'Downloading latest img from ' + uname + '\n' 29 | insta_url = 'https://www.instagram.com/' + uname 30 | img_url = get_latest_img_url(insta_url) 31 | else: 32 | print 'Please input either a uname or img_url' 33 | if not save_location: 34 | save_location = 'download.jpg' 35 | if img_url and save_location: 36 | urllib.urlretrieve(img_url, save_location) 37 | return save_location 38 | 39 | 40 | def get_latest_img_url(address): 41 | """ 42 | Grabs the text of the front page for an Instagram user and finds the url for the latest image posted. 43 | """ 44 | r = requests.get(address) 45 | p1 = '"display_src": "' 46 | p2 = "?" 47 | loc1 = r.text.find(p1) 48 | loc2 = loc1 + r.text[loc1:].find(p2) 49 | return r.text[loc1 + len(p1):loc2] 50 | -------------------------------------------------------------------------------- /stego/bitmap.c: -------------------------------------------------------------------------------- 1 | #include "bitmap.h" 2 | 3 | /* CAREFUL WITH pos AND BITMAP SIZE! */ 4 | 5 | bool bitmapGet(const byte *bitmap, int pos) { 6 | /* gets the value of the bit at pos */ 7 | return get(bitmap[pos/BIT], pos%BIT); 8 | } 9 | 10 | void bitmapSet(byte *bitmap, int pos) { 11 | /* sets bit at pos to 1 */ 12 | set(&bitmap[pos/BIT], pos%BIT); 13 | } 14 | 15 | void bitmapClear(byte *bitmap, int pos) { 16 | /* sets bit at pos to 0 */ 17 | clear(&bitmap[pos/BIT], pos%BIT); 18 | } 19 | 20 | void bitmapFlip (byte *bitmap, int pos) { 21 | xor( &bitmap[pos/BIT], pos%BIT ); 22 | } 23 | 24 | 25 | void bitmapAssign( byte *bitmap, int pos, bool bit) { 26 | /* essentially performs: bitmap[pos]=bit */ 27 | assign( &bitmap[pos/BIT], pos%BIT, bit ? 1 : 0 ); 28 | } 29 | 30 | 31 | int bitmapSearch(byte *bitmap, bool n, int size, int start) { 32 | /* Finds the first n value in bitmap after start */ 33 | /* size is the Bitmap size in bytes */ 34 | int i; 35 | /* size is now the Bitmap size in bits */ 36 | for(i = start+1, size *= BIT; i < size; i++) 37 | if(bitmapGet(bitmap,i) == n) 38 | return i; 39 | return BITMAP_NOTFOUND; 40 | } 41 | 42 | bool get(byte a, byte pos) { 43 | /* pos is something from 0 to 7*/ 44 | return (a >> pos) & 1; 45 | } 46 | 47 | void set(byte *a, byte pos) { 48 | /* pos is something from 0 to 7*/ 49 | /* sets bit to 1 */ 50 | *a |= 1 << pos; 51 | } 52 | 53 | 54 | void assign(byte *a, byte pos, byte bitval) { 55 | byte assign = bitval << pos; 56 | byte bitnum = 1 << pos; 57 | *a = ( *a & (0xff ^ bitnum)) | assign; 58 | } 59 | 60 | 61 | void xor(byte *a, byte pos) { 62 | *a ^= 1<< pos; 63 | } 64 | 65 | void clear(byte *a, byte pos) { 66 | /* pos is something from 0 to 7*/ 67 | /* sets bit to 0 */ 68 | *a &= ~(1 << pos); 69 | } 70 | -------------------------------------------------------------------------------- /instegogram.py: -------------------------------------------------------------------------------- 1 | import os 2 | from time import sleep 3 | from img_prep import img_prep 4 | from stego import stego 5 | from api_access import insta_access 6 | 7 | 8 | def insta_upload(filepath, uname, pword, message=None): 9 | """ 10 | Uploads a given image to the supplied user account with the supplied password. 11 | If a message txt file is supplied, the image will be prepped and encoded with that text before upload. 12 | """ 13 | if not os.path.isfile(filepath): 14 | print 'Please correct img filepath' 15 | 16 | elif message and not os.path.isfile(message): 17 | print 'Please correct message filepath' 18 | 19 | else: 20 | valid_message = message and message[-3:] 21 | if valid_message: 22 | prepped_filepath = img_prep.prep_img(filepath) 23 | encoded_filepath = stego.encode(prepped_filepath, message) 24 | else: 25 | print 'Message pre-encoded' 26 | encoded_filepath = filepath 27 | insta_access.upload(encoded_filepath, uname, pword) 28 | 29 | 30 | def insta_download_latest(uname): 31 | """ Downloads and decodes the latest image posted to a given Instagram account """ 32 | latest_img = insta_access.download(uname) 33 | decoded_text = stego.decode(latest_img) 34 | return decoded_text 35 | 36 | 37 | def insta_roundtrip(filepath, uname, pword, message=None): 38 | """ 39 | Encoded, uploads, and downloads an image. 40 | Finally decodes the image and checks again the original message. 41 | """ 42 | insta_upload(filepath=filepath, uname=uname, pword=pword, message=message) 43 | sleep(5) 44 | decoded_text = insta_download_latest(uname) 45 | if message and message[-3:] and os.path.isfile(message): 46 | with open(message, 'r') as f: 47 | message_txt = ''.join(f.readlines()) 48 | if decoded_text == message_txt: 49 | print 'Successful round trip' 50 | else: 51 | print 'Round trip error' 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Instegogram 2 | 3 | ### Overview 4 | Using Instagram and steganography as a way to transmit images encoded with text by hiding it in prevelant social media traffic. 5 | 6 |
7 | ### Basic Use 8 | Upload image with encoded text: 9 | ```python 10 | import instegogram 11 | instegogram.insta_upload(filepath='examples/piercing.jpg', uname='username', pword='password', message='examples/message.txt') 12 | ``` 13 | Download latest image and decode text: 14 | ```python 15 | instegogram.insta_download_latest(uname='username') 16 | ``` 17 | 18 | Do both to check that it works: 19 | ```python 20 | instegogram.insta_roundtrip(filepath='examples/piercing.jpg', uname='username', pword='password', message='examples/message.txt') 21 | ``` 22 | 23 |
24 | ### Image Prep 25 | Image prep converts an image to square (not perserving aspect ratio) with a maximum size of 1080x1080, which is Instagram's max upload size. It also re-quantizes the image with Instagram's quantization table. This requires [ImageMagic](http://www.imagemagick.org/script/index.php). 26 | ```python 27 | from img_prep import img_prep 28 | img_prep.prep_img(filepath='examples/piercing.jpg') 29 | ``` 30 | 31 |
32 | ### Stego 33 | The example stego technique in C is implemented and called via Python. If you have another method for encoding/decoding you can replace the `subprocess` calls in stego.py with your own. 34 | 35 | To encode a text file into an image: 36 | ```python 37 | from stego import stego 38 | stego.encode(filepath='examples/piercing.jpg', message='examples/message.txt') 39 | ``` 40 | To decode text from an image: 41 | ```python 42 | stego.deocde(filepath='download.jpg') 43 | ``` 44 | 45 | Note: Different images will have different capacities for encoding text. Try to select an image with high average texture in the luminance channel for the 8px by 8px blocks of the image. Or just trial and error until you find a good one (think lots of contrast and not computer generated) 46 | 47 |
48 | ### Instagram API 49 | Instagram is accessed via their API and scraping their website. 50 | 51 | To upload an image: 52 | ```python 53 | from api_access import insta_access 54 | insta_access.upload(filepath='examples/piercing.jpg', uname='username', pword='password') 55 | ``` 56 | 57 | To download the latest image: 58 | ```python 59 | insta_access.download(uname='username') 60 | ``` 61 | or a specific image (found via page source or other means): 62 | ```python 63 | insta_access.download(img_url='https://scontent-sjc2-1.cdninstagram.com/t51.2885-15/s1080x1080/e15/fr/12960199_1704537009805057_827148056_n.jpg') 64 | ``` 65 | 66 | -------------------------------------------------------------------------------- /stego/jinclude.h: -------------------------------------------------------------------------------- 1 | /* 2 | * jinclude.h 3 | * 4 | * Copyright (C) 1991-1994, Thomas G. Lane. 5 | * This file is part of the Independent JPEG Group's software. 6 | * For conditions of distribution and use, see the accompanying README file. 7 | * 8 | * This file exists to provide a single place to fix any problems with 9 | * including the wrong system include files. (Common problems are taken 10 | * care of by the standard jconfig symbols, but on really weird systems 11 | * you may have to edit this file.) 12 | * 13 | * NOTE: this file is NOT intended to be included by applications using the 14 | * JPEG library. Most applications need only include jpeglib.h. 15 | */ 16 | 17 | 18 | /* Include auto-config file to find out which system include files we need. */ 19 | 20 | #include "jconfig.h" /* auto configuration options */ 21 | #define JCONFIG_INCLUDED /* so that jpeglib.h doesn't do it again */ 22 | 23 | /* 24 | * We need the NULL macro and size_t typedef. 25 | * On an ANSI-conforming system it is sufficient to include . 26 | * Otherwise, we get them from or ; we may have to 27 | * pull in as well. 28 | * Note that the core JPEG library does not require ; 29 | * only the default error handler and data source/destination modules do. 30 | * But we must pull it in because of the references to FILE in jpeglib.h. 31 | * You can remove those references if you want to compile without . 32 | */ 33 | 34 | #ifdef HAVE_STDDEF_H 35 | #include 36 | #endif 37 | 38 | #ifdef HAVE_STDLIB_H 39 | #include 40 | #endif 41 | 42 | #ifdef NEED_SYS_TYPES_H 43 | #include 44 | #endif 45 | 46 | #include 47 | 48 | /* 49 | * We need memory copying and zeroing functions, plus strncpy(). 50 | * ANSI and System V implementations declare these in . 51 | * BSD doesn't have the mem() functions, but it does have bcopy()/bzero(). 52 | * Some systems may declare memset and memcpy in . 53 | * 54 | * NOTE: we assume the size parameters to these functions are of type size_t. 55 | * Change the casts in these macros if not! 56 | */ 57 | 58 | #ifdef NEED_BSD_STRINGS 59 | 60 | #include 61 | #define MEMZERO(target,size) bzero((void *)(target), (size_t)(size)) 62 | #define MEMCOPY(dest,src,size) bcopy((const void *)(src), (void *)(dest), (size_t)(size)) 63 | 64 | #else /* not BSD, assume ANSI/SysV string lib */ 65 | 66 | #include 67 | #define MEMZERO(target,size) memset((void *)(target), 0, (size_t)(size)) 68 | #define MEMCOPY(dest,src,size) memcpy((void *)(dest), (const void *)(src), (size_t)(size)) 69 | 70 | #endif 71 | 72 | /* 73 | * In ANSI C, and indeed any rational implementation, size_t is also the 74 | * type returned by sizeof(). However, it seems there are some irrational 75 | * implementations out there, in which sizeof() returns an int even though 76 | * size_t is defined as long or unsigned long. To ensure consistent results 77 | * we always use this SIZEOF() macro in place of using sizeof() directly. 78 | */ 79 | 80 | #define SIZEOF(object) ((size_t) sizeof(object)) 81 | 82 | /* 83 | * The modules that use fread() and fwrite() always invoke them through 84 | * these macros. On some systems you may need to twiddle the argument casts. 85 | * CAUTION: argument order is different from underlying functions! 86 | */ 87 | 88 | #define JFREAD(file,buf,sizeofbuf) \ 89 | ((size_t) fread((void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) 90 | #define JFWRITE(file,buf,sizeofbuf) \ 91 | ((size_t) fwrite((const void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) 92 | -------------------------------------------------------------------------------- /stego/decode.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "cdjpeg.h" /* Common decls for jpeg files */ 5 | 6 | // adapted from code from: http://www.cs.odu.edu/~cs772/demos/steganography/sshettyLinux/ 7 | //USAGE: ./decode 8 | 9 | #define MAX_MSG_BITS 132000 10 | #define MAX_MSG_BYTES 33000 11 | 12 | void JPEG_extractbits( const char * input_filename, char **message, size_t *message_len) { // returns a null-terminating string that must be freed 13 | struct jpeg_decompress_struct srcinfo; 14 | struct jpeg_error_mgr jsrcerr, jdsterr; 15 | jvirt_barray_ptr *coef_arrays; 16 | JDIMENSION i, compnum, rownum, blocknum; 17 | size_t block_row_size; 18 | JBLOCKARRAY row_ptrs[MAX_COMPONENTS]; 19 | 20 | FILE * input_file; 21 | 22 | char mask[MAX_MSG_BITS]; 23 | char * decode_int = calloc( MAX_MSG_BYTES, sizeof(char)); 24 | 25 | int text = 0, length, length_set = 0, data_set = 0 , count_bits = 0, test_mask = 0, l = 0; 26 | int get_a = 0, get_b = 0, val_a = 0, val_b = 0; 27 | 28 | srcinfo.err = jpeg_std_error(&jsrcerr); 29 | jpeg_create_decompress(&srcinfo); 30 | jsrcerr.trace_level = jdsterr.trace_level; 31 | 32 | input_file = fopen(input_filename, "rb"); 33 | 34 | jpeg_stdio_src(&srcinfo, input_file); 35 | (void) jpeg_read_header(&srcinfo, TRUE); 36 | 37 | coef_arrays = jpeg_read_coefficients(&srcinfo); 38 | for (compnum = 0; compnum < srcinfo.num_components; compnum++) 39 | { 40 | block_row_size = (size_t) SIZEOF(JCOEF) * DCTSIZE2 41 | * srcinfo.comp_info[compnum].width_in_blocks; 42 | for (rownum = 0; rownum < srcinfo.comp_info[compnum].height_in_blocks; rownum++) 43 | { 44 | int k , l, j; 45 | row_ptrs[compnum] = ((&srcinfo)->mem->access_virt_barray) 46 | ((j_common_ptr) &srcinfo, coef_arrays[compnum], 47 | rownum, (JDIMENSION) 1, FALSE); 48 | for (blocknum = 0; blocknum < srcinfo.comp_info[compnum].width_in_blocks; 49 | blocknum++) 50 | { 51 | k = -1; l = 0; 52 | for (i = 0; i < DCTSIZE2; i++) 53 | { 54 | if (!length_set) 55 | { 56 | if ( (i > 0 ) && ( row_ptrs[compnum][0][blocknum][i] != 0) && (abs(row_ptrs[compnum][0][blocknum][i]) > 15)) 57 | { 58 | int test_amask = 0x08, k = 0; 59 | int test = abs (row_ptrs[compnum][0][blocknum][i]); 60 | if (get_a == 1 && get_b == 1 ) 61 | { 62 | val_b *= 8; 63 | length_set = 1; 64 | } 65 | if (!get_a ) 66 | { 67 | get_a = 1; 68 | 69 | for ( k = 0 ; k < 4; k++) 70 | { 71 | if (test & test_amask) 72 | val_a += test_amask; 73 | test_amask = test_amask >> 1; 74 | } 75 | } 76 | else 77 | { 78 | 79 | if (test & 0x01) val_b += pow(2, val_a); 80 | val_a -= 1; 81 | if (val_a == 0 ) get_b = 1; 82 | } 83 | 84 | } 85 | } 86 | else 87 | { 88 | if (!data_set) 89 | { 90 | if ( (i > 0 ) && ( row_ptrs[compnum][0][blocknum][i] != 0) && (abs(row_ptrs[compnum][0][blocknum][i]) > 1)) 91 | { 92 | int test_mask = 2, k = 0; 93 | int test = abs (row_ptrs[compnum][0][blocknum][i]); 94 | if ( test >= 4) 95 | { 96 | while (test_mask) 97 | { 98 | if (test & test_mask) 99 | mask[count_bits] = mask[count_bits] | test_mask; 100 | test_mask = test_mask >> 1; 101 | } 102 | count_bits++; 103 | if ( count_bits == (val_b / 2) ) data_set = 1; 104 | } 105 | } 106 | } 107 | } 108 | 109 | } 110 | } 111 | 112 | } 113 | } 114 | 115 | i = 0; 116 | l = 0; 117 | while ( i < count_bits) 118 | { 119 | int k; 120 | int decode_mask = 0x80; 121 | if (!test_mask) test_mask = 0x02; 122 | while (decode_mask) 123 | { 124 | if ( mask[i] & test_mask) 125 | decode_int[l] += decode_mask; 126 | test_mask >>= 1; 127 | if (!test_mask) { i++; test_mask = 0x02; } 128 | 129 | decode_mask >>= 1; 130 | } 131 | l++; 132 | } 133 | (void) jpeg_finish_decompress(&srcinfo); 134 | jpeg_destroy_decompress(&srcinfo); 135 | fclose (input_file); 136 | decode_int[l] = 0; // null terminating 137 | 138 | *message_len = l; 139 | *message = decode_int; 140 | }; 141 | 142 | 143 | int main (int argc, char **argv) 144 | { 145 | if ( argc == 2) 146 | { 147 | char * message; 148 | size_t message_len; 149 | JPEG_extractbits( argv[1], &message, &message_len); 150 | printf("%s\n", message); 151 | free(message); 152 | } 153 | 154 | else 155 | 156 | { 157 | printf ("USAGE : \n"); 158 | printf ("./decode \n"); 159 | } 160 | 161 | return 0; /* suppress no-return-value warnings */ 162 | } 163 | 164 | -------------------------------------------------------------------------------- /stego/cderror.h: -------------------------------------------------------------------------------- 1 | /* 2 | * cderror.h 3 | * 4 | * Copyright (C) 1994-1997, Thomas G. Lane. 5 | * This file is part of the Independent JPEG Group's software. 6 | * For conditions of distribution and use, see the accompanying README file. 7 | * 8 | * This file defines the error and message codes for the cjpeg/djpeg 9 | * applications. These strings are not needed as part of the JPEG library 10 | * proper. 11 | * Edit this file to add new codes, or to translate the message strings to 12 | * some other language. 13 | */ 14 | 15 | /* 16 | * To define the enum list of message codes, include this file without 17 | * defining macro JMESSAGE. To create a message string table, include it 18 | * again with a suitable JMESSAGE definition (see jerror.c for an example). 19 | */ 20 | #ifndef JMESSAGE 21 | #ifndef CDERROR_H 22 | #define CDERROR_H 23 | /* First time through, define the enum list */ 24 | #define JMAKE_ENUM_LIST 25 | #else 26 | /* Repeated inclusions of this file are no-ops unless JMESSAGE is defined */ 27 | #define JMESSAGE(code,string) 28 | #endif /* CDERROR_H */ 29 | #endif /* JMESSAGE */ 30 | 31 | #ifdef JMAKE_ENUM_LIST 32 | 33 | typedef enum { 34 | 35 | #define JMESSAGE(code,string) code , 36 | 37 | #endif /* JMAKE_ENUM_LIST */ 38 | 39 | JMESSAGE(JMSG_FIRSTADDONCODE=1000, NULL) /* Must be first entry! */ 40 | 41 | #ifdef BMP_SUPPORTED 42 | JMESSAGE(JERR_BMP_BADCMAP, "Unsupported BMP colormap format") 43 | JMESSAGE(JERR_BMP_BADDEPTH, "Only 8- and 24-bit BMP files are supported") 44 | JMESSAGE(JERR_BMP_BADHEADER, "Invalid BMP file: bad header length") 45 | JMESSAGE(JERR_BMP_BADPLANES, "Invalid BMP file: biPlanes not equal to 1") 46 | JMESSAGE(JERR_BMP_COLORSPACE, "BMP output must be grayscale or RGB") 47 | JMESSAGE(JERR_BMP_COMPRESSED, "Sorry, compressed BMPs not yet supported") 48 | JMESSAGE(JERR_BMP_NOT, "Not a BMP file - does not start with BM") 49 | JMESSAGE(JTRC_BMP, "%ux%u 24-bit BMP image") 50 | JMESSAGE(JTRC_BMP_MAPPED, "%ux%u 8-bit colormapped BMP image") 51 | JMESSAGE(JTRC_BMP_OS2, "%ux%u 24-bit OS2 BMP image") 52 | JMESSAGE(JTRC_BMP_OS2_MAPPED, "%ux%u 8-bit colormapped OS2 BMP image") 53 | #endif /* BMP_SUPPORTED */ 54 | 55 | #ifdef GIF_SUPPORTED 56 | JMESSAGE(JERR_GIF_BUG, "GIF output got confused") 57 | JMESSAGE(JERR_GIF_CODESIZE, "Bogus GIF codesize %d") 58 | JMESSAGE(JERR_GIF_COLORSPACE, "GIF output must be grayscale or RGB") 59 | JMESSAGE(JERR_GIF_IMAGENOTFOUND, "Too few images in GIF file") 60 | JMESSAGE(JERR_GIF_NOT, "Not a GIF file") 61 | JMESSAGE(JTRC_GIF, "%ux%ux%d GIF image") 62 | JMESSAGE(JTRC_GIF_BADVERSION, 63 | "Warning: unexpected GIF version number '%c%c%c'") 64 | JMESSAGE(JTRC_GIF_EXTENSION, "Ignoring GIF extension block of type 0x%02x") 65 | JMESSAGE(JTRC_GIF_NONSQUARE, "Caution: nonsquare pixels in input") 66 | JMESSAGE(JWRN_GIF_BADDATA, "Corrupt data in GIF file") 67 | JMESSAGE(JWRN_GIF_CHAR, "Bogus char 0x%02x in GIF file, ignoring") 68 | JMESSAGE(JWRN_GIF_ENDCODE, "Premature end of GIF image") 69 | JMESSAGE(JWRN_GIF_NOMOREDATA, "Ran out of GIF bits") 70 | #endif /* GIF_SUPPORTED */ 71 | 72 | #ifdef PPM_SUPPORTED 73 | JMESSAGE(JERR_PPM_COLORSPACE, "PPM output must be grayscale or RGB") 74 | JMESSAGE(JERR_PPM_NONNUMERIC, "Nonnumeric data in PPM file") 75 | JMESSAGE(JERR_PPM_NOT, "Not a PPM/PGM file") 76 | JMESSAGE(JTRC_PGM, "%ux%u PGM image") 77 | JMESSAGE(JTRC_PGM_TEXT, "%ux%u text PGM image") 78 | JMESSAGE(JTRC_PPM, "%ux%u PPM image") 79 | JMESSAGE(JTRC_PPM_TEXT, "%ux%u text PPM image") 80 | #endif /* PPM_SUPPORTED */ 81 | 82 | #ifdef RLE_SUPPORTED 83 | JMESSAGE(JERR_RLE_BADERROR, "Bogus error code from RLE library") 84 | JMESSAGE(JERR_RLE_COLORSPACE, "RLE output must be grayscale or RGB") 85 | JMESSAGE(JERR_RLE_DIMENSIONS, "Image dimensions (%ux%u) too large for RLE") 86 | JMESSAGE(JERR_RLE_EMPTY, "Empty RLE file") 87 | JMESSAGE(JERR_RLE_EOF, "Premature EOF in RLE header") 88 | JMESSAGE(JERR_RLE_MEM, "Insufficient memory for RLE header") 89 | JMESSAGE(JERR_RLE_NOT, "Not an RLE file") 90 | JMESSAGE(JERR_RLE_TOOMANYCHANNELS, "Cannot handle %d output channels for RLE") 91 | JMESSAGE(JERR_RLE_UNSUPPORTED, "Cannot handle this RLE setup") 92 | JMESSAGE(JTRC_RLE, "%ux%u full-color RLE file") 93 | JMESSAGE(JTRC_RLE_FULLMAP, "%ux%u full-color RLE file with map of length %d") 94 | JMESSAGE(JTRC_RLE_GRAY, "%ux%u grayscale RLE file") 95 | JMESSAGE(JTRC_RLE_MAPGRAY, "%ux%u grayscale RLE file with map of length %d") 96 | JMESSAGE(JTRC_RLE_MAPPED, "%ux%u colormapped RLE file with map of length %d") 97 | #endif /* RLE_SUPPORTED */ 98 | 99 | #ifdef TARGA_SUPPORTED 100 | JMESSAGE(JERR_TGA_BADCMAP, "Unsupported Targa colormap format") 101 | JMESSAGE(JERR_TGA_BADPARMS, "Invalid or unsupported Targa file") 102 | JMESSAGE(JERR_TGA_COLORSPACE, "Targa output must be grayscale or RGB") 103 | JMESSAGE(JTRC_TGA, "%ux%u RGB Targa image") 104 | JMESSAGE(JTRC_TGA_GRAY, "%ux%u grayscale Targa image") 105 | JMESSAGE(JTRC_TGA_MAPPED, "%ux%u colormapped Targa image") 106 | #else 107 | JMESSAGE(JERR_TGA_NOTCOMP, "Targa support was not compiled") 108 | #endif /* TARGA_SUPPORTED */ 109 | 110 | JMESSAGE(JERR_BAD_CMAP_FILE, 111 | "Color map file is invalid or of unsupported format") 112 | JMESSAGE(JERR_TOO_MANY_COLORS, 113 | "Output file format cannot handle %d colormap entries") 114 | JMESSAGE(JERR_UNGETC_FAILED, "ungetc failed") 115 | #ifdef TARGA_SUPPORTED 116 | JMESSAGE(JERR_UNKNOWN_FORMAT, 117 | "Unrecognized input file format --- perhaps you need -targa") 118 | #else 119 | JMESSAGE(JERR_UNKNOWN_FORMAT, "Unrecognized input file format") 120 | #endif 121 | JMESSAGE(JERR_UNSUPPORTED_FORMAT, "Unsupported output file format") 122 | 123 | #ifdef JMAKE_ENUM_LIST 124 | 125 | JMSG_LASTADDONCODE 126 | } ADDON_MESSAGE_CODE; 127 | 128 | #undef JMAKE_ENUM_LIST 129 | #endif /* JMAKE_ENUM_LIST */ 130 | 131 | /* Zap JMESSAGE macro so that future re-inclusions do nothing by default */ 132 | #undef JMESSAGE 133 | -------------------------------------------------------------------------------- /stego/cdjpeg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * cdjpeg.h 3 | * 4 | * Copyright (C) 1994-1997, Thomas G. Lane. 5 | * This file is part of the Independent JPEG Group's software. 6 | * For conditions of distribution and use, see the accompanying README file. 7 | * 8 | * This file contains common declarations for the sample applications 9 | * cjpeg and djpeg. It is NOT used by the core JPEG library. 10 | */ 11 | 12 | #define JPEG_CJPEG_DJPEG /* define proper options in jconfig.h */ 13 | #define JPEG_INTERNAL_OPTIONS /* cjpeg.c,djpeg.c need to see xxx_SUPPORTED */ 14 | #include "jinclude.h" 15 | #include "jpeglib.h" 16 | #include "jerror.h" /* get library error codes too */ 17 | #include "cderror.h" /* get application-specific error codes */ 18 | 19 | 20 | /* 21 | * Object interface for cjpeg's source file decoding modules 22 | */ 23 | 24 | typedef struct cjpeg_source_struct * cjpeg_source_ptr; 25 | 26 | struct cjpeg_source_struct { 27 | JMETHOD(void, start_input, (j_compress_ptr cinfo, 28 | cjpeg_source_ptr sinfo)); 29 | JMETHOD(JDIMENSION, get_pixel_rows, (j_compress_ptr cinfo, 30 | cjpeg_source_ptr sinfo)); 31 | JMETHOD(void, finish_input, (j_compress_ptr cinfo, 32 | cjpeg_source_ptr sinfo)); 33 | 34 | FILE *input_file; 35 | 36 | JSAMPARRAY buffer; 37 | JDIMENSION buffer_height; 38 | }; 39 | 40 | 41 | /* 42 | * Object interface for djpeg's output file encoding modules 43 | */ 44 | 45 | typedef struct djpeg_dest_struct * djpeg_dest_ptr; 46 | 47 | struct djpeg_dest_struct { 48 | /* start_output is called after jpeg_start_decompress finishes. 49 | * The color map will be ready at this time, if one is needed. 50 | */ 51 | JMETHOD(void, start_output, (j_decompress_ptr cinfo, 52 | djpeg_dest_ptr dinfo)); 53 | /* Emit the specified number of pixel rows from the buffer. */ 54 | JMETHOD(void, put_pixel_rows, (j_decompress_ptr cinfo, 55 | djpeg_dest_ptr dinfo, 56 | JDIMENSION rows_supplied)); 57 | /* Finish up at the end of the image. */ 58 | JMETHOD(void, finish_output, (j_decompress_ptr cinfo, 59 | djpeg_dest_ptr dinfo)); 60 | 61 | /* Target file spec; filled in by djpeg.c after object is created. */ 62 | FILE * output_file; 63 | 64 | /* Output pixel-row buffer. Created by module init or start_output. 65 | * Width is cinfo->output_width * cinfo->output_components; 66 | * height is buffer_height. 67 | */ 68 | JSAMPARRAY buffer; 69 | JDIMENSION buffer_height; 70 | }; 71 | 72 | 73 | /* 74 | * cjpeg/djpeg may need to perform extra passes to convert to or from 75 | * the source/destination file format. The JPEG library does not know 76 | * about these passes, but we'd like them to be counted by the progress 77 | * monitor. We use an expanded progress monitor object to hold the 78 | * additional pass count. 79 | */ 80 | 81 | struct cdjpeg_progress_mgr { 82 | struct jpeg_progress_mgr pub; /* fields known to JPEG library */ 83 | int completed_extra_passes; /* extra passes completed */ 84 | int total_extra_passes; /* total extra */ 85 | /* last printed percentage stored here to avoid multiple printouts */ 86 | int percent_done; 87 | }; 88 | 89 | typedef struct cdjpeg_progress_mgr * cd_progress_ptr; 90 | 91 | 92 | /* Short forms of external names for systems with brain-damaged linkers. */ 93 | 94 | #ifdef NEED_SHORT_EXTERNAL_NAMES 95 | #define jinit_read_bmp jIRdBMP 96 | #define jinit_write_bmp jIWrBMP 97 | #define jinit_read_gif jIRdGIF 98 | #define jinit_write_gif jIWrGIF 99 | #define jinit_read_ppm jIRdPPM 100 | #define jinit_write_ppm jIWrPPM 101 | #define jinit_read_rle jIRdRLE 102 | #define jinit_write_rle jIWrRLE 103 | #define jinit_read_targa jIRdTarga 104 | #define jinit_write_targa jIWrTarga 105 | #define read_quant_tables RdQTables 106 | #define read_scan_script RdScnScript 107 | #define set_quant_slots SetQSlots 108 | #define set_sample_factors SetSFacts 109 | #define read_color_map RdCMap 110 | #define enable_signal_catcher EnSigCatcher 111 | #define start_progress_monitor StProgMon 112 | #define end_progress_monitor EnProgMon 113 | #define read_stdin RdStdin 114 | #define write_stdout WrStdout 115 | #endif /* NEED_SHORT_EXTERNAL_NAMES */ 116 | 117 | /* Module selection routines for I/O modules. */ 118 | 119 | EXTERN(cjpeg_source_ptr) jinit_read_bmp JPP((j_compress_ptr cinfo)); 120 | EXTERN(djpeg_dest_ptr) jinit_write_bmp JPP((j_decompress_ptr cinfo, 121 | boolean is_os2)); 122 | EXTERN(cjpeg_source_ptr) jinit_read_gif JPP((j_compress_ptr cinfo)); 123 | EXTERN(djpeg_dest_ptr) jinit_write_gif JPP((j_decompress_ptr cinfo)); 124 | EXTERN(cjpeg_source_ptr) jinit_read_ppm JPP((j_compress_ptr cinfo)); 125 | EXTERN(djpeg_dest_ptr) jinit_write_ppm JPP((j_decompress_ptr cinfo)); 126 | EXTERN(cjpeg_source_ptr) jinit_read_rle JPP((j_compress_ptr cinfo)); 127 | EXTERN(djpeg_dest_ptr) jinit_write_rle JPP((j_decompress_ptr cinfo)); 128 | EXTERN(cjpeg_source_ptr) jinit_read_targa JPP((j_compress_ptr cinfo)); 129 | EXTERN(djpeg_dest_ptr) jinit_write_targa JPP((j_decompress_ptr cinfo)); 130 | 131 | /* cjpeg support routines (in rdswitch.c) */ 132 | 133 | EXTERN(boolean) read_quant_tables JPP((j_compress_ptr cinfo, char * filename, 134 | int scale_factor, boolean force_baseline)); 135 | EXTERN(boolean) read_scan_script JPP((j_compress_ptr cinfo, char * filename)); 136 | EXTERN(boolean) set_quant_slots JPP((j_compress_ptr cinfo, char *arg)); 137 | EXTERN(boolean) set_sample_factors JPP((j_compress_ptr cinfo, char *arg)); 138 | 139 | /* djpeg support routines (in rdcolmap.c) */ 140 | 141 | EXTERN(void) read_color_map JPP((j_decompress_ptr cinfo, FILE * infile)); 142 | 143 | /* common support routines (in cdjpeg.c) */ 144 | 145 | EXTERN(void) enable_signal_catcher JPP((j_common_ptr cinfo)); 146 | EXTERN(void) start_progress_monitor JPP((j_common_ptr cinfo, 147 | cd_progress_ptr progress)); 148 | EXTERN(void) end_progress_monitor JPP((j_common_ptr cinfo)); 149 | EXTERN(boolean) keymatch JPP((char * arg, const char * keyword, int minchars)); 150 | EXTERN(FILE *) read_stdin JPP((void)); 151 | EXTERN(FILE *) write_stdout JPP((void)); 152 | 153 | /* miscellaneous useful macros */ 154 | 155 | #ifdef DONT_USE_B_MODE /* define mode parameters for fopen() */ 156 | #define READ_BINARY "r" 157 | #define WRITE_BINARY "w" 158 | #else 159 | #ifdef VMS /* VMS is very nonstandard */ 160 | #define READ_BINARY "rb", "ctx=stm" 161 | #define WRITE_BINARY "wb", "ctx=stm" 162 | #else /* standard ANSI-compliant case */ 163 | #define READ_BINARY "rb" 164 | #define WRITE_BINARY "wb" 165 | #endif 166 | #endif 167 | 168 | #ifndef EXIT_FAILURE /* define exit() codes if not provided */ 169 | #define EXIT_FAILURE 1 170 | #endif 171 | #ifndef EXIT_SUCCESS 172 | #ifdef VMS 173 | #define EXIT_SUCCESS 1 /* VMS is very nonstandard */ 174 | #else 175 | #define EXIT_SUCCESS 0 176 | #endif 177 | #endif 178 | #ifndef EXIT_WARNING 179 | #ifdef VMS 180 | #define EXIT_WARNING 1 /* VMS is very nonstandard */ 181 | #else 182 | #define EXIT_WARNING 2 183 | #endif 184 | #endif 185 | -------------------------------------------------------------------------------- /stego/embed.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "cdjpeg.h" /* Common decls for Reading jpeg files */ 5 | 6 | // adapted from code from: http://www.cs.odu.edu/~cs772/demos/steganography/sshettyLinux/ 7 | // USAGE: ./encode 8 | 9 | 10 | void convert(const char str[], const size_t str_len, char * mask) { 11 | char *result = malloc(str_len*8); // 1 bit for every byte 12 | int i, j, hex_res, k = 0; 13 | 14 | for ( i = 0 ; i < strlen(str) ; i++ ) 15 | { 16 | int test_mask = 0x80; 17 | int test = str[i]; 18 | for ( j = 0 ; j < 8 ; j++ ) 19 | { 20 | int res = test & test_mask; 21 | if (res ) 22 | result[k++] = '1'; 23 | else result[k++] = '0'; 24 | test_mask = test_mask >> 1; 25 | } 26 | } 27 | int result_len = k; 28 | for ( i = 0, j=0 ; i < result_len ; i += 2, j+=1 ) 29 | { 30 | mask[j] = 0; 31 | if ( result[i] == '1') mask[j] |= 0x02; 32 | if ( result[i + 1] == '1') mask[j] |= 0x01; 33 | } 34 | free(result); 35 | } 36 | 37 | 38 | void JPEG_copy_encodebits( const char * input_filename, const char * output_filename, const char * str, const size_t str_len) { 39 | struct jpeg_decompress_struct srcinfo; 40 | struct jpeg_compress_struct dstinfo; 41 | struct jpeg_error_mgr jsrcerr, jdsterr; 42 | jvirt_barray_ptr *coef_arrays; 43 | JDIMENSION i, compnum, rownum, blocknum; 44 | size_t block_row_size; 45 | JBLOCKARRAY coef_buffers[MAX_COMPONENTS], zig_zag[MAX_COMPONENTS]; 46 | JBLOCKARRAY row_ptrs[MAX_COMPONENTS]; 47 | 48 | FILE * input_file = fopen(input_filename , "rb"); 49 | FILE * output_file = fopen(output_filename , "wb"); 50 | 51 | int filesize, text = 0, length_set = 0, data_set = 0, count_bits = 0; 52 | char *bin_str, *res_str, mess_encr[255], encode_str[255]; 53 | 54 | int len_a = 3, val_a = 0, len_b = 0, val_b = 0, test_amask = 0x04, test_bmask; 55 | int len_mask = 0, index, k, l, fp_char = 0, c, temp, elem; 56 | 57 | for ( elem = 0 ; elem <= 15 ; elem++) 58 | { 59 | int temp = pow( 2 , elem); 60 | if (temp > str_len ) 61 | { 62 | len_b = elem - 1; 63 | break; 64 | } 65 | } 66 | test_bmask = pow(2, len_b); 67 | 68 | //bin_str = convert(str); 69 | size_t mask_len = str_len*8/2; 70 | char * mask = calloc( mask_len, sizeof(char)); // 2 bits per char 71 | convert(str, str_len, mask); 72 | 73 | srcinfo.err = jpeg_std_error(&jsrcerr); 74 | jpeg_create_decompress(&srcinfo); 75 | dstinfo.err = jpeg_std_error(&jdsterr); 76 | jpeg_create_compress(&dstinfo); 77 | jsrcerr.trace_level = jdsterr.trace_level; 78 | srcinfo.mem->max_memory_to_use = dstinfo.mem->max_memory_to_use; 79 | 80 | jpeg_stdio_src(&srcinfo, input_file); 81 | 82 | (void) jpeg_read_header(&srcinfo, TRUE); 83 | for (compnum = 0; compnum < srcinfo.num_components; compnum++) 84 | coef_buffers[compnum] = ((&dstinfo)->mem->alloc_barray) 85 | ((j_common_ptr) &dstinfo, JPOOL_IMAGE, 86 | srcinfo.comp_info[compnum].width_in_blocks, 87 | srcinfo.comp_info[compnum].height_in_blocks); 88 | coef_arrays = jpeg_read_coefficients(&srcinfo); 89 | jpeg_copy_critical_parameters(&srcinfo, &dstinfo); 90 | 91 | 92 | for (compnum = 0; compnum < srcinfo.num_components; compnum++) 93 | { 94 | block_row_size = (size_t) SIZEOF(JCOEF) * DCTSIZE2 * srcinfo.comp_info[compnum].width_in_blocks; 95 | 96 | for (rownum = 0; rownum < srcinfo.comp_info[compnum].height_in_blocks; rownum++) 97 | { 98 | int k , l, tag = 1, j; 99 | row_ptrs[compnum] = ((&dstinfo)->mem->access_virt_barray) 100 | ((j_common_ptr) &dstinfo, coef_arrays[compnum], 101 | rownum, (JDIMENSION) 1, FALSE); 102 | for (blocknum = 0; blocknum < srcinfo.comp_info[compnum].width_in_blocks; 103 | blocknum++) 104 | { 105 | int x, y, count = 0; 106 | k = -1; l = 0; 107 | for (i = 0; i < DCTSIZE2; i++) 108 | { 109 | if (!length_set) 110 | { 111 | 112 | if ( (i > 0) && ( row_ptrs[compnum][0][blocknum][i] != 0) && (abs(row_ptrs[compnum][0][blocknum][i]) > 15)) 113 | { 114 | 115 | int test; 116 | if ( len_a == 0 && len_b == 0 ) length_set = 1; 117 | if (len_a != 0) 118 | { 119 | int test_amask = 0x08; 120 | len_a = 0; 121 | test = abs(row_ptrs[compnum][0][blocknum][i]); 122 | test >>= 4; 123 | for ( k = 0 ; k < 4 ; k++ ) 124 | { 125 | test <<= 1; 126 | if ( test_amask & len_b) 127 | test |= 1; 128 | test_amask >>= 1; 129 | } 130 | } 131 | else 132 | { 133 | len_b -= 1; 134 | test = abs(row_ptrs[compnum][0][blocknum][i]); 135 | test >>= 1; 136 | test <<= 1; 137 | if ( str_len & test_bmask) test |= 1; 138 | test_bmask >>= 1; 139 | } 140 | 141 | if ( row_ptrs[compnum][0][blocknum][i] < 0 ) 142 | coef_buffers[compnum][rownum][blocknum][i] = -test; 143 | 144 | else 145 | coef_buffers[compnum][rownum][blocknum][i] = test; 146 | 147 | } 148 | else 149 | coef_buffers[compnum][rownum][blocknum][i] = 150 | row_ptrs[compnum][0][blocknum][i] ; 151 | } 152 | else 153 | { 154 | if (!data_set) 155 | { 156 | if ( ( i > 0 ) && ( row_ptrs[compnum][0][blocknum][i] != 0) && (abs(row_ptrs[compnum][0][blocknum][i]) > 1)) 157 | { 158 | 159 | int k = 0; 160 | int test = abs(row_ptrs[compnum][0][blocknum][i]); 161 | if ( test >= 4 ) 162 | { 163 | int test_mask = 0x02; 164 | test >>= 2; 165 | for ( k = 0 ; k < 2 ; k++ ) 166 | { 167 | test <<= 1; 168 | if ( mask[count_bits] & test_mask) 169 | test |= 0x01; 170 | test_mask >>= 1; 171 | } 172 | if ( test != 0 && test != 1) 173 | { 174 | if ( row_ptrs[compnum][0][blocknum][i] < 0 ) 175 | coef_buffers[compnum][rownum][blocknum][i] = -test; 176 | else 177 | coef_buffers[compnum][rownum][blocknum][i] = test; 178 | count_bits++; 179 | } 180 | } 181 | if ( count_bits == mask_len ) data_set = 1; 182 | 183 | } 184 | else coef_buffers[compnum][rownum][blocknum][i] = row_ptrs[compnum][0][blocknum][i] ; 185 | } 186 | else coef_buffers[compnum][rownum][blocknum][i] = row_ptrs[compnum][0][blocknum][i] ; 187 | } 188 | 189 | } 190 | } 191 | } 192 | } 193 | for (compnum = 0; compnum < srcinfo.num_components; compnum++) 194 | { 195 | block_row_size = (size_t) SIZEOF(JCOEF) * DCTSIZE2 196 | * srcinfo.comp_info[compnum].width_in_blocks; 197 | for (rownum = 0; rownum < srcinfo.comp_info[compnum].height_in_blocks; rownum++) 198 | { 199 | int k , l; 200 | row_ptrs[compnum] = ((&dstinfo)->mem->access_virt_barray) 201 | ((j_common_ptr) &dstinfo, coef_arrays[compnum], 202 | rownum, (JDIMENSION) 1, FALSE); 203 | for (blocknum = 0; blocknum < srcinfo.comp_info[compnum].width_in_blocks; 204 | blocknum++) 205 | for (i = 0; i < DCTSIZE2; i++) 206 | row_ptrs[compnum][0][blocknum][i] = coef_buffers[compnum][rownum][blocknum][i] ; 207 | } 208 | } 209 | jpeg_stdio_dest(&dstinfo, output_file); 210 | jpeg_write_coefficients(&dstinfo, coef_arrays); 211 | jpeg_finish_compress(&dstinfo); 212 | jpeg_destroy_compress(&dstinfo); 213 | (void) jpeg_finish_decompress(&srcinfo); 214 | jpeg_destroy_decompress(&srcinfo); 215 | fclose(output_file); 216 | fclose (input_file); 217 | free(mask); 218 | } 219 | 220 | 221 | int 222 | main (int argc, char **argv) 223 | { 224 | if ( argc == 4 ) 225 | { 226 | FILE * fp_message; 227 | char *str; 228 | char *bin_str; 229 | char c, fp_char; 230 | 231 | fp_message = fopen(argv[2] , "r"); 232 | 233 | /* Read the message to be encoded into the buffer */ 234 | str = (char *) malloc (33000); 235 | while ((c = fgetc(fp_message)) != -1) 236 | str[fp_char++] = c; 237 | str[fp_char] = '\0'; 238 | fseek(fp_message, 0, SEEK_SET); 239 | 240 | printf ("Message File Read and Converted to Bits\n"); 241 | 242 | printf ("Reading High Frequency Components from the Image \n"); 243 | JPEG_copy_encodebits(argv[1], argv[3], str, strlen(str)); 244 | 245 | free(str); 246 | 247 | } else { 248 | printf ("Usage : \n"); 249 | printf ("./embed : \n"); 250 | } 251 | 252 | return 0; /* suppress no-return-value warnings */ 253 | } 254 | 255 | -------------------------------------------------------------------------------- /api_access/InstagramAPI.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # Forked from https://github.com/LevPasha/Instagram-API-python (WTFPL License) 4 | 5 | import requests 6 | import random 7 | import json 8 | import hashlib 9 | import hmac 10 | import urllib 11 | import time 12 | from PIL import Image 13 | from time import strftime 14 | 15 | 16 | class InstagramAPI(): 17 | 18 | API_URL = 'https://i.instagram.com/api/v1/' 19 | USER_AGENT = 'Instagram 8.4.0 Android (18/4.3; 320dpi; 720x1280; Xiaomi; HM 1SW; armani; qcom; en_US)' 20 | IG_SIG_KEY = '3d3d669cedc38f2ea7d198840e0648db3738224a0f661aa6a2c1e77dfa964a1e' 21 | EXPERIMENTS = 'ig_android_progressive_jpeg,ig_creation_growth_holdout,ig_android_report_and_hide,' + \ 22 | 'ig_android_new_browser,ig_android_enable_share_to_whatsapp,' + \ 23 | 'ig_android_direct_drawing_in_quick_cam_universe,ig_android_huawei_app_badging,' + \ 24 | 'ig_android_universe_video_production,ig_android_asus_app_badging,ig_android_direct_plus_button,' + \ 25 | 'ig_android_ads_heatmap_overlay_universe,ig_android_http_stack_experiment_2016,' + \ 26 | 'ig_android_infinite_scrolling,ig_fbns_blocked,ig_android_white_out_universe,' + \ 27 | 'ig_android_full_people_card_in_user_list,ig_android_post_auto_retry_v7_21,ig_fbns_push,' + \ 28 | 'ig_android_feed_pill,ig_android_profile_link_iab,ig_explore_v3_us_holdout,' + \ 29 | 'ig_android_histogram_reporter,ig_android_anrwatchdog,ig_android_search_client_matching,' + \ 30 | 'ig_android_high_res_upload_2,ig_android_new_browser_pre_kitkat,ig_android_2fac,' + \ 31 | 'ig_android_grid_video_icon,ig_android_white_camera_universe,ig_android_disable_chroma_subsampling,' + \ 32 | 'ig_android_share_spinner,ig_android_explore_people_feed_icon,ig_explore_v3_android_universe,' + \ 33 | 'ig_android_media_favorites,ig_android_nux_holdout,ig_android_search_null_state,' + \ 34 | 'ig_android_react_native_notification_setting,ig_android_ads_indicator_change_universe,' + \ 35 | 'ig_android_video_loading_behavior,ig_android_black_camera_tab,liger_instagram_android_univ,' + \ 36 | 'ig_explore_v3_internal,ig_android_direct_emoji_picker,ig_android_prefetch_explore_delay_time,' + \ 37 | 'ig_android_business_insights_qe,ig_android_direct_media_size,ig_android_enable_client_share,' + \ 38 | 'ig_android_promoted_posts,ig_android_app_badging_holdout,ig_android_ads_cta_universe,' + \ 39 | 'ig_android_mini_inbox_2,ig_android_feed_reshare_button_nux,ig_android_boomerang_feed_attribution,' + \ 40 | 'ig_android_fbinvite_qe,ig_fbns_shared,ig_android_direct_full_width_media,' + \ 41 | 'ig_android_hscroll_profile_chaining,ig_android_feed_unit_footer,ig_android_media_tighten_space,' + \ 42 | 'ig_android_private_follow_request,ig_android_inline_gallery_backoff_hours_universe,' + \ 43 | 'ig_android_direct_thread_ui_rewrite,ig_android_rendering_controls,' + \ 44 | 'ig_android_ads_full_width_cta_universe,ig_video_max_duration_qe_preuniverse,' + \ 45 | 'ig_android_prefetch_explore_expire_time,ig_timestamp_public_test,ig_android_profile,' + \ 46 | 'ig_android_dv2_consistent_http_realtime_response,ig_android_enable_share_to_messenger,' + \ 47 | 'ig_explore_v3,ig_ranking_following,ig_android_pending_request_search_bar,' + \ 48 | 'ig_android_feed_ufi_redesign,ig_android_video_pause_logging_fix,ig_android_default_folder_to_camera,' + \ 49 | 'ig_android_video_stitching_7_23,ig_android_profanity_filter,ig_android_business_profile_qe,' + \ 50 | 'ig_android_search,ig_android_boomerang_entry,ig_android_inline_gallery_universe,' + \ 51 | 'ig_android_ads_overlay_design_universe,ig_android_options_app_invite,' + \ 52 | 'ig_android_view_count_decouple_likes_universe,ig_android_periodic_analytics_upload_v2,' + \ 53 | 'ig_android_feed_unit_hscroll_auto_advance,ig_peek_profile_photo_universe,' + \ 54 | 'ig_android_ads_holdout_universe,ig_android_prefetch_explore,ig_android_direct_bubble_icon,' + \ 55 | 'ig_video_use_sve_universe,ig_android_inline_gallery_no_backoff_on_launch_universe,' + \ 56 | 'ig_android_image_cache_multi_queue,ig_android_camera_nux,ig_android_immersive_viewer,' + \ 57 | 'ig_android_dense_feed_unit_cards,ig_android_sqlite_dev,ig_android_exoplayer,ig_android_add_to_last_post,' + \ 58 | 'ig_android_direct_public_threads,ig_android_prefetch_venue_in_composer,ig_android_bigger_share_button,' + \ 59 | 'ig_android_dv2_realtime_private_share,ig_android_non_square_first,ig_android_video_interleaved_v2,' + \ 60 | 'ig_android_follow_search_bar,ig_android_last_edits,ig_android_video_download_logging,' + \ 61 | 'ig_android_ads_loop_count_universe,ig_android_swipeable_filters_blacklist,' + \ 62 | 'ig_android_boomerang_layout_white_out_universe,ig_android_ads_carousel_multi_row_universe,' + \ 63 | 'ig_android_mentions_invite_v2,ig_android_direct_mention_qe,' + \ 64 | 'ig_android_following_follower_social_context' 65 | SIG_KEY_VERSION = '4' 66 | IGDataPath = 'data/' 67 | 68 | # username # Instagram username 69 | # password # Instagram password 70 | # debug # Debug 71 | # uuid # UUID 72 | # device_id # Device ID 73 | # username_id # Username ID 74 | # token # _csrftoken 75 | # isLoggedIn # Session status 76 | # rank_token # Rank token 77 | # IGDataPath # Data storage path 78 | 79 | def __init__(self, username, password, debug=False, IGDataPath=None): 80 | m = hashlib.md5() 81 | m.update(username.encode('utf-8') + password.encode('utf-8')) 82 | self.device_id = self.generateDeviceId(m.hexdigest()) 83 | self.setUser(username, password) 84 | self.isLoggedIn = False 85 | self.LastResponse = None 86 | self.setUser(username, password) 87 | self.login() 88 | 89 | def setUser(self, username, password): 90 | self.username = username 91 | self.password = password 92 | self.uuid = self.generateUUID(True) 93 | 94 | def login(self, force=False): 95 | if (not self.isLoggedIn or force): 96 | self.s = requests.Session() 97 | # if you need proxy make something like this: 98 | # self.s.proxies = {"https" : "http://proxyip:proxyport"} 99 | if (self.SendRequest('si/fetch_headers/?challenge_type=signup&guid=' + self.generateUUID(False), 100 | None, True)): 101 | data = {'phone_id': self.generateUUID(True), 102 | '_csrftoken': self.LastResponse.cookies['csrftoken'], 103 | 'username': self.username, 104 | 'guid': self.uuid, 105 | 'device_id': self.device_id, 106 | 'password': self.password, 107 | 'login_attempt_count': '0'} 108 | 109 | attempt = self.SendRequest('accounts/login/', self.generateSignature(json.dumps(data)), True) 110 | if (attempt): 111 | print self.LastJson 112 | self.sessionid = self.s.cookies['sessionid'] 113 | self.isLoggedIn = True 114 | self.username_id = self.LastJson["logged_in_user"]["pk"] 115 | self.rank_token = "%s_%s" % (self.username_id, self.uuid) 116 | self.token = self.LastResponse.cookies["csrftoken"] 117 | 118 | self.syncFeatures() 119 | print ("Login success!\n") 120 | return True 121 | 122 | def syncFeatures(self): 123 | data = json.dumps({ 124 | '_uuid': self.uuid, 125 | '_uid': self.username_id, 126 | 'id': self.username_id, 127 | '_csrftoken': self.token, 128 | 'experiments': self.EXPERIMENTS 129 | }) 130 | return self.SendRequest('qe/sync/', self.generateSignature(data)) 131 | 132 | def expose(self): 133 | data = json.dumps({ 134 | '_uuid': self.uuid, 135 | '_uid': self.username_id, 136 | 'id': self.username_id, 137 | '_csrftoken': self.token, 138 | 'experiment': 'ig_android_profile_contextual_feed' 139 | }) 140 | return self.SendRequest('qe/expose/', self.generateSignature(data)) 141 | 142 | def buildBody(self, upload_id, uuid, token, filepath): 143 | boundary = uuid 144 | 145 | bodies = [ 146 | { 147 | 'type': 'form-data', 148 | 'name': 'upload_id', 149 | 'data': upload_id, 150 | }, 151 | { 152 | 'type': 'form-data', 153 | 'name': '_uuid', 154 | 'data': uuid, 155 | }, 156 | { 157 | 'type': 'form-data', 158 | 'name': '_csrftoken', 159 | 'data': token, 160 | }, 161 | { 162 | 'type': 'form-data', 163 | 'name': 'image_compression', 164 | 'data': '{"lib_name":"jt","lib_version":"1.3.0","quality":"70"}', 165 | }, 166 | { 167 | 'type': 'form-data', 168 | 'name': 'photo', 169 | 'data': open(filepath, 'rb').read(), 170 | 'filename': 'pending_media_'+str(int((time.time()*1000)))+'.jpg', 171 | 'headers': ['Content-Transfer-Encoding: binary', 172 | 'Content-type: application/octet-stream'], 173 | }, 174 | ] 175 | 176 | body = '' 177 | for b in bodies: 178 | body += '--' + boundary + '\r\n' 179 | body += 'Content-Disposition: ' + b['type'] + '; name="' + b['name'] + '"' 180 | if (b.get('filename', False)): 181 | # ext = pathinfo(b['filename'], PATHINFO_EXTENSION); 182 | body += '; filename="' + 'pending_media_' + str(int((time.time()*1000))) + '.jpg"' 183 | if (b.get('headers', False)): 184 | for header in b['headers']: 185 | body += "\r\n" + header 186 | body += "\r\n\r\n" + b['data'] + "\r\n" 187 | body += '--' + boundary + '--' 188 | return body 189 | 190 | def uploadPhoto(self, filepath, caption=None, upload_id=None): 191 | if not upload_id: 192 | upload_id = str(int((time.time()*1000))) 193 | 194 | data = self.buildBody(upload_id, self.uuid, self.token, filepath) 195 | 196 | endpoint = 'https://i.instagram.com/api/v1/upload/photo/' 197 | 198 | cookies = [c[0]+'='+c[1]+';' for c in self.s.cookies.items()] 199 | cookie_str = ' '.join(cookies) 200 | 201 | headers = {'Connection': 'close', 202 | 'Accept': '*/*', 203 | 'Content-type': 'multipart/form-data; boundary='+self.uuid, 204 | 'Content-Length': str(len(data)), 205 | 'Cookie': cookie_str, 206 | 'Cookie2': '$Version=1', 207 | 'Accept-Language': 'en-US', 208 | 'Accept-Encoding': 'gzip', 209 | 'User-Agent': self.USER_AGENT, 210 | } 211 | 212 | resp = self.s.post(endpoint, data=data, headers=headers) 213 | print json.loads(resp.text) 214 | 215 | self.expose() 216 | self.configure(upload_id, filepath, caption) 217 | 218 | return self.s.cookies 219 | 220 | def configure(self, upload_id, filepath, caption): 221 | im = Image.open(filepath) 222 | size = im.size 223 | 224 | post = json.dumps({ 225 | 'upload_id': upload_id, 226 | 'camera_model': 'HM1S', 227 | 'source_type': 3, 228 | 'date_time_original': strftime("%Y:%m:%d %H:%M:%S"), 229 | 'camera_make': 'XIAOMI', 230 | 'edits': { 231 | 'crop_original_size': [size[0], size[1]], 232 | 'crop_zoom': 1.3333334, 233 | 'crop_center': [0.0, -0.0], 234 | }, 235 | 'extra': { 236 | 'source_width': size[1], 237 | 'source_height': size[0], 238 | }, 239 | 'device': { 240 | 'manufacturer': 'Xiaomi', 241 | 'model': 'HM 1SW', 242 | 'android_version': 18, 243 | 'android_release': '4.3', 244 | }, 245 | '_csrftoken': self.token, 246 | '_uuid': self.uuid, 247 | '_uid': self.username_id, 248 | 'caption': caption, 249 | }) 250 | 251 | post = post.replace('"crop_center":[0,0]', '"crop_center":[0.0,-0.0]') 252 | 253 | return self.SendRequest('media/configure/', self.generateSignature(post)) 254 | 255 | # ###### API calls not used by Instegogram ####### # 256 | 257 | # def logout(self): 258 | # logout = self.SendRequest('accounts/logout/') 259 | # # TODO Instagram.php 180-185 260 | 261 | # def autoCompleteUserList(self): 262 | # return self.SendRequest('friendships/autocomplete_user_list/') 263 | 264 | # def timelineFeed(self): 265 | # return self.SendRequest('feed/timeline/') 266 | 267 | # def megaphoneLog(self): 268 | # return self.SendRequest('megaphone/log/') 269 | 270 | # def uploadVideo(self, video, caption = None): 271 | # # TODO Instagram.php 290-415 272 | # return False 273 | 274 | # def direct_share(self, media_id, recipients, text = None): 275 | # # TODO Instagram.php 420-490 276 | # return False 277 | 278 | # def configureVideo(upload_id, video, caption = ''): 279 | # # TODO Instagram.php 490-530 280 | # return False 281 | 282 | # def editMedia(self, mediaId, captionText = ''): 283 | # data = json.dumps({ 284 | # '_uuid' : self.uuid, 285 | # '_uid' : self.username_id, 286 | # '_csrftoken' : self.token, 287 | # 'caption_text' : captionText 288 | # }) 289 | # return self.SendRequest('media/'+ str(mediaId) +'/edit_media/', self.generateSignature(data)) 290 | 291 | # def removeSelftag(self, mediaId): 292 | # data = json.dumps({ 293 | # '_uuid' : self.uuid, 294 | # '_uid' : self.username_id, 295 | # '_csrftoken' : self.token 296 | # }) 297 | # return self.SendRequest('media/'+ str(mediaId) +'/remove/', self.generateSignature(data)) 298 | 299 | # def mediaInfo(self, mediaId): 300 | # data = json.dumps({ 301 | # '_uuid' : self.uuid, 302 | # '_uid' : self.username_id, 303 | # '_csrftoken' : self.token, 304 | # 'media_id' : mediaId 305 | # }) 306 | # return self.SendRequest('media/'+ str(mediaId) +'/info/', self.generateSignature(data)) 307 | 308 | # def deleteMedia(self, mediaId): 309 | # data = json.dumps({ 310 | # '_uuid' : self.uuid, 311 | # '_uid' : self.username_id, 312 | # '_csrftoken' : self.token, 313 | # 'media_id' : mediaId 314 | # }) 315 | # return self.SendRequest('media/'+ str(mediaId) +'/delete/', self.generateSignature(data)) 316 | 317 | # def comment(self, mediaId, commentText): 318 | # data = json.dumps({ 319 | # '_uuid' : self.uuid, 320 | # '_uid' : self.username_id, 321 | # '_csrftoken' : self.token, 322 | # 'comment_text' : commentText 323 | # }) 324 | # return self.SendRequest('media/'+ str(mediaId) +'/comment/', self.generateSignature(data)) 325 | 326 | # def deleteComment(self, mediaId, captionText, commentId): 327 | # data = json.dumps({ 328 | # '_uuid' : self.uuid, 329 | # '_uid' : self.username_id, 330 | # '_csrftoken' : self.token, 331 | # 'caption_text' : captionText 332 | # }) 333 | # return self.SendRequest('media/'+ str(mediaId) +'/comment/'+ str(commentId) +'/delete/', 334 | # self.generateSignature(data)) 335 | 336 | # def changeProfilePicture(self, photo): 337 | # # TODO Instagram.php 705-775 338 | # return False 339 | 340 | # def removeProfilePicture(self): 341 | # data = json.dumps({ 342 | # '_uuid' : self.uuid, 343 | # '_uid' : self.username_id, 344 | # '_csrftoken' : self.token 345 | # }) 346 | # return self.SendRequest('accounts/remove_profile_picture/', self.generateSignature(data)) 347 | 348 | # def setPrivateAccount(self): 349 | # data = json.dumps({ 350 | # '_uuid' : self.uuid, 351 | # '_uid' : self.username_id, 352 | # '_csrftoken' : self.token 353 | # }) 354 | # return self.SendRequest('accounts/set_private/', self.generateSignature(data)) 355 | 356 | # def setPublicAccount(self): 357 | # data = json.dumps({ 358 | # '_uuid' : self.uuid, 359 | # '_uid' : self.username_id, 360 | # '_csrftoken' : self.token 361 | # }) 362 | # return self.SendRequest('accounts/set_public/', self.generateSignature(data)) 363 | 364 | # def getProfileData(self): 365 | # data = json.dumps({ 366 | # '_uuid' : self.uuid, 367 | # '_uid' : self.username_id, 368 | # '_csrftoken' : self.token 369 | # }) 370 | # return self.SendRequest('accounts/current_user/?edit=true', self.generateSignature(data)) 371 | 372 | # def editProfile(self, url, phone, first_name, biography, email, gender): 373 | # data = json.dumps({ 374 | # '_uuid' : self.uuid, 375 | # '_uid' : self.username_id, 376 | # '_csrftoken' : self.token, 377 | # 'external_url' : url, 378 | # 'phone_number' : phone, 379 | # 'username' : self.username, 380 | # 'full_name' : first_name, 381 | # 'biography' : biography, 382 | # 'email' : email, 383 | # 'gender' : gender, 384 | # }) 385 | # return self.SendRequest('accounts/edit_profile/', self.generateSignature(data)) 386 | 387 | # def getUsernameInfo(self, usernameId): 388 | # return self.SendRequest('users/'+ str(usernameId) +'/info/') 389 | 390 | # def getSelfUsernameInfo(self): 391 | # return self.getUsernameInfo(self.username_id) 392 | 393 | # def getRecentActivity(self): 394 | # activity = self.SendRequest('news/inbox/?') 395 | # # TODO Instagram.php 911-925 396 | # return activity 397 | 398 | # def getFollowingRecentActivity(self): 399 | # activity = self.SendRequest('news/?') 400 | # # TODO Instagram.php 935-945 401 | # return activity 402 | 403 | # def getv2Inbox(self): 404 | # inbox = self.SendRequest('direct_v2/inbox/?') 405 | # # TODO Instagram.php 950-960 406 | # return inbox 407 | 408 | # def getUserTags(self, usernameId): 409 | # tags = self.SendRequest('usertags/'+ str(usernameId) +'/feed/?rank_token='+ \ 410 | # str(self.rank_token) +'&ranked_content=true&') 411 | # # TODO Instagram.php 975-985 412 | # return tags 413 | 414 | # def getSelfUserTags(self): 415 | # return self.getUserTags(self.username_id) 416 | 417 | # def tagFeed(self, tag): 418 | # userFeed = self.SendRequest('feed/tag/'+ str(tag) +'/?rank_token=' + \ 419 | # str(self.rank_token) + '&ranked_content=true&') 420 | # # TODO Instagram.php 1000-1015 421 | # return userFeed 422 | 423 | # def getMediaLikers(self, mediaId): 424 | # likers = self.SendRequest('media/'+ str(mediaId) +'/likers/?') 425 | # # TODO Instagram.php 1025-1035 426 | # return likers 427 | 428 | # def getGeoMedia(self, usernameId): 429 | # locations = self.SendRequest('maps/user/'+ str(usernameId) +'/') 430 | # # TODO Instagram.php 1050-1060 431 | # return locations 432 | 433 | # def getSelfGeoMedia(self): 434 | # return self.getGeoMedia(self.username_id) 435 | 436 | # def fbUserSearch(self, query): 437 | # query = self.SendRequest('fbsearch/topsearch/?context=blended&query='+ str(query) + \ 438 | # '&rank_token='+ str(self.rank_token)) 439 | # # TODO Instagram.php 1080-1090 440 | # return query 441 | 442 | # def searchUsers(self, query): 443 | # query = self.SendRequest('users/search/?ig_sig_key_version='+ str(self.SIG_KEY_VERSION) 444 | # +'&is_typeahead=true&query='+ str(query) +'&rank_token='+ str(self.rank_token)) 445 | # # TODO Instagram.php 1100-1110 446 | # return query 447 | 448 | # def searchUsername(self, usernameName): 449 | # query = self.SendRequest('users/'+ str(usernameName) +'/usernameinfo/') 450 | # # TODO Instagram.php 1080-1090 451 | # return query 452 | 453 | # def syncFromAdressBook(self, contacts): 454 | # return self.SendRequest('address_book/link/?include=extra_display_name,thumbnails', json.dumps(contacts)) 455 | 456 | # def searchTags(self, query): 457 | # query = self.SendRequest('tags/search/?is_typeahead=true&q='+ str(query) + \ 458 | # '&rank_token='+ str(self.rank_token)) 459 | # # TODO Instagram.php 1160-1170 460 | # return query 461 | 462 | # def getTimeline(self): 463 | # query = self.SendRequest('feed/timeline/?rank_token='+ str(self.rank_token) +'&ranked_content=true&') 464 | # # TODO Instagram.php 1180-1190 465 | # return query 466 | 467 | # def getUserFeed(self, usernameId, maxid = '', minTimestamp = None): 468 | # # TODO Instagram.php 1200-1220 469 | # return False 470 | 471 | # def getSelfUserFeed(self): 472 | # return self.getUserFeed(self.username_id) 473 | 474 | # def getHashtagFeed(self, hashtagString, maxid = ''): 475 | # # TODO Instagram.php 1230-1250 476 | # return False 477 | 478 | # def searchLocation(self, query): 479 | # locationFeed = self.SendRequest('fbsearch/places/?rank_token='+ str(self.rank_token) +'&query=' + str(query)) 480 | # # TODO Instagram.php 1250-1270 481 | # return locationFeed 482 | 483 | # def getLocationFeed(self, locationId, maxid = ''): 484 | # # TODO Instagram.php 1280-1300 485 | # return False 486 | 487 | # def getPopularFeed(self): 488 | # popularFeed = self.SendRequest('feed/popular/?people_teaser_supported=1&rank_token='+ \ 489 | # str(self.rank_token) +'&ranked_content=true&') 490 | # # TODO Instagram.php 1315-1325 491 | # return popularFeed 492 | 493 | # def getUserFollowings(self, usernameId, maxid = ''): 494 | # return self.SendRequest('friendships/'+ str(usernameId) +'/following/?max_id='+ str(maxid) 495 | # +'&ig_sig_key_version='+ self.SIG_KEY_VERSION +'&rank_token='+ self.rank_token) 496 | 497 | # def getSelfUsersFollowing(self): 498 | # return self.getUserFollowings(self.username_id) 499 | 500 | # def getUserFollowers(self, usernameId, maxid = ''): 501 | # return self.SendRequest('friendships/'+ str(usernameId) +'/followers/?max_id='+ str(maxid) 502 | # +'&ig_sig_key_version='+ self.SIG_KEY_VERSION +'&rank_token='+ self.rank_token) 503 | 504 | # def getSelfUserFollowers(self): 505 | # return self.getUserFollowers(self.username_id) 506 | 507 | # def like(self, mediaId): 508 | # data = json.dumps({ 509 | # '_uuid': self.uuid, 510 | # '_uid': self.username_id, 511 | # '_csrftoken': self.token, 512 | # 'media_id': mediaId 513 | # }) 514 | # return self.SendRequest('media/' + str(mediaId) + '/like/', self.generateSignature(data)) 515 | 516 | # def unlike(self, mediaId): 517 | # data = json.dumps({ 518 | # '_uuid' : self.uuid, 519 | # '_uid' : self.username_id, 520 | # '_csrftoken' : self.token, 521 | # 'media_id' : mediaId 522 | # }) 523 | # return self.SendRequest('media/'+ str(mediaId) +'/unlike/', self.generateSignature(data)) 524 | 525 | # def getMediaComments(self, mediaId): 526 | # return self.SendRequest('media/'+ mediaId +'/comments/?') 527 | 528 | # def setNameAndPhone(self, name = '', phone = ''): 529 | # data = json.dumps({ 530 | # '_uuid' : self.uuid, 531 | # '_uid' : self.username_id, 532 | # 'first_name' : name, 533 | # 'phone_number' : phone, 534 | # '_csrftoken' : self.token 535 | # }) 536 | # return self.SendRequest('accounts/set_phone_and_name/', self.generateSignature(data)) 537 | 538 | # def getDirectShare(self): 539 | # return self.SendRequest('direct_share/inbox/?') 540 | 541 | # def backup(self): 542 | # # TODO Instagram.php 1470-1485 543 | # return False 544 | 545 | # def follow(self, userId): 546 | # data = json.dumps({ 547 | # '_uuid' : self.uuid, 548 | # '_uid' : self.username_id, 549 | # 'user_id' : userId, 550 | # '_csrftoken' : self.token 551 | # }) 552 | # return self.SendRequest('friendships/create/'+ str(userId) +'/', self.generateSignature(data)) 553 | 554 | # def unfollow(self, userId): 555 | # data = json.dumps({ 556 | # '_uuid' : self.uuid, 557 | # '_uid' : self.username_id, 558 | # 'user_id' : userId, 559 | # '_csrftoken' : self.token 560 | # }) 561 | # return self.SendRequest('friendships/destroy/'+ str(userId) +'/', self.generateSignature(data)) 562 | 563 | # def block(self, userId): 564 | # data = json.dumps({ 565 | # '_uuid' : self.uuid, 566 | # '_uid' : self.username_id, 567 | # 'user_id' : userId, 568 | # '_csrftoken' : self.token 569 | # }) 570 | # return self.SendRequest('friendships/block/'+ str(userId) +'/', self.generateSignature(data)) 571 | 572 | # def unblock(self, userId): 573 | # data = json.dumps({ 574 | # '_uuid' : self.uuid, 575 | # '_uid' : self.username_id, 576 | # 'user_id' : userId, 577 | # '_csrftoken' : self.token 578 | # }) 579 | # return self.SendRequest('friendships/unblock/'+ str(userId) +'/', self.generateSignature(data)) 580 | 581 | # def userFriendship(self, userId): 582 | # data = json.dumps({ 583 | # '_uuid' : self.uuid, 584 | # '_uid' : self.username_id, 585 | # 'user_id' : userId, 586 | # '_csrftoken' : self.token 587 | # }) 588 | # return self.SendRequest('friendships/show/'+ str(userId) +'/', self.generateSignature(data)) 589 | 590 | # def getLikedMedia(self): 591 | # return self.SendRequest('feed/liked/?') 592 | 593 | def generateSignature(self, data): 594 | post_string = 'ig_sig_key_version=' + self.SIG_KEY_VERSION + \ 595 | '&signed_body=' + hmac.new(self.IG_SIG_KEY.encode('utf-8'), data.encode('utf-8'), hashlib.sha256).hexdigest() + \ 596 | '.' + urllib.quote(data) 597 | return post_string 598 | 599 | def generateDeviceId(self, seed): 600 | volatile_seed = "12345" 601 | m = hashlib.md5() 602 | m.update(seed.encode('utf-8') + volatile_seed.encode('utf-8')) 603 | return 'android-' + m.hexdigest()[:16] 604 | 605 | def generateUUID(self, type): 606 | uuid = '%04x%04x-%04x-%04x-%04x-%04x%04x%04x' % (random.randint(0, 0xffff), 607 | random.randint(0, 0xffff), random.randint(0, 0xffff), 608 | random.randint(0, 0x0fff) | 0x4000, 609 | random.randint(0, 0x3fff) | 0x8000, 610 | random.randint(0, 0xffff), random.randint(0, 0xffff), 611 | random.randint(0, 0xffff)) 612 | if (type): 613 | return uuid 614 | else: 615 | return uuid.replace('-', '') 616 | 617 | def SendRequest(self, endpoint, post=None, login=False): 618 | # return self.SendRequest('media/upload/', self.generateSignature(data), post=photo) 619 | if (not self.isLoggedIn and not login): 620 | raise Exception("Not logged in!\n") 621 | return 622 | 623 | self.s.headers.update({'Connection': 'closed', 624 | 'Accept': '*/*', 625 | 'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8', 626 | 'Cookie2': '$Version=1', 627 | 'Accept-Language': 'en-US', 628 | 'User-Agent': self.USER_AGENT}) 629 | 630 | if (post): # POST 631 | response = self.s.post(self.API_URL + endpoint, post) # , verify=False 632 | else: # GET 633 | response = self.s.get(self.API_URL + endpoint) # , verify=False 634 | 635 | if response.status_code == 200: 636 | self.LastResponse = response 637 | self.LastJson = json.loads(response.text) 638 | return True 639 | else: 640 | print ("Request return " + str(response.status_code) + " error!") 641 | return False 642 | --------------------------------------------------------------------------------