Preferred Wifi performance

Most operating systems for computers and mobile devices allow setting the preferred order of WiFi access points (APs). Keeping in mind risks of rogue APs, one may wish to keep open WiFi networks available while preferring more secure networks. Windows WiFi preference is configurable from the command prompt. MacOS allows simple dragging to select preferred WiFi networks. Apple iOS WiFi preference is similarly dynamic and user-configurable. Android has sophisticated WiFi network selection scoring that can be influenced by the user. Linux has several choices of WiFi network managers, most of which allow setting the WiFi preference order.

In general, consider using a full-time VPN for mobile devices and computers, especially when using WiFi that is not your known work or home WiFi network. Unused WiFi networks are a security risk and waste energy by beaconing WiFi SSID names the device has connected to in the past.

Delete infrequently used WiFi networks, especially “open” networks that are at highest risk for spoofing. Suppose a rogue WiFi AP exploiting zero-day flaws in the network stack or presenting a fake login page to glean credentials. The Pwn Pulse shows how much information can be gleaned from Bluetooth and WiFi in a commercial product.

Improve blog post indexation

Google’s Search Console is useful for uncovering lagging performance in web pages, including for blogs. I tend to write terse posts that address very specific issues. Often, these pages perform well, but I saw a few percent of pages being marked in Search Console as status “crawled - currently not indexed”. The underlying theme on these pages was they had too little ordinary paragraph text. If there are too many lists, headers, or preformatted text relative to plain paragraphs, this “not indexed” status is likely to be applied.

A few of these type of pages also suffered from “soft 404” status. I found these were very short pages that contained text with “error” or “missing”. I reworded those articles to avoid those terms. I made sure the titles didn’t include those terms. I also ensured there were not too many header tags relative to the text–perhaps one header at most per “page” of text.

The fix to these issues is generally to include more meaningful text–be sure an article is at least one or two full paragraphs. Add context that would help a more novice user understand why you applied that solution or approach. Avoid sensational or colloquial text as the search engines are smart enough to recognize this as low quality writing. As always, maintaining good spelling and adequate grammar help the search engine better appraise the quality of your content. Also consider short (less than 50 character) but meaningful page titles.

For long-lived blogs such as this one, there is inevitably content that is no longer relevant to anyone except for historical purposes. You may not want to simply delete these posts that you took time to research and share, but realize these old posts are costing your current performance by wasting crawler budget. The approach I take is to mark these old pages with “noindex” metadata. That allows reminiscing about old technology such as Blackberry OS 10 without degrading the performance of my currently relevant content. I think of it as a soft deprecation of the content.

Internet of Vulnerable Things

People at risk such as stalking victims, executives, VIPs, security staff, defense/aerospace and other at-risk device users should consider risks involved in typical wireless use patterns. The proliferation of BYOD, wearables and IoT devices in everything from alarm clocks to utility meters increases the attack surface. Google does not include WearOS in the Android Security Rewards bounty program. State-level actors consider the value of broad swaths of population collection as well as high-value targets. The CDC 2012 National Intimate Partner and Sexual Violence Survey NISVS notes that about 11% of the US population have been stalking victims.

Popular media has depicted “burner phones” as a tactic to avoid tracking. Burner phones can be associated with a victim for subsequent attacks when the attacker knows they’re in proximity of the victim and the victim has the burner phone on. At-risk users should consider the risks of having Bluetooth and WiFi enabled. The attacker typically has the advantage of time, and can patiently pwn the weakest victim device and escalate from there. Subtle and practical attacks are ever evolving. Security by proximity is also not secure. Sniffing devices can be left behind inside innocuous devices such as power strips.

While recent standards including Bluetooth 5 and WiFi 6 / WPA3 include substantial security benefits over earlier standards, devices often implement the standards imperfectly across their firmware / software stacks. Devices wirelessly beacon surprisingly useful to the attacker information. The attacker knowing the victim’s device type can implement a spear phishing attack–perhaps a text message pretending to be from the victim’s employer or wireless carrier with a link to a zero-day exploit.

Signal strength can pinpoint a user’s location even in a crowd. Two meters of separation between victim devices can be detected from an attacker over 15 meters away. The typical victim has a plethora of vulnerable devices including smart TVs, pre-WPA3 WiFi networks, Bluetooth speakers and WiFi security cameras with old firmware. Multiple WiFi APs in the victim’s vicinity, made popular by consumer WiFi mesh networks provide an effective radar to track the victim’s approximate location in a home or office by a passive attacker.

Signal strength target discrimination vs. attacker distance

Wireless energy knows no borders, with maximum range limited by the inverse square law and obstructions. Weak microprocessors with weak encryption to save energy, lazy design and lack of OEM liability leave little supply-side motivation to fix these personal and national security risks.


