2015-01-17

Lenovo Fn-Ctrl Swap

Lenovo laptop keyboards have always had the Fn key as the left most key on the keyboard, like this:

If you are a full touch typist (or simply got used with an external computer keyboard) you would expect Ctrl to be the left most key on the keyboard. And this Fn and Ctrl key swap would drive you crazy.

I used to have a Lenovo W510, which had a bios option to swap Fn and Ctrl keys.

Recently I've got a Lenovo SL500, an older, consumer Lenovo model, which with an SSD drive does its job just fine :) The problem with the SL500 is that it doesn't have the Fn and Ctrl key swap in bios!

The good news is that some people have hacked the Lenovo bios files and swapped the bytes for Fn and Ctrl keys. The hacked bios files can be downloaded from here:  FN-CTRL swap on all Lenovo laptops.

The problem is that when you run WINUPTP.EXE you (might) get an error message like this:


Bummer.

Lenovo provides also an ISO image with the firmware update. Probably with a DOS update utility.

The bad news was that this ISO image was "special". If you burned it to a disk no files were present. If you tried to open it with 7-zip you would get just a Bootable_HardDisk.img file with was 512 bytes in size.

Since we know there must be a DOS firmware utility involved, the ISO file must be in an old DOS format. IsoBuster (one can download an evaluation copy) confirmed this:


The bios file is – $0a6a000.fl1, which was also present in the Windows bios update package. Also worth mentioning the PCDOS_JP label, which points to IBM PC DOS instead of MS DOS. This makes sense since Lenovo used to be part of IBM.

From the first image we can see that NERO Burning Rom was used to create the ISO file. Since I don't have access to NERO Burning Rom I needed something else.

ISO format is not compressed, which means that all the files are somewhere in the ISO file. Since I had the original $0a6a000.fl1 and the modified version all I needed was a program which would search and replace the content of the $0a6a000.fl1 file.

I googled a bit after such an utility, no luck, then decided to roll my own program.

Below is the C++ source code for my breplace program. You can get x64 binaries for Linux, macOS and Windows from GitHub. The "magic" function in this program is std::search which finds the position of the $0a6a00.fl1 fle in the ISO file.


#include <vector>
#include <iostream>
#include <fstream>
#include <algorithm>
#include <string>

size_t readFileToVector(std::istream& file, std::vector<char>& buf)
{
     file.seekg(0, std::ios_base::end);
     size_t size = file.tellg();
     file.seekg(0, std::ios_base::beg);

     buf.resize(size);
     file.read(&buf[0], size);

     return size;
}

int main(int argc, char** argv)
{
     std::vector<std::string> arguments(argv, argv + argc);
     if (arguments.size() != 5)
     {
         std::cout << "usage: breplace <file_container> <file_to_be_replaced>";
         std::cout << "<file_to_replace> <dest_file_container>" << std::endl;
         return 1;
     }

     std::vector<char> container;
     std::ifstream containerFile;
     containerFile.open(arguments[1], std::ios_base::binary | std::ios_base::in);
     size_t containerSize = readFileToVector(containerFile, container);

     std::vector<char> toBeReplaced;
     std::ifstream toBeReplacedFile;
     toBeReplacedFile.open(arguments[2], std::ios_base::binary | std::ios_base::in);
     size_t fileSize = readFileToVector(toBeReplacedFile, toBeReplaced);

     std::vector<char> toReplace;
     std::ifstream toReplaceFile;
     toReplaceFile.open(arguments[3], std::ios_base::binary | std::ios_base::in);
     readFileToVector(toReplaceFile, toReplace);

     std::vector<char> destination;
     std::ofstream destinationFile;
     destinationFile.open(arguments[4], std::ios_base::binary | std::ios_base::out);

     std::vector<char>::iterator itpos = std::search(
         container.begin(), container.end(), toBeReplaced.begin(), toBeReplaced.end());

     if (itpos == container.end())
     {
         std::cout << "File's content " << arguments[2] << " was not found" << std::endl;
         return 1;
     }

     size_t beforeFileSize = std::distance(container.begin(), itpos);
     std::cout << "File's content " << arguments[2] << " found at position: ";
     std::cout << beforeFileSize << std::endl;

     destination.resize(containerSize);
     std::copy(container.begin(), container.begin() + beforeFileSize, destination.begin());
     std::copy(toReplace.begin(), toReplace.end(), destination.begin() + beforeFileSize);
     std::copy(container.begin() + beforeFileSize + fileSize, container.end(),
               destination.begin() + beforeFileSize + fileSize);

     destinationFile.write(&destination[0], containerSize);

     return 0;
}

Then I ran the tool as breplace.exe 6auj19uc.iso $0A6A000.FL1 $0A6A000.FL1_new 6auj19uc_fn_ctrl_swap.iso. In order to be sure that things are as they should be I did a file comparison between the two ISO files using Total Commander:


Things looked "legit" thus decided to burn the new ISO image to a disk and proceed with the bios update.

Everything worked just fine. Now the Lenovo SL500 has the Fn and Ctrl keys swapped! w00t!

Before you decide to flash your Lenovo laptop double check the bios / ISO files. This can brick your laptop!

No comments: