2020-07-05

Gramblr - how I "hacked an Instagram" once

Intro.

Back in the beginning of 2016 when I got my first smartphone and was introduced with smart-world I was looking for an options to upload the image in Instagram (IG) from the PC directly. And I found this partially crippled but so widely used system - Gramblr. It was built online but it has its own downloadable web client. It was more than just an image uploader for Instagram. It has its own lets-make-profit system with users earning credits by giving likes to other user IG images. It had this 2:1 system - you have to like two other images and then you got one credit. And you can spend this Gramblr credit exchanging it to a one "like". There was a list of 100 images loaded for you as an user to check whether you like an image or not. Of course, simple users were earning these credits by clicking on all images posted to maximize their credit profit. The whole system was flawed by this "earning credits" system but somebody got profit. If you want you could buy credits. If I`m not mistaken, 100 guaranteed likes (credits) costed 5 bucks.
Earn Coints to Get More Likes via Gramblr - Techtippr
This is the moment where things got interesting for me. It should be possible to automatize this gaining credits algorithm. And how does the credit system work? And I started to explore.

Tech

I was wondering how does this web client Gramblr.exe works. My antivirus and google chrome shows it as a dangerous malware software. I`m not a security expert at all, I just wanted to see, what the hell it is doing and how does it work and processes and etc. It was blended rather deep in the windows (Yea, I`m sitting on Win 7) automatic startup - it had its service, it was tracks in registry.
And how does it communicate? How does the data is sent, encoded?

Next finding was that my client is actually a simple web-server passing and receiving web requests to the Gramblr online webserver. Well, this gives a relatively simple approach to automatize requests. Of course, requests need analysis, whats in them, how do they work. And I started to play with Python 2.7. The required libraries were:

  1. import requests
  2. import json
  3. import random
  4. import re
  5. import time

so - how to connect? There is a login screen, so I have to be logged in. I was lazy and didnt make any login requests, I noticed that there is a cookie which changes every time I log in and log out and as I mostly stayed logged in, I cheated and logged in in the web client and copied cookie`s id from there for my script.
This was all I need to do a handshake with a server.

  1.  def gramblr_req(self):
  2.         """
  3.         Does GET request to get basic info of user
  4.         """
  5.         headers = {
  6.             "Accept-Encoding":self.h_encoding,
  7.             "Accept-Language":self.h_language,
  8.             "User-Agent":self.h_useragent,
  9.             "Accept":"application/json, text/plain, */*",
  10.             "Referer":self.localhost_url,
  11.             "Cookie":self.h_cookie,
  12.             "Connection":self.h_connection
  13.         }
  14.         try:
  15.             results = requests.get(self.gramblr_url, headers=headers)
  16.             self.coins=json.loads(results._content)["coins"]
  17.         except:
  18.             self.gramblr_req()

as I was lazy I didnt check was all header info actually needed.
After successful handshake the result was a JSON file with all info about my profile. What I was after was credits in the system, called coins. 1 credit = 5 coins. From the script one can recognize a Python class.

When I am authenticated in the system now, next thing was to get a list with an images and all necessary info which were posted by other users to gain likes. In the web client these images were loaded in the "Earn Coins" section. User could click on the images he/she likes and gain those coins. So, I had to emulate this request.

  1.  def GET_list(self):


  2.         results={}
  3.         results["list"]=False
  4.         try:
  5.             counter=0
  6.             while not results["list"]:
  7.                 results = requests.get(self.earn_coins_url, self.headers)
  8.                 self.results=results
  9.                 localtime = time.asctime( time.localtime(time.time()) )
  10.                 counter=counter+1
  11.                 results=json.loads(results._content)
  12.                 time.sleep(1)
  13.             fullstrings=""
  14.             iterstrings=iter(results["list"])

  15.             for each in iterstrings:
  16.                 fullstrings=fullstrings+'{"id":'+str(each["id"])+'},'
  17.             self.liste='{"liked":[],"skipped":[],"ignored":['+fullstrings[:-1]+'],"ig_user":"myinstagramusername"}'

  18.             self.results= self.results._content
  19.         except:
  20.             self.GET_list()
  21.         try:
  22.             outF=open("myOutFile.txt","r+")
  23.             userlist=set(str(line.strip()) for line in outF)
  24.             outF.close()
  25.             for counter, each in enumerate(results["list"]):
  26.                  userlist.add(str(each["details"]["username"]))
  27.             userlist=sorted(userlist)
  28.             outF=open("myOutFile.txt","r+")

  29.             for each in userlist:
  30.                 outF.write(each)
  31.                 outF.write("\n")
  32.             outF.close()
  33.         
  34.         except:
  35.             pass

This was kinda tricky. I noticed that when I was jumping between multiple web clients the list of images sometimes loaded incompletely, sometimes disappeared. From the code snipped one can see the recursion in case of failed requests. Yea, as I was interested in making an automation, its good as long as it works. And it worked.
The other thing I was interested was unique users using Gramblr.  That is why the last part of the script is doing - scraping the usernames and accumulating in the list. I managed to find 98`976 unique users which means max likes one could get is this number which is unrealistic.
When I know which pictures are in list, I can move to "giving likes" request for each picture. But before there, notice the line 40 in previous snippet. A class variable "liste" in JSON format was prepared and this was crucial for the "giving likes" request:

  1. def POST_list(self):
  2.         values=self.liste
  3.         results = requests.post(self.give_like_url,values,self.headers)


Just that simple. Previously prepared JSON data was sent to the server containing which images got likes, which did not. The whole list was passed to the server and it had a weird behavior and also consequences. Through experiments I found a flaw here. The order of requests was the key to successfully gain credits more than I expected with just a simple bot-like automation. It could be done manually with two web-clients open simultaneously too and by clicking on images with a mouse just in a right order.

And this is the last request important for the whole "gaining free more than expected credits" story - request to give a like to some IG image. In reality it meant that this image showed up in the Gramblr image list for credits and everybody who wanted to gain some credits had to give a like to it to earn one credit.

  1.  def POST_like(self,fname):
  2.   
  3.         self.pickIGfromFile(fname)
  4.          try:
  5.             values='{"ig_user":"myinstagramusername",' \
  6.                    '"likes_qty":1,' \
  7.                    '"local_likes":false,' \
  8.                    '"media_pk":"'+self.igUser["media_pk"]+'",' \
  9.                    '"user_pk":'+str(self.igUser["user_pk"])+'}'

  10.             results = requests.post(self.add_likes_url,values,self.headers)
  11.         except:
  12.             self.POST_like(fname)       #recursion

What one can see in this snippet is that I had IG links already prepared in a file and there is another helper function which just picks random link in that file and passes it to the Gramblr request.


I already mentioned "The right order of requests" which was the successful key of gaining more than I expected of this automation. By sending requests in particular order they somewhat reset the credit counting and accumulation in the Gramblr online webserver.

  1. if __name__=="__main__":
  2.     G = Gramblrscripter()
  3.     G.gramblr_req()
  4.     coins_b=G.coins
  5.     ii=1000

  6.     for i in range(ii):

  7.         G.GET_list() #loads image list 
  8.         G.POST_like('iglinks.txt') #gives one like to an IG image (link needed)
  9.         G.POST_list() #noposto listi #posts image list which got likes and which didnt

  10.         G.gramblr_req()
  11.         coins_e=G.coins
  12.         print "gained likes: \t\t\t\t\t|\t", (coins_e-coins_b)/10
  13.         coins_b=coins_e

Most likely this "giving like" request in between both image list requests made Gramblr system to reset credit counting/waiting/managing flag in their database.

The final math and maximized credit gaining data flow would look like this:

0) Lets say, I have 2 credits in the beginning

1) Image List loaded with 100 images - max 100 credits waiting for me to get
2) IG image liked -online system, db reset, 2 credits spent. Now I have 0 credits.
3) modified Image List posted with first image getting only one like from me - I spend 2 credits, I gain 100 credits instead of 1.

///cycle continues///Now I have 100 credits

4) Image List loaded with 99 images (the one I previously gave like in the step 3 is already out of the list) - max 99 credits waiting for me to get
5) IG image liked - online system, db reset. 2 credits spent. Now I have 98 credits.
6) modified Image List posted with "next first" image getting only like from me - I spend 2 credits, I gain 99 credits instead of 1.
Altogether I have 98+99 = 197 credits.

// cycle continues until there is no image left in the loaded image list.


Full python code can be found and analysed here - https://pastebin.com/vV9j1nSD

Conclusions and final thoughts

I highly doubt that this is what killed Gramblr. The system by concept was having flaws, I did not manage to explore and analyse "gaining followers" part, but I had a suspicion that somewhat users were automatically added as followers. My IG account suddenly was following more than 3k users without my knowledge. That means the system is malware in my opinion. 
The amount of unique users (98`976) where most of them most likely were bots and commercial users. In the beginning I was choosing one of my own IG images to find how many likes that IG picture could get. It gained close to 9k likes which is 10% of users. At that time I noticed the system was used by ~30k users, which would say ~30% of the system users could be the actual amount of likes possible to get.
The IG itself is also worth to mention. It has its artificial intelligence for sure. IG top picks and exploration I suspect originally was built on hashtags. But because of the bots and the systems which artificially increase the number of likes, some intellectual measures IG had to implement. Time as a crucial parameter can be measured. If the image in the IG got close to 500 likes within 5 seconds it has to be suspicious, spiking. Rate of change is a perfect indicator to flag unusual and suspicious activities. That was another flaw what Gramblr had. If I had 500 credits and I use them at once to boost my IG image, it may be flagged. Back in 2016 when I was experimenting, I noticed that my images with hashtag showed up in the search but over time they disappeared, suggesting me to think the IG had their preventive measures improved and giving no benefit of low IG users floating on the IG surface among most popular IG influencers. Thus by using Grambrl this did more harm than good to IG users. This is also a reason why I implement helper function self.pickIGfromFile(fname-  to have a lot of random IG images and giving those likes with a random time interval thus reducing the rate of change of getting likes. Less suspicious for IG.

2019-02-18

555 timer examples in falstad circuits

Few 555 timer chip examples built digitally in falstad circuit builder.

This one is Schmitt trigger with 2/3 and 1/3 Vcc hysteresis.
Check out extra sliders and see how current through led changes.

Simulation is here - http://tinyurl.com/y4rnjs4o


Another one - LDR logic board. Similar, but without hysteresis as threshold pin is connected to stationary 10k resistor, so the output is passed on at strict threshold level chosen by adjusting the variable resistor.

And the simulation - http://tinyurl.com/y6l7blt2


Here is a monostable circuit. Use switch as a push button and the LED will stay turned on for a certain time.

http://tinyurl.com/y6o47oxw

2017-12-29

Telnet and ssh from cygwin on windows xp in virtualbox

Another way how to play with shell commands.
By default new cygwin is not possible to install on Windows XP.

1. Get a cygwin installation which suppors windows xp

https://cygwin.com/ml/cygwin/2016-11/msg00071.html

2. Create shortcut for cygwin installation file and add -X (*capital X) in target field.

3. Use fruitbat mirror where to get files from

32-bit ftp://www.fruitbat.org/pub/cygwin/circa/2016/08/30/104223
64-bit ftp://www.fruitbat.org/pub/cygwin/circa/64bit/2016/08/30/104235

4. Add wget from mirror

5. Backup cygwin1.dll from /bin folder

6. wget apt-cyg

wget https://raw.githubusercontent.com/transcode-open/apt-cyg/master/apt-cyg
chmod +x apt-cyg
mv apt-cyg /usr/local/bin

7. install telnet using apt-cyg (telnet is in inetutils package)

apt-cyg install inetutils

8. install ssh using apt-cyg (ssh is in openssh package)

apt-cyg install openssh

9. Enjoy!

2016-08-25

That string is date or float?

Got an interesting situation where I had to distinguish if the piece of string is float or date in Python 2.7. No problems to do that when there is dateutil.parse library in Python. But in my case the tricky situation was that piece of string which actually is float, can be interpreted as... date.
So,  I came to solution - let there be two separate functions.
Checking if the string is float is straightforward.
def is_number(s):   
   try:   
    float(s)   
    return True   
   except ValueError:   
    return False   
Checking if the string is date - first of all, I check if it is a float. And if it is - lets return False.
 def is_date(d):  
   if is_number(d):  
     return False  
   try:  
     parse(d)  
     print parse(d)  
     return True  
   except ValueError:  
     return False  
And that`s it.

2016-03-06

Menmnonics. Anode VS Cathode

How to remember which of those is positive and which is negative?
Triple N could help.

aNode Not Negative.