Bluetooth speakers as found in home or office typically have auto-pairing. This allows that when no paired device is in range, anyone can pair with the speaker without physically touching the speaker. It would be then trivial to monitor the audio input in headset mode (most of these speakers have microphones for two-way audio) without the victim knowing they’re being listened to. Suppose the attacker walks down a street listening for auto-discoverable HSP devices, whether speaker or headphones or other device with microphone. Many neighborhoods have lots of these HSP devices left on 24/7, which can be used to determine user occupancy patterns, travel plans and other confidential victim information.

Bluetooth Low Energy

Bluetooth Low Energy (BLE) is quite distinct from legacy Bluetooth despite the name. technical characteristics: BLE has three advertising channels at 2402, 2426, 2480 MHz, picked to be between WiFi channels for best interference-free range plus 37 data channels. A common attack vector of legacy Bluetooth and BLE is when the attacker hears pairing and trivially cracks the key. The attacker can then at any time force unpairing, masquerade the connection and take unwanted actions such as crash the device wirelessly or passively eavesdrop.

Maximum range of detecting BLE is typically 1 km with $20 antenna. Without special antennas, small BLE devices are typically detectable to 50 meters or so. Inexpensive attacker devices like Ubertooth enable passive monitoring of already paired devices (as with Wifi) based on periodic updates (100ms to 10sec).

Android “Smart Lock” is good for attackers, not victims. If connection can be intercepted and masqueraded, an attacker can physically obtain the unlocked device, install malicious software and return the device undetected. Corporate device policies and end users should consider blocking this. Windows has a similar feature where Bluetooth-connected device is used to imply user proximity.

Android Smart Lock--anything but!

Suppose the attacker passively sniffs the victim MAC/UUID and learns the BLE simple channel hopping pattern. BLE channel hopping pattern is trivially predictable and the UUID is in the clear. The CRACKLE BLE attack enables an attacker to jam the victim BLE wearable, breaking connection and leading the victim to re-pair the wearable (which is the point of vulnerability). The attacker can crack the pairing exchange and then implement passive monitoring or access email, text, phone, calendar, fine location, apps, etc. from 10-100 meters distance from victim.


WiFi devices including wearables, smartphones and laptops beacon several times per minute the WiFi SSIDs they’ve previously connected to, including hidden SSIDs. The probe beacons reveals the device MAC as well. Operating systems for mobile devices and computers have generally implemented user option to rotate random device WiFi MAC addresses. A user may disable this random MAC address without realizing the security implications. Even with random MAC, the victim beacon/probe signal strength can still be used as a means to track the victim’s proximity and approximate location.

Homes with smart TVs, WiFi security cameras and baby monitors typically use obsolete pre-WPA3 WiFi networks with weak protocols such as WEP, WPA or WPS. Cheap security cameras often have unencrypted or weakly encrypted password exchange. Once the Wifi password is cracked, the attacker can then exploit the typically insecure WiFi devices throughout the modern home or office. The victim attempted to secure their home with surveillance, but made themselves substantially less secure against wireless attacks.


Disable homebrew cleanup on MacOS CI

Homebrew’s brew cleanup saves disk space by deleting old versions of packages. From time to time, CI MacOS images get out of date with Homebrew, and auto-cleanup is triggered unintentionally upon brew install during a CI run. There is no benefit to this on CI and it wastes CI time.

Disable Homebrew auto cleanup by setting environment variable HOMEBREW_NO_INSTALL_CLEANUP=1. On GitHub Actions, this is accomplished near the top of the particular .github/workflows/*.yml file:


Windows GCC availability

GCC (including G++ and Gfortran) on Windows is available via several means. We generally prefer MSYS2 for using GCC and numerous libraries with pacman package manager. MSYS2 and WSL have active and competent development teams. We find MSYS2 to almost always work for our needs. For the few difficult corner cases, we use WSL. The other projects are also competently maintained, but MSYS2 or WSL are generally the most up to date by far.

  • MSYS2 (recommended, well-maintained and very up to date)
  • WSL (recommended for apps needing Linux system API)
  • MinGW
  • Cygwin (usually WSL is preferred instead)
  • TDM (usually MSYS2 is preferred instead)

Permute/transpose xarray, Numpy, Matlab arrays

Permuting and transposing array dimensions are a common operation used in data analysis. Ideally these operations would be done with index tricks instead of copying arrays. In Matlab, these operations copy the array (slow, expensive). In Python, these operations are O(1) an index manipulation creating a new view into an array (fast).

In Matlab, transpose and permute are distinct methods. In Matlab, transpose() is strictly for 2-D matrices, while permute() is for reordering dimensions of N-D arrays.

In Python, xarray and Numpy arrays are popular. Both use the .transpose() method for N-D array dimension reordering–there is no separate “permute” method. However, the syntax is distinct between xarray and Numpy.

xarray.Dataset and xarray.DataArray have a .transpose(*dims) method. Note the “*” used to expand the arguments in the top line, which is equivalent to the bottom line.

# A.dims == ('x', 'y', 'z')

B = A.transpose(*('z', 'y', 'x'))

# this is equivalent
B = A.transpose('z', 'y', 'x')

# B.dims == ('z', 'y', 'x')

Numpy transpose can also permute by specifying a tuple of the desired order. For 2-D arrays, transpose by omitting the axes order argument.

B = A.transpose((2,1,0))

D = C.transpose()

Using CMake on Windows

The latest CMake release may be installed in several ways. Without any additional programs, simply download and extract the CMake .zip file and add the new cmake*/bin to the PATH environment variable. Python users may simply “pip install cmake”. MacOS homebrew users can “brew install cmake”.

