Simple scripts to unpack and repack the embedded Linux firmware image for the Hasivo S110W-8XGT-SE router. In addition this repo includes patch files that change the right bytes to make the firmware more chatty.
NOTE: The helper scripts are genated with the help of a LLM, but the instruction patches for the logging are not.
The firmware for this Hasivo managed switch is compatible with different devices ...
Before using these scripts, install the necessary dependencies via apt:
sudo apt update && apt install squashfs-tools cpio lzma python3 gzip xxdPlace your original firmware file (e.g., fw_sh.bin or others) in the right directory, or use the existing one and run it:
./unpack_fw.sh ./firmware/original_s1100wp-8gt-se.binThis will create:
extracted_fs/: The root filesystem of the router.extracted_fs/squashfs-root/: The main application filesystem.extracted_fs/modsquashfs-root/: The kernel modules filesystem.
You can now navigate into the extracted_fs/ folders and add, remove, or edit files.
Some commands that help to figure out what to do
strings -t x rtcore.ko | grep Logmips-linux-gnu-objdump -D -j .text rtcore.ko | grep -C 10 "rt_log"
Example:
000007ec <rt_log>:
7ec: 27bdffd0 addiu sp,sp,-48
7f0: afbf002c sw ra,44(sp)
7f4: afb30028 sw s3,40(sp)
7f8: afb20024 sw s2,36(sp)
7fc: afb10020 sw s1,32(sp)
800: afb0001c sw s0,28(sp)
804: 3c180000 lui t8,0x0
808: 8f180454 lw t8,1108(t8)
80c: 00808021 move s0,a0
810: 00c09021 move s2,a2
814: 13000026 beqz t8,8b0 <rt_log+0xc4>
818: 00e08821 move s1,a3
81c: 3c180000 lui t8,0x0
820: 8f180448 lw t8,1096(t8)
824: 13000010 beqz t8,868 <rt_log+0x7c>
828: 24030009 li v1,9
82c: 24020001 li v0,1
830: 1702002b bne t8,v0,8e0 <rt_log+0xf4>
834: 3c020000 lui v0,0x0
838: 8c43044c lw v1,1100(v0)
83c: 0098c004 sllv t8,t8,a0
840: 0303c024 and t8,t8,v1
844: 17000012 bnez t8,890 <rt_log+0xa4>
848: 3402f011 li v0,0xf011
84c: 8fbf002c lw ra,44(sp)
850: 8fb30028 lw s3,40(sp)
854: 8fb20024 lw s2,36(sp)
858: 8fb10020 lw s1,32(sp)
85c: 8fb0001c lw s0,28(sp)
860: 03e00008 jr ra
864: 27bd0030 addiu sp,sp,48
868: 3c180000 lui t8,0x0
86c: 8f180450 lw t8,1104(t8)
870: 1303fff6 beq t8,v1,84c <rt_log+0x60>
874: 3402f011 li v0,0xf011
878: 28830009 slti v1,a0,9
87c: 1060fff4 beqz v1,850 <rt_log+0x64>
880: 8fbf002c lw ra,44(sp)
884: 0304c02b sltu t8,t8,a0
888: 1700fff2 bnez t8,854 <rt_log+0x68>
88c: 8fb30028 lw s3,40(sp)
890: 3c180000 lui t8,0x0
894: 8f030440 lw v1,1088(t8)
898: 8f180444 lw t8,1092(t8)
89c: 00721824 and v1,v1,s2
8a0: 0311c024 and t8,t8,s1
8a4: 0078c025 or t8,v1,t8
8a8: 1300ffe8 beqz t8,84c <rt_log+0x60>
8ac: 3402f011 li v0,0xf011
8b0: 27b80044 addiu t8,sp,68
8b4: 8fa60040 lw a2,64(sp)
8b8: 3c130000 lui s3,0x0
8bc: 03003821 move a3,t8
8c0: afb80014 sw t8,20(sp)
8c4: 3c180000 lui t8,0x0
8c8: 26640030 addiu a0,s3,48
8cc: 27180000 addiu t8,t8,0
8d0: 0300f809 jalr t8
8d4: 240503ff li a1,1023
8d8: 04410009 bgez v0,900 <rt_log+0x114>
8dc: 3c180000 lui t8,0x0
8e0: 8fbf002c lw ra,44(sp)
8e4: 8fb30028 lw s3,40(sp)
8e8: 8fb20024 lw s2,36(sp)
8ec: 8fb10020 lw s1,32(sp)
8f0: 8fb0001c lw s0,28(sp)
8f4: 2402ffff li v0,-1
8f8: 03e00008 jr ra
8fc: 27bd0030 addiu sp,sp,48
900: 8f030440 lw v1,1088(t8)
904: 8f020444 lw v0,1092(t8)
908: 00729024 and s2,v1,s2
90c: 2e581000 sltiu t8,s2,4096
910: 13000017 beqz t8,970 <rt_log+0x184>
914: 00518824 and s1,v0,s1
918: 0000c021 move t8,zero
91c: 00122040 sll a0,s2,0x1
920: 0800024c j 930 <rt_log+0x144>
924: 2403002c li v1,44
928: 1303000d beq t8,v1,960 <rt_log+0x174>
92c: 3c020000 lui v0,0x0
930: 00183827 nor a3,zero,t8
934: 03111006 srlv v0,s1,t8
938: 00e43804 sllv a3,a0,a3
93c: 33060020 andi a2,t8,0x20
940: 03122806 srlv a1,s2,t8
944: 00e21025 or v0,a3,v0
948: 00a6100b movn v0,a1,a2
94c: 30420001 andi v0,v0,0x1
950: 1040fff5 beqz v0,928 <rt_log+0x13c>
954: 27180001 addiu t8,t8,1
958: 2718ffff addiu t8,t8,-1
95c: 3c020000 lui v0,0x0
960: 0018c080 sll t8,t8,0x2
964: 24420000 addiu v0,v0,0
968: 0800025e j 978 <rt_log+0x18c>
96c: 0058c021 addu t8,v0,t8
970: 3c180000 lui t8,0x0
974: 271800b0 addiu t8,t8,176
978: 8f060000 lw a2,0(t8)
97c: 3c040000 lui a0,0x0
980: 3c180000 lui t8,0x0
984: 24840000 addiu a0,a0,0
988: 02002821 move a1,s0
98c: 27180000 addiu t8,t8,0
990: 0300f809 jalr t8
994: 26670030 addiu a3,s3,48
998: 08000213 j 84c <rt_log+0x60>
99c: 00001021 move v0,zero
9a0: 27bdffe0 addiu sp,sp,-32
9a4: afbf001c sw ra,28(sp)
9a8: 24180001 li t8,1
9ac: afb80010 sw t8,16(sp)
9b0: 3c180000 lui t8,0x0
9b4: 24050200 li a1,512
9b8: 2406000b li a2,11
9bc: 27180000 addiu t8,t8,0
9c0: 0300f809 jalr t8
and a lot of ghidra ...
- Use Ghex or any other Hex Editor to change the needed bytes...
- Changing in ghidra is not recommended (because export the file again changes the binary), but using ghidra to try out what could make sense is a good approach.
- To find the right bytes in the Hex Editor, you can search for bytes sequences (detected from ghidra or objdump) directly occuring before the section that has to be changed. Verify if you found the right piece (the search result should occurs just once).
- After patching check the logic in ghidra and a lot of tryouts & patiences is required.
- There are multiple ways to patch:
Simply change hex values. For strings keep in mind that you can't overwrite the length as this breaks the code.
Search for conditional branches, like bne/beq and others that send you somewhere. You can easily replace with NOP (no operation), so the area is never executed.
This is helpful if you for example want to log the parameters that a function receives, or you want to apply complex logic changes:
- Search for the use of function (JMP) for that you want to log the parameters - For example a(c,d,e)...
- Find now any other function that has a similar structure like trampoline(c,d,e). Make sure it is rarely used (with ghidra).
- Change the JMP of (a) to the address of the trampoline function.
- Remove logic with (NOP) or change the code of the trampoline. If you want to add print, find a string that is usable for that (correct length). Changing strings is easy and does not harm anything.
- Writing complex bytecode is hard, so use ghidra or write yourself a simple application with the function (+logic), compile it for the right architecture, decompile and copy the instructions from there... I also learned that LLMs are pretty good here...
- Call the function a(c,d,e) from the trampoline(c,d,e) again to keep the logic intact. So basically there is one layer between the execution of a(a,b,c) injected.
- The cool part is that you can access the same set of registers in the trampoline function from before. That makes the change easy.
Apply patches automatically
# Just uncomment all unneeded parts in the script & run it:
./patch_firmware.sh
Manual way
For example: Copy the rtcore.ko from the ./extracted_fs/modsquashfs-root/lib/modules/3.18.24/kernel/drivers/net/switch/rtcore/rtcore.ko folder to project base folder.
Binary to hex:
xxd rtcore.ko > rtcore.hex
Apply patch:
patch rtcore.hex < ./patches/rtcore_enable_debug_logging.patch
Convert to binary:
xxd -r original.hex > rtcore_patched.ko
Copy the file back to the firmware folder:
cp rtcore_patched.ko ./extracted_fs/modsquashfs-root/lib/modules/3.18.24/kernel/drivers/net/switch/rtcore/rtcore.ko
Once your changes are done, run the packing script to generate a new flashable image:
./pack_fw.sh firmware/original_s1100wp-8gt-se.bin patched.binYour new image will be saved as patched.bin.