1
0
Fork 0

Compare commits

...

2 commits

Author SHA1 Message Date
saji 521af2d17a move package to separate file
All checks were successful
Build Blog / Build (push) Successful in 4m54s
2025-04-20 09:57:57 -05:00
saji bc2f0fcb81 fixup content, add callout shortcode 2025-04-20 09:56:54 -05:00
9 changed files with 199 additions and 68 deletions

View file

@ -1,4 +1,5 @@
---
title: Beets and Soulseek Or, How I Learned to Give Up and Love The Hoard
date: 2025-02-03
draft: true
---

View file

@ -1,14 +1,14 @@
---
title: Reversing an image format
date: 2025-01-15
tags: reverse-engineering, hacking-hikmicro
tags: [reverse-engineering, hacking-hikmicro]
drafts: true
---
This post is the first in a series on reverse engineering binary file formats using an array of tools,
mainly to serve as insight into how to approach these kinds of challenges as they can be daunting.
Part one focuses on the image format.
# Introduction
*[ITAR]: International Traffic in Arms Regulations
@ -17,18 +17,21 @@ These arrays are higher resolution and faster framerates than what can reasonabl
to low competition and ITAR restrictions. Most popular are the low-cost modules by Infiray, which provides whitelabel solutions
to a host of other companies (TOPDON, Vevor, HTI, UNI-T) to relabel and sell under their own name. They come in two major
form factors: Phone add-on and gun-grip unit. Existing work has documented and reverse-engineered the phone communication protocols,
but
but
I've been burned once by the phone-dongle style before with Seek Thermal. Simply put, the pace of smartphones is longer than
the target lifespan of these products. Therefore I wanted one that could be used in a dedicated fashion. The PC
I got burned once by the phone-dongle style before with Seek Thermal. Simply put, the pace of smartphones is longer than
the target lifespan of these products. This time, I wanted one that was standalone. The PC
connectivity was still important to me, since there are certain operations that can only be done with access to raw
data, like Lock-In Thermography.
For that reason, the Hikmicro line of portable imagers interested me. It's more expensive than the whitelabel Infiray
products, but offers some pretty nice looking PC software and uses a Radiometric JPEG image format for data.
Live USB is advertized and it supposedly also delivers radiometry over the USB (!). This is on
top of being a standalone unit so you don't need to use an odd phone app. However this choice seems less popular on forums
and there is no reverse engineering project that exists already, so we'll have to start from scratch.
For that reason, the Hikmicro line of portable imagers interested me. It's more
expensive than the whitelabel Infiray products, but offers some pretty nice
looking PC software and uses a Radiometric JPEG image format for data.They
advertise Live USB video and it supposedly also delivers radiometry over the
USB (!). This is on top of being a standalone unit so you don't need to use an
odd phone app. However this choice seems less popular on forums and there is no
reverse engineering project that exists already, so we'll have to start from
scratch.
# Getting the picture

View file

