Self-hosted mouse click/keydown counter thingamajig =] (OS X/Wind0ngs lol)

Wazakindjes 804f41224d Fix some v min0r last things 2 months ago
ass 34d9915d22 [SCHEMA UPDATE] Prepare for 'other' mouse clicks [[[[[[[[[[= 2 months ago
bin c815249616 Adjusted zippem skrip for str0kem being in /Applications (pr0lly needed for multi-user systems lel) 11 months ago
release edbec28056 Wind0ngs + Meqqy: release v1.3.0.0 2 months ago
sauce 3c4346bd36 Ayyyyy now we can finally bump the versions [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[= 2 months ago
.editorconfig 4826e19021 Added editorc0nfig =] 1 year ago
.gitattributes 4b7b116ba2 Tfw forgot a couple of mentions to br0gram files xd 3 years ago
.gitignore 61ae326bcf Mac: need to embed libzstd too lel, also remove XIB bullshit entirely 11 months ago
LICENSE 35e2375d44 Initial c0mmit yo 3 years ago 804f41224d Fix some v min0r last things 2 months ago

The fuck is this

This is something very similar to WhatPulse, except the sauce is completely open and you can connect this shit to your own SQL serbur. ;];] It's available for le macOS and 64-bit Wind0ngs.

Muy importante: this is just the client side for monitoring keystr0kes/mouse clicks. You can use (almost) any already available graphing tool to visualise that shit.

Also, the zipperin0s I've included in this repo are debug builds, so when shit hits the fan it should make troubleshooting it a bit easier. =]


A MySQL serbur obviously, just make sure the version is 5.5 or highur. One thing you'll definitely need to do if you want to use SSL/TLS connections to MySQL (and you should): make sure the SSL cert used by the server is a trusted one.


Shit was originally made for El Capitan thru Mojave, but now only supports Catalina and up. Also it supports both the x86_64 (64-butt Intel models) and arm64 (fancy-ass Apple Silicon) architectures. The reason for Catalina is that it received an update recent enough that people might still be using it. It's also the first 64-butt only OS, which makes managing libraries a bit easier.

Since it uses keychain bullshit there's no actual ca-certificates directory by default. The .app has a bundled certs dir containing CAs related to LetsEncrypt only (since you're pr0lly using that anyways) as well as cURL's CA bundle (as a convenience), but you can override this location (see Config section for more deetz).

If you're not using LetsEncrypt:

  • Put all cert files in the directory of your choosing (make sure to put only certs in thur)
  • Run openssl rehash <cert dir> if your version supports it, if you installed it w/ Homebrew then it's not in your PATH so use something similar to /opt/homebrew/Cellar/openssl@1.1/1.1.1q/bin/openssl instead
  • If your openssl doesn't support that shit, it's possible the command is available as /opt/homebrew/Cellar/openssl@1.1/1.1.1q/bin/c_rehash <cert dir> instead (although it might generate wrong hashes so verification will still fail, I'm unsure how to generate the proper ones manually lmao)
  • Specify this directory in the config


Shit was tested on Wind0ngs 10 and 11 because righteously fuck 8.x (and who uses W7 anymore?).

It seems the MySQL client library checks against the machine's certificate store, so there's no need for a certs dir like with macOS. =] It does seem to need cURL's CA bundle to properly establish a chain of trust though (or any valid CA bundle prolly, but cURL's is just more convenient).

Just for reference in case you ever need to add a self-signed cert:

  • Copy the cert file to da wind0ngs b0x (obviously)
  • Simply double click that shit
  • Click Install Certificate
  • Now this is important, because it defaults to the Current User store but you actually need Local Machine my dude, so click that shit
  • On the next step, again don't use the default but click Place all certs in the following store instead
  • Browse to either the Trusted Root Certification Authoritays store or Intermediate Certification Authoritays, depending on what kind of cert it is
  • Just click Next until it says it completed lol
  • Possibly repeat that shit to complete the trust chain


I've included a qt lil' SQL schema, so just import that in your SQL serbur and set up a user for the DB. ez pz

Also, a list of times when the schema was updated since release as a way for you to know if you need to update em schema on your actual DB:

  • 2023-03-15

Client installation


Issa p simple yo:

  • Just download the zipperin0 and extract em, then put the .app file in /Applications or some shit
  • To monitor keypresses it needs some Accessibility privileges, so go to System Preferences > Security & Privacy > Privacy tab > Accessibility on the left > make sure the list on the right contains (maybe use the + button to add em) and check that b0x m8
  • Start that shit lol
  • It will copy an example config outside of the .app bundle, the new (sandboxed) location will be shown in a dialog
  • Edit that shit (see Config section for more deetz)
  • Start that shit again

If you want it to start at user login and restart when it dies:
First, copy the p00perty list file to the proper directory:
cp ass/com.jemoeder.lief.str0kem.plist.example ~/Library/LaunchAgents/com.jemoeder.lief.str0kem.plist

Then make sure the contents of that file are referring to the proper path. By default it looks for /Applications/ so if you didn't move the .app to /Applications, adjust that f00kin path. =] It should never address the .app directly, it always needs the executable "hidden" inside.

And finally:
launchctl load -w ~/Library/LaunchAgents/com.jemoeder.lief.str0kem.plist

Every time the executable rips it'll be restarted by launchd, which usually happens within a few seconds. To stop this shit just use the above command, except substitute load for unload 0bv. ;]


Again fairly simple y0, it doesn't even need memey installer bullshit so it's pretty much a portable exe. =]

  • Download the zipperin0 and extract em, then put the directory it contains wherever the fuck you want (preferably %LOCALAPPDATA% due to permissions, it runs as a regular user and never asks for admin/UAC shit)
  • Start that shit lol
  • It will copy an example config to your user's AppData/Roaming folder, the full location will be shown in a dialog
  • Edit that shit (see Config section for more deetz)
  • Start that shit again

The config is saved to Roaming AppData so it also works with r0aming profiles. I still recommend putting the .exe in Local AppData so it doesn't get roamed, since programs should really be kept local imo tbh famalamlmla.

Note: it requires certain .dll and .pem files to run properly, which I've embedded into the .exe and they get extracted when you run that shit. It will put them in the same direct0ry as the .exe, hence my inclusion of a dir in the .zip file. ;]

If you want it to start at user login and restart when it dies:

  • Press Wind0ngs key + R (brings up Run dial0g)
  • Enter taskschd.msc
  • Right-click on Task Scheduler (Local) followed by Import Task
  • Download/find the configuration file and import that shit (you can remove the downloaded file when it's imported so it doesn't matter where the fuck you put em =])
  • In the dial0g that pops up you'll notice the word CHANGEME under General > Security options, so change that shit to your own user
  • Go to the Triggers tab and edit the At log on triggur, edit the CHANGEME here too
  • Now open the Actions tab and confirm that the path to str0kem.exe exists (default is a direct0ry under %LOCALAPPDATA%), also make sure there are 3 DLLs and a PEM file next to the .exe

Wind0ngs will try to start the pr0gram every minute (can't do it m0ar often w/ Task Scheduler lol) as well as at user l0g0n, but it won't run duplicate instances. You could run it as a service but there's no way to run it just for a specific user. ;]

Note: apparently sometimes the extraction of embedded files might fail when str0kem runs through the scheduled task. In that case you could just enable Run with highest privileges in the task settings. =] But really it should work without that because your own account should have full permissions on its AppData directories, so if there's a permissions error you likely fucked shit up. [=[[=[==[[==[


Since """proper""" JSON doesn't actually allow comments, I'll mention a bit about em config here (although it should be pretty clear imo tbh fams):

	"main": {
		"host": "",
		"port": 3306,
		"ssl": true,
		"sslverify": true,
		"sslcapath": "/Users/toplels/local/etc/certs",
		"user": "ayylmao",
		"pass": "poopfarts",
		"dbname": "str0kem",
		"dbtable": "keymouse",
		"dbinterval": 300,
		"logrotate": 2,
		"debug": false

Muy importante: don't end the last lines in a hash with a comma, that shit rips on wind0ngs kek (technically that's """invalid""" JSON too).

Pretty much half of it is required, exceptions being:

  • port: defaults to 3306
  • ssl: defaults to true, since you're prolly connecting over tEh iNtErWEbS
  • sslverify: verify SSL certs, defaults to true cuz inb4MITM my d00d
  • sslcapath: path containing a bunch of hashed Certification Authority certs, defaults to the bundled certs dir for macOS only and simply NULL for Wind0ngs (because it doesn't work on there, it requires one CA bundle which is embedded in the exec00table)
  • dbinterval: how often to connect to the DB, defaults to 300 sec
  • logrotate: rotate the log file when it gets this big (in megabutts lol), defaults to 2 MB
  • debug: log extra shit to logfile, defaults to n0 obv

If you don't wanna use an optional configurable, just leave out the entire line. ;] In some cases it works to pass an empty value but just omit it to be sure.


If the br0gram runs into any problems along the way it'll most likely notify you about it via a dial0g. It also logs some shit to a file in the same dir as the config.

Also, if shit like writing to log fails we obviously cannot log that on disk anywhere, so there are a couple of printf statements too. You can see these by running the executable directly.


You'll need to call the "hidden" binary directly from a terminal: /Applications/


Since cmd won't wait for GUI/WinMain applications to finish (i.e. returns to prompt), you gotta use a bogus pipe: "%LOCALAPPDATA%\str0kem\str0kem.exe" | rem

Otherwise any printf output will be mixed together with the terminal's prompt and shit. =]


If the program receives a termination signal it will (try to) write the current in-memory counters to a file before it actually exits. Next time it starts up it will start from that point so you won't lose hits. For all platforms there's a timer that runs roughly every minute to write that shit as well.


The counter file should be written if the br0gram receives any of these signals: SIGTERM, SIGINT, SIGQUIT, SIGHUP, SIGUSR1, SIGUSR2. macOS seems to be running timers in series so there's no real need to purposely stall/offset the timer for writing the counter file.

As far as I can tell, unloading through Launch Services/launchctl and using the Quit option in Activity Manager (i.e. SIGTERM) both result in the file being properly written. Using Force Quit will obviously send SIGKILL and prevents writing. Shutting down y0 Mac goes through Launch Services as well so it should be the same for that. [[=[=[==[[=[==[[==[=[


It doesn't actually generate signals the way Unixy systems do, but instead uses the TerminateProcess API which might kill the process without notification (thanks Microsoft). Meaning the file might only be written on termination if you run the .exe from a cmd and you Ctrl+C that shit (which does seem to gener8 SIGINT). In my tests (on W11 at least) it did get written every time when I used taskkill /im str0kem.exe, so maybe they finally fixed that bullshit. Also, Wind0ngs seems to be running timers in parallel so we do have to purposely stall/offset the timer for writing the counter file. Otherwise both the MySQL insert and this timer might run together, resulting in a counter file with wrong data. As such this particular timer will fire every minute but inside of it there's a wait for at least 200 milliseconds, it will also sleep for an additional 20ms as long as MySQL is inserting.

Custom compilation

First off, I've included project files/solution bullshit so you should be able to just open those in your IDE and start editing. They should all contain relative paths regarding sauce/incl00d dirs etc, but to make sure it werks try compiling right away without any modifications. Note that you still have to install the MySQL library first, since str0kem #includes one of its header files.


Since it was a fucking pain to figure out how to properly link (embedded) shit, I'm gonna be a br0 and explain all that shit right here. =]

You need a few external libraries for that shit, and I'll be assuming you're using Homebrew:

  • libmysqlclient: used to be the mysql-connector-c package, but that's ripped now, so instead the library is found within mysql-client
  • libzstd: not really sure where the fuck this comes from but libmysqlclient seems to need it lel, can prolly be found under /opt/homebrew/Cellar/zstd (may need to install the zstd package first doe)
  • libssl and libcrypto lel (openssl package obviously)

Note that Homebrew by default installs dylibs for your specific CPU architecture only, so compiling a multi-arch application isn't possible by default. That shit needs some additional steps. In my case I'm on arm64 and I need to download x86_64 libs so I can target both architectures. One thing to check beforehand: you'll want to have the most recent version of the libs for your default architecture. Since we'll be downloading for the other arch straight from Homebrew, that will (most likely) be the latest version and even a slight mismatch between versions could cause issues. Always compare the version you have installed vs. the one Homebrew downloaded.

An example with openssl:

# Or wherever the fuck the sauce dir is actually located for you ;];];];]
$ cd ~/git/str0kem

# Adjust the arch, OS name and package as needed (you can omit the @version specifier if you want to use the default version, but in openssl's case that would be 3.x while we need 1.x)
# As I mentioned bef0re, for ARM you'd use e.g. 'arm64_monterey' here
$ brew fetch --force --bottle-tag=x86_64_monterey openssl@1.1

# The relevant part from the output is this:
Downloaded to: /Users/muhbrew/Library/Caches/Homebrew/downloads/8e55d6c7baefb1ace7a73413ad6b3bf7e58686352d65ec0c6897733b63a5d146--openssl@1.1--1.1.1q.monterey.bottle.tar.gz

# Gotta untar that shit, but only need any .dylib files (some are not needed for str0kem though)
$ tar xzvf '/Users/muhbrew/Library/Caches/Homebrew/downloads/8e55d6c7baefb1ace7a73413ad6b3bf7e58686352d65ec0c6897733b63a5d146--openssl@1.1--1.1.1q.monterey.bottle.tar.gz' '*.dylib'
x openssl@1.1/1.1.1q/lib/engines-1.1/capi.dylib
x openssl@1.1/1.1.1q/lib/engines-1.1/padlock.dylib
x openssl@1.1/1.1.1q/lib/libcrypto.1.1.dylib
x openssl@1.1/1.1.1q/lib/libcrypto.dylib
x openssl@1.1/1.1.1q/lib/libssl.1.1.dylib
x openssl@1.1/1.1.1q/lib/libssl.dylib

# Prolly useful to create a temporary dir to store all the arch's libs
$ mkdir x86_64

# As mentioned before, we need the numbered files
# Also we only actually need libssl and libcrypto
$ mv openssl@1.1/1.1.1q/lib/lib{crypto,ssl}.1.1.dylib x86_64/

# Remove the extracted dir cuz we no longer need it
$ rm -rf openssl@1.1

# Now do the same for other libs
# Afterwards, the temp dir should contain something like this:
$ ls x86_64
libcrypto.1.1.dylib  libmysqlclient.21.dylib  libssl.1.1.dylib  libzstd.1.5.2.dylib

# Compare with the libs actually used by str0kem
$ ls sauce/mac/lib/
libcrypto.1.1.dylib  libmysqlclient.21.dylib  libssl.1.1.dylib  libzstd.1.5.2.dylib

# Check the file types
$ file x86_64/*
x86_64/libcrypto.1.1.dylib:     Mach-O 64-bit dynamically linked shared library x86_64
x86_64/libmysqlclient.21.dylib: Mach-O 64-bit dynamically linked shared library x86_64
x86_64/libssl.1.1.dylib:        Mach-O 64-bit dynamically linked shared library x86_64
x86_64/libzstd.1.5.2.dylib:     Mach-O 64-bit dynamically linked shared library x86_64

$ file sauce/mac/lib/*
sauce/mac/lib/libcrypto.1.1.dylib:     Mach-O 64-bit dynamically linked shared library arm64
sauce/mac/lib/libmysqlclient.21.dylib: Mach-O 64-bit dynamically linked shared library arm64
sauce/mac/lib/libssl.1.1.dylib:        Mach-O 64-bit dynamically linked shared library arm64
sauce/mac/lib/libzstd.1.5.2.dylib:     Mach-O 64-bit dynamically linked shared library arm64

# Now combine the separate architectures into the same file, then move the new file into the directory used by str0kem
$ for muhlib in libcrypto.1.1.dylib libmysqlclient.21.dylib libssl.1.1.dylib libzstd.1.5.2.dylib; do
$	lipo "sauce/mac/lib/$muhlib" "x86_64/$muhlib" -output "$muhlib" -create && mv -v "$muhlib" sauce/mac/lib/
$ done

# Check the file types again (I cleaned up the output a little to make it more readable lel)
$ file sauce/mac/lib/*
sauce/mac/lib/libcrypto.1.1.dylib: Mach-O universal binary with 2 architectures
sauce/mac/lib/libcrypto.1.1.dylib (for architecture arm64):  Mach-O 64-bit dynamically linked shared library arm64
sauce/mac/lib/libcrypto.1.1.dylib (for architecture x86_64): Mach-O 64-bit dynamically linked shared library x86_64

sauce/mac/lib/libmysqlclient.21.dylib: Mach-O universal binary with 2 architectures
sauce/mac/lib/libmysqlclient.21.dylib (for architecture arm64):  Mach-O 64-bit dynamically linked shared library arm64
sauce/mac/lib/libmysqlclient.21.dylib (for architecture x86_64): Mach-O 64-bit dynamically linked shared library x86_64

sauce/mac/lib/libssl.1.1.dylib: Mach-O universal binary with 2 architectures
sauce/mac/lib/libssl.1.1.dylib (for architecture arm64):  Mach-O 64-bit dynamically linked shared library arm64
sauce/mac/lib/libssl.1.1.dylib (for architecture x86_64): Mach-O 64-bit dynamically linked shared library x86_64

sauce/mac/lib/libzstd.1.5.2.dylib: Mach-O universal binary with 2 architectures
sauce/mac/lib/libzstd.1.5.2.dylib (for architecture arm64):  Mach-O 64-bit dynamically linked shared library arm64
sauce/mac/lib/libzstd.1.5.2.dylib (for architecture x86_64): Mach-O 64-bit dynamically linked shared library x86_64


One thing you'll very likely need to adjust after that is at the top of str0kemAppDelegate.m and looks something like this:
#include </opt/homebrew/Cellar/mysql-client/8.0.31/include/mysql/mysql.h>
Make sure the path and version here match the library installed on your system.

Then also make sure it links against the .dylib file of the matching version. If you happen to have the same version of the connector libs as used in the actual c0de, then you should be good to go. If it still n0 werkies or you have a different version, you gotta edit some shit. Most of the steps required to embed libraries are already done, so it should be a simple matter of:

  • Go to the direct0ry: /opt/homebrew/Cellar/mysql-client/8.0.31/lib (or something similar, obviously)
  • In there you should find a couple of files: libmysqlclient.xx.dylib (where xx is a number, in this case 21) and libmysqlclient.dylib (a symlink to the numbered variant)
  • Copy the non-symlink to this repo's sauce tree under sauce/mac/lib, overwriting any existing files in the process
  • Now click the project file in the editor
  • If the xx is different from before, you'll also need to tell Xcode to forget about the old one and embed the new one instead: click str0kem under Targets > General tab > Frameworks, Libraries and Embedded Content section (set it to Embed & Sign)
  • Click on em Build Phases tab and scroll down to the Run Script section
  • In there you'll notice a path referring to the libmysqlclient.xx.dylib you copied earlier, make sure the xx in the script matches your file's version too

And that should d0 it. =]

Note: My custom build script used by Xcode has to re-codesign some libraries because we need to modify the "rpath" pointing to another library inside them. Xcode's own codesigning step happens before this so the resulting application will otherwise "crash" due to an incorrect signature. =] This also means that technically letting Xcode Embed & Sign libraries is not necessary, but I seem to recall it still whined "hurrr durrrr unsigned library bruh" when building. Anyways, the build script should be able to pick up on your codesigning identity automatically, but if not then just copy the to (next to str0kem.xcodeproj) and edit the export line so it uses the proper identity.

Protip: Xcode builds the application within the sauce tree (bin/ because writing directly to /Applications usually isn't allowed (also I prefer to keep everything contained within the sauce tree to begin with), but macOS kinda wants you to start br0grams from /Applications. So how to fix that? Simple, move the bin/ to /Applications manually and symlink that shit: ln -s /Applications/ ~/git/str0kem/bin/ (adjust the second path to match wherever the git tree is 0bv). This puts the symlink in the sauce tree, so when Xc0dd builds shit it follows em to /Applications/, which (now) has the proper permissions since you moved it there and thus is properly writable. [[==[[=


>my fucking face when trying to figure out where to get the proper downl0ads, THANKS (WH)ORACLE

You need a few external libraries for that shit:

So uh, the C connector is nowhere to be found on official sources??÷¿? Their downloads page mentions it's included in the community installer, except it fucking isn't (at least not anym0ar). ;_; So we're just gonna have to pull that shit from the MySQL server:

  • Go to the relevant downl0ad paeg
  • Make sure wind0ngs is selected for OS and download the regular Windows (x86, 64-bit), ZIP Archive (i.e. without tests and debug shit)
  • Create a directory to install the library to, I recommend something based on the major + minor version (e.g. 8.0 for 8.0.32) in Program Files: C:/Program Files/MySQL/Connector C 8.0 (the C++ connector uses almost the exact same path)
  • Unpack the zipperin0 and copy include and lib in their entirety to the installation dir (you don't need any of the binaries for it to work)
  • At this point you can actually use the development headers but you still need to copy C:/Program Files/MySQL/Connector C 8.0/lib/libmysql.dll to sauce/win/lib in str0kem's sauce tree, overwriting the existing file in the process

So uhhhhh, MySQL depends on OpenSSL but it's nowhere to be found either??¿¿??÷÷?/ Apparently it is contained within the C++ connect0r though, so again we have to pull shit manually:

  • Go to the community installer downl0ad paeg (yes, this is different than before), also we're going to use an actual installer this time so it's easier to update it in the future
  • Scroll down and pick either of the installers, the difference being web/online vs. full offline inst0ll
  • It might ask you to upgrade the installer once you run em, so just do eet fagg0t
  • Click Add... on the right when it finally opens -- it might also instead present you with a choice for the installation type, in that case pick Custom
  • Expand everything under MySQL Connectors > MySQL Connector/C++ until you finally get to choose between x64 and x86
  • You can only install one of the architecture variants, but you should prolly just go with x64 because who the fuck still runs x86?
  • Install that shit B R U H
  • You should now have a directory similar to C:/Program Files/MySQL/Connector C++ 8.0
  • In there you should find another dir lib64, which in turn finally contains the DLLs we need: libcrypto-1_1-x64.dll and libssl-1_1-x64.dll
  • Copy both of those to the same sauce/win/lib dir, again overwriting the existing files

The above steps should also let it link against the .dll library files of the matching version. Obviously, if some of the versions within the file names changed you'll have to adjust the project to match.

One thing you might need to adjust after all that is at the top of str0kem.cpp and looks something like this:
#include <C:/Program Files/MySQL/Connector C 8.0/include/mysql.h>
Make sure the path and version here match the library installed on your system.

And that should d0 it. =]

Protip: Visual st00di0 builds the application within the sauce tree (bin/str0kem.exe) because I prefer to keep everything contained within there, but the scheduled task looks under %LOCALAPPDATA%\str0kem. So how to fix that? Simple, remove the .exe from %LOCALAPPDATA%\str0kem and instead make it a symlink: mklink %LOCALAPPDATA%\str0kem\str0kem.exe %USERPROFILE%/git/str0kem/bin/str0kem.exe (adjust the second path to match wherever the git tree is 0bv). Unlike l00nixy systems, wind0ngs isn't smart enough to follow the symlink and build an .exe at the location it points to, it actually overwrites the symlink itself. Which is why we keep the .exe in its original place and just provide an entry point that's compatible with the scheduled task. Note that you can (and should) still run str0kem using %LOCALAPPDATA%\str0kem\str0kem.exe, that way it will also unpack its embedded shit in the directory where the symlink is (instead of el git dir).