The CMake build process is the same across operating systems and compilers:

  1. configure: This is normally run only once unless making major project changes.
  2. build: This is the command run each time you make a change to the project code.
  3. install (optional): Put the binary artifacts into a convenient directory like /bin or /lib
cmake -S myproj -B myproj/build

cmake --build myproj/build

cmake --install myproj/build

On Windows, CMake defaults to Visual Studio and Nmake. This is usually not a useful default. We use the Ninja build system with CMake, which is generally faster on any operating system. Ninja on Windows solves numerous issues vs. GNU Make. Ninja works with Visual Studio as well.

Override the default CMake generator by setting environment variable


CMAKE_GENERATOR can be overridden like:

cmake -G "Visual Studio 16 2019"

Older CMake versions on Windows may get the message below, which is fixed by upgrading to CMake ≥ 3.17.

sh.exe was found in your PATH, here:
C:/Program Files/Git/user/bin/sh.exe
For MinGW make to work correctly sh.exe must NOT be in your path.
Run cmake from a shell that does not have sh.exe in your PATH.
If you want to use a UNIX shell, then use MSYS Makefile

Matlab vs. Python Numpy cumsum

A common numerical operation is cumulative summing. The cumsum() operation has the output array of the same shape as the input array, with each element as the sum of all the previous elements in that dimension. Matlab cumsum and Python numpy.cumsum operate quite similarly. When translating code between Matlab and Python, as always keep in mind Matlab’s one-based indexing vs. Python’s zero-based indexing. That is, when using cumsum() over an axis, be sure to select the correct axis–Matlab cumsum(..., 1) is equivalent to numpy.cumsum(..., axis=0).

These examples are equivalent. Suppose input:

x = [2, 3, 5, 8]


y = cumsum(x)
y =
     2     5    10    18


y = numpy.cumsum(x)
>>> y
array([ 2,  5, 10, 18])

Matlab - Python meshgrid vs. ndgrid

When working with large grids, the grid itself can consume considerable memory. Large grids can require more advanced techniques like working with interpolants and using sparse grids. Many grids that aren’t too large can use common functions like “meshgrid” to make certain plotting or linear algebra operations easier. Matlab and Python (Numpy) can readily work with these data structures. We show the differences and similarities to allow understanding and translating code between Matlab and Python.


Matlab meshgrid and ndgrid generate distinct data for the first two dimensions, but other dimensions remain the same. This is due to meshgrid() being intended for plotting, and ndgrid() intended for matrix operations.

[x,y] = meshgrid([1,2], [1,2])

x =
     1     2
     1     2

y =
     1     1
     2     2
[x,y] = ndgrid([1,2], [1,2])

x =
     1     1
     2     2

y =
     1     2
     1     2

Python Numpy

Numpy is used for most array computation in Python–many Python numerical libraries use Numpy internally. Numpy has additional advanced grid generation functions numpy.mgrid() and numpy.ogrid()–here we will focus on numpy.meshgrid. numpy.meshgrid also has copy=False and sparse=True options that allow conserving memory. For simplicity, we will use the defaults, which is a dense copied mesh.

Equivalent to Matlab meshgrid():

x,y = numpy.meshgrid([1,2], [1,2], indexing='xy')

>>> x
array([[1, 2],
       [1, 2]])
>>> y
array([[1, 1],
       [2, 2]])

Equivalent to Matlab ndgrid():

x,y = numpy.meshgrid([1,2], [1,2], indexing='ij')

>>> x
array([[1, 1],
       [2, 2]])
>>> y
array([[1, 2],
       [1, 2]])