Wednesday, September 7, 2022

Getting a little jumper-y

 This will be a short post. The tagline mentions jumper because this write-up is about reverse engineering the L2 cache jumpers on a 1990's Pentium (P54) motherboard. There are more jumpers that need some figuring out, and they will not appear until a future write-up.

While sorting through 'tech' I had stowed for future hobbies, I found myself unable to parrt with an AT (clone) case from what was once a 386DX/25 on which I first learned MS-DOS, Windows 3.1 and Microsoft Publisher.

That AT case no longer has anything from its "386" days except for the original two floppy drives and a power supply. At some point I had stashed a rescued Pentium motherboard in the case - Full Yes Incorporated SIS P5 PCI; the motherboard had 256K of 15ns cache  installed (eight chips) and a single cache chip with 25ns rating. The motherboard functioned only with cache disabled. 

With the cache disabled in BIOS, I uncovered another problem.  Since stashing the motherboard a decade ago, the Dallas CMOS RTC battery had faded. I was now greeted with a NMI error.  A site hinted that  bad CMOS can potentially interfere with being able to establish a healthy cache, resulting in memory errors and a dreaded NMI on boot. NMI errors are also reported as happening with bad disk blocks or a corrupted boot sector.

I ordered a new RTC from DigiKey, installed it, and my problems were not solved. 

Replacing the RTC allowed the CMOS to keep settings which meant the next challenge was to find out why I was getting the dreaded NMI.  The NMI ended up being a simple fix - either install parity SIMMs or disable the parity check in the BIOS.  I tried both ways, but I had yet another problem.  Upon getting to POST, the system hanged when the cache was enabled.

The motherboard uses a SIS 50x chipset. I read up on SIS chipsets and learned that most of the SIS motherboards supported 1MB to 2MB of cache.  This motherboard has 20 sockets for DIP32 cache chips.

I placed an order for 128Kx8's and upon receiving them, tested them in my Xgecu TL866ii to eliminate bad modules.

I installed the cache chips and the system would hang on POST with either a report of 0K or 64K of cache. I fussed with the jumpers in proximity to the cache sockets and found settings which claimed 2MB of alleged cache on POST; however, the system still hanged after POST or shortly after reaching a prompt in FreeDOS.

I read about how someone had managed to revise their system board - from the same era and same chipset - to accommodate 2MB of cache despite being manufactured with support for 1MB. That gave me incentive to pull out a multimeter and begin ohming connections on the system board.  I discovered that a block of jumpers positioned between the TAG RAM sockets and the CPU socket were not anything other than jumpers for specifying the TAG RAM configuration.  Up until this point I suspected they were settings for CPU or cache voltage.  Instead, neither.  Before filling all sockets, I double-checked that the TAG RAM sockets would accept 128Kx8's.  Other SIS system board manuals hinted that 1MB of cache with 128Kx8's requires a single 64Kx8 TAG SRAM.  Two banks of 1MB would follow to equal two 64Kx8's. 

Well, shoot; I only ordered 128Kx8's.

A 128Kx8 will address up to line A16, but what if the pin for A16 is neither connected to ground nor Vcc - in fact, not connected at all?  If that happened to be the case, I would need to short them ground; otherwise it might float between either of two 64K banks on the the 128Kx8 DIP32.  If they had been floating, that might explain the variance between 0K and 64K, or now with the jumpers configured to 2MB, wavering between 0K and 2MB.

A check with the multimeter showed line A16 of both TAG SRAM sockets was properly hooked to ground.  But the findings for the middle set of jumpers - JP7, JP8, JP9 and JP10 showed some measurements I did not immediately follow.  JP7, JP8 and JP10 all had been in a position to short to ground.  JP9 jumpered A13 to Vcc (+5V). The explanation for this became clear when I looked up the pin out for a DIP28 8Kx8.  The pin that feeds A13 on a 128Kx8 is actually the Vcc for an 8Kx8. From this I concluded that if I shifted JP7, JP8 and JP10 away from grounding, and JP9 away from Vcc, the configuration of twenty 128Kx8's would give me 2MB of fully functional cache.  Indeed it does.

I hope this helps someone else.

Here are the settings I established to configure a full 2MB cache using ISSI 61C1024-15Ns DIP32's

JP7  [32]1  means if 3-2-1 then [32] are jumpered and 1 is NC.  

JP8  [32]1  there are no markings for pin numbers so the order

JP9  [32]1  is purely based on JP4's position with respect to

JP10 [32]1  the other jumpers on the system board.


    [32]1 JP1

    [32]1 JP2

    [32]1 JP3

[54][32]1 JP4


That's all. Thanks for reading!



 

Thursday, February 10, 2022

Over my dead eMMc...

You find yourself in an impossible situation.  Impossible in that, it's never going to happen to you.

Until it happens.


You have a Chromebook that will not boot.  You discover the internal eMMC is defunct.  Cracking it open, you discover the inevitable truth; the eMMC is soldered on.  

After removing the WP screw, you succeed in preparing a flash drive that includes MrChromebox's BIOS for the Chromebook and a few moments later, it reboots and splashes a welcoming running rabbit.

Your favorite Linux distro now boots from a USB drive and a SD-CARD. But, you now have a new problem. 

The ex-Chromebook will not suspend because the internal eMMC can't be communicated with in full, and inhibits ACPI suspend.  

What now?

You learn by trial and error that after a warm reboot from a successful initramfs prompt, the internal  eMMC is temporarily out of the way, and suspend works.  

You now face the challenge of how to make that eMMC disappear without physical intervention.

You fiddle around with the init ramfs and arrive at a solution.  You realize you're no expert, but it suffices.

You make the business portion of this bundle communicate a shutdown to plymouth, which disrupts any cryptsetup (decrypt password prompts) and follow with a call to systemctl to start the shutdown.target.  An older iteration tried using pkill ^plymouth and a reboot command, but after realizing calling binaries directly is a tad abrupt, you fall back on using modernized system administrator techniques.

You drop this in /usr/lib/systemd/system

#  SPDX-License-Identifier: LGPL-2.1-or-later
#
#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

[Unit]
Description=Reboot the system if a more than one mmc is found.
Documentation=man:man
DefaultDependencies=no
After=systemd-udev-settle.service
Conflicts=cryptsetup.target
Before=lvm2-activation-early.service cryptsetup.target local-fs-pre.target dmraid-activation.service
Wants=systemd-udev-settle.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/lib/systemd/deadmmcreboot

[Install]
WantedBy=initrd.target

And you drop this in /usr/lib/systemd

#!/usr/bin/bash
# 2022-02-06 This ASUS C300 chromebook landing in my possession
# has a defunct internal eMMC. On cold boot, the eMMC can be
# read from the BIOS and even reaches a GRUB menu, and after
# the eMMC is enumerated by the Linux driver, a subsequent
# reboot appears to take the eMMC offline for future warm
# state reboots.  This helper attempts to detect if the eMMC
# is present and reboot at least once.
printMatch () {
    echo "Looks like this list qualifies for a reboot:"
    echo "  $(dmesg | grep mmcblk.:)"
  }
plymouthShutdown () {
    plymouth change-mode --shutdown
   }

for W in $(cat /proc/cmdline) 
  do
  IFS="="
  for X in ${W[@]}
    do 
    if [ ! -z $Y ]; then 
      if [ -e "/dev/${X}" ]; then
        printMatch
        plyouthShutdown
        systemctl start reboot.target
        fi
      unset Y
      fi
    if [ $X = "deadmmc" ]; then
      Y=1
      fi
    done
  done
# A crude way to determine if more than one mmc is detected.
# A fall-through in case the system is not configured with
# a kernel parameter.
# In the case of an ASUS C300 Chromebook with a defunct
# mmc, the kernel enumerates the internal eMMC as 
# mmcblk1
if [ ! -z "$(ls /dev/mmcblk0)" ] && [ ! -z "$(ls /dev/mmcblk1)" ]; then
  echo "Example: vmlinuz ... deadmmc=mmcblk1"
  printMatch
  plymouthShutdown
  systemctl start reboot.target
  fi

You create a directory 02deadmmcreboot under /usr/lib/dracut/modules.d.  Nearly done, you drop this in /usr/lib/dracut/modules.d/02deadmmcreboot

