[Android General] pulser_g2:
I've modified the script to work on the Galaxy Note II (N7100) on JB (4.1.1), as Samsung stores their settings elsewhere. It should be the same on the S3 as well :) Just expand to see it and copy/paste,,,

#
# Android pin change
# change the pin without Android UI restrictions bothering you
# allows to use a different pin on the lockscreen than the password
# for device encryption
#
# Version 1.0 / 2012-05-29
# by robberknight
#
# modified by Pulser_G2 (pulser_at_xda-developers.com) to work with
# Samsung devices (tested on Galaxy Note II N7100 JB 4.1.1)
# No Warranty!

import android
import subprocess
import sys
import hashlib
import struct
import binascii

class PinChange(object):

def __init__(self):
self.droid = android.Android()

def askNewPin(self):
pin1 = self.droid.dialogGetInput('New PIN (numeric)').result
if not pin1:
return False

if not pin1.isdigit() or len(pin1) < 4 or len(pin1) > 16:
self.droid.dialogCreateAlert("Error", "Invalid PIN")
self.droid.dialogSetNeutralButtonText('Abort')
self.droid.dialogShow()
return False

pin2 = self.droid.dialogGetInput('Confirm PIN').result
if not pin2:
return False

if pin1 != pin2:
self.droid.dialogCreateAlert("Error", "PINs don't match")
self.droid.dialogSetNeutralButtonText('Abort')
self.droid.dialogShow()
return False

return pin1

def getFileContent(self,filename):
try:
catpipe = subprocess.Popen(["su", "-c", "cat \""+filename+"\" 2>/dev/null"], stdout = subprocess.PIPE)
catoutput = catpipe.communicate()[0]
if catpipe.returncode == 0:
return catoutput
else:
return None
except:
self.droid.dialogCreateAlert("Error", "Can't execute su call (not rooted?)")
self.droid.dialogSetNeutralButtonText('Abort')
self.droid.dialogShow()
sys.exit(1)

def sqliteFound(self):
try:
statpipe = subprocess.Popen(["sqlite3", "-version"], stdout = subprocess.PIPE)
statoutput = statpipe.communicate()[0]
if statpipe.returncode == 0 and statoutput[0] >= '3':
return True
except:
pass
self.droid.dialogCreateAlert("Error", "Can't find sqlite3 command")
self.droid.dialogSetNeutralButtonText('Abort')
self.droid.dialogShow()
sys.exit(1)

def createBackup(self):
if self.getFileContent("/data/system/password.sav") == None or \
len(self.getFileContent("/data/system/password.sav")) == 0:
try:
statpipe = subprocess.Popen(["su", "-c", "cat /data/system/password.key >/data/system/password.sav"], stdout = subprocess.PIPE)
statoutput = statpipe.communicate()[0]
if statpipe.returncode != 0:
self.droid.dialogCreateAlert("Error", "Can't create password backup")
self.droid.dialogSetNeutralButtonText('Abort')
self.droid.dialogShow()
sys.exit(1)
chmodpipe = subprocess.Popen(["su", "-c", "chmod 600 /data/system/password.sav"], stdout = subprocess.PIPE)
chmodpipe.communicate()[0]
if chmodpipe.returncode != 0:
self.droid.dialogCreateAlert("Error", "Can't secure password backup")
self.droid.dialogSetNeutralButtonText('Abort')
self.droid.dialogShow()
sys.exit(1)
sqlitepipe = subprocess.Popen(["su", "-c", "sqlite3 -batch \
/data/system/locksettings.db \"SELECT value FROM locksettings WHERE name = 'lockscreen.password_type'\" >/data/system/password_type.sav"],
stdout = subprocess.PIPE)
sqlitepipe.communicate()[0]
if sqlitepipe.returncode != 0:
self.droid.dialogCreateAlert("Error", "Can't backup password type")
self.droid.dialogSetNeutralButtonText('Abort')
self.droid.dialogShow()
sys.exit(1)
except:
self.droid.dialogCreateAlert("Error", "Can't execute su call (not rooted?)")
self.droid.dialogSetNeutralButtonText('Abort')
self.droid.dialogShow()
sys.exit(1)

def restoreBackup(self):
if self.getFileContent("/data/system/password.sav") != None and \
len(self.getFileContent("/data/system/password.sav")) > 0 and \
self.getFileContent("/data/system/password_type.sav") != None and \
len(self.getFileContent("/data/system/password_type.sav")) > 0:
try:
statpipe = subprocess.Popen(["su", "-c", "cat /data/system/password.sav >/data/system/password.key"], stdout = subprocess.PIPE)
statoutput = statpipe.communicate()[0]
if statpipe.returncode != 0:
self.droid.dialogCreateAlert("Error", "Can't restore password backup")
self.droid.dialogSetNeutralButtonText('Abort')
self.droid.dialogShow()
sys.exit(1)
except:
self.droid.dialogCreateAlert("Error", "Can't execute su call (not rooted?)")
self.droid.dialogSetNeutralButtonText('Abort')
self.droid.dialogShow()
sys.exit(1)
pwdtype=self.getFileContent("/data/system/password_type.sav")
pwdtype=pwdtype.splitlines()[0].strip()
self.writePasswordType(pwdtype)
self.droid.dialogCreateAlert("Success", "Backup restored")
self.droid.dialogSetPositiveButtonText('Ok')
self.droid.dialogShow()

