[{"content":"","date":"September 14 2025","externalUrl":null,"permalink":"/","section":"","summary":"","title":"","type":"page"},{"content":" A fresh install of Windows isn\u0026rsquo;t really fresh. Windows 11\u0026rsquo;s defaults include Copilot integrations, Microsoft account sign-in prompts, bundled apps, OneDrive, and telemetry data that I prefer to disable. While these defaults might be useful for other people, for me, before I can actually use my computer, I have to run through a second setup process of removing apps I don\u0026rsquo;t need, changing settings, and just getting the system back to the way I like it.\nSince I have several computers (and sometimes reinstall Windows), running through a massive checklist of configuration options in addition to reinstalling all my apps was a process that became overwhelming very quickly. I needed something that could run through the Windows installation process for me and make all of my tweaks.\nWhy not a customized image? # Many corporations (and OEMs) create an image using Microsoft Sysprep\u0026thinsp;external link with all their tweaks and apps installed so they can easily deploy a pre-configured Windows install to new computers. While that\u0026rsquo;s convenient for big organizations, as an individual, it\u0026rsquo;s rather annoying for me to reapply all my tweaks when a new Windows feature update releases.\nInstead of maintaining a whole custom Windows image, I wanted something closer to a recipe: start with the normal Windows installer ISO, automatically answer the setup prompts, and apply my changes on top of it. That way, when Microsoft pushes a new ISO release, all I need to do is tweak a few lines in a configuration file rather than rebuilding and preparing an entire image from scratch.\nWindows supports that through autounattend.xml, an answer file that automates the installer and launches post-install scripts. From there, I could use Local Group Policy and Registry tweaks to configure the system without maintaining a full system image.\nGoals # I want this configuration to automatically:\nanswer Windows installer prompts, remove unnecessary built-in applications, apply common settings (privacy, usability, and other opinionated tweaks), and ask for my manual input only when needed (disk partitioning, computer name, and Wi-Fi). This entire process consisted of testing out my settings on a fresh Windows 11 install inside of a virtual machine, updating my config files, and testing out the install on another VM. There was a lot of trial and error!\nGroup Policy # I started off with Group Policy, a feature that allows administrators to configure Windows settings. Companies can set Group Policy settings to be synced across their entire fleet of devices, but they can also be managed using the Local Group Policy Editor. Windows provides a built-in set of Group Policy Objects under the \u0026ldquo;Administrative Templates\u0026rdquo; category, allowing for various features and settings to be controlled. These settings each came with documentation and specific toggles, making them very straightforward to set up.\nThe challenge, however, was finding a way to import these Group Policies automatically. That\u0026rsquo;s where the LGPO\u0026thinsp;external link tool came in. It allows Group Policy settings to be exported into a .pol file, which can then later be restored. By enabling the UseConfigurationSet option in autounattend.xml, I was able to place the .pol file and the LGPO executable itself into the distribution share folder\u0026thinsp;external link ($OEM$\\$$\\Setup\\Scripts) and have the Windows installer copy them into the install (C:\\Windows\\Setup\\Scripts).\nWindows Registry # While Group Policy settings covered the most important and commonly-used settings, there were more preferences that simply didn\u0026rsquo;t have a corresponding Group Policy Object. Instead, they were managed by keys inside of the Windows Registry.\nSome options, such as enabling UTC in Windows\u0026thinsp;external link , are well-documented on the Internet. Others, however, such as whether the Snap Layouts menu appears when hovering over the maximize button, couldn\u0026rsquo;t be found anywhere.\nFor those options, I had to find the Registry key controlling them myself. Process Monitor\u0026thinsp;external link from the Sysinternals suite allowed me to track Registry changes, filtered down to just the Settings apps. I then ran through all the options I would change in Settings, jotted down the Registry key path, and tried manually toggling the Registry key myself through the Registry Editor to change the setting.\nFrom my findings, I compiled a handful of .reg files which could then be imported to Windows.\nBuilding autounattend.xml # The unattend generator tool\u0026thinsp;external link helped me quickly make an autounattend.xml with my installer answers, along with my Group Policy and Registry settings.\nAnd here\u0026rsquo;s the final result! https://github.com/nebulanw/autounattend\u0026thinsp;external link ","date":"September 14 2025","externalUrl":null,"permalink":"/projects/autounattend/","section":"Projects","summary":"","title":"Automating My Windows 11 Setup","type":"projects"},{"content":"","date":"September 14 2025","externalUrl":null,"permalink":"/projects/","section":"Projects","summary":"","title":"Projects","type":"projects"},{"content":" Nowadays, cars rely on an interconnected network of computers in addition to mechanical systems. A modern vehicle can have more than 50 embedded systems handling braking, steering, and sensors, among other tasks. These devices run on firmware, which needs updates to patch flaws and improve safety. But an update system also creates a new way for things to go wrong. For instance, someone could maliciously modify an update, downgrade to a vulnerable version, or make the device run untrusted code.\nAt MIT\u0026rsquo;s Beaver Works Summer Institute, I addressed this problem on a smaller scale with a secure bootloader for a Tiva microcontroller modeled as an automotive control unit. Though it didn\u0026rsquo;t control a real vehicle, it forced me to work on the same question: how do you safely update firmware on a small embedded device when the update channel and even the hardware itself could be attacked?\nThis was a group project, and my main work was on the architecture, the firmware file format, and the bootloader verification logic. We submitted our project as part of an embedded Capture The Flag (eCTF) mini-competition, where teams earned points by finding and exploiting vulnerabilities in each other\u0026rsquo;s designs.\nI worked to design a bootloader in C, paired with a Python packaging and updater tool. The Python side encrypted and packaged the firmware; the bootloader received it over UART, verified its integrity and version, and finally wrote it to flash.\nSecurity goals # The microcontroller is a device an attacker can physically access, connect to over UART, and potentially try to access using JTAG. With those things in mind, the bootloader needed to accomplish:\nConfidentiality: firmware shouldn\u0026rsquo;t be readable from the update file, and it shouldn\u0026rsquo;t be easy to extract from the device. Integrity and authenticity: the device should only install and run firmware created with the trusted key. Anti-rollback: the bootloader should reject older firmware versions than the one currently installed. All of these features needed to be carefully implemented within the hardware constraints of a tiny Tiva microcontroller, which had low memory and computational limits.\nArchitecture # The update system is divided into two parts: a trusted build/protection pipeline for creating valid firmware updates, and an update path to simply deliver those updates to the board.\nbl_build.py builds the bootloader, generates the ChaCha20-Poly1305 key, writes it to the protected build output, and compiles the same key into the bootloader binary. fw_protect.py uses that key to package firmware into the protected update format. fw_update.py sends an already-protected firmware bundle to the board over UART in 16-byte chunks, without knowledge of the key. bootloader.c runs on the Tiva microcontroller and decides whether the received bundle should be installed using cryptographic verification. Cryptography # To prevent firmware contents from being read, I used ChaCha20-Poly1305, a single authenticated encryption scheme that provides confidentiality through a stream cipher. The Poly1305 authentication tag provides integrity, allowing the bootloader to confirm the encrypted content matches the original firmware produced by the developer. Its high efficiency provides an advantage on low-powered microcontrollers like the Tiva boards.\nDuring updates, the bootloader:\nDecrypts metadata and firmware blocks Uses Poly1305 tags to authenticate integrity Rejects any block failing verification Firmware file format # The bundle produced by fw_protect.py stores the metadata and firmware as separate blocks, each with its own nonce and authentication tag.\nThe encrypted firmware file contains three major sections:\nMetadata block # Length 16 bytes 12 bytes 1028 bytes Type Tag Nonce Encrypted metadata Encrypted metadata block # Length 2 bytes 1024 bytes 2 bytes Type Version Message Firmware length Firmware block # Length 16 bytes 12 bytes 2 bytes 2 bytes Variable Type Tag Nonce Padding Encrypted version Encrypted firmware Bootloader workflow # Once the protected firmware bundle reaches the Tiva over UART, the bootloader handles the installation. It verifies the update before modifying the main firmware region in flash.\nMetadata verification # The bootloader first receives the encrypted metadata block. This block contains the firmware version, firmware length, and release message. The bootloader decrypts it with ChaCha20-Poly1305 and rejects the update if the authentication tag does not pass verification.\nAfter decrypting the metadata, the bootloader performs validation before it continues. The firmware length must fit within the allowed flash region and the incoming version must be at least as new as the currently installed version.\nFirmware verification # If the metadata passed verification, the bootloader continues receiving the encrypted firmware block. This block contains another copy of the firmware version followed by the firmware contents. The bootloader decrypts and authenticates the block, then compares the version inside the firmware block against the version from the metadata block.\nThis duplicate version check prevents attackers from mixing metadata from one update with firmware from another update.\nFlash installation # The bootloader writes the update to flash after both blocks pass verification. It stores the version number, copies the release message, and writes the decrypted firmware into the firmware region.\nIf any check fails, the bootloader returns an error code and clears temporary buffers, instead of installing the update.\nChallenges and limitations # Microcontrollers often have limited memory, making it impossible to store encrypted firmware in flash that is decrypted on-the-fly into RAM, and perform cryptographic operations that require larger memory buffers. Without making major memory optimizations, I was forced to store the installed firmware unencrypted in flash.\nTo prevent attackers from connecting via JTAG and dumping decrypted firmware, I locked all debugging functionality on the debug interface. Once locked, reading memory through the debug interface requires a full chip erase, which also wipes the firmware.\n","date":"August 21 2025","externalUrl":null,"permalink":"/projects/bwsi/","section":"Projects","summary":"","title":"Building a Secure Bootloader","type":"projects"},{"content":"In the FIRST Robotics Competition program, scouting is an important aspect of being a successful team. Our rookie FRC team quickly learned that becoming a top-seeded alliance captain with no data to rely on for our partner picks was quite the strategic challenge. And so, in our second season, we began exploring methods to scout teams at our competitions.\nGenerally, the scouting process involves a team observing teams\u0026rsquo; performance in matches to understand their strategy, find trends, and ultimately help plan their strategy to succeed. But each team has their own preferences for what data they need, and we needed to find ours!\nTrying the basics # We started our journey with paper scouting forms. A scouter would fill out a new page for each match a team plays. Unfortunately, the paper form was overwhelming for our scouters, who had to constantly look between their clipboards and the field. Our strategists, too, had to sort through a whole stack of forms to find the data they were looking for.\nSo we quickly concluded after our first event of scouting that paper wouldn\u0026rsquo;t cut it, leaving digital tools as the only other option. There were existing scouting tools created by other teams, like Lovat\u0026thinsp;external link and ScoutRadioz\u0026thinsp;external link . They allowed for quick collection of specific data, a variety of analytics, had easy-to-understand user interfaces, and were what we stuck with for most of the season. However, these tools were still designed around the needs of the teams that created them, and didn\u0026rsquo;t perfectly fit our strategy. At the end of our second season, we concluded that we needed to develop our own tools to have more control over what data and analytics we could use.\nObjectives # Before getting into the UI, I discussed the requirements of our scouting program with our strategy team.\nI needed to design:\na simple user interface for scouters, with large buttons and fewer fields of input per screen a simple way to view statistics for the game strategy team, featuring charts showing various metrics of performance changing over time for a particular team and average statistics for score contribution, missed scoring, etc. Stack # Backend # I chose MongoDB, a document-oriented database, to store our scouting information in a readable JSON-like format and allow for quick modifications.\nThe backend is written in Python and uses the FastAPI library\u0026thinsp;external link to provide endpoints for the user-facing app. FastAPI let me easily generate API documentation from my code and comments and enforce strict input validation with Pydantic schemas. To get basic information on teams, events, and matches, we use The Blue Alliance\u0026thinsp;external link .\nFrontend # The frontend is written in Dart using the Flutter framework, which allows us to use the same code to release our app both as a website, and as an app for Android and iOS.\nScouting process # Making assignments # At the beginning of an event, scouters must be assigned to each team in each match so that data is available for every team. The scouting lead provides a list of scouters and a range of matches to assign, then the first six scouters in the list are assigned for the first five matches, then the next six, and so on, wrapping back to the first scouter at the end of the list.\nCreating reports # Each scouter submits a report, which provides information for a team in one match. This varies from season to season, though the basic details usually stay the same:\nDoes the robot have a preloaded item? Where does the robot start? How does the robot move autonomously? How effective are the team\u0026rsquo;s defensive strategies? After these, we have a set of season-specific details we\u0026rsquo;d like to gather, which usually involves a general list of robot actions, such as picking up a game piece and scoring it.\nAt the conclusion of the match, scouters provide some qualitative information about what they observed, such as if the team did any particularly effective strategies or experienced any failures.\nAnalyzing data # After a scouter submits a report, the backend computes some analytical information which gets attached to the match report, and is used to speed up aggregate analytics. This usually includes the total number of game pieces scored, the count of how many times the robot played defense, and an estimated score contribution to the match.\nAnalytics # Team members can use the scouting app to perform analysis on the collected scouter data.\nQuantitative # The backend creates trends for each available metric for a particular team, such as the number of points contributed, the accuracy of the robot, game pieces scored, and further season-specific data. It shows how a team\u0026rsquo;s performance changes as they progress from match to match.\nThe list of all teams further includes brief overview statistics and allows strategists to quickly sort through top teams by specific criteria.\nQualitative # In addition to quantitative data, the scouting app also provides the ability to view qualitative and subjective data provided by scouters. For each team in each match, scouters are given the option to provide additional written notes on the performance of the robot. This can help further with strategizing, as it conveys information excluded from quantitative data.\nTo quickly analyze written notes, the scouting backend makes summaries using the Google Gemini API, providing the LLM with context about the game and qualitative data such as scouter notes, which it summarizes into short snippets.\nUsing the interface # After scouter assignments are generated, scouters can log in to their own accounts and see their assigned matches.\nDuring the match, the scouter is presented with various options that correspond to season-specific robot actions. The entry screen transitions between several states when buttons are pressed, showing only relevant options to minimize confusion.\nAt the end of the match, there is a post-match form that asks for the robot\u0026rsquo;s final state, as well as some subjective notes.\nFuture plans # Of course, this is only how our scouting app has evolved up to this point. Some improvements that I\u0026rsquo;m considering are expanding support for the backend to allow for more than one team to use it at once, providing a more detailed administrative page to easily manage different aspects of the app, and expanding on the flexibility of the data collected. We still have more upcoming seasons to strategize for, and I\u0026rsquo;ll definitely be implementing more features!\nAnd here\u0026rsquo;s the backend code! https://github.com/nebulanw/insight-api\u0026thinsp;external link ","date":"March 25 2024","externalUrl":null,"permalink":"/projects/scouting/","section":"Projects","summary":"","title":"FRC Scouting App","type":"projects"},{"content":"","externalUrl":null,"permalink":"/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"},{"content":"","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"}]