#!/usr/bin/bash

# called by dracut
check() {
    return 0
}

# called by dracut
depends() {
    return 0
}

# called by dracut
installkernel() {
    return 0
    local _arch=${DRACUT_ARCH:-$(uname -m)}

    instmods mmc_block sdhci_acpi sdhci_pci sdhci cqhci mmc_core
    hostonly="" instmods mmc_block mmc_core

    dracut_instmods -o -s ${_funcs} =drivers/mmc 
    return 0
}

# called by dracut
cmdline() {
    return 0
}

# called by dracut
install() {
    inst_simple "$systemdsystemunitdir"/deadmmcreboot.service
    inst_simple "$systemdutildir"/deadmmcreboot
    $SYSTEMCTL -q --root "$initdir" enable "deadmmcreboot.service"
}

Finally you run dracut.  
Because you weren't sure if this was going to work for all dead eMMCs, you remember that you could also try a boot parameter of deadmmc=mmcblk1 just in case, and add that to /etc/default/grub's default options.


Float this idea...

Since the early 1980's, it was known the floating bus in the Apple II would hold the value of a recently sampled value of memory displayed on screen.  When it comes to those who have taken advantage of the floating bus' availability, French Touch, deater (VMW), and John Brooks (VidHD) come to mind.  

One of the issues in using the floating bus is its availability across the various family of Apple II's.  On a //e, register $C022 will return a floating bus value, but on a IIGS it will not.  The IIGS and the //c use a number of the $C0xx IO addresses for newer purposes, and are otherwise unused in a II, II+ and //e.  

Mixed video modes are one of the neat things that can be made using the floating bus. I tested to see if I could reliably sync up the signal to provide any combination of mixed modes as seen in demos by French Touch and deater. I succeeded in making a program that reads the keyboard and based on user input, toggles the desired soft switch for the preferred mode while swapping every other scanline from page 1 and page 2.  Another program uses pages of 256-bytes to indicate any one of four IO registers to read, again, unique for page 1 and page 2.  Both work well enough for my exploration, but more ideas came to mind with regard to the floating bus.

I read that only if an expandable II series slots are not fully populated, it will reflect a recent video signal byte on the floating bus. If a slot is not populated, the address space for that card will be filled with floating bus values; but for the IIgs and some emulators, the floating bus is not exposed, rather just zeroes.  I have not tested if this also holds true for when the slot's expansion ROM space ($C800 - $CFFF).  For the sake of this exercise, slot 1 is vacant and on a //e, if the firmware is not enabled and the slot is vacant, $C100-$C1FF will take on the values of the floating bus.  This can be seen at a monitor prompt (CALL-151 from BASIC), and issuing C100L.

Slot ROM, either in the specific range, or in the expansion ROM address range, can it be used to invoke code? That question came to mind.

  • Can the displayed video contents of the floating bus be executed as code?

The answer is yes. There is a one caveat.  This caveat could be used to confound a person debugging code.  Not all instructions have a linear cycle count ratio to the number of bytes the instruction and data consume.  For example, a LDA $C030 (AD 30 C0) takes four cycles; however, the video refresh will pass over four bytes even though the instruction only consumes three.  A new question comes to mind.

  • What happens to the byte after the 2nd data byte of a LDA $absolute instruction?

At this time, I have not sought an answer to this question. I suspect the PC is now at the fifth byte and will direct the processor to that byte as the next opcode.

There is one another conditional caveat, and that is the hardware on which the scenario is applied.  On a physical enhanced Apple //e, the PC correctly fetches data for the instruction in at least some situations, such as a JMP (opcode $4C). In AppleWin 1.30, only zeroes are fetched for a JMP command. If at address $0000 a JMP $0300 (0: 4c 00 03), and if text page 2 is used for this example, and the text page contains all zeroes (800:0 m 801<800.bfem) except for address $800, and $800 has the JMP opcode (800: 4c), and the break vector is set to an address with a floating bus ($c030 on a //e), and text page 2 is actively displayed (can be lo-res but not hires), then if a BRK is executed (801g), the break vector redirects the PC to the value of $c030, which fetches the opcode (and data) from the floating bus.  If a BRK, the cycle continues. If not a BRK, the only other expected value is a JMP, which in AppleWin forms a JMP $0000, and in turn the JMP $0300 results.

On a physical //e, with slot 1 vacant, I filled text page 2 with a repeating pattern of AD 30 C0 EA EA EA EA EA except at $BF0 where I put in 4C 00 03.  I displayed text page 2 and invoked C100G.  In this exercise the BRK vector was set to an address that floats ($C050), which would not be used unless the invocation to C100G happened to land on the 00 in 4C 00 03 or some other value from the floating bus came up as 00 - say from the screen holes or during the blanking interval. The Apple //e ended up at the code at $300 every time.  This likely needs more in depth exploration as all the technical resources (Bishop's) describe that the blanking interval and RTZ can turn up some unexpected values.

That's all for now.  


Wednesday, November 24, 2021

Wake me up before you go-go or better yet, don't

In the few months I have noticed my Fedora notebook wake up in my backpack.  It has also awakened in the middle of the night and by morning the battery was nearly depleted. I first noticed the wake up when I paired another laptop with it.  When the other laptop powered up in to the operating system, my Fedora notebook would wake up.  I made a connection between wake on demand and bluetooth.

Initially I figured I had a device in the house that was paired with it.  I fired up Wireshark on the bluetooth adapter and found that every time that laptop awakened, there was a broadcast event from an Apple device. 

I began to suspect that I had an Apple device paired with it.  I turned off bluetooth from all Apple devices and it still awakened a few seconds later.

However, just the other day I found it awakened readily even far away from my house.

I repeated a Wireshark/bluetooth capture and sure enough, another Apple device.

I investigated wakeup parameters in the /sys tree and found this works to keep the laptop from waking on bluetooth events.

As root: find /sys/devices -name wakeup | grep usb2/2- | while read F; do echo disabled > $F; done

UPDATE 2021-12-31 - I have recently replaced the operating system with a newer release. I will watch and see if the issue persists.




Monday, November 22, 2021

I cannot hear my BUDDY

BUDDY is the system (board) name of an Acer Chromebase.

In September 2021, I procured a Acer Chromebase 24 CA24-CT as a wall-mounted organizational assistant and intercom.  

The project started out nicely with easy hardware upgrades, but went downhill sharply upon discovering that the bdw-rt5650 makes no sound in Linux operating systems.

I started a quest to figure out whether this was pre module (like using a module parm to load specific CODEC parms) or post module, say a mapping from the sound service, like ALSA.

Using my chromegraphy skills from years tinkering with prepping Windows laptops with compatible official builds, I loaded the official latest BUDDY build on to the Chromebase already having flashed in the full MrChromebox UEFI (tianocore) ROM. 

As usual, after preparing a fresh /dev/sda1 (STATE), the recovery screen began the 5 minute countdown, and I chose not to wait, although a person could wait out the timer and it would reboot having applied the necessary STATE changes.

The next step was to unverity the volume.  Using live meda, I did this by loading zram and setting the disksize module parameter (find /sys/devices -name disksize) to the same size as blockdev --getsize64 /dev/{device}3. Then I formatted the zram0, mounted it, mounted /dev/{device}3  "ROOT-A" read-only to a makeshift mount point for /dev/{device}3, rsync -av from /tmp/sda3 to zram0, umount both zram0 and {device}3, and dd from zram0 to /dev/{device}3.

This netted a modifiable ROOT-A and absent verity, means an easy mount from Linux.

The next step was to see if my distro would produce sound with the official Chrome kernel.

I grabbed the kernel modules (4.14.228-37743-g49d2c603c1a5) and nested them under /lib/modules of my Linux distro.  I then rebuilt the initramfs using dracut and attempted to boot my distro, and grabbed vmlinuz.A from the /syslinux directory of /dev/{device}12 "EFI-SYSTEM". I added a boot entry to (EFI) grub and tried to boot but grub presented that the kernel did not accept EFI handover

I switched to using the grub that exists on /dev/{device}12 "EFI-SYSTEM" but all I got was a blank screen - the screen right before the kernel slurps in the boot screen (chrome logo) from the ROOT-A file system.

