1

I'm about to sell my Ruby app on Windows, and I want to protect my software from being stolen or copied. I won't publish it, so it needs to be machine related.

I need my code to obtain the MAC address of any pc at startup, and compare it with a file stored in my server. I tried to build a unique serial code from the MAC address, but the 'macaddr' gem returns an error on some computers. This code will fail on some computers:

require 'macaddr'
mac = Mac.addr
puts mac
# >> NoMethodError: undefined method 'pfamily' for nil:NilClass

How can I get some unique system ids and build a serial code from them? Is there another way to get a unique hardware ID from a computer in Ruby?

lucaortis
  • 430
  • 2
  • 11
  • [PR 25](https://github.com/ahoward/macaddr/pull/25) has a fix for the error, but isn't likely to get merged. It just adds a safety check for the method call. So this will prevent the error, but might lose information on those systems. You might want to either fork the gem or look into an existing fork. – Tom Feb 13 '19 at 09:36
  • @Tom thanks for your reply. I will leave this as the last option, i would like to use other "codes" besides the MAC address. Since also if i solve the error rescuing it, i won't get the address the same, like you said. You know for example how to get hardware serial numbers? – lucaortis Feb 13 '19 at 09:51
  • `system_profiler SPHardwareDataType` returns a lot of information including ` Hardware UUID:` that you could regex out . I know it works on macOS, but cannot test elsewhere. You may have to check the environment and use different commands respectively. You can run that from Ruby by wrapping it in backticks or `system ...` or a couple other ways. – Tom Feb 13 '19 at 09:59
  • `system(system_profiler SPHardwareDataType).scan(/(?<=Hardware UUID: ).+/).first` – Tom Feb 13 '19 at 10:08
  • @Tom that's a command you run on macOS terminals? Unfortunately i am on Windows and i am gonna distribute this app only through Windows. Should i log the output of `ipconfig /all`, or any other utility, to a file and then parse it? Is this the best way? Actually i have a GUI application, and calling `system` cause a cmd window to pop up, so i'm using `win32ole` to call cmd scripts from Ruby. – lucaortis Feb 13 '19 at 10:09
  • https://stackoverflow.com/a/20832728/5399937 maybe something like this? This isn't really my forte, but kinda curious myself. – Tom Feb 13 '19 at 10:16
  • @Tom i'm on standard Ruby, not Rails, but i think i will do the following on a Windows machine: `wmic csproduct get "UUID" #Unique UUID.` `wmic nic get "MACAddress" #All MAC addresses on the computer.` This is only for Windows. I think these commands are almost the same as macOS `system_profiler` utility. I will try this and if it works i will update my question or answer it. I think the safer way is to combine the UUID and the MAC address together to make a "license code", still not safe at 100% but it should prevent the ordinary user to copy and paste the code somewhere else – lucaortis Feb 13 '19 at 10:30
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/188335/discussion-between-lucaortis-and-tom). – lucaortis Feb 13 '19 at 10:32
  • Since this is Ruby – how do you prevent the user from simply changing the Ruby code which performs the check? – Stefan Feb 13 '19 at 12:48
  • @Stefan the code will be downloaded when the app starts, and an ordinary user won't be able to change the code, ok that ruby is easy, but i don't think anyone can deal with it. Plus its all compressed into an exe file. Again, if someone who has coding skills gonna try to break into this, before or later of course he will. But a person who doesn't know how to get the code from an exe then he wont be able to. – lucaortis Feb 13 '19 at 13:06

2 Answers2

0

Firstly, I'd recommend you DON'T use MAC addresss. This can be spoofed very easily.

Secondly, I'd recommend you DON'T use hardware ids. Aside from the obvious difficulty you're finding in getting one reliably (I guess windows license would be your best bet), this may bind your software to a particular machine.

Instead, I'd recommend some kind of login system, send a username and hash(password) to a server, get back a UUID. Every time someone uses the software (or every hour depending on what suits your software better) ping your server with the uuid to check that the user hasn't logged in on another machine. Be generous in how you handle the case where your software doesn't get a response, don't lock the software immediately - allow an hour, maybe even a day. If appropriate for your software, pop up a small warning saying the software will be locked if it doesn't detect an internet connection within X minutes.

Remember that you want people to recommend your software to others. If your licensing system is mostly invisible, people will think higher of your software. If it's constantly in their face, they'll just end up resenting you.

user208769
  • 2,216
  • 1
  • 18
  • 27
  • Thanks for your reply. As said in my comments, i want this app to be machine related. I won't publish it anywhere, i just want to prevent the user to copy it on other computers, so this must work only on the costumer computer. – lucaortis Feb 13 '19 at 12:24
  • Hi @lucaortis - without knowing the nature of your product, it's hard to tell whether that license model is appropriate, but I'd warn that of all software that tries to lock itself to the hardware, > 90% of them probably shouldn't. That said, your solution looks good - but remember, MAC address can be changed VERY easily, and SMBIOS data (which is where the UUID comes from) can be set manually if the software is run within a Virtual Machine. So just be careful - it will work, but it's more brittle, and less secure than a login-based solution. – user208769 Feb 15 '19 at 11:42
  • 1
    Thanks for your advices. I'm not an expert so any of these are really usefull to me. I managed to use only the MAC address, but for the reason you explained, isn't the best choice at all. I will work for sure on that login-based system. By the way, my application already have a login system, since it's an email client, and this points me to wonder how could i join the mail login system, and mine login system. Sorry for my poor english too. – lucaortis Feb 16 '19 at 12:20
0

I solved by calling these cmd commands from Ruby:

system('wmic nic get "MACAddress" |more >> mac.txt')
# >> Will print all MAC addresses inside "mac.txt" file

system('wmic csproduct get "UUID"')
# >> Will print an unique UUID

Maybe there are better ways, but i'm not an expert and this fixed my problem.
I'm on Windows 10 Pro 64bit and my Ruby version is:

C:\>ruby -v
# >> ruby 2.3.3p222 (2016-11-21 revision 56859) [x64-mingw32]
lucaortis
  • 430
  • 2
  • 11
  • You could use backticks instead of system - e.g. `a = \`wmic nic ...\`` - also, what does piping it to "more" acheive? – user208769 Feb 15 '19 at 11:46
  • Hello @user208769, actually i have a gui application, built with the tcl/tk library, when i call shell commands with `system`, `exec`, or also backsticks, i get an annoying cmd window popping up. So i'm forced to use `win32ole` library, i used `system` here to make it more clear. The `|more` will grant your output to be correctly decoded, since calling `wmic` will print its output with another encoding. – lucaortis Feb 16 '19 at 12:15