def writePasswordType(self,typeno):
try:
sqlitepipe = subprocess.Popen(["su", "-c", "sqlite3 -batch \
/data/system/locksettings.db \"UPDATE locksettings SET value='"+str(typeno)+"' WHERE name = 'lockscreen.password_type'\""],
stdout = subprocess.PIPE)
sqlitepipe.communicate()
if sqlitepipe.returncode == 0:
return
except:
pass
self.droid.dialogCreateAlert("Error", "Can't write pwd type")
self.droid.dialogSetNeutralButtonText('Abort')
self.droid.dialogShow()
sys.exit(1)

def writePassword(self,pwdhash):
try:
catpipe = subprocess.Popen(["su", "-c", "cat >/data/system/password.key"],
stdout = subprocess.PIPE, stdin = subprocess.PIPE)
catoutput = catpipe.communicate(pwdhash)[0]
if catpipe.returncode == 0:
return
except:
pass
self.droid.dialogCreateAlert("Error", "Can't write password")
self.droid.dialogSetNeutralButtonText('Abort')
self.droid.dialogShow()
sys.exit(1)

def readSalt(self):
try:
sqlitepipe = subprocess.Popen(["su", "-c", "sqlite3 -batch \
/data/system/locksettings.db \"SELECT value FROM locksettings WHERE name = 'lockscreen.password_salt'\""],
stdout = subprocess.PIPE)
sqliteoutput = sqlitepipe.communicate()[0].splitlines()[0].strip()
salt=long(sqliteoutput)
if sqlitepipe.returncode == 0 and sqliteoutput == str(salt):
return salt
except:
pass
self.droid.dialogCreateAlert("Error", "Can't read salt")
self.droid.dialogSetNeutralButtonText('Abort')
self.droid.dialogShow()
sys.exit(1)

def saltToHex(self,saltlong):
blob=struct.pack(">q",saltlong)
longagain=struct.unpack(">Q",blob)[0]
hexstr=hex(longagain).lstrip("0x").rstrip("L")
return hexstr.lower()

def hashPinOriginal(self,pin,salt):
# this is what google is doing in stock android
salted=str(pin)+self.saltToHex(salt)
md5str=hashlib.md5(salted).hexdigest()
sha1str=hashlib.sha1(salted).hexdigest()
return (sha1str+md5str).upper()

def hashPinSamsung(self,pin,salt):
# samsung has modified the code, at least on the GT-I9100
salted=str(pin)+self.saltToHex(salt)
hashbuf=str()
i=0
while i < 1024:
hashbuf=hashlib.sha1(hashbuf+str(i)+salted).digest()
i=i+1
return binascii.hexlify(hashbuf).upper()

def checkCurrentPassword(self,salt):
pwd = self.droid.dialogGetInput('Enter current Password').result
if not pwd:
return False

pwdfile=self.getFileContent("/data/system/password.key")
pwdhash=self.hashPinOriginal(pwd,salt)

if pwdhash == pwdfile:
return "original"
else:
pwdhash=self.hashPinSamsung(pwd,salt)
if pwdhash == pwdfile:
return "samsung"
else:
self.droid.dialogCreateAlert("Error", "Password hash compare mismatch")
self.droid.dialogSetNeutralButtonText('Abort')
self.droid.dialogShow()
return False

def setNewPin(self):
salt=self.readSalt()
curpwdtype=self.checkCurrentPassword(salt)
if not curpwdtype:
return False
newpin = self.askNewPin()
if not newpin:
return False
if curpwdtype == "original":
newhash=self.hashPinOriginal(newpin,salt)
elif curpwdtype == "samsung":
newhash=self.hashPinSamsung(newpin,salt)
else:
return
self.createBackup()
self.writePassword(newhash)
# pwd types:
# 65536 = no protection
# 131072 = numeric pin
# 262144 = alphanumeric password
self.writePasswordType(131072)
self.droid.dialogCreateAlert("Success", "New Password written")
self.droid.dialogSetPositiveButtonText('Ok')
self.droid.dialogShow()

def run(self):
self.sqliteFound()
if self.getFileContent("/data/system/password.sav") != None:
self.droid.dialogCreateAlert("Backup found", "Restore from backup or set new PIN?")
self.droid.dialogSetPositiveButtonText('Restore')
self.droid.dialogSetNegativeButtonText('Set PIN')
self.droid.dialogShow()
if self.droid.dialogGetResponse().result['which'] == 'positive':
self.restoreBackup()
if self.droid.dialogGetResponse().result['which'] == 'negative':
self.setNewPin()
else:
self.setNewPin()

pinchange = PinChange()
pinchange.run()