I did some research and found that Chrome kernels do not use initrds.  Therefore, although the kernel command line has boot=local rootwait ro noresume noswap loglevel=7 noinitrd console= , perhaps I should not expect an initrd to be passed off to the kernel.

Nonetheless, I unpacked the initramfs using skipcpio and dumped the contents on to an ext2 partition. I pointed root=/dev/{device}17 in the kernel command line, but got the same results

I then went to contemplate other ways I might verify if the sound is tied up on the kernel or a service.  I remembered Cloudready, and shimmed in the official kernel with Cloudready expecting sound.  

The shimming required again using the official Chrome grub loader to deal with booting the official non EFI kernel on the UEFI BIOS from MrChromebox.

Alas, no sound.  The same holds true for the 5.x kernel that Cloudready uses - no sound.  I verified that alsa was using the cras service and although there is volume control, in the Chromium setup interface, neither the welcome chime is heard, nor any sound curves "right parens" on the speaker symbol respective to the current volume level.

At this point, it suggests that the absence of sound is not likely a problem with the module or module parms, but potentially the sound mapping in a sound service.  Let's hope.  


UPDATE: November 24th, 2021

The driver in Linux works. In Fedora 35, the /usr/share/alsa/ucm directory is empty and the ucm2 directory has sound device configurations. By grabbing the bdw-rt5650 configurations from the /usr/share/alsa/ucm directory of the most recent recovery image*, and adding a header of Syntax 4 to the bdw-rt5650.conf file, then restarting pipewire-session-manager**, I was able to use aplay*** and hear sounds.  The first time the sounds were quiet, so before testing with aplay, invoke alsamixer and press F6 to switch to the bdw-rt5650 and max the master and speaker volumes.

* As root: modprobe zram; echo $(unzip -l chromeos_12239.92.0_codename.zip | grep "1 file" | cut -d' ' -f1) > $(find /sys/devices -name disksize); zcat chromeos_12239.92.0_codename.zip | dd of=/dev/zram0; losetup /dev/loop4 -P /dev/zram0; mkdir /tmp/root-a; mount /dev/loop4p3 /tmp/root-a -o ro; cp -av /tmp/root-a/usr/share/alsa/ucm /usr/share/alsa; umount /tmp/root-a; losetup -d /dev/loop4; rmmod zram

** systemctl --user restart pipewire-session-manager

*** aplay -D $(aplay -L | grep ^sysdefault) $(find /usr/share -name *.wav)

This does not produce sound through firefox/youtube, but the restart does appear to reload alsa's device configuration.  In order to hear sound through firefox/youtube, I duplicated Speaker details in HiFi.conf (of the same directory as bdw-rt5650) to match with another config.  Though the sound through firefox/youtube was extremely quiet, direct alsa sound was loud and clear.

Saturday, October 30, 2021

Fedora 34 live and wine

I am a fan of live media. When I am not feeling adventurous I leverage an XFCE spin of Fedora.  The release as of writing is Fedora 34 x86_64. 

I depend on a Windows program in my mostly Linux-only lifestyle, and wine makes this possible.  When dabbling with this dependency in conjunction with live media, I discovered I ran out of RAM-backed file system space.

I am not versed in how Fedora yields a RAM-backed file system overlay. From a cat /proc/filesystems, it does not appear to use overlayfs. overlayfs is not in the list. Does it manifest in the pivot through /run/initramfs?  I am unsure, and will not explore or find the answer in this post. 

What I need is a way to dnf -y install wine.  

After two other rounds of trial and error, I got this foundation script. It does not check or handle errors, but in essence, automates the steps necessary to get wine installed so I can use my Windows program.

My laptop has 32GB of RAM, and in my experience, the modern live images demand a minimum of 4GB of RAM to do the very minimum and with web browsing out of the question.

The cheatiest angle - (a new word for me => cheatiest) - is to make more space on /usr since that is where most libraries and binaries will end up.  I did not evaluate if any directory below /usr (e.g. /usr/share or /usr/lib) is a better candidate because, as said, cheatiest.

Now that you know I target /usr, the overall size of /usr hovers slightly above 4 GB uncompressed.  It should be an appreciable amount less than 4 GB when subjected to the fast compression used by the zram module.  The overall size of /usr (df -h) with wine installed, registers slightly above 6.3 GB.  This means my cheatiest approach is expecting at least 8 GB of installed RAM.  There is ample room for improvement, yet this solves my need for a live system where I can use my Windows program without concern of a computer's primary purpose.

EDIT: updated on 11/4/2021. 

Here's the code. 

#!/bin/sh
sudo swapoff /dev/zram0
sudo rmmod zram
sudo modprobe zram num_devices=8
sudo find /sys/devices -name disksize -exec chmod 777 {} \;
find /sys/devices -name disksize | while read F; do echo $((16384*1048576)) > ${F}; done
sudo mkfs.ext4 /dev/zram0
sudo mkdir /tmp/0
sudo mount /dev/zram0 /tmp/0
cd /usr
sudo cp -av ./* /tmp/0
sudo setenforce permissive
sudo umount /tmp/0
sudo mount /dev/zram0 /usr
host dns.google || ping -c 1 dns.google || read -p "Check your internet connection...press ENTER when solved."
host dns.google && ping -c 1 dns.google && echo "Looks good."
host dns.google || ping -c 1 dns.google || echo "No good. Restart this script." || exit 1
sudo dnf -y install wine
echo "That should have worked without running out of file system space."
echo "But for reasons (unknown to me) doing the same kind of effort by "
echo "first dd'ing the live image (using its dev loop to a zram dev)"
echo "and cp -av'ing the file system over to a larger ext4 formatted"
echo "ext4 fs, did not work when mounting to root.  It shows the mount"
echo "as being updated, but df -h does not; rather it shows the previous"
echo "space from the live mount. Again, I do not understand how the live"
echo "mount provides the compressed fs image, and subsequently, gives a "
echo "RAM overlay."
cd ~
mkdir .wine
sudo mount tmpfs -t tmpfs $(pwd)/.wine -o exec,uid=1000
wine cmd


Sunday, February 14, 2021

A method to relocate a BASIC program

On an Apple II with ROM BASIC, a BASIC program can run anywhere there is space in memory with the caveat that one step must be performed or the NEW command might fail. The first byte of the fresh location where BASIC will run must be set to 0. 

BASIC initially locates programs at $800. The decimal conversion of $800 is 2048.  To demonstrate the problem caused by the first byte, enter a fresh BASIC session. Issue the following commands:

] NEW
] LIST

] POKE 2048,255
] NEW
?SYNTAX ERROR 

] POKE 2048,0
] NEW

* no syntax error is encountered

] REM $0C00 IS 3072 IN DECIMAL
] POKE 3072,255
] REM $0C IS THE HIGH BYTE AND IS 12 IN DECIMAL
] POKE 104,12
] NEW
? SYNTAX ERROR

] POKE 3072,0
] NEW

* no syntax error is encountered


This is fantastic. A program can be located above the TEXT PAGE 2 ($0800-$0BFF) and use it for LORES graphics.  But, these steps must be performed each new session.

] REM START BASIC ABOVE GR PAGE 2 AT $0C00
] POKE 104,12
] POKE 3072,0

Could this be automated if loading from DOS or ProDOS? 
If yes, could it be automated per each program?

The short answer is yes & yes. The answer is provided in part by left over, or stale, data in memory.  I think of that state of data like dinosaur bones. That data is a sign of what happened in the past and may only be interesting to a few people. 

Retrieving that data in a BASIC program can be performed by using the PEEK command provided some awareness to where the data resides. If the data happens to be ASCII text, that data can be exposed to BASIC by using the POKE command to alter an already defined string.  I will demonstrate this later.

For my purpose, I will use the input buffer of ProDOS stored at $BCBD and the input buffer length at $BCBC.

Process Background
BASIC uses the zero page when operations are performed on variables.  There are dinosaur bones of BASIC variables found in the zero page. In particular, a region identified as scratch registers by Jon Relay's* very useful Apple II references.

Via trial and inspection, I discovered that scratch registers $85/$86 and $8C/$8D are affected when a new string is assigned the value of an old string.  In my example, N$ is the new string and O$ is the old string.

This assignment leaves dinosaur bones in the zero page, specifically the most recently used addresses of the strings.
N$ = O$

In this assignment, $85/86 is the location in memory of the old string and $8C/$8D is the location of the new string.

$85 = low address of old string
$86 = high address of old string

$8C = low address of new string
$8D = high address of new string

In the references* mentioned above, a keen reader will notice that the last used variable address is found in memory at $83/$84.  In the assignment operation N$ = "ascii text", $83/$84 is very much equal to $85/$86, and is the primary last used variable address and not one of the scratch registers.  
 
$83 = 131 in decimal
$84 = 132 in decimal

At first glance, this might appear to provide direct access to the string variable:
A = PEEK(131)+PEEK(132)*256

However, capturing the address of the numerical variable is not directly possible with a numerical variable. The address of the numerical variable receiving the value is assigned to $83/$84 and $85/$86 regardless of any operations on the right side of the assignment.  The value stored in the new variable is the address of itself, and not the anticipated address of the string.

Addresses $83 ... $86 will not keep value when capturing the last used variable address as a numerical variable. 

Lucky for us, the knowledge of how $8C/$8D store the last used variable addresses can help in this situation. 

Assigning a new string with the value of the old string, and even self assignment like O$ = O$ will suffice and keep a copy of the address of O$ in $8C/$8D. 

If the next operation is as follows, the address to the string is obtained:
A = PEEK(140)+PEEK(141)*256

Now for a word about how the string is stored.  The string payload, as in the ASCII text, is not located inline with the variable. The byte stored at A is the string length.  A+1 and A+2 are a pointer to the absolute locations of the string.  A+1 is the low byte and A+2 is the high byte of the string payload pointer.

We have the location of the string length and the pointer. With both these pieces of information, we can resize and repoint the string to the name of the program. 

This will be useful if we RUN THE.PROGRAM to get it loaded at our desired BASIC location.  If at first your LOAD the program, a RUN will not succeed.  

If the intent is to LOAD the program, this method is not needed as a the POKE statements on lines 10 and 20 can be issued before the LOAD.  This code that follows takes care of loading the BASIC program to the desired location.   

10 IF PEEK(104) <> 8 GOTO 50

20 POKE 12 * 256,0: POKE 104,12

30 A$ = “/”: A$ = A$: A = PEEK(140)+PEEK(141)*256: POKE A,PEEK(48316): POKE A+1,189:POKE A+2,188

40 NA$ = A$: PRINT CHR$(4);”LOAD”;NA$


Line 10 checks if the BASIC program location is at the default.  If it has already been altered, skip to program execution.
Line 20 clears the problematic first byte of the relocation address and then sets the high byte of the BASIC program location to 12 ($C00). 
Line 30 defines A$, performs the assignment that leaves dinosaur bones in $8C/$8D, stores the address from $8C/$8D in the variable A, pokes the value of 48316 ($BCBC) to the string, and closes by setting the high and low bytes of the ProDOS keyboard input buffer to A+1 and A+2.
Line 40 performs an assignment to copy the string, and invokes a ProDOS BASIC LOAD of the file.

There and done.

More on Variable Zero Page registers
In my experience, the last used variable address pointer in the zero page is only valid immediately after a variable has received an assignment.  Further instructions may clobber it.  Take this derivative of the code above which is not relocated:

30 A$ = “/”: A$ = A$: A = PEEK(140)+PEEK(141)*256: POKE A,PEEK(48316): POKE A+1,189:POKE A+2,188

40 NA$ = A$

50 PRINT PEEK(131)+PEEK(132)*256

60 GOTO 60


The output is 2163 from line 50, which in fact points to the original A$ having the updated paload pointer to $BCBD, but as of executing the GOTO in line 60, that value of decimal addresses 131/132 ($83/$84) are now $824, somewhere in the middle of the BASIC code.  The scratch registers at $85/$86 point to $881 and scratch registers $8C/$8D point to $009D.  No time is spent on $8C/$8D since the pointer destination fails to correspond to any string payload.  $881, however, points to the string region $95F7.  Inspection of the Start of string storage $6F/$70 yields $95F7, confirming a viable region in which BASIC is allowed to provision string payloads. The payload for the $NA assigned in line 40 and located in memory at $95F7 has the ASCII string RUN (CTRL-M) and substantiates that the variable assignment at line 40 successfully copied the data from the $BCBD text input buffer.


This hopefully stresses the importance of fetching the payload pointer immediately after performing an assignment that populates $8C/$8D.


Thanks for reading.






Friday, December 11, 2020

Apple cider vinegar and gnats not quite as good as...

In 2018 when fall arrived, I brought my plans back indoors.  With them came fungus gnats. I could not get rid of them even with an apple cider vinegar solution.  I left it out for over a week and not a single catch.

In 2020 when fall arrived, I brought some strawberry clippings indoors.  They had a few weeks in small pots to establish roots. But like 2018, when they came in, so did fungus gnats. 

I tried H2O2 rinse of the soil; that didn't work.  I tried a soapy solution along the bottom of the planters and wetting the topsoil surfaces.  That also did not work.  I tried a dusting of diatomaceous earth and saw no reduction in gnats.

Lastly I tried my own sugary water solution to attempt to catch the gnats and much to surprise, this works quite well.  I think I will try this along with another H2O2 rinse.

The mixture consists of 4 cups of brown sugar mixed with 1 gallon of water


Wednesday, October 21, 2020

TUYA camera offline media files, offline!

Following a vehicle theft from in front my house, I became keenly interested in DIY camera surveillance.  One of the types I tried is a TUYA Smart Battery Wifi Camera.  Nothing but a sticker on the box genuinely indicates it is marketed by TUYA.  Most everything is generic black and white documentation.

The camera RTSP's to a TUYA cloud, and there is nothing about the wireless feature that I like. It only connects to an open network.  There is no management access to the camera core.

The quality cannot compare to Lorex.  One feature this camera has, which a Lorex system does not, is an SD-CARD slot.

I positioned the camera for an overnight watch of my backyard.  It is motioned activated, and only captured my positioning and a single clip that seemed to be the result of the neighbor's porch light.

The SD-CARD contained a DCIM directory and because the camera is not configured, a date of 1969.  The subdirectories had a simple .info file with the content "V1" and a handful of .media files.

Neither /bin/file, binwalk, nor vlc could make sense of the .media files.  Some lucky search results indicated this was a NHNT interleaved video format.

I took a wild guess that FFMPEG might be able to transcode.  Sure enough, it does

ffmpeg -i 0001media /tmp/video.mpg

And there you have it.

I wrapped the conversions in to a basic shell command.

I=1; find -name *.media | while read X; do ffmpeg -i ${X} /tmp/$(basename $X)_$I.mpg; I=$((I+1)); done

Thursday, October 1, 2020

Magic, as in Crosswords not Unicorns




* Recently => September 2020

Recently*, a famous, mysterious cracker (who I dub a "KraXpert"TM), in the Apple II community posted his findings about additional latent copy protection on an Apple II title called Crossword Magic.  The version he mentioned was 4.0. 

I remembered that while in high school, years 1988 - 1992, my primary school had a software title by the same name.  For reasons I cannot explain, I felt no guilt about having made my own copy of the program and keeping it in my library. 

When this came on topic, I located the disk and booted. It was in fact version 4.0.

This KraXpert had identified that Crossword Magic 4.0 had three recurrences of an E7 bitslip protection, each with a slight variation.  

Back in the day, I discovered my own method to slip around the E7 protection scheme.  The method allowed for easy bit copying in most cases.  I never had any motivation to make these backups normalized 16-sectors.

In fact, it employed some minor changes to a standard 16-sector format, and my knowledge of sectors was limited back then, so the slightly modified backup served my purpose. This particular backup showed evidence of my resync technique characterized by invalid sector checksums.

This KraXpert gave some examples of how an incomplete recreation of the E7 fields fails in interesting ways. One type of failure results in clearing the all memory and stopping at ] cursor prompt.

Using some powerful disk imaging (Applesauce), the KraXpert revealed where the extra zeroes appeared in the E7 stream.  Only the correct pattern would pass muster.  I questioned whether my copy worked in the same circumstances this KraXpert would be using - an emulator.  But first I would test on actual equipment.

TL;DR? Jump to the end of this blog post.

I booted my backup on a real //e, absent a printer interface, and although it began to print, it failed quickly to a prompt.  

I imaged the disk with Applesauce and tried a WOZ in Virtual II. It printed perfectly.  

Although this worked perfectly, it was in WOZ format, which preserves a remarkable amount of bit integrity necessary for sophisticated copy protection to survive inside emulation.  

I anticipated the the KraXpert would be using a tool of their own design that completely normalizes DOS-based RWTS.  I have used this tool to verify disks; it's an amazing piece of software that helps with validation of different title releases by normalizing to a standard 16-sector format.  

The most recent release scans through an original disk of Crossword Magic and finds unformatted tracks $12 through $22.  It uses the disk's own DOS to read sectors, and succeeds on tracks $F through $11.  Upon reaching track $E it gets an error. On my backup copy it failed at track $F due to an error I had made long ago.  I located the WOZ-A-DAY version and started with it, fancying I could always re-establish my E7 sequences.

Unable to use this tool to normalize Crossword Magic, I attempted to track down a tool I used in my youth.  I did not find that old tool, but I discovered that Applewin, in combination with Copy II Plus 8.3, was able to write an unprotected  version of it in a DSK format.  All sectors, with DOS 3.3 PATCHED set from the sector editor, ended up reporting an error 5 with SECTOR COPY, but the contents in most cases were correct.  The exception surfaced when Copy II Plus 8.3 was used to perform a sector copy of either bad sectors (like my WOZ) or empty tracks $12 through $22, where Copy II Plus appeared to fill in already established sector data across all those tracks.  Realistically, those tracks should have all blank sectors (either all zeros or all FF's).  

Now that I had the disk contents unprotected, I could begin testing if my E7 sequence worked universally or if only in special cases. This is where the fun began.

The first hold up began in the boot loader in track $00 sector $00.  The boot sector uses the disk II ROM to pull in sectors, but on its own terms.  Once it is through with track $00, it calls a disk arm routine to advance to track $01.  It continues to use the disk II ROM routine, but it now fails to read any sectors from an unprotected disk's track $01.  This is because the disk II ROM loader expects to only load from track $00 unless the caller has set zero page $41 to $01.  This constitutes the first bit of code patching.

After it gets to track $02, it now has its own early sector header locator in text page 1. The routine in this page specifically requires that the header epilogues end with $FF and the sector loader in page $5A00 end with a sector epilogue of $FF.  Patching as follows in Applewin gets through the boot process

$44D => DE

$457 => AA

$5ACE => DE

The secondary protection is E7 field based.  The main program will fail if the DOS sector at track $02 sector $08 lacks pattern

EE E7 FC EE E7 FC EE EE FC

If that sequence exists in the E7 field, the main menu will allow for selection of various operations; however, as the KraXpert reported in a Apple II workspace, symptoms will appear; such as the inability to play a puzzle and successfully print. I do not know this KraXpert personally, so I do not know if the symptoms are caused only by an invalid E7 sequence or related to other alterations.  For instance, I propose alterations below that may have a latent effect.  

Moving on, there are two more E7 fields on track $11.  If either one of those is absent, the protection check before printing will fail and it will ask again for the program disk.  The KraXpert; however, noted that an incorrect pattern may result in endless printing of garbage. 

Because this disk is no longer in a protected sector structure, another likely protective measure, that allows for successful reading of the original disk's protected tracks, will now fail because it adjusts the epilogues back to the $FF's before the check and restores them to $DE AA / $ DE following the check.

AppleWin provides a convenient means to inject code through the debugger (F7 - debugger).  After lifting the 16-sector protection for tracks $2 through $E, the boot sequence should be disrupted through a breakpoint at $802 by using the following debugger command prior to booting (F2 - reset). 

BP 802

Resume execution with F7.  When AppleWin breaks at $802, supply the following commands in the debugger.

MEB 863 20 E0 08

MEB 8E0 E6 41 4C 82 08

MEB 879 20 E5 08

MEB 8E5 A9 DE 8D 4D 04 8D CE 5A A9 AA 8D 57 04 A9 02 4C 82 08

Resume execution with F7.

When reaching the CROSSWORD MAGIC splash screen, enter the debugger (F7 - debugger) and enter the following command at the debugger.

MEB 9FE7 8C

This final change will cause the code to  $DE AA epilogues instead.

I am repeating the sequence from above - track $2, so it is closer to the latent E7 sequences on track $11, for the sake of easy comparison.

Track $02, DOS sector $08 can work with this E7 field sequence.

EE E7 FC EE E7 FC EE EE FC

Raw sector AB AF can work with this E7 field sequence.

EE E7 FC E7 EE FC EE FC

Raw sector AE AB can work with this E7 field sequence.

EE E7 FC EE E7 EE FC EE FC

All of the above E7 sequences must be inserted at the correct position in the respective E7 field.  A nibble editor can be used to insert the sequence, followed by a sector editor to regenerate a proper checksum.**    

At the start of any E7 field, the first three E7's are left as is; this is the accepted approach used by the KraXpert.  The next three signature bytes are EF F3 FC, followed by the desired E7 sequence from above.

E7 E7 E7 EF F3 FC pp aa tt tt ee rr nn aa bb oo vv ee 

TL;DR From limited testing in AppleWin, MAME, and Virtual II, it behaves identically to a WOZ version. MAME shows an interesting video artifact with both //e and //c after selecting some of the menu options, and this artifact is present with the WOZ.

Another interesting discovery, Crossword Magic detects a //c and prevents changes to the printer interface and slot.  Larry did an incredible job on this title.

// that's all folks. thanks for reading.

** One caveat I found is that if the sector on track $02 is corrected in a nibble editor and written back to a DSK type image in Applewin, sector $00 will be written as a blank sector, which yields a very interesting effect in Crossword Magic.  The effect is that the blank sector is all CPU BRK instructions, for which Crossword Magic has a BRK handler.  It cleanly exits to a ] prompt for showing the title screen.

Sunday, September 20, 2020

Android to IOS and the elusive call logs.

 Wanting to move back to IOS, I found myself unable to succeed in whole with any of the available tools. I read a few reviews of top apps, and made an attempt with X-Transfer (available on both IOS and Android) but the app crashed upon scanning SMS.  So quickly in fact that I could not even provide the developer feedback within the app.

I tried Move to IOS but it wouldn't transfer photos,  I tried without photos and it worked, but it was missing my text messages.  My call log did transfer, but at this point I am unable to reproduce it.

I elected to use another  Android phone as a go-between.  I set the backup to exclude Photos and it quickly completed.  I restored the backup to the go-between and it synchronized both SMS/MMS and the call log.  

I tried Move to IOS, and it completed everything including SMS/MMS, but without ever transferring my call log.  I repeated a transfer with X-Transfer, but it failed to allow me to target my call log - the call log was greyed out.  I tried a different tool, but likewise, it would not import to the IOS 13.5 call log.

I finally decided to connect adb to the go-between and manually fish out the call logs.  

I found them as an SQLite DB under /data/data/com.android.providers.contacts/databases/calllog.db.

The date for each call appears to be epoch-time format but needs to be divided by 1000 to be consumed by the Linux date command.


Sunday, September 13, 2020

An unexpected wipe out (AICP style)

My Pixel completely stopped receiving security updates in October 2019.  One year later it's time to move toward a more secure device.  

Last night I decided to see if I could restore my Android 10 Pixel to another Android 10 instance.  The motivation is to capture a point-in-time snapshot of my texts and calls; data which I've found no way to transfer from Android to IOS (the converse however works awesome thanks to the migration tool Google provides in the Pixel).

I had no data of interest on the HTC m8 selected to receive the restore.  I invoked reset all data through the Android 10 UI and it reboot in to Captain Throwback's SAR TWRP (already installed).  I used the defaults for wiping.  When I rebooted, it dived straight into recovery.  I tried adb sideload downloadedbuild.zip, but still went to recovery next boot.  I tried adb push downloadedbuild.zip /sdcard and installing through the TWRP UI, but same result; recovery.

Finally I remembered adb shell and fumbled around with sgdisk to check out if I had a good partition table on mmcblk0.  I did.  cache and data were mounted automatically, but system did not automatically mount.  I went back in to the mount function of TWRP and mounted system.  Back at the shell, it was now mounted at /system_root.  I repeated the adb push downloadedbuild.zip /sdcard and installed it. Upon reboot it booted in to the splash screen instead of recovery.

You might think I was in the clear at this stage, but the entire time writing this, it stayed on the aicp color changing boot screen.

So in closure, the point of this post is that system_root ha to be mounted in order for the aicp 10 zip to successfully apply.  I suspect my issue is that I need to back level to an aicp and 'apply' this 10 zip over the top. We'll see.

See, I did.  I ended up booting back in to TWRP and after several rounds of trying a sideload over of AICP and Lineage 17, I think I finally found the right combo that results in loading instead of faulting back to invoking TWRP.

if already in an Android 10 build, perform a factory reset

in TWRP choose a wipe and format

push over LINEAGE to /sdcard

mount system

install from /sdcard 

optional, test - but it might not complete boot (pwr+vol down for 10 seconds)

if not in TWRP, boot back in to recovery

invoke side-load

mount system

side-load AICP

optional - test, that it works before installing open_gapps, then boot back in to TWRP

side-load over open_gapps-arm-10.0-micro-20200912.zip

that should do it


phone.android.com continued to crash. Setup appeared to restore from backup correctly.



Saturday, June 6, 2020

Must I repeat myself?

I mentioned in a past post that I snagged an HP ENVY and upgraded it  It is now beyond the HP 90-day factory refurbished warranty and I have pushed myself to use it daily as a way to diagnose the frequency of the hardware issues. 

These intermittent hardware issues have come up over the last two months and could be related to the upgrades.
Freeze or blue screen followed by an inability to boot
Freeze or blue screen followed by an inability to power on
Always passes a quick system test and full memory test
Blue screens either a fault in an ATI driver or a page fault in non paged area
A device disconnected notification from Windows when waking up from suspend.

One issue I suspect is unrelated to a hardware issue is a complete disregard of a fastest setting of the keyboard repeat rate when waking from suspend. A reboot always fixes the issue, but I also found that adjusting the Repeat Delay to full left Long, Apply, then full right Short and Apply fixes the issue without a reboot

Wednesday, April 22, 2020

Briefly a word on two - maybe three - USB-C docks

Two years ago I bought a HooToo USB-C 100W powered dock to provide my Dell Precision 3520 with "one connector" to rule them all.  The HooToo was the first dock that I found which advertised a power rating more than 60 watts.

The Precision 3520 would often report between 80 to 90 watts power being less than the 130 watts the laptop is designed to receive.  I have the slow charging high capacity versus the smaller fast charging battery.  

I used the HooToo for the better part of a year and over that time, I found that the dock would require several plug-ins to the laptop before successfully bringing the charge feature online.  Once in a while the USB-C network adapter on the dock would fail to show up.  In every case, the HDMI port showed up.  I equipped myself with some cablematters USB-C extension cables and instead of "cycling" the laptop plug port directly, I "cycled" the junction formed between the cablematters plug and the USB-C plug coming out of the dock.  The idea was to reduce wear on the laptop.  Although this worked, it was inconvenient.

A few months ago I saw an Elrigs USB-C dock on "not quite" 50% off sale.  Too good a deal to pass up, I ordered one.  This Elrigs delivers the Precision a meager 60 to 65W power, but it adequately provides power to my mouse, keyboard, and the limited power is sufficient charge the battery over the course of a couple hours.  However, I find the Elrigs USB-C networking device (which uses the r8152 module of the Linux kernel) does not always re-register when I awaken the laptop from suspend.  For what it's worth, my Philips 499P9 display has a built-in dock with the same kind of network chipset (r8152) and it does, too.  Inspection with ifconfig shows that the kernel does not have the 'eth' device registered.  

The lease disruptive fix I have found is to sudo rmmod r8152; sudo modprobe r8152.  Once I invoked those two commands, NetworkManager immediately brought the interface up.

Sunday, March 22, 2020

I don't ENVY you, or maybe I do?

This post is about HP's AMD-based Envy x360 (2019) and the upgrade threshold of RAM and SATA 2.5" SSD.

In January (2020) a family member asked me for advice on a new laptop that would consolidate all of the old tower PC and laptop data.  Advice from another family member, sought out before me, was to find a dual drive HP with 32GB of RAM and a 2TB drive.  To me, that sounded like a $2,000 investment.

I fielded back my recommendation of buying from a hassle-free provider, "try before you buy," and choose a newer 9th or 10th generation Intel-based system.  A mainstream computer outlet was suggested, and I countered with Costco and then Amazon.  An older lady with whom I was working brought her personal Alienware laptop because the store sales person had recommended she get a gaming laptop.  Seeing it first hand, it was more laptop than she needed, by a long shot.  As for my family member, when the nearest city with those kinds of stores is 60 miles away, you benefit from knowing what you want before going in the store.

Over several days we volleyed makes and models  between us until I finally saw an opportunity.  Woot had some refurbished HP's.  I have had good experience with HP's refurbished products. In the assortment was an Envy x360 with a 256 GB m.2 and an AMD Ryzen 2500U (Passmark of 6,824 where my i7-7700HQ gets 6,985).   Research indicated it could be upgraded with more than 8 GB of RAM and a secondary SATA disk.  It can, but not out of the box.  You must buy the drive caddy.

I spent $449 on the laptop. Another $229.99 on a 1.92 TB SATA SSD. Another $16.88 on the caddy.  Once all the parts arrived I tracked down a How To.  I was not successful finding a How To video for the 15-cp0053cl, but I located the service manual.

The service manual shows how to remove and replace memory and how to remove and replace the M.2 SSD, but it did not show clearly where the memory or M.2 are located. Based on an exploded illustrated parts diagram, the memory and M.2 should be on the underside of the motherboard.  I located a metallic shield on the underside that was slightly larger than the area summation of the both. I delicately pried from under one corner of the shield, and to my surprise it popped up quite easily.  I moved around under the shield perimeter leaving the black tape as a sort of "hinge."  I hinged it back and found both the memory and the M.2.

The service manual says the largest SATA disk is 1 TB.  The 2 TB SATA SSD is fully recognized.  The service manual says the largest RAM is 12 GB.  It supports my combination of 16 GB PC4-19200 and 8 GB PC4-19200 for a total of 24 GB PC4-19200.  Having written this original post on March 22, 2020, I came back a month later to comment on a final RAM configuration.  I found a forum where one person upgraded their ENVY with two 16 GB PC4-19200 (BLS16G4S240FSD).  I ordered a Crucial PC4-21333 (2666) 16 GB (2R) module to match the SK HYNIX 8 GB PC4-21333 (2666V) HP supplied, and it did not work in either slot.  It also did not function correctly in a Dell Precision 3520 where the part is listed as supported.  Instead of dealing with a RMA, I opted to work with Crucial support, who convinced me the RMA refund was a better no-cost option.  Performing a RMA through Crucial meant I would have to pay return shipping.  I sent that bad stick back to the seller (via Amazon) and chose to order OWC PC-21333 16 GB modules in a 32 GB (2x module) kit.  The modules came in a few days. I tested the modules in the ENVY and all I found was a blinking power light and a CAPS light blinking an error code.  The PC4-21333 is, however, supported in the Dell Precision 3520 and I am pleased to report it is working quite well.  The ENVY now has the 24 GB of SK HYNIX PC4-19200 RAM formerly serving the Dell Precision.  Although this is not ideal, it will suffice.

I hope others find this useful.

http://h10032.www1.hp.com/ctg/Manual/c06001791
Supports DDR4 1.2-V 2666 and 2400 memory in the following conÆ­gurations ● 16-GB (8-GB × 2) ● 12-GB (8-GB + 4-GB) ● 8-GB (8-GB × 1 or 4-GB × 2) Storage Support for a 1-TB, 7200-rpm, SATA 7.0-mm hard drive Support for the following solid-state drives: ● 256-GB, M.2 2280, peripheral component interconnect express (PCIe)3×4, non-volatile memory express (NVMe), SuperSpeed (SS) solid-state drive with triple-level cell (TLC) ● 256-GB, 2280, PCIe, NVMe solid-state drive with TLC ● 128-GB, M.2 2280, SATA-3 solid-state drive with TLC Supports the following hard drive and solid-state drive combinations: 1-TB, 7200-rpm, SATA 7.0-mm hard drive + 256-GB, 2280, PCIe, NVMe solid-state drive with TLC 1-TB, 7200-rpm, SATA 7.0-mm hard drive + 128-GB, M.2 2280-SATA3 solid-state drive with TLC
The parts sourced as follows:

  • Amazon (by word of TechBargains) had the Micron 5100 MAX 2 TB for $229.99 (March 2020)
  • Amazon had the PartEgg caddy for $16.88. It listed as PartEGG SSD Hard Drive Cable + SATA HDD Hard Drive Caddy Bracket with 2X Rubber Rails for HP Envy X360 15-BP 15-CP000 15-CN 14-CD 15-BP101TX 15m-CN0xxx 15m-CP0011DX 15-BQ000CL
While it lasts, this is what the caddy looks like.


PartEGG SSD Hard Drive Cable + SATA HDD Hard Drive Caddy Bracket with 2X Rubber Rails for HP Envy X360 15-BP 15-CP000 15-CN 14-CD 15-BP101TX 15m-CN0xxx 15m-CP0011DX 15-BQ000CL
$



  •  












Sunday, January 5, 2020

qrencode, you won't make scannable OTP codes, so who the heck QR you?

I previously wrote about my adventure backing up all data on my ZTE before sending it to ewaste recycling. Part of that backup was preserving my OTP MFA codes.  This blog entry is limited to a tiny bit of historical information and how I made viable OTP MFA codes with qrencode for Microsoft Authenticator.

I learned the importance of having a backup plan years ago after I had established my first OTP account on the ZTE. The ZTE was running slowly and I proceeded to wipe it. I forgot about that MFA registration until the next time I was prompted for the MFA code.  At that point I began to register MFA OTP on a second device.

In November 2017 I picked up a Verizon PAYG LG VS810PP to help activate a BYOD hotspot. I kept the LG in service as a backup MFA device. When adding a new MFA OTP accounts on the ZTE, I captured the QR code and later scanned it in to Microsoft Authenticator on the LG.

Having lost the original LG box, and within that box, the plastic backing to keep the battery in place, the LG needed to go away. 

Keeping the QR codes can be handy in case you lose your only OTP device or in the event you want to migrate to a newer device.  The issue is how to store QR codes.  Surely QR codes can be encrypted, but they are the result of the OTP URI metadata. An easier approach I found is to retain metadata containing the issuer, secret, and account and keep all OTP entries in a comma-separated values (CSV) file and encrypt that file.

In December I obtained the Microsoft Authenticator database from /data/com.azure.authenticator/databases/PhoneFactor.

The challenge I ran into in early December was the inability to generate viable QR codes for use with Microsoft Authenticator.  I rummaged through documentation like https://github.com/google/google-authenticator/wiki/Key-Uri-Format and https://stefansundin.github.io/2fa-qr/, of which the latter is rather nice because it allowed me to compare a 2fa-qr generated QR with the output of qrencode. No matter why I tried, I was unable to create an exact QR of that which 2fa-qr creates, but I did get a QR to work.

You might be thinking "why don't you use 2fa-qr."  My answer is "because I do not know the provenance or insides of 2fa-qr and for all I know, it could be logging user input data to a server."

To make a short story shorter, this is the command line that finally generated a QR code that Microsoft Authenticator would accept:

qrencode -8 -c -l L -o /tmp/qr.png -v 3 "otpauth://totp/zanzamar@earthling.net?secret=FFFFFFFFFFFFFFFF"

The above works great for a single code.  However, I have a handful of services that either require or permit MFA. I was seeking a routine to generate all the OTP QR codes for bulk repeat scanning in Microsoft Authenticator.  The above is not an actual OTP account, but rather what I fabricated to compare between 2fa-qr and qrencode.

The Microsoft Authenticator app uses an SQLite database named PhoneFactor and sqlitebrowser opens it.  In sqlitebrowser I invoked a query of "select * from accounts;"

The query output was displayed and I selected all the content. I copied and pasted in to a text editor (geany), performed a search and replace on " " (in the actual file, it is a tab character in between the quotes)  with a comma (,) and conveniently provided a .csv extension.

With a usable data source, I came up with the one liner loop. IIRC there are shell variable extraction methods I could have substituted in place of cut.  For now, I'll use cut.

NUM=1; cat ./otp.csv | sed -e 's/"//g' -e 's/ /%20/g' -e 's/@/%40/g' | while read LINE; do ACCOUNT=$(echo $LINE | cut -d, -f4); SECRET=$(echo $LINE | cut -d, -f7); ISSUER=$(echo $LINE | cut -d, -f3); qrencode -v 3 -8 -c -l -L L -o /tmp/qr-${NUM}.png "otpauth://totp/${ACCOUNT}?secret=${SECRET}&issuer=${ISSUER}; NUM=$((NUM+1)); done

With the QR codes visible in a file browser with image preview, I  scanned my OTP secrets in to Microsoft Autheticator.

After all eight OTP secrets were imported, I double-checked the OTP codes against my LG phone.  Only after confirming that all the OTP codes were identical, between the new phone with Microsoft Authenticator and the LG, did I factory wipe the LG.

I hope others find this useful.

EDIT 2020-01-05

While setting up a secondary device for MFA, I forced myself to learn the bash method of breaking the CSV lines in to tokens.
The format of the SQLite database has columns:

ms_mfa_entry_number, big_zeros_number, OTP_ISSUER,OTP_ACCOUNT,empty,1,OTP_SECRET,empty

ADDR[3] = OTP_ISSUER
ADDR[4] = OTP_ACCOUNT
ADDR[6] = OTP_SECRET - perhaps because the actual 6th column is empty so 7 assumes the place of 6?

IFS=',';NUM=1; cat ./otp.csv | sed -e 's/"//g' -e 's/ /%20/g' -e 's/@/%40/g' | while read -ra ADDR; do qrencode -v 3 -8 -c -l -L L -o /tmp/qr-${NUM}.png "otpauth://totp/${ADDR[3]}?secret=${ADDR[6]}&issuer=${ADDR[2]}; NUM=$((NUM+1)); done

Sunday, November 24, 2019

Are you the valet?

Early November I wanted to extract the data I had on my very first TracFone Android device, a ZTE Valet.  It turns out the ZTE Valet is easily temp rooted.  Search for RootMyValet and a github result is the starting point.  Buried under the github lib directory (IIRC) is the roothandler binary.  I mention this because I spent days looking for the cited roothandler binary in su packages as described by the README.

All along I was misunderstanding the README.   The README states to locate my favorite su package, and extract the su binary.  It then instructs to copy the roothandler file in to the same /data/local/tmp directory as where the su binary is dropped, which gave me the impression roothandler is bundled with my favorite su package.  But enter another issue, the Valet is Android 4.1 and all the recent su binaries I extracted required PIE (not to be confused with Android P/pie). 

Eventually I found an old enough su without PIE, but not before taking an alternate path with KingRoot and discovering KingRoot not only failed to root, but successfully installed a bunch of shims to phone home statistics to qq.com and yahoo, and fails to offer the ability to uninstall.

Now I had two problems.  The mostly well mannered "malware" KingRoot -- and -- I still did not have a backup.

First off, the native Android ADB backup didn't produce anything but 504 byte sized file and Move to IOS crashed.  I tried another backup program from the Play Store, but it too, crashed. How bad is KingRoot?  After setting up a hostapd + dnsmasq + tcpdump, I didn't see anything other than small packets headed toward qq and yahoo, but that doesn't mean it didn't lift some data during the initial installation.  I'll save that for some later point in time.

As far as the RootMyValet presence on github, I gather that the RootMyValet.apk is purely a UI to invoke roothandler easily, and the github repo is the wrapper around the whole kit, which includes the apk for the optional UI invocation.

After crawling through the source code and finding a reference to a getroot function, I figured the github repo was an 'apk' that I am supposed to build myself. With that all cleared up and in possession of the roothandler binary, I prepared my own version of the script that roothandler executes.

In my own script, I had it chdir to /data and run 'busybox find | busybox cpio > /sdcard/data.cpio".  This is a simplified version of the actual command as I don't recall the precise flags that the cpio plugin of this busybox required, but it suffices to say it would have been  similar to "ov -H crc" or "ov -H newc".

If you're struggling to follow me up to this point, I used the platform-tools ADB binary, which allowed me to get a shell.  In that shell I was able to invoke roothandler and roothandler, in turn, invokes the shell script (as described in the README) to run as root anything you require during the temp root.

Happy, I got my backup successfully migrated my OTP and organization's Duo multi-factor to different devices.

Update
I dug in a bit more in to the behavior of KingRoot on a Valet.  It does work on the Valet, provided your /data volume is not encrypted.  If you encrypted your Valet using the Android setting, KingRoot will not succeed, but it will install the hooks to ship telemetry.

After it succeeds on an unencrypted Valet, and after installing SuperSU, invoke adb shell and type su. 

In this repeat attempt, I took these steps.
1. install KingRoot and launch.
2. Try Root - it succeeds.
3. install SuperSU
4. Grant SuperSU permission in KingRoot
5. adb shell, and try su, but it failed with Segmentation fault.
6. reboot the phone
7. run KingRoot again, and Try Root
8. adb shell, and try su, this time it worked.

Making the switch from IOS to Android - an iPhone Notes rescue story

In August 2019 I picked up a Pixel XL to replace my soon to be unsupported iPhone 6. I mostly use a phone for jotting down notes, texting, satisfying my curiosity on wikipedia, reading Science Daily, capturing interesting photo moments, navigation, and the weekly phone call. Almost any modern phone satisfies my use case, aside from one requirement: it needs to receive regular security updates.

The Pixel XL received test versions of Android 10, which implied it would receive a full edition. As long as I can tolerate the differences, Android 10

S&D iPhone 7's on a particular Amazon-owned deal site were above $360 and a open box new Pixel XL listed for $210.  The Pixel seemed to be a cost effective way to meet my needs.

I ordered a battery case, which is normal for any phone I pack around. Wearing the battery case, it's not huge -- it's annoyingly "Phabulous," yet I am committed to see this through.

To get my content migrated, I used the Pixel's migration assistant with the Pixel crossover dongle and the lightning cable.  It moved photos, text messages, call history, and a fairly impressive "vis-a-vis" of apps; however, app data for those it located in the Play Store, didn't make the transfer.  For instance, my TracFone app had to set up all over again.  Office Lens lacked any of the associated captures, but that did not bother me.

To my chagrin, my images sent and received via texts were not there.  Add to that, my iPhone notes had no corresponding app to receive them.  Notes were not there.

With Apple's iCloud service, you might be thinking "why is this an issue?  You can access your notes from iCloud."  I could, yes, but my data size is over the free 5GB, and I already have a OneDrive subscription and for reasons, funding iCloud instead of OneDrive is not presently an option.

Linux is all I use, and despite what others might classify as limitations, it has certain applications which make deviation from the trodden path, possible.

Committed to retiring the iPhone 6 and wanting to keep my health trend data from a Viatom app, I tried idevicebackup2. It works and produces an (optionally) unencrypted backup.  The result is a big tree of hex numbered directories under which it stores some number of hexadecimal GUIDish files under the folder tree.  Using

find . -type f -exec file {} \;

is informative and reveals the file type. Common types are Apple binary data, ASCII text, an SQLite database, a JPEG, and PNG. From this information I assembled a pipeline to include a grep for image files.  With image files, I figured I could begin to determine the app-to-image relationship and locate the apps and their corresponding files, like the database or ASCII text, or for that matter, my iPhone Notes.

I opened the Notes app on my iPhone and located a message with a distinct word that is unlikely to be found anywhere in the backup tree.  I used

grep -r "Lincoln" ./*

where the current directory (. and thus the . in ./*) is the parent directory I specified in the idevicebackup2 command line.

I found the file and copied it to a scratch space.  I used the 'file' command on it and it revealed an SQLite DB. 

I installed sqlitebrowser and opened the file.  I found that the note zdata was compressed and a simple online search yielded the syntax I needed to extract the data.  I arrived at this script which extracted everything that I cared about: the text.  I had two notes with attachments.  Upon reviewing them in my iPhone, I still put no value on keeping them.  According to the DB file, I had 473 notes.

This is the command I used. I hope it can be of help to others!

bash% COUNTER=1; while [ $COUNTER -lt 474 ]; do ZCOUNT=${COUNTER}; [ $ZCOUNT -lt 100 ] && ZCOUNT="0${ZCOUNT}"; [ $COUNTER -lt 10 ] && ZCOUNT="00${ZCOUNT}"; sqlite3 ./notes.sql3 "SELECT writefile('/home/mk/Documents/object${ZCOUNT}.gz', zdata) FROM ZICNOTEDATA WHERE z_pk = ${COUNTER};"; zcat /home/mk/Documents/object${ZCOUNT}.gz | strings > /home/mk/Documents/note-${ZCOUNT}.txt; COUNTER=$((COUNTER+1)); done


Wednesday, October 2, 2019

More about the TPM-iness

Last week while fumbling around through the source code for tpm_managerd, I noticed some mock units.  I got the crazy idea, what if those mock units could chat back to the TPM client and make it all happy; except that sounds like a good idea until it isn't.  Clearly the TPM needs to store key details.  If I sufficiently understand the TPM's role, and if cryptohomed thinks it has stored the key that it will need next boot, but said key did not get stored, thanks for the exercise in cryptoshredding.

The chromefy / croissant team has done a great job of making swtpm available. Kudos to croissant for helping support Chrome on devices absent a TPM2.  I have previously ranted why, personally, I do not condone third party binaries like what croissant delivers, but that set aside, providing future chrome users a way to build their own and overlay it atop an integral filesystem sounds plausible, but only thanks to the croissant team paving the way.

Admist my searching for the steps that croissant uses to build a TPM capable image, I found chromefy's steps to leverage the older Chromebook's TPM components.  Then...lightbulb comes on. 

Why not script-o-matically download two images to install an official EFI Chrome build.  At least until swtpm is absolutely required?
1. Download banjo and park under /tmp/source/TPM
2. Download release with TPM2 requirement and park under /tmp/source/ROOT-A
3. Good idea or bad? Take partition #7 (ROOT-C), resize, and store a overlay - a ext4 rootfs if you wanted.
4. Use futility to extract the EFI vmlinuz and update partition #12
5. If an initramfs can't be exposed (tested CPIO and it didn't work), edit grub to mount partition #5 - yep, no difference from croissant at this point.
6. Resize STATE and format. 
7. Create and store a remerge script on partition #7 which basically does all of the above so updates* can be incorporated by downloading the latest TPM1 available and merge with the new update on partition #5.

* - With a FALCO I updated to the latest August 29, 2019 build.  Falco was released September 29, 2013 and despite the five year life-cycle for Chrome devices, it received an update.  What does this mean in respect to the warning I received that my BANJO installation wouldn't receive any future updates?  At this point more investigate must happen.

Ah, probably all crazy speak.

Sunday, September 22, 2019

The pretender: Fooling 3dmark SkyDiver to test your card

This is a quick post about a funny discovery I made when testing a GTX 460 I received second-hand.  I also received a GTX 550 knowing that it needed a GPU fan replaced, but, in a PC built from the usual modular pieces, I put in both cards just to see if the drivers would squawk about the SLI cable I affixed between them.  It did not, but it neither activated SLI.  I ran the Sky Diver benchmark and the GTX 460 completed.  Power off. I slapped on a fan to the GTX 550 which required removal of the GTX 460 due to the unsightly tape-fastened fan and it benched slower than the GTX 460. Power off. Swapped in GTX 460 in place of the GTX 550, and launched the benchmark.  But the benchmark stated I needed a capable video card.  It reported the GTX 460 as the video card installed in the system.  Power off.  Removed the ad-hoc fan from the GTX 550 and installed it in the slot neighboring the GTX 460.  Power on with both cards. Run benchmark.

My takeaway: you stand a chance of benching other DirectX 12 cards if 3dmark's Sky Diver detects you have at least one card capable of running the benchmark.  

This does make me wonder how awful a GT 630 performs on Sky Diver. The passmark video score for a GTX 460 is around 2,600 and the GT 630 - later generation but really tiny - was below 900.