@ -4,13 +4,16 @@ date: 2025-04-17
tags: NixOS
---
Note: this post assumes familiarity with the Nix Ecosystem.
{% callout %}
This post assumes familiarity with the Nix ecosystem.
{% endcallout %}
Nix and NixOS make managing servers much easier than bespoke scripts or
complex Ansible playbooks. While I don't think NixOS is ready for
personal computers, it absolutely makes sense on less dynamic devices
like servers or embedded-ish machines.
We can use NixOS and nixpkgs to derive a fully defined operating system and services.
Then, we can:
@ -19,7 +22,7 @@ Then, we can:
- push the new configuration to a remote server using [deploy-rs]()
The latter is interesting. With a bit of setup, we can do GitOps with rollback
and push-deployments. `deploy-rs` is a glorified `nix copy` with some extra magic
and push-deployments. `deploy-rs` is like `nix profile` with helper scripts
to "activate" the copied closure. Activation typically means running `home-manager switch`
or switching the NixOS profile.
@ -37,46 +40,60 @@ And then have `nginx` use `/home/blog/_site/` as the site root. This works, but
Other users have tied their blog to the NixOS system-level configuration, like [this user](https://jeancharles.quillet.org/posts/2023-08-01-Deploying-a-static-website-with-nix.html). While I think this
is a good solution, it has the issue of relying on root access for updating the site.
To me, this feels excessive. I should be able to deploy my site from a lesser-privileged user.
This method also ties sysadmin-tasks to our blog, which isn't keeping our concerns separate.
# Scaffolding the Solution
Let's recap:
1. Build the site using a nix derivation
2. Copy the derivation to a server using a locked down non-root account.
3. Create well-known path that points to the latest version of the site in the nix store.
4. Configure `nginx` to serve from this well known path.
2. Configure `nginx` to serve from a well-known path (`/var/lib/site/public`)
3. Copy the derivation to a server using a locked down non-root account.
4. Symlink `/var/lib/site/public` to our static site in the nix store.
I'll do it manually to get an idea of how it should work.
## nix build blog
I build my blog using [11ty](https://www.11ty.dev), and I use `npm run build` to generate
the static sources. Nix supports building npm-like packages with `buildNpmPackage`, which
uses a [Fixed-output derivation](https://nix.dev/manual/nix/2.28/store/derivation/outputs/content-address#fixed)
to store the dependencies of the project. Then, for the `installPhase`, we just copy the built contents
to `$out`. I need to add `vips` and `pkg-config` as well because 11ty processes my images.
I build my blog using [11ty](https://www.11ty.dev), and I use `npm run build`
to generate the static sources. Nix supports building npm-like packages with
`buildNpmPackage`. This uses a [Fixed-output
derivation](https://nix.dev/manual/nix/2.28/store/derivation/outputs/content-address#fixed)
to store the dependencies of the project if it is stored elsewhere, but I found
a better method that imports the `package.lock` directly. Then, for the
`installPhase`, we just copy the built contents to `$out`. I need to add `vips`
and `pkg-config` as well because 11ty processes my images. You should add any
other tools you'd need to build your site.
The end result looks like this:
```nix
default = pkgs.buildNpmPackage {
{
nodejs,
pkg-config,
buildNpmPackage,
importNpmLock,
vips,
}: buildNpmPackage {
name = "myblog";
version = "unstable";
buildInputs = with pkgs; [
buildInputs = [
nodejs
vips
];
nativeBuildInputs = with pkgs; [
nativeBuildInputs = [
pkg-config
];
npmDepsHash = "sha256-Q7rhCjAPPn44DyUZ/uoD+7o4XH33IATfL+v1azEhuW0=";
npmDeps = importNpmLock {
npmRoot = ./.;
};
npmConfigHook = importNpmLock.npmConfigHook;
src = ./.;
installPhase = ''
mkdir -p $out/public
cp -ar _site/ $out/public
cp -ar _site/* $out/public/
'';
};
}
```
Then, running `nix build` I get my site files in `result/public`, which is what we want.
@ -109,9 +126,14 @@ module that will set this up:
# TODO: write this
```
Now we have a scoped user, with an ssh key authorized. It needs a shell so we can actually log
in remotely.
The last step is creating that symlink. This is where the concept of "activation" comes into play.
For NixOS, `deploy-rs` activation just calls `switch-to-configuration` to make the system change the profile.
We can effectively do whatever we want here.
For NixOS, `deploy-rs` activation just calls `switch-to-configuration` to make
the system change the profile. We can effectively do whatever we want here.
Reading the [custom activator](https://github.com/serokell/deploy-rs/blob/aa07eb05537d4cd025e2310397a6adcedfe72c76/flake.nix#L58C13-L96C17) source:
```nix
@ -172,8 +194,9 @@ ln -sn $PROFILE/public /var/lib/static-site/public
Then when deploying with `deploy-rs`:
```
error: cannot add path '/nix/store/2sad737aglfzmil72phv0j8s34zzmvzi-myblog' because it lacks a signature by a trusted key
```bash
error: cannot add path '/nix/store/2sad737aglfzmil72phv0j8s34zzmvzi-myblog'
because it lacks a signature by a trusted key
```
Drat. This makes sense though, since it would be a bit dangerous to allow any old user write access to
@ -182,17 +205,14 @@ the nix store. We have two options:
1. Make `static-site` a trusted user
2. Create a trusted keypair to sign our closure when it's built.
Pick your poison - the keypair mechanism is slightly more secure.
Pick your poison - the keypair mechanism is more secure, as it means that
the `static-site` user can't upload arbitrary packages.
I just want to get this working, so I made `static-site` a trusted user.
Note that if you wanted to use the keypair instead, `deploy-rs` has a
[secret environment variable](https://github.com/serokell/deploy-rs/blob/aa07eb05537d4cd025e2310397a6adcedfe72c76/src/push.rs#L131)
called `LOCAL_KEY` which is a file that contains the signing key.
Regardless of the outcome you choose, when you re-deploy, it should work properly.
Let's follow the symlink.

View file

@ -1,4 +1,3 @@
---
title: Zig is Dark Souls
description: This is both good and bad.

View file

@ -145,6 +145,14 @@ export default async function (eleventyConfig) {
//eleventyConfig.addPairedShortcode("section", async (content, transition = "none") => {
// return `<section data-transition=${transition}> ${mdIt.renderInline(content)} </section>`;
//})
//
// Paired shortcode for callout
eleventyConfig.addPairedShortcode("callout", function(content) {
// The 'content' variable holds the text/HTML placed between
// {% callout %} and {% endcallout %}
// We wrap it with our div structure.
return `<div class="callout">${content}</div>`;
});
// Features to make your build faster (when you need them)

View file

@ -16,30 +16,22 @@
));
in {
packages = forAllSystems (pkgs: rec {
default = pkgs.buildNpmPackage {
name = "myblog";
version = "unstable";
buildInputs = with pkgs; [
nodejs
vips
];
nativeBuildInputs = with pkgs; [
pkg-config
];
# npmDepsHash = "sha256-rMU1q2BPvPihovOyjbCezd1UyEODSrtCLr9TJedOVl0=";
npmDeps = pkgs.importNpmLock {
npmRoot = ./.;
};
npmConfigHook = pkgs.importNpmLock.npmConfigHook;
src = ./.;
installPhase = ''
mkdir -p $out/public
cp -ar _site/* $out/public/
'';
apps = forAllSystems (pkgs: {
"deploy" = {
type = "app";
program = let
ci = (pkgs.writeShellApplication {
name = "ci.sh";
text = ''
nix build
'';
});
in "${ci}/ci.sh";
};
});
packages = forAllSystems (pkgs: {
default = pkgs.callPackage ./package.nix {};
});
deploy.nodes.myblog = {
hostname = "saji.dev";
profiles.mysite = {

32
package.nix Normal file
View file

@ -0,0 +1,32 @@
{
nodejs,
pkg-config,
buildNpmPackage,
importNpmLock,
vips,
lib,
}: buildNpmPackage {
name = "myblog";
version = "unstable";
buildInputs = [
];
nativeBuildInputs = [
pkg-config
nodejs
vips
];
npmDeps = importNpmLock {
npmRoot = ./.;
};
npmConfigHook = importNpmLock.npmConfigHook;
src = ./.;
installPhase = ''
mkdir -p $out/public
cp -ar _site/* $out/public/
'';
meta = {
description = "My blog, yay";
homepage = "https://saji.dev";
platforms = lib.platforms.all;
};
}

View file

@ -7,16 +7,41 @@
/* Theme colors */
:root {
--color-black: #000000;
--color-black-bright: #5a4e51;
--color-red: #cf5080;
--color-red-bright: #e26b95;
--color-green: #7b8d39;
--color-green-bright: #8fa445;
--color-yellow: #b57235;
--color-yellow-bright: #cf8544;
--color-blue: #3c8cbf;
--color-blue-bright: #4fa1d7;
--color-magenta: #9468d8;
--color-magenta-bright: #a782e5;
--color-cyan: #a782e5;
--color-cyan-bright: #47ac9b;
--color-white: #c2bdbd;
--color-white-bright: #ffffff;
--color-gray-20: #e0e0e0;
--color-gray-50: #C0C0C0;
--color-gray-90: #333;
--background-color: #fff;
--background-color: var(--color-white);
--text-color: var(--color-gray-90);
--text-color-link: #082840;
--text-color-link-active: #5f2b48;
--text-color-link-visited: #17050F;
--text-color-link: var(--color-cyan);
--text-color-link-active: var(--color-cyan-bright);
--text-color-link-visited: var(--color-cyan);
--syntax-tab-size: 2;
}
@ -26,13 +51,16 @@
--color-gray-20: #e0e0e0;
--color-gray-50: #C0C0C0;
--color-gray-90: #dad8d8;
--background-color: var(--color-black);
/* --text-color is assigned to --color-gray-_ above */
--text-color-link: #1493fb;
--text-color-link-active: #6969f7;
--text-color-link-visited: #a6a6f8;
/* --text-color-link: #1493fb; */
/* --text-color-link-active: #6969f7; */
/* --text-color-link-visited: #a6a6f8; */
--text-color-link: var(--color-cyan);
--text-color-link-active: var(--color-cyan-bright);
--text-color-link-visited: var(--color-cyan);
--background-color: #15202b;
}
}
@ -145,6 +173,9 @@ pre:not([class*="language-"]) {
}
code {
word-break: break-all;
color: var(--color-black);
background-color: var(--color-white);
}
/* Header */
@ -277,3 +308,45 @@ h1, h2, h3 {
font-family: WO3;
letter-spacing: .1rem;
}
/* Callout styles */
.callout {
padding: 2em; /* Adjust padding as needed */
position: relative; /* Needed for positioning pseudo-elements */
border: none; /* Ensure no default border */
margin: 1em 0; /* Add some margin for spacing */
overflow: hidden; /* Prevents pseudo-elements from causing scrollbars if they slightly exceed bounds */
}
.callout p {
margin: 0; /* Remove default paragraph margin inside callout */
}
/* Top-left corner bracket */
.callout::before {
content: '';
position: absolute;
top: 0.5em; /* Adjust position */
left: 0.5em; /* Adjust position */
width: 1.5em; /* Adjust size */
height: 1.5em; /* Adjust size */
border-top: 3px solid #ffffff; /* White border, adjust thickness */
border-left: 3px solid #ffffff; /* White border, adjust thickness */
box-sizing: border-box;
}
/* Bottom-right corner bracket */
.callout::after {
content: '';
position: absolute;
bottom: 0.5em; /* Adjust position */
right: 0.5em; /* Adjust position */
width: 1.5em; /* Adjust size */
height: 1.5em; /* Adjust size */
border-bottom: 3px solid #ffffff; /* White border, adjust thickness */
border-right: 3px solid #ffffff; /* White border, adjust thickness */
box-sizing: border-box;
}

View file

@ -21,3 +21,6 @@ nixpkgs?
NixOS
keypair
whitelabel