Skip to content

Instantly share code, notes, and snippets.

@pudquick
Last active November 30, 2017 03:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pudquick/785a464ab1b0fffe5fa7 to your computer and use it in GitHub Desktop.
Save pudquick/785a464ab1b0fffe5fa7 to your computer and use it in GitHub Desktop.
from Foundation import *
def readPlistFromData(data_obj):
# Reads binary and XML plists from a NSData object / python string / raw bytes
every_byte = ''.join([x for x in data_obj])
nsdata_obj = NSData.dataWithBytes_length_(every_byte, len(every_byte))
data, p_format, error = NSPropertyListSerialization.propertyListFromData_mutabilityOption_format_errorDescription_(nsdata_obj, \
NSPropertyListMutableContainersAndLeaves, None, None)
return data
from Foundation import *
import os.path
signature_array = CFPreferencesCopyValue('items-1', \
os.path.expanduser('~/Library/Containers/com.apple.Preview/Data/Library/Preferences/com.apple.Preview.signatures.plist'), \
kCFPreferencesCurrentUser, kCFPreferencesAnyHost)
first_sig = signature_array[0]
sig_plist = readPlistFromData(first_sig)
from Foundation import *
sig_decoded = NSKeyedUnarchiver.unarchiveObjectWithData_(first_sig)
@interface PVSignature : NSObject <NSCoding>
{
NSData *_undecryptedData;
BOOL _cannotDecrypt;
PVSignaturePayload *_payload;
NSImage *_largeThumbnail;
NSImage *_largeWhiteThumbnail;
NSImage *_smallThumbnail;
BOOL _thumbnailsGenerated;
int _uid;
BOOL _shouldPersist;
}
from Foundation import *
class PVSignature(NSObject):
_undecryptedData = None
_uid = None
_cannotDecrypt = None
_shouldPersist = None
_largeThumbnail = None
_smallThumbnail = None
_payload = None
def init(self):
self = super(PVSignature, self).init()
if self == None: return None
self._undecryptedData = None
self._uid = None
self._cannotDecrypt = None
self._shouldPersist = None
self._largeThumbnail = None
self._smallThumbnail = None
self._payload = None
return self
def initWithCoder_(self, coder):
# use decodeObjectForKey when the key value could be something other than a primitive data type (looks like NSData here...)
self._undecryptedData = coder.decodeObjectForKey_(u"data")
# use decodeIntForKey because it's pretty obvious from sig_plist['$objects'][1]
self._uid = coder.decodeInt32ForKey_(u"uid")
self._shouldPersist = 1
# We won't set the other values to 0, because in this context that's actually a null pointer for them
return self
# Define our class for itself
NSKeyedUnarchiver.setClass_forClassName_(PVSignature, "PVSignature")
# Attempt to decode again!
sig_decoded = NSKeyedUnarchiver.unarchiveObjectWithData_(first_sig)
import binascii
hex_key = 'E29D5B36A433C4E592B7A3991EF8691C'
binary_key = binascii.unhexlify(hex_key)
from Crypto.Cipher import AES
iv = '\x00'*16 # 16 byte character array of zeroes
cipher = AES.new(binary_key, AES.MODE_CBC, IV=iv)
decrypted_data = cipher.decrypt(str(sig_decoded._undecryptedData))
class PKCS7Encoder():
class InvalidBlockSizeError(Exception):
"""Raised for invalid block sizes"""
pass
def __init__(self, block_size=16):
if block_size < 2 or block_size > 255:
raise PKCS7Encoder.InvalidBlockSizeError('The block size must be ' \
'between 2 and 255, inclusive')
self.block_size = block_size
def encode(self, text):
text_length = len(text)
amount_to_pad = self.block_size - (text_length % self.block_size)
if amount_to_pad == 0:
amount_to_pad = self.block_size
pad = chr(amount_to_pad)
return text + pad * amount_to_pad
def decode(self, text):
pad = ord(text[-1])
return text[:-pad]
encoder = PKCS7Encoder()
unpadded_decrypted_data = encoder.decode(decrypted_data)
decrypted_plist = readPlistFromData(unpadded_decrypted_data[32:])
from Foundation import *
nsdata_decrypted_plist = NSData.dataWithBytes_length_(unpadded_decrypted_data[32:], len(unpadded_decrypted_data[32:]))
real_sig_decoded = NSKeyedUnarchiver.unarchiveObjectWithData_(nsdata_decrypted_plist)
@interface PVSignaturePayload : NSObject <NSCoding>
{
NSBezierPath *path;
double baselineHeight;
NSDate *creationDate;
NSDate *lastUsedDate;
}
from Foundation import *
# This one isn't defined in Foundation
from AppKit import NSBezierPath
class PVSignaturePayload(NSObject):
path = None
baselineHeight = None
creationDate = None
lastUsedDate = None
def init(self):
self = super(PVSignaturePayload, self).init()
if self == None: return None
self.path = None
self.baselineHeight = None
self.creationDate = None
self.lastUsedDate = None
return self
def initWithCoder_(self, coder):
# use decodeObjectForKey when the key value could be something other than a primitive data type (looks like NSData here...)
self._undecryptedData = coder.decodeObjectForKey_(u"data")
# use decodeIntForKey because it's pretty obvious from sig_plist['$objects'][1]
self._uid = coder.decodeInt32ForKey_(u"uid")
self._shouldPersist = 1
# We won't set the other values to 0, because in this context that's actually a null pointer for them
self.path = coder.decodeObjectForKey_(u"path")
self.baselineHeight = coder.decodeFloatForKey_(u"baselineHeight")
self.creationDate = coder.decodeObjectForKey_(u"creationDate")
self.creationDate = coder.decodeObjectForKey_(u"creationDate")
return self
# Define our class for itself
NSKeyedUnarchiver.setClass_forClassName_(PVSignaturePayload, "PVSignaturePayload")
# Define some of the other classes that come out
NSKeyedUnarchiver.setClass_forClassName_(NSDate, "NSDate")
NSKeyedUnarchiver.setClass_forClassName_(NSBezierPath, "NSBezierPath")
real_sig_decoded = NSKeyedUnarchiver.unarchiveObjectWithData_(nsdata_decrypted_plist)
from Foundation import *
from AppKit import *
import math
sig_path = real_sig_decoded.path
shift_path = NSAffineTransform.alloc().init()
origin = sig_path.bounds().origin
# We translate by the opposite X and Y values - with +0.1 to nudge it into the positive
shift_path.translateXBy_yBy_(origin.x * -1. + 0.1, origin.y * -1. + 0.1)
sig_path_shifted = sig_path.copy()
sig_path_shifted.transformUsingAffineTransform_(shift_path)
import os.path
bounds = sig_path_shifted.bounds()
width = int(math.ceil(bounds.origin.x + bounds.size.width))
height = int(math.ceil(bounds.origin.y + bounds.size.height))
# Build a pixels bitmap to draw onto
signature_bitmap = NSBitmapImageRep.alloc().initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bytesPerRow_bitsPerPixel_(None, width, height, 8, 4, True, False, NSDeviceRGBColorSpace, 0, 0)
# Save our current graphic information (we really don't care, but it's polite)
NSGraphicsContext.saveGraphicsState()
# Set the focus to our bitmap
NSGraphicsContext.setCurrentContext_(NSGraphicsContext.graphicsContextWithBitmapImageRep_(signature_bitmap))
# Set the color to black
NSColor.blackColor().setFill()
# Fill our signature on the bitmap with black
sig_path_shifted.fill()
# Restore our previous graphics state
NSGraphicsContext.restoreGraphicsState()
# Build a data stream of a PNG from our bitmap
data = signature_bitmap.representationUsingType_properties_(NSPNGFileType, None)
# Save it to a file
result = data.writeToFile_atomically_(os.path.expanduser("~/Desktop/my_signature.png"), True)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment