Fixed identifier for a machine (uuid.getnode) - python

I'm trying to find something I can use as a unique string/number for my script that is fixed in a machine and easily obtainable(cross-platform). I presume a machine would have a network card. I don't need it to be really unique, but the necessary is it should be fixed in a long run and as rare as possible.
I know MAC can be changed and I'd probably make a warning about it in my script, however I don't expect anyone to change MAC each morning.
What I came up with is uuid.getnode(), but in the docs there is:
If all attempts to obtain the hardware address fail, we choose a random 48-bit number
Does it mean that for each function call I get another random number, therefore it's not possible to use it if MAC is unobtainable?
...on a machine with multiple network interfaces the MAC address of any one of them may be returned.
Does this sentence mean getnode() gets a random(or first) MAC from all available? What if it'd get MAC A in first run and MAC B next time? There'd be no problem if I'd get a fixed list(sort, concatenate, tadaaa!)
I'm asking because I have no way how to test it myself.

I managed to test the first part on my android device and on each new python run it created random number, so it's not usable at all for this purpose.
The second problem kind of drowned itself, because if in the docs it mentioned that it may return any one of them, then it's not something you could rely on (+I couldn't find a machine I could test it on). A nice package netifaces came to rescue, which does a similar thing
netifaces.interfaces() # returns e.g. ['lo', 'eth0', 'tun2']
netifaces.ifaddresses('eth0')[netifaces.AF_LINK]
# returns [{'addr': '08:00:27:50:f2:51', 'broadcast': 'ff:ff:ff:ff:ff:ff'}]
However I rather gave up using MACs, I got something rather more stable.
Now to the identifiers:
1) Windows:
Executing this one and getting output may be good enough:
wmic csproduct get UUID
or the one I used and is available in registry (HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography):
import _winreg
registry = _winreg.HKEY_LOCAL_MACHINE
address = 'SOFTWARE\\Microsoft\\Cryptography'
keyargs = _winreg.KEY_READ | _winreg.KEY_WOW64_64KEY
key = _winreg.OpenKey(registry, address, 0, keyargs)
value = _winreg.QueryValueEx(key, 'MachineGuid')
_winreg.CloseKey(key)
unique = value[0]
2) Linux:
/sys/class/dmi/id/board_serial
or
/sys/class/dmi/id/product_uuid
or if not root:
cat /var/lib/dbus/machine-id
3) Android:
If you are working with python and don't want to mess with Java stuff, then this should work pretty good:
import subprocess
cmd = ['getprop', 'ril.serialnumber']
self.unique = subprocess.check_output(cmd)[:-1]
but if you like Java, then go for this answer although even ANDROID_ID's uniqueness is rather debatable if it's allowed to change, therefore a serial number is most likely a safer bet.
Note that like it's already mentioned in the linked answer, even ril.serialnumber can be null/empty or non-existing (missing key). Same thing happens even with the official Android API where it's clearly stated this:
A hardware serial number, if available.
Mac/iPhone:
I couldn't find any solution as I don't have access to any of these, but if there is a variable that holds the machine id value, then you should be able to get there with simple subprocess.check_output()

I wouldn't recommend using a MAC address for a unique machine identifier, since it can change depending on the network being used. Rather, I'd recommend using the machine's native GUID, assigned by the operating system during install. I wrote a small, cross-platform PyPI package that queries a machine's native GUID called py-machineid.
Essentially, it looks like this, but with some Windows-specific WMI registry queries for more a accurate ID. The package also has support for hashing the ID, to anonymize it.
import subprocess
import sys
def run(cmd):
try:
return subprocess.run(cmd, shell=True, capture_output=True, check=True, encoding="utf-8") \
.stdout \
.strip()
except:
return None
def guid():
if sys.platform == 'darwin':
return run(
"ioreg -d2 -c IOPlatformExpertDevice | awk -F\\\" '/IOPlatformUUID/{print $(NF-1)}'",
)
if sys.platform == 'win32' or sys.platform == 'cygwin' or sys.platform == 'msys':
return run('wmic csproduct get uuid').split('\n')[2] \
.strip()
if sys.platform.startswith('linux'):
return run('cat /var/lib/dbus/machine-id') or \
run('cat /etc/machine-id')
if sys.platform.startswith('openbsd') or sys.platform.startswith('freebsd'):
return run('cat /etc/hostid') or \
run('kenv -q smbios.system.uuid')

For Mac/iphone you can try below command:
import subprocess
subprocess.check_output("ioreg -rd1 -c IOPlatformExpertDevice | grep -E '(UUID)'", shell=True).split('"')[-2] # for me i got it on list value -2 if having any trouble try getting it with any alternative list element.

uuid.getnode will return the same value for every call wthinin a single run of your app. If it has to defer to the random algorithm, then you will get a different value when you start a new instance of your app.
The implementation for getNode shows why. This is sort of what the routine looks like in python 3.7 (comments mine, code simplified for clarity)
_node = None
def getnode():
global _node
if _node is not None:
# Return cached value
return _node
# calculate node using platform specific logic like unix functions, ifconfig, etc
_node = _get_node_from_platform()
if not _node:
# couldn't get node id from the system. Just make something up
_node = _get_random_node()
return _node

Related

Setting NSSpeechSynthesizer mode from Python

I am using PyObjC bindings to try to get a spoken sound file from phonemes.
I figured out that I can turn speech into sound as follows:
import AppKit
ss = AppKit.NSSpeechSynthesizer.alloc().init()
ss.setVoice_('com.apple.speech.synthesis.voice.Alex')
ss.startSpeakingString_toURL_("Hello", AppKit.NSURL.fileURLWithPath_("hello.aiff"))
# then wait until ve.isSpeaking() returns False
Next for greater control I'd like to turn the text first into phonemes, and then speak them.
phonemes = ss.phonemesFromText_("Hello")
But now I'm stuck, because I know from the docs that to get startSpeakingString to accept phonemes as input, you first need to set NSSpeechSynthesizer.SpeechPropertyKey.Mode to "phoneme". And I think I'm supposed to use setObject_forProperty_error_ to set that.
There are two things I don't understand:
Where is NSSpeechSynthesizer.SpeechPropertyKey.Mode in PyObjC? I grepped the entire PyObjC directory and SpeechPropertyKey is not mentioned anywhere.
How do I use setObject_forProperty_error_ to set it? I think based on the docs that the first argument is the value to set (although it's called just "an object", so True in this case?), and the second is the key (would be phoneme in this case?), and finally there is an error callback. But I'm not sure how I'd pass those arguments in Python.
Where is NSSpeechSynthesizer.SpeechPropertyKey.Mode in PyObjC?
Nowhere.
How do I use setObject_forProperty_error_ to set it?
ss.setObject_forProperty_error_("PHON", "inpt", None)
"PHON" is the same as NSSpeechSynthesizer.SpeechPropertyKey.Mode.phoneme
"inpt" is the same as NSSpeechSynthesizer.SpeechPropertyKey.inputMode
It seems these are not defined anywhere in PyObjC, but I found them by firing up XCode and writing a short Swift snippet:
import Foundation
import AppKit
let synth = NSSpeechSynthesizer()
let x = NSSpeechSynthesizer.SpeechPropertyKey.Mode.phoneme
let y = NSSpeechSynthesizer.SpeechPropertyKey.inputMode
Now looking at x and y in the debugger show that they are the strings mentioned above.
As for how to call setObject_forProperty_error_, I simply tried passing in those strings and None as the error handler, and that worked.

Get full computer name from a network drive letter in python

I am using python to populate a table with the file pathways of a number of stored files. However the pathway needs to have the full network drive computer name not just the drive letter, ie
//ComputerName/folder/subfolder/file
not
P:/folder/subfolder/file
I have investigated using the win32api, win32file, and os.path modules but nothing is looking like its able to do it. I need something like win32api.GetComputerName() but with the ability to drop in a known drive letter as an argument and it return the computer name that is mapped to the letter.
So is there anyway in python to look up a drive letter and get back the computer name?
Network drives are mapped using the Windows Networking API that's exported by mpr.dll (multiple provider router). You can create a network drive via WNetAddConnection2. To get the remote path that's associated with a local device, call WNetGetConnection. You can do this using ctypes as follows:
import ctypes
from ctypes import wintypes
mpr = ctypes.WinDLL('mpr')
ERROR_SUCCESS = 0x0000
ERROR_MORE_DATA = 0x00EA
wintypes.LPDWORD = ctypes.POINTER(wintypes.DWORD)
mpr.WNetGetConnectionW.restype = wintypes.DWORD
mpr.WNetGetConnectionW.argtypes = (wintypes.LPCWSTR,
wintypes.LPWSTR,
wintypes.LPDWORD)
def get_connection(local_name):
length = (wintypes.DWORD * 1)()
result = mpr.WNetGetConnectionW(local_name, None, length)
if result != ERROR_MORE_DATA:
raise ctypes.WinError(result)
remote_name = (wintypes.WCHAR * length[0])()
result = mpr.WNetGetConnectionW(local_name, remote_name, length)
if result != ERROR_SUCCESS:
raise ctypes.WinError(result)
return remote_name.value
For example:
>>> subprocess.call(r'net use Y: \\live.sysinternals.com\tools')
The command completed successfully.
0
>>> print(get_connection('Y:'))
\\live.sysinternals.com\tools
I think you just need to look at more of pywin32... As you can see here, there is already an API that converts local drive names to full UNC paths.
For completeness, here is some code that works for me.
import win32wnet
import sys
print(win32wnet.WNetGetUniversalName(sys.argv[1], 1))
And this gives me something like this when I run it:
C:\test>python get_unc.py i:\some\path
\\machine\test_share\some\path
you could run net use and parse the output.
i am posting this from my mobile but i am going to improve this answer when i am in front of a real computer.
here are some links, that can help in the meantime:
https://docs.python.org/2/library/subprocess.html#module-subprocess.
https://technet.microsoft.com/en-us/library/gg651155.aspx.
My answer to a similar question:
Here's how to do it in python ≥ 3.4, with no dependencies!*
from pathlib import Path
def unc_drive(file_path):
return str(Path(file_path).resolve())
*Note: I just found a situation in which this method fails. One of my company's network shares has permissions setup such that this method raises a PermissionError. In this case, win32wnet.WNetGetUniversalName is a suitable fallback.
If you just need the hostname, you can use the socket module:
socket.gethostname()
or you may want to use the os module:
os.uname()[1]
os.uname() returns a 5 tuple that contains (sysname, nodename, release, version, machine)

Python - How to get the start/base address of a process?

How do I get the start/base address of a process? Per example Solitaire.exe (solitaire.exe+BAFA8)
#-*- coding: utf-8 -*-
import ctypes, win32ui, win32process
PROCESS_ALL_ACCESS = 0x1F0FFF
HWND = win32ui.FindWindow(None,u"Solitär").GetSafeHwnd()
PID = win32process.GetWindowThreadProcessId(HWND)[1]
PROCESS = ctypes.windll.kernel32.OpenProcess(PROCESS_ALL_ACCESS,False,PID)
print PID, HWND,PROCESS
I would like to calculate a memory address and for this way I need the base address of solitaire.exe.
Here's a picture of what I mean:
I think the handle returned by GetModuleHandle is actually the base address of the given module. You get the handle of the exe by passing NULL.
Install pydbg
Source: https://github.com/OpenRCE/pydbg
Unofficial binaries here: http://www.lfd.uci.edu/~gohlke/pythonlibs/#pydbg
from pydbg import *
from pydbg.defines import *
import struct
dbg = pydbg()
path_exe = "C:\\windows\\system32\\calc.exe"
dbg.load(path_exe, "-u amir")
dbg.debug_event_loop()
parameter_addr = dbg.context.Esp #(+ 0x8)
print 'ESP (address) ',parameter_addr
#attach not working under Win7 for me
#pid = raw_input("Enter PID:")
#print 'PID entered %i'%int(pid)
#dbg.attach(int(pid)) #attaching to running process not working
You might want to have a look at PaiMei, although it's not very active right now https://github.com/OpenRCE/paimei
I couldn't get attach() to work and used load instead. Pydbg has loads of functionality, such as read_proccess_memory, write_process_memory etc.
Note that you can't randomly change memory, because an operating system protects memory of other processes from your process (protected mode). Before the x86 processors there were some which allowed all processors to run in real mode, i.e. the full access of memory for every programm. Non-malicious software usually (always?) doesn't read/write other processes' memory.
The HMDOULE value of GetModuleHandle is the base address of the loaded module and is probably the address you need to compute the offset.
If not, that address is the start of the header of the module (DLL/EXE), which can be displayed with the dumpbin utility that comes with Visual Studio or you can interpret it yourself using the Microsoft PE and COFF Specification to determine the AddressOfEntryPoint and BaseOfCode as offsets from the base address. If the base address of the module isn't what you need, one of these two is another option.
Example:
>>> BaseAddress = win32api.GetModuleHandle(None) + 0xBAFA8
>>> print '{:08X}'.format(BaseAddress)
1D0BAFA8
If The AddressOfEntryPoint or BaseOfCode is needed, you'll have to use ctypes to call ReadProcessMemory following the PE specification to locate the offsets, or just use dumpbin /headers solitaire.exe to learn the offsets.
You can use frida to easy do that.
It is very useful to make hack and do some memory operation just like make address offset, read memory, write something to special memory etc...
https://github.com/frida/frida
2021.08.01 update:
Thanks for #Simas Joneliunas reminding
There some step using frida(windows):
Install frida by pip
pip install frida-tools # CLI tools
pip install frida # Python bindings
Using frida api
session = frida.attach(processName)
script = session.create_script("""yourScript""")
script.load()
sys.stdin.read() #make program always alive
session.detach()
Edit your scrip(using JavaScrip)
var baseAddr = Module.findBaseAddress('solitaire.exe');
var firstPointer = baseAddr.add(0xBAFA8).readPointer();
var secondPointer = firstPointer.add(0x50).readPointer();
var thirdPointer = secondPointer.add(0x14).readPointer();
#if your target pointer points to a Ansi String, you can use #thirdPointer.readAnsiString() to read
The official site https://frida.re/

In Python, how do I check if a drive exists w/o throwing an error for removable drives?

Here's what I have so far:
import os.path as op
for d in map(chr, range(98, 123)): #drives b-z
if not op.isdir(d + ':/'): continue
The problem is that it pops up a "No Disk" error box in Windows:
maya.exe - No Disk: There is no disk in
the drive. Please insert a disk into
drive \Device\Harddisk1\DR1 [Cancel, Try Again, Continue]
I can't catch the exception because it doesn't actually throw a Python error.
Apparently, this only happens on removable drives where there is a letter assigned, but no drive inserted.
Is there a way to get around this issue without specifically telling the script which drives to skip?
In my scenario, I'm at the school labs where the drive letters change depending on which lab computer I'm at. Also, I have zero security privileges to access disk management.
Use the ctypes package to access the GetLogicalDrives function. This does not require external libraries such as pywin32, so it's portable, although it is a little clunkier to work with. For example:
import ctypes
import itertools
import os
import string
import platform
def get_available_drives():
if 'Windows' not in platform.system():
return []
drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
return list(itertools.compress(string.ascii_uppercase,
map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1])))
itertools.compress was added in Python 2.7 and 3.1; if you need to support <2.7 or <3.1, here's an implementation of that function:
def compress(data, selectors):
for d, s in zip(data, selectors):
if s:
yield d
Here's a way that works both on Windows and Linux, for both Python 2 and 3:
import platform,os
def hasdrive(letter):
return "Windows" in platform.system() and os.system("vol %s: 2>nul>nul" % (letter)) == 0
If you have the win32file module, you can call GetLogicalDrives():
def does_drive_exist(letter):
import win32file
return (win32file.GetLogicalDrives() >> (ord(letter.upper()) - 65) & 1) != 0
To disable the error popup, you need to set the SEM_FAILCRITICALERRORS Windows error flag using pywin:
old_mode = win32api.SetErrorMode(0)
SEM_FAILCRITICALERRORS = 1 # not provided by PyWin, last I checked
win32api.SetErrorMode(old_mode & 1)
This tells Win32 not to show the retry dialog; when an error happens, it's returned to the application immediately.
Note that this is what Python calls are supposed to do. In principle, Python should be setting this flag for you. Unfortunately, since Python may be embedded in another program, it can't change process-wide flags like that, and Win32 has no way to specify this flag in a way that only affects Python and not the rest of the code.
As long as a little parsing is acceptable, this is one way to do it without installing win32api and without iterating through all possible drive letters.
from subprocess import check_output
def getDriveLetters():
args = [
'wmic',
'logicaldisk',
'get',
'caption,description,providername',
'/format:csv'
]
output = check_output(args)
results = list()
for line in output.split('\n'):
if line:
lineSplit = line.split(',')
if len(lineSplit) == 4 and lineSplit[1][1] == ':':
results.append(lineSplit[1][0])
return results
You could also parse for specific drive types, such as "Network Connection" to get a list of all network mounted drive letters by adding and lineSplit[2] == 'Network Connection' for example.
Alternatively, rather than returning a list, you could return a dictionary, where keys are drive letters and values are unc paths (lineSplit[3]). Or whatever other info you want to pull from wmic. To see more options: wmic logicaldisk get /?
import os
possible_drives_list = [chr(97 + num).upper() for num in range(26)]
for drive in possible_drives_list:
print(drive + ' exists :' + str(os.path.exists(drive + ':\\')))
import os
def IsDriveExists(drive):
return os.path.exists(drive + ':\\')
print(IsDriveExists('c'))
print(IsDriveExists('d'))
print(IsDriveExists('e'))
print(IsDriveExists('x'))
print(IsDriveExists('v'))
this works in any os

Programmatically detect system-proxy settings on Windows XP with Python

I develop a critical application used by a multi-national company. Users in offices all around the globe need to be able to install this application.
The application is actually a plugin to Excel and we have an automatic installer based on Setuptools' easy_install that ensures that all a project's dependancies are automatically installed or updated any time a user switches on their Excel. It all works very elegantly as users are seldom aware of all the installation which occurs entirely in the background.
Unfortunately we are expanding and opening new offices which all have different proxy settings. These settings seem to change from day to day so we cannot keep up with the outsourced security guys who change stuff without telling us. It sucks but we just have to work around it.
I want to programatically detect the system-wide proxy settings on the Windows workstations our users run:
Everybody in the organisazation runs Windows XP and Internet Explorer. I've verified that everybody can download our stuff from IE without problems regardless of where they are int the world.
So all I need to do is detect what proxy settings IE is using and make Setuptools use those settings. Theoretically all of this information should be in the Registry.. but is there a better way to find it that is guaranteed not to change with people upgrade IE? For example is there a Windows API call I can use to discover the proxy settings?
In summary:
We use Python 2.4.4 on Windows XP
We need to detect the Internet Explorer proxy settings (e.g. host, port and Proxy type)
I'm going to use this information to dynamically re-configure easy_install so that it can download the egg files via the proxy.
UPDATE0:
I forgot one important detail: Each site has an auto-config "pac" file.
There's a key in Windows\CurrentVersion\InternetSettings\AutoConfigURL which points to a HTTP document on a local server which contains what looks like a javascript file.
The pac script is basically a series of nested if-statements which compare URLs against a regexp and then eventually return the hostname of the chosen proxy-server. The script is a single javascript function called FindProxyForURL(url, host)
The challenge is therefore to find out for any given server which proxy to use. The only 100% guaranteed way to do this is to look up the pac file and call the Javascript function from Python.
Any suggestions? Is there a more elegant way to do this?
Here's a sample that should create a bullet green (proxy enable) or red (proxy disable) in your systray
It shows how to read and write in windows registry
it uses gtk
#!/usr/bin/env python
import gobject
import gtk
from _winreg import *
class ProxyNotifier:
def __init__(self):
self.trayIcon = gtk.StatusIcon()
self.updateIcon()
#set callback on right click to on_right_click
self.trayIcon.connect('popup-menu', self.on_right_click)
gobject.timeout_add(1000, self.checkStatus)
def isProxyEnabled(self):
aReg = ConnectRegistry(None,HKEY_CURRENT_USER)
aKey = OpenKey(aReg, r"Software\Microsoft\Windows\CurrentVersion\Internet Settings")
subCount, valueCount, lastModified = QueryInfoKey(aKey)
for i in range(valueCount):
try:
n,v,t = EnumValue(aKey,i)
if n == 'ProxyEnable':
return v and True or False
except EnvironmentError:
break
CloseKey(aKey)
def invertProxyEnableState(self):
aReg = ConnectRegistry(None,HKEY_CURRENT_USER)
aKey = OpenKey(aReg, r"Software\Microsoft\Windows\CurrentVersion\Internet Settings", 0, KEY_WRITE)
if self.isProxyEnabled() :
val = 0
else:
val = 1
try:
SetValueEx(aKey,"ProxyEnable",0, REG_DWORD, val)
except EnvironmentError:
print "Encountered problems writing into the Registry..."
CloseKey(aKey)
def updateIcon(self):
if self.isProxyEnabled():
icon=gtk.STOCK_YES
else:
icon=gtk.STOCK_NO
self.trayIcon.set_from_stock(icon)
def checkStatus(self):
self.updateIcon()
return True
def on_right_click(self, data, event_button, event_time):
self.invertProxyEnableState()
self.updateIcon()
if __name__ == '__main__':
proxyNotifier = ProxyNotifier()
gtk.main()
As far as I know, In a Windows environment, if no proxy environment variables are set, proxy settings are obtained from the registry's Internet Settings section. .
Isn't it enough?
Or u can get something useful info from registry:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ProxyServer
Edit:
sorry for don't know how to format comment's source code, I repost it here.
>>> import win32com.client
>>> js = win32com.client.Dispatch('MSScriptControl.ScriptControl')
>>> js.Language = 'JavaScript'
>>> js.AddCode('function add(a, b) {return a+b;}')
>>> js.Run('add', 1, 2)
3

Categories