├── LICENSE ├── README.md └── tools ├── 2fa ├── .bochsrc.txt ├── .gitignore ├── 2fa.c ├── Makefile ├── README.md ├── boot.S ├── cmos.h ├── crt0.h ├── io.h ├── kbd.h ├── linker.ld ├── tiny2fa │ ├── google_pam │ │ ├── LICENSE │ │ ├── base32.h │ │ ├── hmac.h │ │ └── sha1.h │ └── tiny2fa.h ├── types.h ├── ui.h ├── util.h └── vga.h ├── README.md ├── basic ├── .bochsrc.txt ├── .gitignore ├── IBM_BASIC_C10.bin ├── Makefile ├── README.md ├── cassette_idt_h.img ├── csum.c └── pnp.asm ├── bios_nim ├── README.md ├── bbnim.asm ├── defines.inc ├── docs │ ├── LBCar.pdf │ ├── Oded Stanford Annual Forum 2014.pdf │ └── cache_as_ram_lb_09142006.pdf └── nim.asm ├── find_asm.sh ├── find_gaps ├── .gitignore ├── Makefile └── find_gaps.c ├── mdump ├── .bochsrc.txt ├── .gitignore ├── Makefile ├── README.md ├── mdump.asm └── mdump.c ├── nim ├── .bochsrc.txt ├── .gitignore ├── Makefile ├── boot.S ├── linker.ld └── nim.S ├── sdtlist ├── .gitignore ├── Makefile ├── README.md └── sdt.c └── skip_strings.sh /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Davidson Francis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tools/2fa/.bochsrc.txt: -------------------------------------------------------------------------------- 1 | megs: 16 2 | cpu: model=p4_willamette 3 | romimage: file="/usr/share/bochs/BIOS-bochs-latest" 4 | vgaromimage: file="/usr/share/bochs/VGABIOS-lgpl-latest" 5 | floppya: 1_44="2fa.img", status=inserted 6 | boot: a 7 | log: /dev/null 8 | mouse: enabled=0 9 | clock: sync=none, time0=utc 10 | display_library: x, options="gui_debug" 11 | magic_break: enabled=1 12 | com1: enabled=1, mode=socket-client, dev=localhost:2345 13 | -------------------------------------------------------------------------------- /tools/2fa/.gitignore: -------------------------------------------------------------------------------- 1 | *.img 2 | *.o 3 | bx_enh_dbg.ini 4 | -------------------------------------------------------------------------------- /tools/2fa/2fa.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2023 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "crt0.h" 26 | #include "tiny2fa.h" 27 | 28 | #include "vga.h" 29 | #include "ui.h" 30 | #include "kbd.h" 31 | 32 | /* 33 | * SECRET_KEY must be a 32-char key encoded in BASE32: 34 | * - All characters must be in upper case and 35 | * - should be in the following alphabet: A-Z, 2-7 36 | */ 37 | #ifndef SECRET_KEY 38 | #warning "Using default secret key, please change it!" 39 | #define SECRET_KEY "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD" 40 | #endif 41 | 42 | /* 43 | * Backup password: this password must be sent for when 44 | * the date & hour are reset... choose at most 20 chars. 45 | */ 46 | #ifndef BACK_PASSWD 47 | #warning "Using default password, please change it!" 48 | #define BACK_PASSWD "foo" 49 | #endif 50 | 51 | static uint8_t secret_key[] = SECRET_KEY; 52 | static char back_passwd[] = BACK_PASSWD; 53 | 54 | /** 55 | * @brief Displays an error message on screen 56 | * and waits the user to press any key. 57 | */ 58 | static void ui_error_window(void) 59 | { 60 | vga_cursor_disable(); 61 | vga_set_color(RED, WHITE); 62 | 63 | ui_draw_box(34,3, 64 | CENTER_X(80,34), CENTER_Y(25,3), 65 | 1, "Wrong Code!" 66 | ); 67 | vga_put_string( 68 | CENTER_X(80,32), CENTER_Y(25,3) + 1, 69 | "~~ Press any key to try again ~~" 70 | ); 71 | 72 | kbd_read_char(); 73 | } 74 | 75 | /** 76 | * @brief Main window: reads the 2FA code (or password) 77 | * and check for its validity: if wrong, tell the user 78 | * and read again, if right, return to the previous 79 | * code. 80 | */ 81 | static void ui_main_window(void) 82 | { 83 | const char *input; 84 | 85 | again: 86 | vga_cursor_enable(); 87 | vga_set_color(BLUE, WHITE); 88 | 89 | ui_draw_box( 90 | 40, 10, 91 | CENTER_X(80,40), CENTER_Y(25,10), 92 | 1, 93 | " Two-Factor Authentication Required " 94 | ); 95 | 96 | vga_put_string( 97 | CENTER_X(80,40) + 1, CENTER_Y(25,10) + 2, 98 | "You must type a 2FA code below to\n" 99 | "grant access to this PC:" 100 | ); 101 | 102 | input = ui_input_box(20, 1, 103 | CENTER_X(80,20), CENTER_Y(25,10) + 5 104 | ); 105 | 106 | /* 107 | * Check if input matches the expected 2FA 108 | * key, or the default password. 109 | */ 110 | if (strcmp(input, back_passwd) 111 | && t2_verify_key(secret_key, str2int(input), 0) <= 0) 112 | { 113 | ui_error_window(); 114 | goto again; 115 | } 116 | } 117 | 118 | /* Main. */ 119 | int twofa_main(void) 120 | { 121 | vga_set_8025(); 122 | ui_main_window(); 123 | return (0); 124 | } 125 | -------------------------------------------------------------------------------- /tools/2fa/Makefile: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2023 Davidson Francis 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | CC ?= cc 24 | CFLAGS += -Wall -Wextra -Wno-unused-function 25 | CFLAGS += -I$(CURDIR)/tiny2fa -I$(CURDIR)/tiny2fa/google_pam -I$(CURDIR) 26 | CFLAGS += -std=gnu99 -Os -nostdlib -m32 -march=i386 -ffreestanding 27 | LDFLAGS = --oformat binary -m elf_i386 -T linker.ld -S 28 | OBJ = boot.o 2fa.o 29 | BIN = 2fa.img 30 | 31 | # Boot size + dummy logo size 32 | FIRST_STAGE=$$((512+2048)) 33 | # Code start address offset 34 | CODE_ADDR=$$((0x8a10)) 35 | 36 | # BIOS builds? 37 | ifeq ($(BIOS),yes) 38 | CFLAGS += -DBIOS 39 | ASMFLAGS += --defsym BIOS=1 40 | FIRST_STAGE = 2048 41 | endif 42 | 43 | # Set secret key and default passwd 44 | ifneq ($(SECRET_KEY),) 45 | CFLAGS += -DSECRET_KEY=\"$(SECRET_KEY)\" 46 | endif 47 | ifneq ($(BACK_PASSWD),) 48 | CFLAGS += -DBACK_PASSWD=\"$(BACK_PASSWD)\" 49 | endif 50 | 51 | .PHONY: all clean 52 | 53 | all: $(BIN) 54 | 55 | boot.o: boot.S 56 | $(AS) $^ $(ASMFLAGS) -o $@ --32 57 | 2fa.o: 2fa.c 58 | $(CC) $^ $(CFLAGS) -c 59 | 60 | # 61 | # Main 62 | # 63 | # The final image needs to be 'adjusted', because the 64 | # linker adds physical padding to the file when adjusting 65 | # the section start offset (and I couldn't get around that). 66 | # 67 | # The data: 68 | # - FIRST_STAGE: 512 (boot size) + 2048 (logo size) 69 | # - CODE_ADDR: 0x8a10: start of code 70 | # 71 | 2fa.img: .2fa_big.img 72 | dd if=.2fa_big.img of=2fa.img bs=1 count=$(FIRST_STAGE) 73 | dd if=.2fa_big.img of=2fa.img bs=1 skip=$(CODE_ADDR) \ 74 | seek=$(FIRST_STAGE) conv=notrunc 75 | 76 | .2fa_big.img: $(OBJ) 77 | $(LD) $^ $(LDFLAGS) -o $@ 78 | 79 | # Run on Bochs and QEMU 80 | bochs: 2fa.img 81 | bochs -q -f .bochsrc.txt 82 | qemu: 2fa.img 83 | qemu-system-i386 -boot a -fda 2fa.img -rtc base=localtime 84 | 85 | clean: 86 | $(RM) $(OBJ) 87 | $(RM) $(BIN) 88 | $(RM) .2fa_big.img 89 | # $(RM) bx_enh_dbg.ini 90 | -------------------------------------------------------------------------------- /tools/2fa/README.md: -------------------------------------------------------------------------------- 1 | # 2FA 2 | 2FA is a tool for two-factor authentication in BIOS via TOTP, so authentication is 3 | required when turning on the computer. 4 | 5 | Although for some it may seem useless, I really liked this idea and decided to go ahead: 6 | requiring a physical device (e.g., smartphone) to turn on the computer seemed like a 7 | good idea, and that's why I did it. It amazes me that this doesn't already exist on 8 | modern PCs. 9 | 10 |

11 | 2FA screen 13 |
14 | 2FA screen 15 |

16 | 17 | ## Building 18 | The 2FA algorithm requires some boring bits of code that would otherwise be quite 19 | complicated to write in full assembly, such as HMAC, base32, and SHA1. 20 | 21 | Because of that, I chose to use GCC and write all (or almost all) the code in C, but 22 | still in real mode. 23 | 24 | So to build 2FA just: 25 | ```bash 26 | $ git clone https://github.com/Theldus/AMI_BIOS_CodeInjection 27 | $ cd tools/2fa 28 | $ make 29 | 30 | # Or for AMI BIOS builds: 31 | make BIOS=yes 32 | ``` 33 | 34 | By default the following 2FA key will be used: `AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD`, 35 | and the following password as well: `foo`. 36 | 37 | They can be changed with: 38 | ```bash 39 | make BIOS=yes SECRET_KEY="key" BACK_PASSWD="passwd" 40 | ``` 41 | The `SECRET_KEY` must be a base32-encoded string with exactly 32 characters. This 42 | should also be added to the phone or other device to generate the 2FA codes. 43 | 44 | The `BACK_PASSWD` is a backup password: if the BIOS date and time is too different 45 | from the current date, the PC would become inaccessible. since there would be no way 46 | to access the BIOS setup to adjust the time again. Thinking about it, a 'backup password' 47 | was introduced: when inserted, access to the PC is granted, without the need for 48 | a 2FA code. 49 | 50 | ## Acknowledgements 51 | None of this would have been possible without Skeeto's amazing blog post on 52 | ['How to build DOS COM files with GCC'](https://nullprogram.com/blog/2014/12/09/). 53 | There he explains how it is possible to use traditional GCC to emit 32-bit code in 54 | real mode, without the need for any kind of extra toolchain. 55 | 56 | Thanks to that, I was able to build '2FA' in a simple way and without having to write 57 | everything in assembly. However, it's important to make it clear that there are several 58 | 'quirks' that need to be considered with this kind of thing, like GCC issuing the 59 | '0x66' prefix for everything (even when you don't want to! (like in far call/retf), 60 | assuming that DS=ES=SS and so on. 61 | 62 | If you intend to build something to run on real-mode with C, read this code carefully, 63 | it should give you some insights =). 64 | -------------------------------------------------------------------------------- /tools/2fa/boot.S: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2023 Davidson Francis 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | .code16 24 | 25 | .section .boot 26 | 27 | .ifndef BIOS 28 | boot_init: 29 | cli 30 | mov $0x7C0, %ax 31 | add $544, %ax 32 | mov %ax, %ss 33 | mov $4096, %sp 34 | 35 | # Read 10 sectors 36 | mov $0x1000, %ax # segment 37 | mov %ax, %es 38 | mov $0x8210, %bx # offset addr 39 | mov $18, %al # num sectors to read 40 | mov $2, %cl # from sector 2 (1-based) 41 | call read_sectors 42 | 43 | # Far call to our module 44 | lcall $0x1000, $0x8A10 45 | 46 | # Hang 47 | 1: hlt 48 | jmp 1b 49 | 50 | read_sectors: 51 | mov $2, %ah 52 | mov $0, %ch 53 | mov $0, %dh 54 | int $0x13 55 | jc .again 56 | ret 57 | .again: 58 | xor %ax, %ax 59 | int $0x13 60 | jmp read_sectors 61 | ret 62 | 63 | .fill 510-(.-boot_init), 1, 0 64 | .word 0xAA55 65 | 66 | .endif 67 | 68 | # Add our fake logo 69 | .fill 2048, 1, 0x0A 70 | -------------------------------------------------------------------------------- /tools/2fa/cmos.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2023 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef CMOS_H 26 | #define CMOS_H 27 | 28 | #include "types.h" 29 | #include "io.h" 30 | 31 | /* CMOS register addresses */ 32 | #define CMOS_REG_SEC 0x00 /* seconds register */ 33 | #define CMOS_REG_MIN 0x02 /* minutes register */ 34 | #define CMOS_REG_HOUR 0x04 /* hours register */ 35 | #define CMOS_REG_DOM 0x07 /* day of month register */ 36 | #define CMOS_REG_MON 0x08 /* month register */ 37 | #define CMOS_REG_YEAR 0x09 /* year register */ 38 | #define CMOS_REG_STATUS_A 0x0A /* status register A */ 39 | #define CMOS_REG_STATUS_B 0x0B /* status register B */ 40 | 41 | /* Macro to convert binary-coded decimal (BCD) to binary */ 42 | #define BCD_TO_BIN(x) (((x)&0x0f) + ((x) / 16) * 10) 43 | 44 | /* Flags for CMOS register STATUS_B */ 45 | #define BCD_FORMAT_FLAG 0x04 /* BCD time format flag */ 46 | #define TIME_FORMAT_FLAG 0x02 /* 24-hour time format flag */ 47 | 48 | /* CMOS Ports and Masks */ 49 | #define CMOS_ADDR_PORT 0x70 /* CMOS address port. */ 50 | #define CMOS_DATA_PORT 0x71 /* CMOS data port. */ 51 | #define CMOS_NMI_DISABLE_MASK 0x80 /* NMI disable mask */ 52 | 53 | /** 54 | * @brief CMOS Time Structure. 55 | */ 56 | static struct cmos_time 57 | { 58 | unsigned char sec; /**< Seconds. */ 59 | unsigned char min; /**< Minutes. */ 60 | unsigned char hour; /**< Hour. */ 61 | unsigned char dom; /**< Day of Month. */ 62 | unsigned char mon; /**< Month. */ 63 | unsigned short year; /**< Year. */ 64 | } cmos_time; 65 | 66 | /** 67 | * @brief Current millennium. 68 | */ 69 | #define CURR_MILLENNIUM 2000 70 | 71 | /** 72 | * @brief Current locale 73 | * (If your PC BIOS date is set to UTC, leave this as 0!) 74 | * 75 | * Please pay attention that this is the offset to adjust 76 | * the local time to UTC. If your local time is UTC-3, 77 | * the variable LOCALE should be 3, not -3!. If UTC+3, 78 | * your 'LOCALE' should be set to -3, not 3!. 79 | */ 80 | #define LOCALE 3 /* >> CHANGE_HERE << */ 81 | 82 | /** 83 | * @brief Read a byte from the CMOS device. 84 | * 85 | * @param addr Target address. 86 | * @return The value read. 87 | */ 88 | static uint8_t cmos_read(uint8_t addr) 89 | { 90 | /* Disable NMI at the highest order bit of the address. */ 91 | outputb(CMOS_ADDR_PORT, CMOS_NMI_DISABLE_MASK | addr); 92 | return inputb(CMOS_DATA_PORT); 93 | } 94 | 95 | /** 96 | * @brief Convert CMOS time to Unix timestamp. 97 | * 98 | * @note Since register 0x09 gives only last 2 digits of 99 | * the year, it's our responsibility to add it with 100 | * right offset. 101 | * 102 | * @return The Unix timestamp at UTC (or should be). 103 | */ 104 | static int32_t cmos_to_unix_time(void) 105 | { 106 | int year = CURR_MILLENNIUM + cmos_time.year; 107 | int month = cmos_time.mon; 108 | int day = cmos_time.dom; 109 | int hour = cmos_time.hour; 110 | int minute = cmos_time.min; 111 | int second = cmos_time.sec; 112 | 113 | int era = 0; /* Era is a 400 yr period */ 114 | int yoe = 0; /* Year of era [0, 399] */ 115 | int doy = 0; /* Day of year [0, 365] */ 116 | int doe = 0; /* Day of era [0, 146096] */ 117 | int num_days = 0; /* # of days since Epoch */ 118 | int num_secs = 0; /* # of clock-ticks since Epoch */ 119 | 120 | /* Subtract 1 from year if month is January or February */ 121 | year -= month <= 2; 122 | /* Determine the Gregorian era based on year */ 123 | era = (year >= 0 ? year : year - 399) / 400; 124 | /* Determine the year of era */ 125 | yoe = (year - era * 400); 126 | /* Determine the day of year (1-365) */ 127 | doy = (153 * (month + (month > 2 ? -3 : 9)) + 2) / 5 + day - 1; 128 | /* Determine the day of era */ 129 | doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; 130 | /* Determine the number of days since 1/1/1970 */ 131 | num_days = era * 146097 + doe - 719468; 132 | /* Determine the total number of seconds */ 133 | num_secs = (num_days * 86400) + (hour * 3600) + (minute * 60) + second; 134 | return (num_secs + (LOCALE * 60 * 60)); 135 | } 136 | 137 | /** 138 | * @brief Read the current time from the CMOS device and 139 | * return it as a Unix timestamp. 140 | * 141 | * @return The current Unix timestamp. 142 | */ 143 | static int32_t cmos_read_unix_time(void) 144 | { 145 | uint8_t register_b; 146 | 147 | /* Read the time from the CMOS device. */ 148 | do 149 | { 150 | cmos_time.sec = cmos_read(CMOS_REG_SEC); 151 | cmos_time.min = cmos_read(CMOS_REG_MIN); 152 | cmos_time.hour = cmos_read(CMOS_REG_HOUR); 153 | cmos_time.dom = cmos_read(CMOS_REG_DOM); 154 | cmos_time.mon = cmos_read(CMOS_REG_MON); 155 | cmos_time.year = cmos_read(CMOS_REG_YEAR); 156 | } while (cmos_time.sec != cmos_read(CMOS_REG_SEC)); 157 | 158 | register_b = cmos_read(CMOS_REG_STATUS_B); 159 | if (!(register_b & BCD_FORMAT_FLAG)) 160 | { 161 | /* Convert BCD values to binary. */ 162 | cmos_time.sec = BCD_TO_BIN(cmos_time.sec); 163 | cmos_time.min = BCD_TO_BIN(cmos_time.min); 164 | cmos_time.hour = BCD_TO_BIN(cmos_time.hour & 0x7f) | 165 | (cmos_time.hour & 0x80); 166 | cmos_time.dom = BCD_TO_BIN(cmos_time.dom); 167 | cmos_time.mon = BCD_TO_BIN(cmos_time.mon); 168 | cmos_time.year = BCD_TO_BIN(cmos_time.year); 169 | } 170 | 171 | /* Adjust hour if 12-hour format. */ 172 | if (!(register_b & TIME_FORMAT_FLAG) && (cmos_time.hour & 0x80)) 173 | cmos_time.hour = ((cmos_time.hour & 0x7f) + 12) % 24; 174 | 175 | return (cmos_to_unix_time()); 176 | } 177 | 178 | #endif /* CMOS_H */ 179 | -------------------------------------------------------------------------------- /tools/2fa/crt0.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2023 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | /** 26 | * This is our very first entrypoint, either via bootable code, 27 | * or where the BIOS must make a far call to. 28 | * 29 | * The following code sets up the stack, segments, and everything 30 | * else so that the C code can run smoothly. It's virtually our 31 | * 'crt0.c' for all intents and purposes. 32 | */ 33 | 34 | #define dbg asm ("xchg %bx, %bx\n"); 35 | #define nop asm ("nop\n"); 36 | 37 | asm ( 38 | ".code16gcc\n" 39 | "xchg %bx, %bx\n" 40 | 41 | /* ================== PROLOGUE ================ */ 42 | 43 | /* Backup registers. */ 44 | "pushal\n" 45 | "pushfl\n" 46 | "push %ds\n" 47 | "push %es\n" 48 | 49 | /** 50 | * GCC normally requires that DS=ES=SS, and this, for real 51 | * mode and within the BIOS, complicates our life quite 52 | * a bit. 53 | * 54 | * To get around this, the code below sets up a secondary 55 | * stack in the code segment itself, and when it returns 56 | * (in the epilogue), it retrieves the original BIOS stack 57 | * and returns as if nothing had happened. Interesting 58 | * isn't it? 59 | */ 60 | 61 | /* Backup old stack. */ 62 | "mov $_stack_addr, %bx\n" 63 | "movw %sp, %cs:(%bx)\n" 64 | "movw %ss, %cs:0x2(%bx)\n" 65 | 66 | /* Setup new stack. */ 67 | "sub $4, %bx\n" 68 | "mov %bx, %sp\n" 69 | "mov %cs, %ax\n" 70 | "mov %ax, %ss\n" 71 | 72 | /* Configure remaining data seg. */ 73 | "mov %ax, %ds\n" 74 | "mov %ax, %es\n" 75 | 76 | /* ================== CODE ================ */ 77 | "call twofa_main\n" 78 | "xchg %bx, %bx\n" 79 | 80 | /* ================== EPILOGUE ================ */ 81 | 82 | /* Restore BIOS/old stack. */ 83 | "mov $_stack_addr, %bx\n" 84 | "movw %cs:(%bx), %sp\n" 85 | "movw %cs:0x2(%bx), %ax\n" 86 | "movw %ax, %ss\n" 87 | 88 | /* Restore registers. */ 89 | "pop %es\n" 90 | "pop %ds\n" 91 | "popfl\n" 92 | "popal\n" 93 | 94 | #ifdef BIOS 95 | /* 96 | * >> CHANGE_HERE << 97 | * Add your backup instructions here. 98 | */ 99 | "mov $0x4F02, %ax\n" 100 | "int $0x10\n" 101 | #endif 102 | 103 | ".byte 0xCB\n" /* true 16-bit retf. */ 104 | ); 105 | -------------------------------------------------------------------------------- /tools/2fa/io.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2023 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef IO_H 26 | #define IO_H 27 | 28 | #include "types.h" 29 | 30 | /** 31 | * @brief Read a single byte from the I/O port @p port. 32 | * 33 | * @return Returns the read byte. 34 | */ 35 | static inline uint8_t inputb(uint16_t port) 36 | { 37 | uint8_t value; 38 | asm volatile ( 39 | "inb %1, %0" 40 | : "=a" (value) 41 | : "Nd" (port) 42 | ); 43 | return (value); 44 | } 45 | 46 | /** 47 | * @brief Writes a single byte @p value to the I/O port 48 | * @p port. 49 | * 50 | * @param port Target I/O port to be written. 51 | * @param value Value to be written. 52 | */ 53 | static inline void outputb(uint16_t port, uint8_t value) { 54 | asm volatile ( 55 | "outb %0, %1" 56 | : 57 | : "a" (value), "Nd" (port) 58 | ); 59 | } 60 | 61 | #endif /* IO_H */ 62 | -------------------------------------------------------------------------------- /tools/2fa/kbd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2023 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef KBD_H 26 | #define KBD_H 27 | 28 | #include "types.h" 29 | #include "io.h" 30 | #include "util.h" 31 | 32 | /* PS/2 registers. */ 33 | #define PS2_DATA_PORT 0x60 34 | #define PS2_STATUS_REG 0x64 35 | 36 | /* ASCII characters. */ 37 | #define ESC 27 38 | #define BACKSPACE '\b' 39 | #define TAB '\t' 40 | #define ENTER '\n' 41 | #define RETURN '\r' 42 | #define NEWLINE ENTER 43 | 44 | /* Non-ASCII special scan-codes. */ 45 | #define KESC 1 46 | #define KF1 0x80 47 | #define KF2 (KF1 + 1) 48 | #define KF3 (KF2 + 1) 49 | #define KF4 (KF3 + 1) 50 | #define KF5 (KF4 + 1) 51 | #define KF6 (KF5 + 1) 52 | #define KF7 (KF6 + 1) 53 | #define KF8 (KF7 + 1) 54 | #define KF9 (KF8 + 1) 55 | #define KF10 (KF9 + 1) 56 | #define KF11 (KF10 + 1) 57 | #define KF12 (KF11 + 1) 58 | 59 | /* Cursor keys. */ 60 | #define KINS 0x90 61 | #define KDEL (KINS + 1) 62 | #define KHOME (KDEL + 1) 63 | #define KEND (KHOME + 1) 64 | #define KPGUP (KEND + 1) 65 | #define KPGDN (KPGUP + 1) 66 | #define KLEFT (KPGDN + 1) 67 | #define KUP (KLEFT + 1) 68 | #define KDOWN (KUP + 1) 69 | #define KRIGHT (KDOWN + 1) 70 | #define KCTRL (KRIGHT + 1) 71 | 72 | /* Non-shift PS/2 scan codes table. */ 73 | static uint8_t ascii_non_shift[] = { 74 | '\0', ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9', 75 | '0', '-', '=', BACKSPACE, TAB, 'q', 'w', 'e', 'r', 't', 76 | 'y', 'u', 'i', 'o', 'p', '[', ']', ENTER, 0, 'a', 's', 77 | 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 78 | 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, 0, 0, 79 | ' ', 0, KF1, KF2, KF3, KF4, KF5, KF6, KF7, KF8, KF9, KF10, 80 | 0, 0, KHOME, KUP, KPGUP,'-', KLEFT, '5', KRIGHT, '+', KEND, 81 | KDOWN, KPGDN, KINS, KDEL, 0, 0, 0, KF11, KF12 82 | }; 83 | 84 | /** 85 | * @brief Read a single char from the keyboard PS/2 86 | * (via polling) and returns it. 87 | */ 88 | static char kbd_read_char(void) 89 | { 90 | uint8_t ch, rd_ch; 91 | ch = rd_ch = 0; 92 | 93 | again: 94 | /* Wait to buffer have something. */ 95 | while (!(inputb(PS2_STATUS_REG) & 1)); 96 | 97 | ch = inputb(PS2_DATA_PORT); 98 | if (ch == 0xE0) /* Ignore if 0xE0. */ 99 | goto again; 100 | 101 | /* Check if key release. */ 102 | if (ch & 0x80) 103 | return (ascii_non_shift[rd_ch]); 104 | 105 | /* If key pressed. */ 106 | rd_ch = ch; 107 | goto again; 108 | return (0); 109 | } 110 | 111 | #endif /* KBD_H. */ 112 | -------------------------------------------------------------------------------- /tools/2fa/linker.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT(binary) 2 | SECTIONS 3 | { 4 | .boot : 5 | { 6 | boot.o *(.boot) 7 | } 8 | 9 | /* 10 | * ## Warning ## 11 | * The final image needs to be 'adjusted', because 12 | * the linker adds physical padding to the file when 13 | * adjusting the section start offset (and I couldn't 14 | * get around that). 15 | * 16 | * If anyone knows of a cleaner way to do this, I'd 17 | * really appreciate. 18 | * 19 | * Note: Please checkout the Makefile to get a better 20 | * idea on how I achieve this. 21 | */ 22 | 23 | . = 0x8a10; 24 | .text : 25 | { 26 | *(.text) 27 | } 28 | .data : 29 | { 30 | *(.bss) 31 | *(.data) 32 | *(.rodata) 33 | } 34 | 35 | /* Reserve space for our secondary stack. */ 36 | _stack_addr = . + 2048; 37 | .stack : 38 | { 39 | FILL(0x00) 40 | . = 2047; 41 | BYTE(0x90) 42 | } 43 | 44 | /DISCARD/ : 45 | { 46 | *(.comment) 47 | *(.note) 48 | *(.eh_frame) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tools/2fa/tiny2fa/google_pam/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /tools/2fa/tiny2fa/google_pam/base32.h: -------------------------------------------------------------------------------- 1 | // Base32 implementation 2 | // 3 | // Copyright 2010 Google Inc. 4 | // Author: Markus Gutschke 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | // 18 | // Encode and decode from base32 encoding using the following alphabet: 19 | // ABCDEFGHIJKLMNOPQRSTUVWXYZ234567 20 | // This alphabet is documented in RFC 4648/3548 21 | // 22 | // We allow white-space and hyphens, but all other characters are considered 23 | // invalid. 24 | // 25 | // All functions return the number of output bytes or -1 on error. If the 26 | // output buffer is too small, the result will silently be truncated. 27 | 28 | #ifndef _BASE32_H_ 29 | #define _BASE32_H_ 30 | 31 | #include "types.h" 32 | 33 | static int base32_decode(const uint8_t *encoded, uint8_t *result, int bufSize) { 34 | int buffer = 0; 35 | int bitsLeft = 0; 36 | int count = 0; 37 | for (const uint8_t *ptr = encoded; count < bufSize && *ptr; ++ptr) { 38 | uint8_t ch = *ptr; 39 | if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '-') { 40 | continue; 41 | } 42 | buffer <<= 5; 43 | 44 | // Deal with commonly mistyped characters 45 | if (ch == '0') { 46 | ch = 'O'; 47 | } else if (ch == '1') { 48 | ch = 'L'; 49 | } else if (ch == '8') { 50 | ch = 'B'; 51 | } 52 | 53 | // Look up one base32 digit 54 | if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) { 55 | ch = (ch & 0x1F) - 1; 56 | } else if (ch >= '2' && ch <= '7') { 57 | ch -= '2' - 26; 58 | } else { 59 | return -1; 60 | } 61 | 62 | buffer |= ch; 63 | bitsLeft += 5; 64 | if (bitsLeft >= 8) { 65 | result[count++] = buffer >> (bitsLeft - 8); 66 | bitsLeft -= 8; 67 | } 68 | } 69 | if (count < bufSize) { 70 | result[count] = '\000'; 71 | } 72 | return count; 73 | } 74 | 75 | static int base32_encode(const uint8_t *data, int length, uint8_t *result, 76 | int bufSize) { 77 | if (length < 0 || length > (1 << 28)) { 78 | return -1; 79 | } 80 | int count = 0; 81 | if (length > 0) { 82 | int buffer = data[0]; 83 | int next = 1; 84 | int bitsLeft = 8; 85 | while (count < bufSize && (bitsLeft > 0 || next < length)) { 86 | if (bitsLeft < 5) { 87 | if (next < length) { 88 | buffer <<= 8; 89 | buffer |= data[next++] & 0xFF; 90 | bitsLeft += 8; 91 | } else { 92 | int pad = 5 - bitsLeft; 93 | buffer <<= pad; 94 | bitsLeft += pad; 95 | } 96 | } 97 | int index = 0x1F & (buffer >> (bitsLeft - 5)); 98 | bitsLeft -= 5; 99 | result[count++] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"[index]; 100 | } 101 | } 102 | if (count < bufSize) { 103 | result[count] = '\000'; 104 | } 105 | return count; 106 | } 107 | 108 | #endif /* _BASE32_H_ */ 109 | -------------------------------------------------------------------------------- /tools/2fa/tiny2fa/google_pam/hmac.h: -------------------------------------------------------------------------------- 1 | // HMAC_SHA1 implementation 2 | // 3 | // Copyright 2010 Google Inc. 4 | // Author: Markus Gutschke 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #ifndef _HMAC_H_ 19 | #define _HMAC_H_ 20 | 21 | #include "types.h" 22 | 23 | static void hmac_sha1(const uint8_t *key, int keyLength, 24 | const uint8_t *data, int dataLength, 25 | uint8_t *result, int resultLength) { 26 | SHA1_INFO ctx; 27 | uint8_t hashed_key[SHA1_DIGEST_LENGTH]; 28 | if (keyLength > 64) { 29 | // The key can be no bigger than 64 bytes. If it is, we'll hash it down to 30 | // 20 bytes. 31 | sha1_init(&ctx); 32 | sha1_update(&ctx, key, keyLength); 33 | sha1_final(&ctx, hashed_key); 34 | key = hashed_key; 35 | keyLength = SHA1_DIGEST_LENGTH; 36 | } 37 | 38 | // The key for the inner digest is derived from our key, by padding the key 39 | // the full length of 64 bytes, and then XOR'ing each byte with 0x36. 40 | uint8_t tmp_key[64]; 41 | for (int i = 0; i < keyLength; ++i) { 42 | tmp_key[i] = key[i] ^ 0x36; 43 | } 44 | if (keyLength < 64) { 45 | memset(tmp_key + keyLength, 0x36, 64 - keyLength); 46 | } 47 | 48 | // Compute inner digest 49 | sha1_init(&ctx); 50 | sha1_update(&ctx, tmp_key, 64); 51 | sha1_update(&ctx, data, dataLength); 52 | uint8_t sha[SHA1_DIGEST_LENGTH]; 53 | sha1_final(&ctx, sha); 54 | 55 | // The key for the outer digest is derived from our key, by padding the key 56 | // the full length of 64 bytes, and then XOR'ing each byte with 0x5C. 57 | for (int i = 0; i < keyLength; ++i) { 58 | tmp_key[i] = key[i] ^ 0x5C; 59 | } 60 | memset(tmp_key + keyLength, 0x5C, 64 - keyLength); 61 | 62 | // Compute outer digest 63 | sha1_init(&ctx); 64 | sha1_update(&ctx, tmp_key, 64); 65 | sha1_update(&ctx, sha, SHA1_DIGEST_LENGTH); 66 | sha1_final(&ctx, sha); 67 | 68 | // Copy result to output buffer and truncate or pad as necessary 69 | memset(result, 0, resultLength); 70 | if (resultLength > SHA1_DIGEST_LENGTH) { 71 | resultLength = SHA1_DIGEST_LENGTH; 72 | } 73 | memcpy(result, sha, resultLength); 74 | 75 | // Zero out all internal data structures 76 | memset(hashed_key, 0, sizeof(hashed_key)); 77 | memset(sha, 0, sizeof(sha)); 78 | memset(tmp_key, 0, sizeof(tmp_key)); 79 | } 80 | 81 | #endif /* _HMAC_H_ */ 82 | -------------------------------------------------------------------------------- /tools/2fa/tiny2fa/google_pam/sha1.h: -------------------------------------------------------------------------------- 1 | // SHA1 header file 2 | // 3 | // Copyright 2010 Google Inc. 4 | // Author: Markus Gutschke 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | #ifndef SHA1_H__ 19 | #define SHA1_H__ 20 | 21 | #include "types.h" 22 | 23 | #define SHA1_BLOCKSIZE 64 24 | #define SHA1_DIGEST_LENGTH 20 25 | 26 | typedef struct { 27 | uint32_t digest[8]; 28 | uint32_t count_lo, count_hi; 29 | uint8_t data[SHA1_BLOCKSIZE]; 30 | int local; 31 | } SHA1_INFO; 32 | 33 | #define BYTE_ORDER 1234 /* Little endian. */ 34 | 35 | #ifndef TRUNC32 36 | #define TRUNC32(x) ((x) & 0xffffffffL) 37 | #endif 38 | 39 | /* SHA f()-functions */ 40 | #define f1(x,y,z) ((x & y) | (~x & z)) 41 | #define f2(x,y,z) (x ^ y ^ z) 42 | #define f3(x,y,z) ((x & y) | (x & z) | (y & z)) 43 | #define f4(x,y,z) (x ^ y ^ z) 44 | 45 | /* SHA constants */ 46 | #define CONST1 0x5a827999L 47 | #define CONST2 0x6ed9eba1L 48 | #define CONST3 0x8f1bbcdcL 49 | #define CONST4 0xca62c1d6L 50 | 51 | /* truncate to 32 bits -- should be a null op on 32-bit machines */ 52 | #define T32(x) ((x) & 0xffffffffL) 53 | 54 | /* 32-bit rotate */ 55 | #define R32(x,n) T32(((x << n) | (x >> (32 - n)))) 56 | 57 | /* the generic case, for when the overall rotation is not unraveled */ 58 | #define FG(n) \ 59 | T = T32(R32(A,5) + f##n(B,C,D) + E + *WP++ + CONST##n); \ 60 | E = D; D = C; C = R32(B,30); B = A; A = T 61 | 62 | /* specific cases, for when the overall rotation is unraveled */ 63 | #define FA(n) \ 64 | T = T32(R32(A,5) + f##n(B,C,D) + E + *WP++ + CONST##n); B = R32(B,30) 65 | 66 | #define FB(n) \ 67 | E = T32(R32(T,5) + f##n(A,B,C) + D + *WP++ + CONST##n); A = R32(A,30) 68 | 69 | #define FC(n) \ 70 | D = T32(R32(E,5) + f##n(T,A,B) + C + *WP++ + CONST##n); T = R32(T,30) 71 | 72 | #define FD(n) \ 73 | C = T32(R32(D,5) + f##n(E,T,A) + B + *WP++ + CONST##n); E = R32(E,30) 74 | 75 | #define FE(n) \ 76 | B = T32(R32(C,5) + f##n(D,E,T) + A + *WP++ + CONST##n); D = R32(D,30) 77 | 78 | #define FT(n) \ 79 | A = T32(R32(B,5) + f##n(C,D,E) + T + *WP++ + CONST##n); C = R32(C,30) 80 | 81 | 82 | static void 83 | sha1_transform(SHA1_INFO *sha1_info) 84 | { 85 | int i; 86 | uint8_t *dp; 87 | uint32_t T, A, B, C, D, E, W[80], *WP; 88 | 89 | dp = sha1_info->data; 90 | 91 | #undef SWAP_DONE 92 | 93 | #if BYTE_ORDER == 1234 94 | #define SWAP_DONE 95 | for (i = 0; i < 16; ++i) { 96 | T = *((uint32_t *) dp); 97 | dp += 4; 98 | W[i] = 99 | ((T << 24) & 0xff000000) | 100 | ((T << 8) & 0x00ff0000) | 101 | ((T >> 8) & 0x0000ff00) | ((T >> 24) & 0x000000ff); 102 | } 103 | #endif 104 | 105 | #if BYTE_ORDER == 4321 106 | #define SWAP_DONE 107 | for (i = 0; i < 16; ++i) { 108 | T = *((uint32_t *) dp); 109 | dp += 4; 110 | W[i] = TRUNC32(T); 111 | } 112 | #endif 113 | 114 | #if BYTE_ORDER == 12345678 115 | #define SWAP_DONE 116 | for (i = 0; i < 16; i += 2) { 117 | T = *((uint32_t *) dp); 118 | dp += 8; 119 | W[i] = ((T << 24) & 0xff000000) | ((T << 8) & 0x00ff0000) | 120 | ((T >> 8) & 0x0000ff00) | ((T >> 24) & 0x000000ff); 121 | T >>= 32; 122 | W[i+1] = ((T << 24) & 0xff000000) | ((T << 8) & 0x00ff0000) | 123 | ((T >> 8) & 0x0000ff00) | ((T >> 24) & 0x000000ff); 124 | } 125 | #endif 126 | 127 | #if BYTE_ORDER == 87654321 128 | #define SWAP_DONE 129 | for (i = 0; i < 16; i += 2) { 130 | T = *((uint32_t *) dp); 131 | dp += 8; 132 | W[i] = TRUNC32(T >> 32); 133 | W[i+1] = TRUNC32(T); 134 | } 135 | #endif 136 | 137 | #ifndef SWAP_DONE 138 | #define SWAP_DONE 139 | for (i = 0; i < 16; ++i) { 140 | T = *((uint32_t *) dp); 141 | dp += 4; 142 | W[i] = TRUNC32(T); 143 | } 144 | #endif /* SWAP_DONE */ 145 | 146 | for (i = 16; i < 80; ++i) { 147 | W[i] = W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16]; 148 | W[i] = R32(W[i], 1); 149 | } 150 | A = sha1_info->digest[0]; 151 | B = sha1_info->digest[1]; 152 | C = sha1_info->digest[2]; 153 | D = sha1_info->digest[3]; 154 | E = sha1_info->digest[4]; 155 | WP = W; 156 | #ifdef UNRAVEL 157 | FA(1); FB(1); FC(1); FD(1); FE(1); FT(1); FA(1); FB(1); FC(1); FD(1); 158 | FE(1); FT(1); FA(1); FB(1); FC(1); FD(1); FE(1); FT(1); FA(1); FB(1); 159 | FC(2); FD(2); FE(2); FT(2); FA(2); FB(2); FC(2); FD(2); FE(2); FT(2); 160 | FA(2); FB(2); FC(2); FD(2); FE(2); FT(2); FA(2); FB(2); FC(2); FD(2); 161 | FE(3); FT(3); FA(3); FB(3); FC(3); FD(3); FE(3); FT(3); FA(3); FB(3); 162 | FC(3); FD(3); FE(3); FT(3); FA(3); FB(3); FC(3); FD(3); FE(3); FT(3); 163 | FA(4); FB(4); FC(4); FD(4); FE(4); FT(4); FA(4); FB(4); FC(4); FD(4); 164 | FE(4); FT(4); FA(4); FB(4); FC(4); FD(4); FE(4); FT(4); FA(4); FB(4); 165 | sha1_info->digest[0] = T32(sha1_info->digest[0] + E); 166 | sha1_info->digest[1] = T32(sha1_info->digest[1] + T); 167 | sha1_info->digest[2] = T32(sha1_info->digest[2] + A); 168 | sha1_info->digest[3] = T32(sha1_info->digest[3] + B); 169 | sha1_info->digest[4] = T32(sha1_info->digest[4] + C); 170 | #else /* !UNRAVEL */ 171 | #ifdef UNROLL_LOOPS 172 | FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); 173 | FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); 174 | FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); 175 | FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); 176 | FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); 177 | FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); 178 | FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); 179 | FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); 180 | #else /* !UNROLL_LOOPS */ 181 | for (i = 0; i < 20; ++i) { FG(1); } 182 | for (i = 20; i < 40; ++i) { FG(2); } 183 | for (i = 40; i < 60; ++i) { FG(3); } 184 | for (i = 60; i < 80; ++i) { FG(4); } 185 | #endif /* !UNROLL_LOOPS */ 186 | sha1_info->digest[0] = T32(sha1_info->digest[0] + A); 187 | sha1_info->digest[1] = T32(sha1_info->digest[1] + B); 188 | sha1_info->digest[2] = T32(sha1_info->digest[2] + C); 189 | sha1_info->digest[3] = T32(sha1_info->digest[3] + D); 190 | sha1_info->digest[4] = T32(sha1_info->digest[4] + E); 191 | #endif /* !UNRAVEL */ 192 | } 193 | 194 | /* initialize the SHA digest */ 195 | 196 | static void 197 | sha1_init(SHA1_INFO *sha1_info) 198 | { 199 | sha1_info->digest[0] = 0x67452301L; 200 | sha1_info->digest[1] = 0xefcdab89L; 201 | sha1_info->digest[2] = 0x98badcfeL; 202 | sha1_info->digest[3] = 0x10325476L; 203 | sha1_info->digest[4] = 0xc3d2e1f0L; 204 | sha1_info->count_lo = 0L; 205 | sha1_info->count_hi = 0L; 206 | sha1_info->local = 0; 207 | } 208 | 209 | /* update the SHA digest */ 210 | 211 | static void 212 | sha1_update(SHA1_INFO *sha1_info, const uint8_t *buffer, int count) 213 | { 214 | uint32_t clo; 215 | 216 | clo = T32(sha1_info->count_lo + ((uint32_t) count << 3)); 217 | if (clo < sha1_info->count_lo) { 218 | ++sha1_info->count_hi; 219 | } 220 | sha1_info->count_lo = clo; 221 | sha1_info->count_hi += (uint32_t) count >> 29; 222 | if (sha1_info->local) { 223 | int i = SHA1_BLOCKSIZE - sha1_info->local; 224 | if (i > count) { 225 | i = count; 226 | } 227 | memcpy(((uint8_t *) sha1_info->data) + sha1_info->local, buffer, i); 228 | count -= i; 229 | buffer += i; 230 | sha1_info->local += i; 231 | if (sha1_info->local == SHA1_BLOCKSIZE) { 232 | sha1_transform(sha1_info); 233 | } else { 234 | return; 235 | } 236 | } 237 | while (count >= SHA1_BLOCKSIZE) { 238 | memcpy(sha1_info->data, buffer, SHA1_BLOCKSIZE); 239 | buffer += SHA1_BLOCKSIZE; 240 | count -= SHA1_BLOCKSIZE; 241 | sha1_transform(sha1_info); 242 | } 243 | memcpy(sha1_info->data, buffer, count); 244 | sha1_info->local = count; 245 | } 246 | 247 | 248 | static void 249 | sha1_transform_and_copy(unsigned char digest[20], SHA1_INFO *sha1_info) 250 | { 251 | sha1_transform(sha1_info); 252 | digest[ 0] = (unsigned char) ((sha1_info->digest[0] >> 24) & 0xff); 253 | digest[ 1] = (unsigned char) ((sha1_info->digest[0] >> 16) & 0xff); 254 | digest[ 2] = (unsigned char) ((sha1_info->digest[0] >> 8) & 0xff); 255 | digest[ 3] = (unsigned char) ((sha1_info->digest[0] ) & 0xff); 256 | digest[ 4] = (unsigned char) ((sha1_info->digest[1] >> 24) & 0xff); 257 | digest[ 5] = (unsigned char) ((sha1_info->digest[1] >> 16) & 0xff); 258 | digest[ 6] = (unsigned char) ((sha1_info->digest[1] >> 8) & 0xff); 259 | digest[ 7] = (unsigned char) ((sha1_info->digest[1] ) & 0xff); 260 | digest[ 8] = (unsigned char) ((sha1_info->digest[2] >> 24) & 0xff); 261 | digest[ 9] = (unsigned char) ((sha1_info->digest[2] >> 16) & 0xff); 262 | digest[10] = (unsigned char) ((sha1_info->digest[2] >> 8) & 0xff); 263 | digest[11] = (unsigned char) ((sha1_info->digest[2] ) & 0xff); 264 | digest[12] = (unsigned char) ((sha1_info->digest[3] >> 24) & 0xff); 265 | digest[13] = (unsigned char) ((sha1_info->digest[3] >> 16) & 0xff); 266 | digest[14] = (unsigned char) ((sha1_info->digest[3] >> 8) & 0xff); 267 | digest[15] = (unsigned char) ((sha1_info->digest[3] ) & 0xff); 268 | digest[16] = (unsigned char) ((sha1_info->digest[4] >> 24) & 0xff); 269 | digest[17] = (unsigned char) ((sha1_info->digest[4] >> 16) & 0xff); 270 | digest[18] = (unsigned char) ((sha1_info->digest[4] >> 8) & 0xff); 271 | digest[19] = (unsigned char) ((sha1_info->digest[4] ) & 0xff); 272 | } 273 | 274 | /* finish computing the SHA digest */ 275 | static void 276 | sha1_final(SHA1_INFO *sha1_info, uint8_t digest[20]) 277 | { 278 | int count; 279 | uint32_t lo_bit_count, hi_bit_count; 280 | 281 | lo_bit_count = sha1_info->count_lo; 282 | hi_bit_count = sha1_info->count_hi; 283 | count = (int) ((lo_bit_count >> 3) & 0x3f); 284 | ((uint8_t *) sha1_info->data)[count++] = 0x80; 285 | if (count > SHA1_BLOCKSIZE - 8) { 286 | memset(((uint8_t *) sha1_info->data) + count, 0, SHA1_BLOCKSIZE - count); 287 | sha1_transform(sha1_info); 288 | memset((uint8_t *) sha1_info->data, 0, SHA1_BLOCKSIZE - 8); 289 | } else { 290 | memset(((uint8_t *) sha1_info->data) + count, 0, 291 | SHA1_BLOCKSIZE - 8 - count); 292 | } 293 | sha1_info->data[56] = (uint8_t)((hi_bit_count >> 24) & 0xff); 294 | sha1_info->data[57] = (uint8_t)((hi_bit_count >> 16) & 0xff); 295 | sha1_info->data[58] = (uint8_t)((hi_bit_count >> 8) & 0xff); 296 | sha1_info->data[59] = (uint8_t)((hi_bit_count >> 0) & 0xff); 297 | sha1_info->data[60] = (uint8_t)((lo_bit_count >> 24) & 0xff); 298 | sha1_info->data[61] = (uint8_t)((lo_bit_count >> 16) & 0xff); 299 | sha1_info->data[62] = (uint8_t)((lo_bit_count >> 8) & 0xff); 300 | sha1_info->data[63] = (uint8_t)((lo_bit_count >> 0) & 0xff); 301 | sha1_transform_and_copy(digest, sha1_info); 302 | } 303 | 304 | #endif 305 | -------------------------------------------------------------------------------- /tools/2fa/tiny2fa/tiny2fa.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2018-23 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef TINY2FA_H 26 | #define TINY2FA_H 27 | 28 | #include "cmos.h" 29 | #include "util.h" 30 | #include "sha1.h" 31 | #include "hmac.h" 32 | #include "base32.h" 33 | 34 | #include "types.h" 35 | 36 | /* Secret key size, in bytes. */ 37 | #define T2_SECRET_KEY_SIZE 20 38 | 39 | /* Time padded size, in bytes. */ 40 | #define T2_TIME_PADDED 8 41 | 42 | /* Interval for generating keys, in seconds. */ 43 | #define T2_KEY_INTERVAL 30 44 | 45 | /* Amount of digits for the final key. */ 46 | #define T2_TOTP_DIGITS 1000000 47 | 48 | /* Key length while encoded in base32. */ 49 | #define T2_KEY_ENCODED_LENGTH 32 50 | 51 | /* Default window size. */ 52 | #define T2_DEFAULT_WINDOW_SIZE 3 53 | 54 | /** 55 | * Gets the current key based in the base32 secret 56 | * key passed by parameter and the reference time. 57 | * 58 | * @param b32_secret_key Your secret-key, generated by 59 | * the method @m t2_generate_secret_key. 60 | * 61 | * @param tm target time in Unix Time Stamp, if 0, uses 62 | * the current time. 63 | * 64 | * @return Returns the key equivalent for the current time 65 | * and secret key. 66 | * 67 | * @see t2_generate_secret_key 68 | */ 69 | static int t2_get_key(const uint8_t *b32_secret_key, uint32_t tm) 70 | { 71 | uint8_t sk[T2_SECRET_KEY_SIZE]; /* Original secret key. */ 72 | uint8_t hmac[SHA1_DIGEST_LENGTH]; /* HMAC. */ 73 | uint8_t time_padded[T2_TIME_PADDED]; /* Time padded into 8 bytes. */ 74 | uint64_t ctime; /* Reference time. */ 75 | int offset; /* Result offset. */ 76 | int result; /* Result. */ 77 | 78 | /* Time. */ 79 | ctime = (tm != 0) ? 80 | tm / T2_KEY_INTERVAL : 81 | (uint32_t)cmos_read_unix_time() / T2_KEY_INTERVAL; 82 | 83 | /* Decode secret-key. */ 84 | base32_decode(b32_secret_key, sk, T2_SECRET_KEY_SIZE); 85 | 86 | /* Pad time into a byte array. */ 87 | for (int i = T2_TIME_PADDED - 1; i >= 0; i--) 88 | { 89 | time_padded[i] = ctime; 90 | ctime >>= 8; 91 | } 92 | 93 | /* Calculates hmac. */ 94 | hmac_sha1(sk, T2_SECRET_KEY_SIZE, time_padded, T2_TIME_PADDED, hmac, 95 | SHA1_DIGEST_LENGTH); 96 | 97 | /* Get offset and result. */ 98 | offset = hmac[SHA1_DIGEST_LENGTH - 1] & 0xF; 99 | result = ( 100 | ((hmac[offset + 0] & 0x7F) << 24) | 101 | ((hmac[offset + 1] & 0xFF) << 16) | 102 | ((hmac[offset + 2] & 0xFF) << 8) | 103 | ((hmac[offset + 3] & 0xFF)) ); 104 | 105 | result &= 0x7FFFFFFF; 106 | result %= T2_TOTP_DIGITS; 107 | 108 | /* Clear sk, hmac and time_padded. */ 109 | memset(sk, 0, T2_SECRET_KEY_SIZE); 110 | memset(hmac, 0, SHA1_DIGEST_LENGTH); 111 | memset(time_padded, 0, T2_TIME_PADDED); 112 | 113 | return (result); 114 | } 115 | 116 | /** 117 | * Verifies a given base32 secret key through the key provided 118 | * and a certain window. 119 | * 120 | * @param b32_secret_key Your secret-key, generated by 121 | * the method @m generate_secret_key. 122 | * 123 | * @param key User provided key, this is the key to be checked. 124 | * 125 | * @param window Validation window. Passing 0 the default value 126 | * (default = 3) will be used. With default value, will be 127 | * generated 3 keys: before current time, current time and after. 128 | * The key will be compared with these n-window keys and if one of 129 | * them is equal to the key provided the function returns a non 130 | * negative number and 0 otherwise. 131 | * 132 | * @return Returns a non-negative number if the key is valid and 133 | * 0 otherwise. 134 | */ 135 | static int t2_verify_key(const uint8_t *b32_secret_key, int key, int window) 136 | { 137 | uint32_t ctime; /* Current time. */ 138 | int offset; /* Start window multiplier. */ 139 | ctime = cmos_read_unix_time(); 140 | 141 | /* Check window size. */ 142 | if (!window) 143 | window = T2_DEFAULT_WINDOW_SIZE; 144 | 145 | /* Window must be an odd number. */ 146 | if ( !(window & 1) ) 147 | return (-1); 148 | 149 | /* Check through all the keys given the window range. */ 150 | offset = -((window-1)/2); 151 | for (int i = 0; i < window; i++) 152 | { 153 | uint32_t iter_time = ctime + (offset * T2_KEY_INTERVAL); 154 | if (t2_get_key(b32_secret_key, iter_time) == key) 155 | return (1); 156 | 157 | offset++; 158 | } 159 | 160 | return (0); 161 | } 162 | 163 | #endif /* TINY2FA_H */ 164 | -------------------------------------------------------------------------------- /tools/2fa/types.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2023 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef TYPES 26 | #define TYPES 27 | 28 | /* 29 | * Pico 'stdint' and 'stddef' equivalent with the minimal 30 | * amount of typedefs in order to work without any 31 | * compiler headers. 32 | */ 33 | typedef char int8_t; 34 | typedef unsigned char uint8_t; 35 | typedef short int16_t; 36 | typedef unsigned short uint16_t; 37 | typedef int int32_t; 38 | typedef unsigned int uint32_t; 39 | typedef long long int64_t; 40 | typedef unsigned long long uint64_t; 41 | 42 | typedef uint32_t size_t; 43 | typedef int32_t ssize_t; 44 | 45 | #define NULL ((void *)0) 46 | 47 | #endif /* TYPES_H. */ 48 | -------------------------------------------------------------------------------- /tools/2fa/ui.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2023 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef UI_H 26 | #define UI_H 27 | 28 | #include "vga.h" 29 | #include "kbd.h" 30 | 31 | #define CENTER_X(sW,rW) (((sW)/2) - ((rW)/2)) 32 | #define CENTER_Y(sH,rH) (((sH)/2) - ((rH)/2)) 33 | 34 | /* Box Drawing Light characters. */ 35 | #define BDL_VERTICAL '\xb3' 36 | #define BDL_HORIZONTAL '\xc4' 37 | #define BDL_DOWNR '\xda' 38 | #define BDL_DOWNL '\xbf' 39 | #define BDL_UPR '\xc0' 40 | #define BDL_UPL '\xd9' 41 | 42 | /* Input buffer. */ 43 | static char input_buff[20 + 1]; 44 | static uint8_t input_counter; 45 | 46 | /** 47 | * @brief Draw a colored box of given @p width and @p height 48 | * containing @p draw_margin and @p title. 49 | * 50 | * @param width Box width (amount of columns). 51 | * @param height Box height (amount of rows). 52 | * @param sc Starting column (x). 53 | * @param sr Starting row (y). 54 | * @param draw_margin Whether to draw a margin or not. 55 | * @param title Optional title. 56 | */ 57 | static void ui_draw_box(int width, int height, int sc, int sr, 58 | uint8_t draw_margin, const char *title) 59 | { 60 | int r, c; 61 | 62 | /* Draw box. */ 63 | for (r = sr; r < height+sr; r++) 64 | for (c = sc; c < width+sc; c++) 65 | vga_put_char(c, r, ' '); 66 | 67 | if (!draw_margin) 68 | return; 69 | 70 | /* 71 | * Draw lines 72 | * - Vertical left 73 | * - Horizontal top 74 | * - Vertical right 75 | * - Horizontal bottom 76 | */ 77 | for (r = sr, c = sc; r < height + sr; r++) 78 | vga_put_char(c, r, BDL_VERTICAL); 79 | for (r = sr, c = sc; c < width + sc; c++) 80 | vga_put_char(c, r, BDL_HORIZONTAL); 81 | for (r = sr, c = sc + width - 1; r < height + sr; r++) 82 | vga_put_char(c, r, BDL_VERTICAL); 83 | for (r = sr + height - 1, c = sc; c < sc+width; c++) 84 | vga_put_char(c, r, BDL_HORIZONTAL); 85 | 86 | /* 87 | * Draw margin decorators: 88 | * - Upper left 89 | * - Bottom left 90 | * - Upper right 91 | * - Down right 92 | */ 93 | vga_put_char(sc + 0, sr + 0, BDL_DOWNR); 94 | vga_put_char(sc + width - 1, sr + height - 1, BDL_UPL); 95 | vga_put_char(sc + width - 1, sr + 0, BDL_DOWNL); 96 | vga_put_char(sc + 0, sr + height - 1, BDL_UPR); 97 | 98 | /* Draw title. */ 99 | if (title) 100 | vga_put_string(CENTER_X(80,strlen(title)), 101 | CENTER_Y(25,height), title); 102 | } 103 | 104 | /** 105 | * @brief Creates an input box and waits the user to type 106 | * something and hit ENTER. 107 | * 108 | * @param width Input box width (columns). 109 | * @param height Input box height (rows). 110 | * @param sc Starting column (x). 111 | * @param sr Starting row (y). 112 | * 113 | * @returns Returns the entered input when the user press 114 | * ENTER. 115 | */ 116 | static const char* ui_input_box(int width, int height, int sc, int sr) 117 | { 118 | uint8_t ch; 119 | 120 | vga_set_color(LIGHT_GRAY, BLACK); 121 | 122 | /* Draw box. */ 123 | ui_draw_box( 124 | width, height, 125 | sc, sr, 126 | 0, NULL 127 | ); 128 | 129 | /* Move cursor. */ 130 | vga_cursor_move(sc, sr); 131 | 132 | /* Empty buffer. */ 133 | memset(input_buff, 0, sizeof(input_buff)); 134 | input_counter = 0; 135 | 136 | while (1) 137 | { 138 | ch = kbd_read_char(); 139 | 140 | /* Skip if not letter or number. */ 141 | if ((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')) 142 | { 143 | /* 144 | * Buffer full, read again in the hope to empty 145 | * the buffer (via backspace). 146 | */ 147 | if (input_counter >= sizeof input_buff - 1) 148 | continue; 149 | 150 | vga_put_char(sc++, sr, ch); 151 | input_buff[input_counter++] = ch; 152 | } 153 | 154 | /* If some control key, check if ENTER or BACKSPACE. */ 155 | else if (ch == BACKSPACE) 156 | { 157 | /* Nothing to erase. */ 158 | if (!input_counter) 159 | continue; 160 | 161 | input_buff[--input_counter] = '\0'; 162 | vga_put_char(--sc, sr, ' '); 163 | } 164 | 165 | else if (ch == ENTER) 166 | { 167 | /* Nothing read. */ 168 | if (!input_counter) 169 | continue; 170 | 171 | return (input_buff); 172 | } 173 | 174 | vga_cursor_move(sc, sr); 175 | } 176 | } 177 | 178 | #endif /* UI_H. */ 179 | -------------------------------------------------------------------------------- /tools/2fa/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2023 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef UTIL_H 26 | #define UTIL_H 27 | 28 | #include "types.h" 29 | 30 | /** 31 | * @brief Copies @p n bytes from @p src to @p dest. 32 | * 33 | * @param dest Destination memory. 34 | * @param src Source memory. 35 | * @param n Amount of bytes. 36 | * 37 | * @return Returns a pointer to @p dest. 38 | */ 39 | static void *memcpy(void *dest, const void *src, size_t n) 40 | { 41 | char *d; /* Write pointer. */ 42 | const char* s; /* Read pointer. */ 43 | 44 | s = src; 45 | d = dest; 46 | 47 | while (n-- > 0) 48 | *d++ = *s++; 49 | 50 | return (dest); 51 | } 52 | 53 | /** 54 | * @brief Sets @p n bytes of memory in the address @p ptr 55 | * with the char @p c. 56 | * 57 | * @param ptr Destination memory. 58 | * @param c Fill char. 59 | * @param n Amount of bytes. 60 | */ 61 | static void *memset(void *ptr, int c, size_t n) 62 | { 63 | unsigned char *p; 64 | 65 | p = ptr; 66 | 67 | /* Set bytes. */ 68 | while (n-- > 0) 69 | *p++ = (unsigned char) c; 70 | 71 | return (ptr); 72 | } 73 | 74 | /** 75 | * @brief Gets the length of a given string pointed by 76 | * @p s. 77 | * 78 | * @param s Input string. 79 | * 80 | * @return Returns the string length. 81 | */ 82 | static size_t strlen(const char *s) 83 | { 84 | const char *p; 85 | 86 | /* Count the number of characters. */ 87 | for (p = s; *p != '\0'; p++); 88 | 89 | return (p - s); 90 | } 91 | 92 | /** 93 | * @brief Compares two strings, pointed by @p str1 and 94 | * @p str2. 95 | * 96 | * @param str1 First string. 97 | * @param str2 Second string. 98 | * 99 | * @return Zero if the strings are equal, and non-zero 100 | * otherwise. 101 | */ 102 | static int strcmp(const char *str1, const char *str2) 103 | { 104 | /* Compare strings. */ 105 | while (*str1 == *str2) 106 | { 107 | /* End of string. */ 108 | if (*str1 == '\0') 109 | return (0); 110 | 111 | str1++; 112 | str2++; 113 | } 114 | 115 | return (*str1 - *str2); 116 | } 117 | 118 | /** 119 | * @brief Converts the input integer to string. 120 | * 121 | * @param n Integer to be converted. 122 | * 123 | * @return Returned the integer converted. 124 | */ 125 | static char *int2str(int n) 126 | { 127 | static char buff[16]; 128 | char *s = buff, *t, tmp; 129 | int sign = 1; 130 | 131 | if (n < 0) 132 | { 133 | sign = -1; 134 | n = -n; 135 | } 136 | 137 | do 138 | { 139 | *s++ = (n % 10) + '0'; 140 | n /= 10; 141 | } while (n > 0); 142 | 143 | if (sign < 0) 144 | *s++ = '-'; 145 | 146 | *s-- = '\0'; 147 | t = buff; 148 | 149 | while (t < s) 150 | { 151 | tmp = *t; 152 | *t++ = *s; 153 | *s-- = tmp; 154 | } 155 | 156 | return buff; 157 | } 158 | 159 | /** 160 | * @brief Converts the input string to integer. 161 | * 162 | * @param str Input string to be converted. 163 | * 164 | * @return Returns the string converted. 165 | */ 166 | static int str2int(const char *str) 167 | { 168 | int n = 0; 169 | int sign = 1; 170 | 171 | if (*str == '-') 172 | { 173 | sign = -1; 174 | str++; 175 | } 176 | 177 | while (*str != '\0') 178 | { 179 | if (*str >= '0' && *str <= '9') 180 | { 181 | n = n * 10 + (*str - '0'); 182 | str++; 183 | } 184 | else 185 | break; 186 | } 187 | return sign * n; 188 | } 189 | 190 | #endif /* UI_H */ 191 | -------------------------------------------------------------------------------- /tools/2fa/vga.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2023 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef VGA_H 26 | #define VGA_H 27 | 28 | #include "types.h" 29 | #include "io.h" 30 | 31 | /* Video registers. */ 32 | #define VIDEO_CRTL_REG 0x3d4 /* Video control register. */ 33 | #define VIDEO_DATA_REG 0x3d5 /* Video data register. */ 34 | #define VIDEO_CLH 0x0e /* Cursor location high. */ 35 | #define VIDEO_CLL 0x0f /* Cursor location low. */ 36 | #define VIDEO_CS 0x0a /* Cursor start. */ 37 | #define VIDEO_CE 0x0b /* Cursor end. */ 38 | 39 | /* Video colors. */ 40 | enum VIDEO_COLOR { 41 | BLACK, BLUE, GREEN, CYAN, RED, MAGENTA, BROWN, LIGHT_GRAY, 42 | DARK_GRAY, LIGHT_BLUE, LIGHT_GREEN, LIGHT_CYAN, LIGHT_RED, 43 | LIGHT_MAGENTA, YELLOW, WHITE 44 | }; 45 | 46 | static uint8_t current_color; 47 | 48 | /** 49 | * @brief Set the current drawing color. 50 | * 51 | * @param bg Background color 52 | * @param fg Foreground (text) color. 53 | */ 54 | static void vga_set_color(uint8_t bg, uint8_t fg) { 55 | current_color = (bg << 4) | fg; 56 | } 57 | 58 | /** 59 | * @brief Set the current video mode to 80x25, mode 3 60 | */ 61 | static inline void vga_set_8025(void) 62 | { 63 | /* Set video mode: 80x25, 16-color text. */ 64 | asm volatile ( 65 | /* Set video mode. */ 66 | "mov $0x0003, %%ax \n" 67 | "int $0x10 \n" 68 | ::: "ax" 69 | ); 70 | } 71 | 72 | /** 73 | * @brief Moves the cursor to @p col and @p row. 74 | * 75 | * @param col Target column to move. 76 | * @param row Target row to move. 77 | */ 78 | static void vga_cursor_move(uint16_t col, uint16_t row) 79 | { 80 | uint16_t cpos = row*80 + col; 81 | outputb(VIDEO_CRTL_REG, VIDEO_CLH); 82 | outputb(VIDEO_DATA_REG, (uint8_t) ((cpos >> 8) & 0xFF)); 83 | outputb(VIDEO_CRTL_REG, VIDEO_CLL); 84 | outputb(VIDEO_DATA_REG, (uint8_t) (cpos & 0xFF)); 85 | } 86 | 87 | /** 88 | * @brief Disable cursor. 89 | */ 90 | static void vga_cursor_disable(void) 91 | { 92 | outputb(VIDEO_CRTL_REG, VIDEO_CS); 93 | outputb(VIDEO_DATA_REG, 0x20); 94 | } 95 | 96 | /** 97 | * @brief Enable cursor. 98 | */ 99 | static void vga_cursor_enable(void) 100 | { 101 | outputb(VIDEO_CRTL_REG, VIDEO_CS); 102 | outputb(VIDEO_DATA_REG, (inputb(VIDEO_DATA_REG) & 0xC0) | 0); 103 | outputb(VIDEO_CRTL_REG, VIDEO_CE); 104 | outputb(VIDEO_DATA_REG, (inputb(VIDEO_DATA_REG) & 0xE0) | 15); 105 | } 106 | 107 | /** 108 | * @brief Puts a single char into the screen. 109 | * 110 | * @param col Target column position. 111 | * @param row Target row position. 112 | * @param ch Character to be put. 113 | * 114 | * @note This routine is shamely not optimized, but this would 115 | * not be a huge problem, since this program is not 'video-heavy' 116 | * in any way (I hope...). 117 | */ 118 | static void vga_put_char(uint16_t col, uint16_t row, uint8_t ch) 119 | { 120 | uint16_t off = (row * 160) + (col * 2); 121 | asm volatile ( 122 | "push %%es\n" 123 | "mov $0xB800, %%bx\n" 124 | "mov %%bx, %%es\n" 125 | "mov %[off], %%bx\n" 126 | "mov %[ch], %%es:(%%bx)\n" 127 | "inc %%bx\n" 128 | "mov %[color], %%es:(%%bx)\n" 129 | "pop %%es\n" 130 | : /* out */ 131 | : [off] "d" (off), 132 | [ch] "a" (ch), 133 | [color] "c" (current_color) 134 | : "bx", "memory" 135 | ); 136 | } 137 | 138 | /** 139 | * @brief Puts a given string into the screen, in the coordinates 140 | * indicated by @p col and @p row. 141 | * 142 | * @param col Target column position. 143 | * @param row Target row position. 144 | * @param s Target string. 145 | * 146 | * @note Ideally, this routine should be asm-optimized too, 147 | * instead of relying on 'vga_put_char()', but I'm too lazy... 148 | */ 149 | static void vga_put_string(uint16_t col, uint16_t row, 150 | const char *s) 151 | { 152 | const char *p; 153 | uint16_t c = col; 154 | uint16_t r = row; 155 | 156 | for (p = s; *p != '\0'; p++) 157 | { 158 | if (*p == '\n') 159 | { 160 | r++, c = col; 161 | continue; 162 | } 163 | vga_put_char(c++, r, *p); 164 | } 165 | } 166 | 167 | #endif /* VGA_H */ 168 | -------------------------------------------------------------------------------- /tools/README.md: -------------------------------------------------------------------------------- 1 | # Tools 2 | Below is a list of tools that I developed throughout the BIOS reverse engineering process: 3 | 4 | ## [bread](https://github.com/Theldus/bread) 5 | BREAD (BIOS Reverse Engineering & Advanced Debugging) is an 'injectable' real-mode x86 6 | debugger that can debug arbitrary real-mode code (on real HW) from another PC via serial 7 | cable. 8 | 9 | ## [mdump](https://github.com/Theldus/AMI_BIOS_CodeInjection/tree/main/tools/mdump) 10 | `mdump` is a lightweight memory dump tool that can be used to dump the entire memory of 11 | a system over a serial cable. It is designed to be minimally invasive and can be booted 12 | or injected into a system to perform memory dumps. 13 | 14 | Example output: 15 | ```bash 16 | $ ./mdump /dev/ttyUSB0 output_file.bin $((4<<20)) 17 | Waiting to read 4194304 bytes... 18 | Device /dev/ttyUSB0 opened for reading... 19 | Dumping memory: [################################] done =) 20 | Received CRC-32: ce18133e 21 | Calculated CRC-32: ce18133e, match?: yes 22 | Checking output file... 23 | Success! 24 | ``` 25 | 26 | ## [find_gaps](https://github.com/Theldus/AMI_BIOS_CodeInjection/tree/main/tools/find_gaps) 27 | `find_gaps` is a small C utility that looks for gaps (sequences of 0xFF or 0x00) in a given 28 | file of at least the specified size. This is useful for finding potential spots in the ROM 29 | that can be used to inject code. 30 | 31 | Example output: 32 | ```bash 33 | $ ./find_gaps 34 | Usage: ./find_gaps 35 | 36 | $ ./find_gaps 0 5000 post.rom 37 | Found sequence at offset 180980, length 9479 bytes 38 | Found sequence at offset 307343, length 29557 bytes 39 | Found sequence at offset 336933, length 5779 bytes 40 | Found sequence at offset 345607, length 8691 bytes 41 | ``` 42 | 43 | ## [find_asm.sh](https://github.com/Theldus/AMI_BIOS_CodeInjection/blob/main/tools/find_asm.sh) 44 | `find_asm.sh` is a small shell script that searches for one (or more) assembly (16-bit) instructions 45 | for a given file. 46 | 47 | Example output: 48 | ```bash 49 | $ ./tools/find_asm.sh post.rom 50 | mov eax,0x80000002 51 | ^D # Press Ctrl+D to finish 52 | 53 | Offset 70934: 54 | 00000000 66B802000080 mov eax,0x80000002 55 | 00000006 0FA2 cpuid 56 | 00000008 E84300 call 0x4e 57 | 0000000B 66B803000080 mov eax,0x80000003 58 | 00000011 0FA2 cpuid 59 | 00000013 E83800 call 0x4e 60 | 00000016 66B804000080 mov eax,0x80000004 61 | 0000001C 0FA2 cpuid 62 | 0000001E E8 db 0xe8 63 | 0000001F 2D db 0x2d 64 | ``` 65 | 66 | ## [skip_string.sh](https://github.com/Theldus/AMI_BIOS_CodeInjection/blob/main/tools/skip_strings.sh) 67 | `skip_string.sh` is a small shell script that generates a 'skip list' for ndisasm using the strings 68 | command. This list contains the strings offsets that are then ignored by ndisasm. 69 | 70 | ## [sdtlist](https://github.com/Theldus/AMI_BIOS_CodeInjection/tree/main/tools/sdtlist) 71 | `sdtlist` is a tool that, given the 1B module of an AMIBIOS8 BIOS, lists all the 72 | SMM handlers (i.e., the SMM Dispatch Table) present in that module and their 73 | respective physical positions in the file. 74 | 75 | The main idea is to be able to explore the existing modules and modify them 76 | freely as needed. 77 | 78 | # Games 79 | Below is a list of games I made to run in the BIOS: 80 | 81 | ## [NIM](https://github.com/Theldus/AMI_BIOS_CodeInjection/tree/main/tools/nim) 82 | Nim is a BIOS game where each player takes turns removing sticks from a row. The game 83 | starts with a pre-determined number of sticks, and players can remove as many sticks as they like 84 | on their turn, but they must remove at least one. The player who removes the last stick loses 85 | the game. 86 | 87 | The computer will only fully boot if the player wins the game, which can be a difficult and 88 | time-consuming task. As a result, this game was made solely for entertainment purposes. 89 | 90 | Video: https://youtu.be/4SEeUinVLCQ 91 | 92 | ## [BIOS NIM](https://github.com/Theldus/AMI_BIOS_CodeInjection/tree/main/tools/bios_nim) 93 | BIOS Nim is a tiny-BIOS/bootblock (with emphasis on tiny) made to work on PCWare IPM41-D3/Intel 94 | DG41WV motherboards where its only purpose is: to run the NIM game via serial port, without RAM. 95 | 96 | Demo video: https://youtu.be/oCrixs5SdVk 97 | 98 | # Security 99 | Below is a list of tools focused on 'security': 100 | 101 | ## [2FA](https://github.com/Theldus/AMI_BIOS_CodeInjection/tree/main/tools/2fa) 102 | 2FA is a tool for two-factor authentication in BIOS via TOTP, so authentication is required when 103 | turning on the computer. 104 | 105 | Although for some it may seem useless, I really liked this idea and decided to go ahead: requiring 106 | a physical device (e.g., smartphone) to turn on the computer seemed like a good idea, and that's why 107 | I did it. It amazes me that this doesn't already exist on modern PCs. 108 | 109 | Video: https://youtu.be/ycLIlmRGP6M 110 | 111 | # Miscellaneous 112 | Below is a diverse list of random code that might be interesting/useful: 113 | 114 | ## [basic](https://github.com/Theldus/AMI_BIOS_CodeInjection/tree/main/tools/basic) 115 | BASIC is an attempt to bring the IBM (Cassette) BASIC 1.0 back to modern PCs as an OptionROM. 116 | Once injected into the computer, a new interrupt (`int 0xC8`) will be available, and when invoked, 117 | the original IBM BASIC will be executed. 118 | 119 | Video: https://youtu.be/ugK_S9CgUto 120 | -------------------------------------------------------------------------------- /tools/basic/.bochsrc.txt: -------------------------------------------------------------------------------- 1 | megs: 16 2 | cpu: model=p4_willamette 3 | romimage: file="/usr/share/bochs/BIOS-bochs-latest" 4 | vgaromimage: file="/usr/share/bochs/VGABIOS-lgpl-latest" 5 | optromimage1: file="pnp.rom", address=0xd0000 6 | #boot: a 7 | log: /dev/null 8 | mouse: enabled=0 9 | clock: sync=realtime, time0=utc 10 | display_library: x, options="gui_debug" 11 | magic_break: enabled=1 12 | floppya: 1_44="cassette_idt_h.img", status=inserted 13 | -------------------------------------------------------------------------------- /tools/basic/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | csum 3 | pnp.rom 4 | -------------------------------------------------------------------------------- /tools/basic/IBM_BASIC_C10.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Theldus/AMI_BIOS_CodeInjection/8b0dd523ba0b02d24bd20439dc1e32138fa3db7b/tools/basic/IBM_BASIC_C10.bin -------------------------------------------------------------------------------- /tools/basic/Makefile: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2023 Davidson Francis 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | BIN = csum pnp.rom 24 | 25 | .PHONY: all 26 | 27 | all: $(BIN) Makefile 28 | 29 | # Checksum code 30 | csum: csum.o 31 | 32 | # PNP rom 33 | pnp.rom: pnp.asm csum 34 | nasm -fbin pnp.asm -o pnp.rom 35 | ./csum pnp.rom 36 | 37 | # Run on Bochs and QEMU 38 | bochs: pnp.rom 39 | bochs -q -f .bochsrc.txt 40 | 41 | clean: 42 | $(RM) $(BIN) csum.o 43 | $(RM) bx_enh_dbg.ini 44 | -------------------------------------------------------------------------------- /tools/basic/README.md: -------------------------------------------------------------------------------- 1 | # IBM BASIC OptionROM 2 | This project is an attempt to bring IBM (Cassette) BASIC 1.0 back to 3 | modern PCs as an OptionROM. 4 | 5 | Once injected into the computer, a new interrupt (`int 0xC8`) will be 6 | available, and when invoked, the original IBM BASIC will be executed. 7 | 8 | Demo video: https://youtu.be/ugK_S9CgUto 9 | 10 | In the video above, the BASIC is running on the PCWare IPM41-D3 motherboard 11 | only if there are no bootable devices available, just like in the old 12 | IBM PCs =). 13 | 14 | ## How it works? 15 | Running IBM BASIC itself is not complicated; it simply waits to be invoked 16 | from `SEGMENT:0` (just like in the original IBM PC, which was at `F600:0000`). 17 | The segment number doesn't matter as long as the code is in real mode. 18 | 19 | The interesting part that this project brings involves the implementation 20 | of interrupts 21 | [15h AH=0, 1, 2, and 3](http://mirror.cs.msu.ru/oldlinux.org/Linux.old/docs/interrupts/int-html/int-15.htm), 22 | which are related to Cassette I/O. 23 | This means that it's possible to read/save BASIC programs just as it was 24 | done in the past, but this time using your hard drive, USB flash drive, 25 | floppy disk, and etc. 26 | 27 | From an implementation perspective, this was achieved using 28 | [int 13h AH=2 and 3](http://mirror.cs.msu.ru/oldlinux.org/Linux.old/docs/interrupts/int-html/int-13.htm). 29 | The challenge here was that int 15h (AH=2 and 3 as well) reads in terms 30 | of bytes, whereas int 13h reads in terms of 512-byte blocks. Therefore, 31 | aligning everything between the two interrupts was relatively laborious, 32 | but I believe it's now functional. 33 | 34 | ## Building 35 | To build, simply invoke 'make,' and the pnp.rom file will be generated, as in: 36 | 37 | ```bash 38 | $ make 39 | ``` 40 | 41 | However, please note that this ROM is for testing purposes. To generate something 42 | compatible with real hardware, edit `pnp.asm` and comment out the line `%define TEST`. 43 | -------------------------------------------------------------------------------- /tools/basic/cassette_idt_h.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Theldus/AMI_BIOS_CodeInjection/8b0dd523ba0b02d24bd20439dc1e32138fa3db7b/tools/basic/cassette_idt_h.img -------------------------------------------------------------------------------- /tools/basic/csum.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2023 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | /* Error and exit */ 35 | #define errx(...) \ 36 | do { \ 37 | fprintf(stderr, __VA_ARGS__); \ 38 | exit(1); \ 39 | } while (0) 40 | 41 | #define PNP_HDR_OFF 0x1A 42 | #define PNP_HDR_IDX 5 /* Header length index. */ 43 | #define PNP_CSUM_IDX 9 /* Checksum index. */ 44 | 45 | /** 46 | * @brief Calculates the entire file checksum and 47 | * saves it into the last byte. 48 | * 49 | * @param file File buffer. 50 | * @param size File size. 51 | */ 52 | static void file_chksum(uint8_t *file, size_t size) 53 | { 54 | uint8_t csum; 55 | 56 | /* Calculate file checksum. */ 57 | csum = 0; 58 | for (size_t i = 0; i < size - 1; i++) 59 | csum += file[i]; 60 | csum = 256 - csum; 61 | 62 | /* Put it to the last byte. */ 63 | file[size - 1] = csum; 64 | printf("File checksum: 0x%02x\n", csum); 65 | } 66 | 67 | /** 68 | * @brief Calculates the checksum for the PnP header 69 | * and saves it. 70 | * 71 | * @param file Opened file buffer. 72 | * @param size Total file size. 73 | */ 74 | static void pnp_chksum(uint8_t *file, size_t size) 75 | { 76 | off_t pnp_hdr_off; 77 | uint8_t *pnp_hdr; 78 | int hdr_len; 79 | uint8_t csum; 80 | 81 | /* No PnP hdr location. */ 82 | if (size < PNP_HDR_OFF + 1) 83 | return; 84 | 85 | pnp_hdr_off = 86 | ((uint16_t)file[PNP_HDR_OFF + 1] << 8) + 87 | file[PNP_HDR_OFF]; 88 | 89 | /* Check if pointed PnP header exists. */ 90 | if (size < pnp_hdr_off + PNP_CSUM_IDX) 91 | return; 92 | 93 | /* Check if there _is_ a PnP signature. */ 94 | if (strncmp(file + pnp_hdr_off, "$PnP", 4)) 95 | return; 96 | 97 | /* Get header length and check if it exists entirely. */ 98 | hdr_len = (int)file[pnp_hdr_off + PNP_HDR_IDX] * 16; 99 | if (size < pnp_hdr_off + hdr_len) 100 | errx("PnP header is broken!, aborting...\n"); 101 | 102 | /* Reset checksum field and calculate new checksum. */ 103 | pnp_hdr = file + pnp_hdr_off; 104 | pnp_hdr[PNP_CSUM_IDX] = 0; 105 | 106 | csum = 0; 107 | for (int i = 0; i < hdr_len; i++) 108 | csum += pnp_hdr[i]; 109 | csum = 256 - csum; 110 | 111 | /* Put the new checksum. */ 112 | pnp_hdr[PNP_CSUM_IDX] = csum; 113 | printf("PnP header checksum: 0x%02X\n", csum); 114 | } 115 | 116 | /** 117 | * Main 118 | */ 119 | int main(int argc, char **argv) 120 | { 121 | struct stat st = {0}; 122 | uint8_t *file; 123 | uint8_t csum; 124 | int fd; 125 | 126 | fd = open(argv[1], O_RDWR); 127 | if (fd < 0) 128 | errx("Unable to open file (%s)\n", argv[1]); 129 | 130 | fstat(fd, &st); 131 | file = mmap(0, st.st_size, PROT_READ|PROT_WRITE, 132 | MAP_SHARED, fd, 0); 133 | 134 | if (file == MAP_FAILED) 135 | errx("Failed to mmap file!\n"); 136 | 137 | /* Calculate PnP header checksum. */ 138 | pnp_chksum(file, st.st_size); 139 | 140 | /* Calculate file checksum. */ 141 | file_chksum(file, st.st_size); 142 | 143 | msync(file, st.st_size, MS_SYNC); 144 | munmap(file, st.st_size); 145 | close(fd); 146 | return (0); 147 | } 148 | -------------------------------------------------------------------------------- /tools/basic/pnp.asm: -------------------------------------------------------------------------------- 1 | ; MIT License 2 | ; 3 | ; Copyright (c) 2023 Davidson Francis 4 | ; 5 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 6 | ; of this software and associated documentation files (the "Software"), to deal 7 | ; in the Software without restriction, including without limitation the rights 8 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | ; copies of the Software, and to permit persons to whom the Software is 10 | ; furnished to do so, subject to the following conditions: 11 | ; 12 | ; The above copyright notice and this permission notice shall be included in all 13 | ; copies or substantial portions of the Software. 14 | ; 15 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | ; SOFTWARE. 22 | 23 | [BITS 16] 24 | [ORG 0] 25 | 26 | %define ROUND_SIZE(s) (((s) + (512)-1) & -(512)) 27 | %define dbg xchg bx, bx 28 | 29 | %macro delay_ms 1 30 | mov cx, (%1*1000)>>16 31 | mov dx, (%1*1000)&0xFFFF 32 | call delay 33 | %endmacro 34 | 35 | ; 36 | ; ROM header 37 | ; 38 | db 0x55, 0xAA ; ROM signature 39 | db ROM_SIZE_BLOCKS ; ROM size in blocks of 512-bytes 40 | jmp _init 41 | 42 | ; Comment/uncomment to enable/disable tests 43 | %define TEST 44 | 45 | %define BASIC_SEGMENT 0x7000 46 | %define WORK_SEGMENT BASIC_SEGMENT 47 | 48 | ; 49 | ; Drive number 50 | ; 0 = floppy a 51 | ; 1 = floppy b 52 | ; 80h=drive 0, 81h=drive 1.... 53 | ; 54 | ; USB Flash Drives also identify as 80h in my 55 | ; motherboard, so thats why I'm using it... 56 | ; 57 | %ifdef TEST 58 | %define DRIVE_NUMBER 0x0 59 | %define NEW_INT 0x18 60 | %else 61 | %define DRIVE_NUMBER 0x80 62 | %define NEW_INT 0xC8 63 | %endif 64 | 65 | %ifdef TEST 66 | %warning "Test build is enabled!" 67 | %else 68 | %warning "Test build is disabled!, this should work on real HW!" 69 | %endif 70 | 71 | ; 512 bytes of gap between ROM basic 72 | ; and anything else 73 | %define WORK_GAP 512 74 | 75 | ; Where it should store the CS:IP for the int15 76 | %define WORK_OFF_INT15_IP basicrom_size+WORK_GAP+0 ; 2 bytes (78200h) 77 | %define WORK_OFF_INT15_CS WORK_OFF_INT15_IP+2 ; 2 bytes (78202h) 78 | 79 | ; Where it should store the 512-bytes floppy sector 80 | %define WORK_OFF_FLOPPY_SEC WORK_OFF_INT15_CS+2 ; 512 bytes (78204h) 81 | 82 | ; Cassette read bytes pointer 83 | %define WORK_OFF_CASSETTE_POS WORK_OFF_FLOPPY_SEC+512 ; 2 bytes (78404h) 84 | 85 | ; Magic number 86 | %define WORK_MAGIC WORK_OFF_CASSETTE_POS+2 ; 4 bytes (78408h) 87 | 88 | ; 89 | ; Init routine 90 | ; 91 | ; Warning: 92 | ; As per PCI Local Bus specs, the memory *inside* the ROM 93 | ; is already copied to the RAM and *is* writable, but, 94 | ; if changed, the checksums must be recalculated. 95 | ; 96 | ; This strictly true if one wants to register a 'BEV' 97 | ; (as the headers will be parsed again). Otherwise, 98 | ; should be ok to change. 99 | ; 100 | ; As I want to register this as a 'bootable device', I have 101 | ; to follow the specs and *do not* change the memory here. 102 | ; 103 | _init: 104 | pusha ; Assuming we have stack 105 | pushf 106 | push ds 107 | 108 | ; 109 | ; Install our interrupt handler; 110 | ; 111 | ; This handler is the one responsible to copy 112 | ; the basic into elsewhere and then start from 113 | ; there. It also installs the int15 handler. 114 | ; 115 | ; This is necessary since the specs requires 116 | ; the OptionROM to be read-only after the _init 117 | ; call. 118 | ; 119 | xor ax, ax 120 | mov ds, ax 121 | mov ax, cs 122 | mov word [NEW_INT * 4 + 0], pre_basic_init 123 | mov word [NEW_INT * 4 + 2], ax 124 | 125 | pop ds 126 | popf 127 | popa 128 | retf 129 | 130 | ; 131 | ; Copy the BASIC ROM to the right place, configure 132 | ; some initial values and then jump to the BASIC code 133 | ; 134 | pre_basic_init: 135 | pusha ; Assuming we have stack 136 | pushf 137 | push ds 138 | push es 139 | 140 | ; Copy our BASIC to BASIC_SEGMENT:0000 141 | mov ax, cs 142 | mov ds, ax 143 | mov ax, BASIC_SEGMENT 144 | mov es, ax 145 | mov si, basicrom_bin 146 | mov di, 0 147 | 148 | mov cx, basicrom_size 149 | .copy_basic: 150 | lodsb 151 | stosb 152 | loop .copy_basic 153 | 154 | ; Backup original int15 handler 155 | xor ax, ax 156 | mov ds, ax 157 | mov word bx, [0x15 * 4 + 0] ; ip 158 | mov word cx, [0x15 * 4 + 2] ; cs 159 | mov word [es:WORK_OFF_INT15_IP], bx 160 | mov word [es:WORK_OFF_INT15_CS], cx 161 | 162 | ; Install our new int15 handler 163 | mov ax, cs 164 | mov word [0x15 * 4 + 0], int15_handler 165 | mov word [0x15 * 4 + 2], ax 166 | 167 | ; Set our sector initial pos to '0' 168 | mov word [es:WORK_OFF_CASSETTE_POS], 0 169 | 170 | ; Populate magic number 171 | mov dword [es:WORK_MAGIC], 0xdeadbeef 172 | 173 | ; Exit 174 | pop es 175 | pop ds 176 | popf 177 | popa 178 | 179 | ; Jump to BASIC 180 | jmp BASIC_SEGMENT:0 181 | 182 | ; 183 | ; Our hooked int15 handler 184 | ; 185 | int15_handler: 186 | ; 187 | ; Check if AH is between 0 and 3 (inclusive), if not, 188 | ; call the original int15 handler 189 | ; 190 | cmp ah, 0x0 191 | je .handle_cassette_ah0 192 | cmp ah, 0x1 193 | je .handle_cassette_ah1 194 | cmp ah, 0x2 195 | je .handle_cassette_ah2 196 | cmp ah, 0x3 197 | je .handle_cassette_ah3 198 | 199 | ; 200 | ; Not cassette, we should call the default handler 201 | ; 202 | 203 | ; Prepare to jump to original int15h 204 | push es 205 | 206 | push WORK_SEGMENT 207 | pop es 208 | push word [es:WORK_OFF_INT15_CS] 209 | push word [es:WORK_OFF_INT15_IP] 210 | 211 | push bp 212 | push ax 213 | 214 | ; 215 | ; Stack right now: 216 | ; 0 - AX 0-> AX -> IP -> IP 217 | ; 2 - BP 2-> BP -> IP -> CS 218 | ; 4 - IP 4-> IP -> CS 219 | ; 6 - CS -> 6-> IP -> __ 220 | ; 8 - ES -> 8-> CS -> __ 221 | ; 222 | 223 | ; Retrieve ES 224 | mov bp, sp 225 | mov word ax, [bp+8] 226 | mov es, ax 227 | ; Move CS into ES 228 | mov word ax, [bp+6] 229 | mov word [bp+8], ax 230 | ; Mov IP into CS 231 | mov word ax, [bp+4] 232 | mov word [bp+6], ax 233 | 234 | ; Retrieve Backup 235 | pop ax 236 | pop bp 237 | add sp, 2 238 | 239 | ; Jump to original int15h 240 | retf 241 | 242 | ; Turn on tape drive's motor (stub) 243 | .handle_cassette_ah0: 244 | clc ; CF = 0 == success 245 | iret 246 | 247 | ; Turn off tape drive's motor (stub) 248 | .handle_cassette_ah1: 249 | clc 250 | iret 251 | 252 | ; 253 | ; CASSETTE - READ DATA (PC and PCjr only) 254 | ; AH = 02h 255 | ; CX = number of bytes to read 256 | ; ES:BX -> buffer 257 | ; 258 | ; Return: 259 | ; CF = clear if successful 260 | ; DX = number of bytes read 261 | ; ES:BX -> byte following last byte read 262 | ; CF = set on error 263 | ; AH = status (see #00409) 264 | ; 265 | ; (Table 00409) 266 | ; Values for Cassette status: 267 | ; 00h successful 268 | ; 01h CRC error 269 | ; 02h bad tape signals 270 | ; 04h no data 271 | ; 80h invalid command 272 | ; 86h no cassette present 273 | ; 274 | .handle_cassette_ah2: 275 | %define OFF_CX_BCK 16 276 | %define OFF_DEST_BUFF_SEG 8 277 | %define OFF_DEST_BUFF_OFF 6 278 | %define OFF_AMNT_BYTES_REM 4 279 | %define OFF_START_SECTOR 2 280 | %define OFF_FIRST_SECTOR 0 281 | 282 | ; Backup 283 | push bp 284 | push cx 285 | push si 286 | push di 287 | push ds 288 | 289 | ; Working values 290 | push es ; original ES buffer 291 | push bx ; original BX buffer 292 | push cx ; amount of bytes remaining 293 | 294 | ; Calculate starting sector 295 | mov ax, WORK_SEGMENT 296 | mov es, ax 297 | mov ax, [es:WORK_OFF_CASSETTE_POS] 298 | mov bx, ax 299 | shr ax, 9 ; /512 300 | inc ax ; sector is 1-based 301 | push ax ; starting sector 302 | 303 | ; Calculate first-sector offset 304 | and bx, 511 305 | push bx 306 | 307 | ; Stack: 308 | ; bp[0] = first-sector offset 309 | ; bp[2] = sector number 310 | ; bp[4] = amout of bytes remaining 311 | ; bp[6] = BX buffer 312 | ; bp[8] = ES backup 313 | ; Backup: 314 | ; bp[10] = DS backup 315 | ; bp[12] = DI backup 316 | ; bp[14] = SI backup 317 | ; bp[16] = CX backup 318 | ; bp[18] = BP backup 319 | ; 320 | mov bp, sp 321 | 322 | .read_sector_loop: 323 | ; Dest segment 324 | mov ax, WORK_SEGMENT 325 | mov es, ax 326 | mov ds, ax ; Just in case an early return 327 | ; Dest offset 328 | mov bx, WORK_OFF_FLOPPY_SEC 329 | 330 | ; INT 13 331 | ; cylinder (ch) + sector number (cl) 332 | mov word cx, [ss:bp+OFF_START_SECTOR] 333 | mov dh, 0 ; head number 334 | mov dl, DRIVE_NUMBER 335 | mov ah, 2 336 | mov al, 1 ; read single sector 337 | int 0x13 338 | jc .done_error 339 | 340 | ; 341 | ; Check if we should start at beginning (or not) 342 | ; from the sector 343 | ; 344 | ; Obs: This is kind of dumb because... 345 | ; 346 | cmp word [ss:bp+OFF_FIRST_SECTOR], 0 347 | je .do_copy 348 | add word bx, [ss:bp+OFF_FIRST_SECTOR] 349 | mov word [ss:bp+OFF_FIRST_SECTOR], 0 350 | 351 | .do_copy: 352 | ; 353 | ; Copy up to 512 bytes into the true destination buffer 354 | ; 355 | 356 | ; Origin 357 | mov ax, WORK_SEGMENT 358 | mov ds, ax 359 | mov si, bx ; BX holds our current offset 360 | 361 | ; Dest buffer 362 | mov ax, [ss:bp+OFF_DEST_BUFF_SEG] ; ES 363 | mov es, ax 364 | mov ax, [ss:bp+OFF_DEST_BUFF_OFF] ; 'BX' 365 | mov di, ax 366 | 367 | ; 368 | ; Bytes remaining (BX) and sector size counter 369 | ; 370 | mov bx, word [ss:bp+OFF_AMNT_BYTES_REM] 371 | 372 | .copy_to_buf: 373 | ; If bytes remaining == 0 374 | cmp bx, 0 375 | je .done_success 376 | ; If already copied 512 bytes 377 | cmp si, 512+WORK_OFF_FLOPPY_SEC 378 | je .copy_to_buf_end 379 | 380 | lodsb ; source (tmp buffer, DS:SI) 381 | stosb ; dest (ES:DI, originally ES:BX) 382 | 383 | ; Update indexes 384 | dec bx ; Decrease bytes remaining 385 | jmp .copy_to_buf 386 | 387 | .copy_to_buf_end: 388 | ; Update dest buffer offset 389 | mov [ss:bp+OFF_DEST_BUFF_OFF], di 390 | ; Update bytes remaining 391 | mov [ss:bp+OFF_AMNT_BYTES_REM], bx 392 | ; Increase start sector 393 | inc word [ss:bp+OFF_START_SECTOR] 394 | jmp .read_sector_loop 395 | 396 | .done_error: 397 | stc 398 | mov ah, 0x4 ; no data 399 | .done_success: 400 | clc 401 | mov ah, 0 ; successful 402 | 403 | .done: 404 | ; 405 | ; Return values 406 | ; 407 | 408 | ; ES:BX with updated value 409 | ; (ES needs to be updated just in case its called 410 | ; right after int13) 411 | mov cx, [ss:bp+OFF_DEST_BUFF_SEG] 412 | mov es, cx 413 | mov bx, di 414 | 415 | ; Update our 'tape' position 416 | mov cx, [ss:bp+OFF_CX_BCK] ; original count value 417 | add [ds:WORK_OFF_CASSETTE_POS], cx 418 | 419 | ; Remove the 6 working values 420 | pushf ; Keep CF untouched (from CLC/STC) 421 | pop cx ; backup in CX 422 | add sp, 5*2 423 | push cx 424 | popf ; restore EFLAGS via CX =) 425 | 426 | ; Restore other values 427 | pop ds 428 | pop di 429 | pop si 430 | ; Amount of bytes read (just the same in the beginning) 431 | pop cx 432 | ; this is required to be returned in dx... 433 | mov dx, cx 434 | pop bp 435 | 436 | iret 437 | 438 | ; 439 | ; CASSETTE - WRITE DATA (PC and PCjr only) 440 | ; AH = 03h 441 | ; CX = number of bytes to write 442 | ; ES:BX -> data buffer 443 | 444 | ; Return: 445 | ; CF clear if successful 446 | ; ES:BX -> byte following last byte written 447 | ; CF set on error 448 | ; AH = status (see #00409) 449 | ; CX = 0000h 450 | ; 451 | .handle_cassette_ah3: 452 | ; Backup 453 | push bp 454 | push dx 455 | push si 456 | push di 457 | push ds 458 | 459 | ; Working values 460 | push bx ; original BX buffer 461 | push es ; original ES buffer 462 | push 0 ; dest BX 463 | push 0 ; dest ES 464 | push cx ; amount of bytes remaining 465 | 466 | ; Calculate current sector 467 | mov ax, WORK_SEGMENT 468 | mov es, ax 469 | mov ax, [es:WORK_OFF_CASSETTE_POS] 470 | mov bx, ax 471 | shr ax, 9 ; Current sector (/512, incremented by 1 later) 472 | push ax 473 | 474 | ; Calculate current offset (address) 475 | and bx, 511 476 | add bx, WORK_OFF_FLOPPY_SEC 477 | push bx 478 | 479 | %define STCK_ORIG_BX 12 480 | %define STCK_ORIG_ES 10 481 | %define STCK_DEST_BX 8 482 | %define STCK_DEST_ES 6 483 | %define STCK_BYTES_REM 4 484 | %define STCK_CURR_SECTOR 2 485 | %define STCK_CURR_OFF 0 486 | 487 | mov bp, sp 488 | .do_write_loop: 489 | cmp word [ss:bp+STCK_BYTES_REM], 0 490 | je .write_success 491 | 492 | inc word [ss:bp+STCK_CURR_SECTOR] 493 | 494 | ; Check if we can safely write an entire sector 495 | ; or we should to fill our tmp buffer first 496 | cmp word [ss:bp+STCK_BYTES_REM], 512 497 | jl .do_fill_read 498 | cmp word [ss:bp+STCK_CURR_OFF], 0 499 | jne .do_fill_read 500 | 501 | ; 502 | ; We can, prepare our values and jump 503 | ; 504 | mov ax, [ss:bp+STCK_ORIG_ES] 505 | mov [ss:bp+STCK_DEST_ES], ax 506 | mov ax, [ss:bp+STCK_ORIG_BX] 507 | mov [ss:bp+STCK_DEST_BX], ax 508 | 509 | ; Adjust counters and jump 510 | sub word [ss:bp+STCK_BYTES_REM], 512 511 | add word [ss:bp+STCK_ORIG_BX], 512 512 | add word [es:WORK_OFF_CASSETTE_POS], 512 513 | jmp .do_write_sector 514 | 515 | ; 516 | ; We cannot copy our current sector before reading 517 | ; it first and then filling it with our values 518 | ; 519 | .do_fill_read: 520 | ; Dest segment 521 | mov ax, WORK_SEGMENT 522 | mov es, ax 523 | ; Dest offset 524 | mov bx, WORK_OFF_FLOPPY_SEC 525 | 526 | ; INT 13 527 | ; cylinder (ch) + sector number (cl) 528 | mov word cx, [ss:bp+STCK_CURR_SECTOR] 529 | mov dh, 0 ; head number 530 | mov dl, DRIVE_NUMBER 531 | mov ah, 2 532 | mov al, 1 ; read single sector 533 | int 0x13 534 | jc .write_error 535 | 536 | ; source 537 | mov ax, [ss:bp+STCK_ORIG_ES] 538 | mov ds, ax 539 | mov si, [ss:bp+STCK_ORIG_BX] 540 | ; 541 | ; dest 542 | ; 543 | mov ax, WORK_SEGMENT 544 | mov es, ax 545 | mov di, [ss:bp+STCK_CURR_OFF] ; index, i / < 512 546 | 547 | ; indexes 548 | mov cx, [ss:bp+STCK_BYTES_REM] ; CX 549 | mov dx, [es:WORK_OFF_CASSETTE_POS] ; curr_amnt/tmp 550 | 551 | .do_fill: 552 | ; i < 512 / i.e., buff < buff_start+512 553 | cmp di, WORK_OFF_FLOPPY_SEC+512 554 | jge .do_fill_out 555 | ; cx <= 0 556 | cmp cx, 0 557 | jle .do_fill_out 558 | lodsb ; ES:BX orig, (DS:SI) 559 | stosb ; (ES:DI, tmp_buffer) 560 | dec cx ; bytes_rem-- 561 | inc dx ; curr_amnt++ 562 | jmp .do_fill 563 | .do_fill_out: 564 | ; Update values 565 | mov [ss:bp+STCK_BYTES_REM], cx 566 | mov [ss:bp+STCK_ORIG_BX], si 567 | mov [es:WORK_OFF_CASSETTE_POS], dx 568 | 569 | 570 | ; Reset current offset because the padding is 571 | ; already done 572 | mov word [ss:bp+STCK_CURR_OFF], WORK_OFF_FLOPPY_SEC 573 | 574 | ; Prepare 'ES:BX' to write the sector 575 | mov ax, WORK_SEGMENT 576 | mov [ss:bp+STCK_DEST_ES], ax 577 | mov word [ss:bp+STCK_DEST_BX], WORK_OFF_FLOPPY_SEC 578 | 579 | .do_write_sector: 580 | ; Buffer 581 | mov ax, [ss:bp+STCK_DEST_ES] 582 | mov es, ax 583 | mov bx, [ss:bp+STCK_DEST_BX] 584 | 585 | ; Write 586 | mov word cx, [ss:bp+STCK_CURR_SECTOR] 587 | mov dh, 0 ; head number 588 | mov dl, DRIVE_NUMBER 589 | mov ah, 3 ; Write disk sector 590 | mov al, 1 ; read single sector 591 | int 0x13 592 | jc .write_error 593 | 594 | jmp .do_write_loop 595 | 596 | .write_error: 597 | stc 598 | mov ax, 0x0400 ; no data 599 | .write_success: 600 | clc 601 | mov ax, 0x0000 ; success 602 | 603 | .write_done: 604 | ; 605 | ; Return values 606 | ; 607 | 608 | ; ES:BX with updated value 609 | mov cx, [ss:bp+STCK_ORIG_ES] 610 | mov es, cx 611 | mov bx, [ss:bp+STCK_ORIG_BX] 612 | 613 | ; Tape already updated 614 | 615 | ; Remove working values 616 | pushf ; Keep CF untouched 617 | pop cx ; backup in CX 618 | add sp, 7*2 619 | push cx 620 | popf ; restore EFLAGS via CX =) 621 | 622 | ; Restore remaining 623 | pop ds 624 | pop di 625 | pop si 626 | pop dx 627 | xor cx, cx ; CX must be 0 628 | pop bp 629 | 630 | iret 631 | 632 | ; =================== 633 | ; BASIC ROM here!!!! 634 | ; =================== 635 | basicrom_bin: 636 | incbin 'IBM_BASIC_C10.bin' 637 | basicrom_size: equ $-basicrom_bin 638 | 639 | ; Sizes 640 | ROM_SIZE equ $-$$ 641 | ROM_SIZE_ROUND equ ROUND_SIZE(ROM_SIZE) 642 | ROM_SIZE_BLOCKS equ ROM_SIZE_ROUND/512 643 | 644 | ; Padding 645 | times ROM_SIZE_ROUND-ROM_SIZE db 0 646 | -------------------------------------------------------------------------------- /tools/bios_nim/README.md: -------------------------------------------------------------------------------- 1 | # BIOS Nim 2 | BIOS Nim is a tiny-BIOS/bootblock (with emphasis on tiny) made to work 3 | on PCWare IPM41-D3/Intel DG41WV motherboards where its only purpose is: 4 | to run the NIM game via serial port, without RAM. 5 | 6 | Demo video: https://youtu.be/oCrixs5SdVk 7 | 8 | ## How it works? 9 | This game just initializes the very basics to work: 10 | - Talk to Southbridge (Intel® ICH7) to initialize LPC/Super I/O 11 | - Then talk to Super I/O (Winbond W83627DHG) to enable UART ports 12 | - Configure CAR (Cache-as-RAM) for data/stack 13 | 14 | ### About CAR 15 | Every x86 PC boots up without using any RAM: it is the responsibility 16 | of the BIOS to talk to the chipset and initialize it. 17 | 18 | At this _very_ early stage, all the code is loaded directly from the 19 | BIOS flash memory without using any RAM, and consequently, the code 20 | being limited to just using registers. 21 | 22 | Turns out, there is a feature in x86 processors that allows memory 23 | ranges to be mapped directly to the processor's cache memory 24 | (search for MTRRs and CAR, or Cache-as-RAM), be it L1, L2 or L3. 25 | With this memory mapped, you can then configure a stack, a data 26 | section, etc... it's even possible to jump to protected mode and 27 | run 32-bit C code, in your own BIOS, amazing isn't it? 28 | 29 | ## Building 30 | The code is divided into 2 files: `bbnim.asm` and `nim.asm`. The first 31 | is BIOS/bootblock and generates a 1M ROM ready to be flashed. The second 32 | is a bootable version, designed to be tested on a real machine/VM in an 33 | easy way. 34 | 35 | To compile, simply: 36 | ```bash 37 | $ nasm -fbin bbnim.asm -o nim.rom # ROM file 38 | # or 39 | $ nasm -fbin nim.asm -o bootable.bin # Bootable game 40 | ``` 41 | 42 | ## Good references 43 | - Must-read reference: [Intel SDM, Vol 3A: Ch 11, Memory Cache Control: MEMORY TYPE RANGE REGISTERS (MTRRS)](https://www.intel.com.br/content/www/br/pt/architecture-and-technology/64-ia-32-architectures-software-developer-vol-3a-part-1-manual.html) 44 | - A *very* awesome project that I wish more people knew about, I learned a lot from reading his src: [Universal BIOS Recovery console for x86 PCs](https://github.com/pbatard/ubrx/) 45 | - Cache-as-RAM very good insights: [What use is the INVD instruction?](https://stackoverflow.com/questions/41775371/what-use-is-the-invd-instruction), 46 | [Cache-as-Ram (no fill mode) Executable Code](https://stackoverflow.com/questions/27699197/cache-as-ram-no-fill-mode-executable-code), and [UBRX - L2 cache as instruction RAM (CAiR)](https://pete.akeo.ie/2011/08/ubrx-l2-cache-as-instruction-ram.html) 47 | - Excellent introductory material on BIOS crafting: [Crafting a BIOS from scratch](https://pete.akeo.ie/2011/06/crafting-bios-from-scratch.html) 48 | - Huge source of content: if Coreboot supports the motherboard you want to support, then it has the code you need to read: [Coreboot src](https://github.com/coreboot/coreboot) 49 | 50 | Also check out the [docs/](docs/) folder for some interesting PDFs on the subject. 51 | -------------------------------------------------------------------------------- /tools/bios_nim/defines.inc: -------------------------------------------------------------------------------- 1 | ; -------------------------------------------------------- 2 | ; Defines 3 | ; -------------------------------------------------------- 4 | 5 | %define dbg xchg bx, bx 6 | 7 | %define MB (1<<20) 8 | %define KB (1<<10) 9 | %define ROM_SIZE (1*MB) 10 | %define BB_SIZE (8*KB) 11 | %define FILL_BYTE (0x00) 12 | %define FILL(s) times (s)-($-$$) db (FILL_BYTE) 13 | 14 | ; Logical device selection 15 | %define SIO_LDSEL 0x07 16 | ; Multi-function pin selection 17 | %define SIO_MFPS 0x2C 18 | ; Register enable 19 | %define SIO_REG_EN 0x30 20 | ; Logical Device 2, UART A 21 | %define SIO_LD_UART_A 0x02 22 | ; Logical Device Address 23 | %define SIO_REG_ADDR 0x60 24 | 25 | ; PCI 26 | %define PCI_ADDR 0xCF8 27 | %define PCI_DATA 0xCFC 28 | %define ICH_FLAG_DECODE_LPC 0x30010010 29 | 30 | ; MTTR constants 31 | %define IA32_MTRR_DEF_TYPE 0x2FF 32 | %define IA32_MTRR_FIX4K_C0000 0x268 33 | 34 | ; 35 | ; UART constants 36 | ; 37 | %define UART_CLOCK_SIGNAL 1843200 38 | %define UART_BASE 0x3F8 39 | %define UART_BAUD 9600 ; set to 9600 if things go wrong 40 | %define UART_DIVISOR UART_CLOCK_SIGNAL / (UART_BAUD << 4) 41 | %define UART_RB UART_BASE + 0 ; Receiver Buffer (R). 42 | %define UART_IER UART_BASE + 1 ; Interrupt Enable Register (RW). 43 | %define UART_FCR UART_BASE + 2 ; FIFO Control Register (W). 44 | %define UART_LCR UART_BASE + 3 ; Line Control Register (RW). 45 | %define UART_MCR UART_BASE + 4 ; Modem Control Register (W). 46 | %define UART_LSR UART_BASE + 5 ; Line Status Register (R). 47 | ; Line Control Register values 48 | %define UART_LCR_DLA 0x80 ; Divisor Latch Access. 49 | %define UART_LCR_BPC_8 0x3 ; 8 bits per character. 50 | ; Modem Control Register values 51 | %define UART_MCR_OUT2 0x8 ; OUT2 pin 52 | %define UART_MCR_RTS 0x2 ; Request to Send 53 | %define UART_MCR_DTR 0x1 ; Data Terminal Ready 54 | ; Divisor register 55 | %define UART_DLB1 UART_BASE + 0 ; Divisor Latch LSB (RW). 56 | %define UART_DLB2 UART_BASE + 1 ; Divisor Latch MSB (RW). 57 | ; FIFO Control Register bits. 58 | %define UART_FCR_CLRRECV 0x1 ; Clear receiver FIFO. 59 | %define UART_FCR_CLRTMIT 0x2 ; Clear transmitter FIFO. 60 | ; FIFO Controle Register bit 7-6 values 61 | %define UART_FCR_TRIG_1 0x0 ; Trigger level 1-byte. 62 | ; Line status register 63 | %define UART_LSR_TFE 0x20 ; Transmitter FIFO Empty. 64 | 65 | 66 | ; -------------------------------------------------------- 67 | ; Macros 68 | ; -------------------------------------------------------- 69 | %macro ROM_CALL 1 70 | mov sp, $+6 ; 3 bytes from mov + 3 bytes from jump 71 | jmp near %1 ; force jump to be 3 bytes 72 | %endmacro 73 | 74 | %macro sio_enter_pnp 0 75 | nop 76 | mov dx, 0x2E 77 | mov al, 0x87 78 | out dx, al 79 | out dx, al 80 | %endmacro 81 | 82 | %macro sio_exit_pnp 0 83 | nop 84 | mov dx, 0x2E 85 | mov al, 0xAA 86 | out dx, al 87 | %endmacro 88 | 89 | %macro DEBUG 1 90 | mov bl, %1 91 | ROM_CALL early_uart_write_byte 92 | %endmacro 93 | 94 | %macro DEBUG_CALL 1 95 | mov al, %1 96 | call print_char 97 | %endmacro 98 | 99 | ; 100 | ; Params 101 | ; first: reg 102 | ; second: value 103 | ; 104 | %macro sio_write_value_to_reg 2 105 | nop 106 | mov dx, 0x2E 107 | mov al, %1 ; reg 108 | out dx, al 109 | mov dx, 0x2F 110 | mov ax, %2 ; value 111 | out dx, al 112 | %endmacro 113 | 114 | ; 115 | ; 116 | ; 117 | %macro sio_select_logical_device 1 118 | sio_write_value_to_reg SIO_LDSEL, %1 119 | %endmacro 120 | 121 | %macro sio_set_enable 1 122 | sio_write_value_to_reg SIO_REG_EN, %1 123 | %endmacro 124 | 125 | ; 126 | ; Params: 127 | ; first: index 128 | ; second: iobase (16-bit) 129 | ; 130 | %macro sio_set_iobase 2 131 | sio_write_value_to_reg %1+0, ((%2 >> 8) & 0xFF) 132 | sio_write_value_to_reg %1+1, (%2 & 0xFF) 133 | %endmacro 134 | 135 | ; 136 | ; Read from an I/O into al 137 | ; Parameters: 138 | ; first: I/O port 139 | ; 140 | %macro inputb 1 141 | mov dx, %1 142 | in al, dx 143 | %endmacro 144 | 145 | ; 146 | ; Output a byte to an I/O port 147 | ; Parameters: 148 | ; first: I/O port 149 | ; second: byte 150 | ; 151 | %macro outbyte 2 152 | mov ax, %2 153 | mov dx, %1 154 | out dx, al 155 | %endmacro 156 | 157 | ; 158 | ; Output a dword value (32-bit) into some PCI 159 | ; device specified in the bus,device,function 160 | ; and offset 161 | ; 162 | ; Parameters: 163 | ; 1st: bus 164 | ; 2nd: device 165 | ; 3rd: function 166 | ; 4th: offset 167 | ; 5th: data 168 | ; 169 | %macro pci_out_dword 5 170 | mov eax, 0x80000000 | (%1 << 24) | (%2 << 11) | (%3 << 8) | %4 171 | mov dx, PCI_ADDR 172 | out dx, eax 173 | mov dx, PCI_DATA 174 | mov eax, %5 175 | out dx, eax 176 | %endmacro 177 | -------------------------------------------------------------------------------- /tools/bios_nim/docs/LBCar.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Theldus/AMI_BIOS_CodeInjection/8b0dd523ba0b02d24bd20439dc1e32138fa3db7b/tools/bios_nim/docs/LBCar.pdf -------------------------------------------------------------------------------- /tools/bios_nim/docs/Oded Stanford Annual Forum 2014.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Theldus/AMI_BIOS_CodeInjection/8b0dd523ba0b02d24bd20439dc1e32138fa3db7b/tools/bios_nim/docs/Oded Stanford Annual Forum 2014.pdf -------------------------------------------------------------------------------- /tools/bios_nim/docs/cache_as_ram_lb_09142006.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Theldus/AMI_BIOS_CodeInjection/8b0dd523ba0b02d24bd20439dc1e32138fa3db7b/tools/bios_nim/docs/cache_as_ram_lb_09142006.pdf -------------------------------------------------------------------------------- /tools/bios_nim/nim.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; MIT License 3 | ; 4 | ; Copyright (c) 2021-23 Davidson Francis 5 | ; 6 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 7 | ; of this software and associated documentation files (the "Software"), to deal 8 | ; in the Software without restriction, including without limitation the rights 9 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | ; copies of the Software, and to permit persons to whom the Software is 11 | ; furnished to do so, subject to the following conditions: 12 | ; 13 | ; The above copyright notice and this permission notice shall be included in all 14 | ; copies or substantial portions of the Software. 15 | ; 16 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | ; SOFTWARE. 23 | ; 24 | 25 | [BITS 16] 26 | section BOOTSECTOR start=0 27 | 28 | %include "defines.inc" 29 | 30 | boot_init: 31 | cli 32 | 33 | ; Read 10 sectors 34 | mov ax, 0x1000 ; segment 35 | mov es, ax 36 | mov bx, 0x0000 ; offset addr 37 | mov al, 10 ; num sectors to read 38 | mov cl, 2 ; from sector 2 (1-based) 39 | call read_sectors 40 | 41 | ; Far call to our module 42 | call 0x1000:0x0000 43 | 44 | ; Hang 45 | jmp $ 46 | 47 | read_sectors: 48 | mov ah, 2 49 | mov ch, 0 50 | mov dh, 0 51 | int 0x13 52 | jc .again 53 | ret 54 | .again: 55 | xor ax, ax 56 | int 0x13 57 | jmp read_sectors 58 | ret 59 | 60 | times 510-($-$$) db 0 61 | dw 0xAA55 62 | 63 | ; 64 | ; Section game text 65 | ; 66 | 67 | section GAME_TEXT follows=BOOTSECTOR vstart=0 68 | 69 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 70 | ; System Helpers & Boot stuff # 71 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 72 | 73 | ; Backup registers and flags 74 | _start: 75 | ; Define proper segment registers 76 | mov ax, cs 77 | mov ds, ax 78 | mov es, ax 79 | mov fs, ax 80 | 81 | ; Stack 82 | mov ax, 0x2000 83 | mov ss, ax 84 | mov sp, 0xFFF 85 | 86 | ; 87 | ; Configure serial 88 | ; 89 | ; Calculate and set divisor 90 | outbyte UART_LCR, UART_LCR_DLA 91 | outbyte UART_DLB1, UART_DIVISOR & 0xFF 92 | outbyte UART_DLB2, UART_DIVISOR >> 8 93 | ; Set line control register: 94 | outbyte UART_LCR, UART_LCR_BPC_8 95 | ; Reset FIFOs and set trigger level to 1 byte. 96 | outbyte UART_FCR, UART_FCR_CLRRECV|UART_FCR_CLRTMIT|UART_FCR_TRIG_1 97 | ; IRQs enabled, RTS/DSR set 98 | outbyte UART_MCR, UART_MCR_OUT2|UART_MCR_RTS|UART_MCR_DTR 99 | 100 | ; Hide cursor 101 | mov si, str_hide_cursor 102 | call print 103 | 104 | ; Reset data 105 | call reset_data 106 | 107 | ; Jump to game 108 | jmp main_screen 109 | 110 | ; ---------------------------------------------------- 111 | ; reset_data: Reset all game data at the beginning 112 | ; ---------------------------------------------------- 113 | reset_data: 114 | mov byte ss:[curr_turn], 0xF 115 | mov byte ss:[sticks_count], 16 116 | mov si, sticks 117 | mov word ss:[si+0], 1 118 | mov word ss:[si+2], 3 119 | mov word ss:[si+4], 5 120 | mov word ss:[si+6], 7 121 | ret 122 | 123 | ; ---------------------------------------------------- 124 | ; read_number: Read a number with the arrow keys (up 125 | ; and down) from 0 to 7, Enter selects the number. 126 | ; 127 | ; Returns: 128 | ; cx: Number read. 129 | ; ---------------------------------------------------- 130 | read_number: 131 | call read_kbd 132 | cmp al, KBD_UP 133 | jne .l1 134 | add cx, 1 135 | and cx, 7 136 | jmp .l3 137 | .l1: 138 | cmp al, KBD_DOWN 139 | jne .l2 140 | sub cx, 1 141 | and cx, 7 142 | jmp .l3 143 | .l2: 144 | cmp al, KBD_ENTER 145 | jne read_number 146 | jmp .l4 147 | .l3: 148 | ; Move cursor to left 149 | mov si, str_cursor_back 150 | call print 151 | 152 | ; Draw again (overwriting the previous) 153 | mov al, cl 154 | add al, '0' 155 | call print_char 156 | 157 | jmp read_number 158 | .l4: 159 | ret 160 | 161 | ; --------------------------------------------- 162 | ; read_kbd: Reads a keypress from the keyboard 163 | ; 164 | ; This routine uses a polling approach because 165 | ; the BIOS at this stage does not have keyboard 166 | ; ints yet! (and I'm too lazy to setup one) 167 | ; 168 | ; Returns: 169 | ; ah: Scan code 170 | ; al: ASCII Character code 171 | ; --------------------------------------------- 172 | read_kbd: 173 | push dx 174 | .loop: 175 | inputb UART_LSR 176 | bt ax, 0 177 | jnc .loop 178 | inputb UART_RB ; WARN: Do not handle ANSI escape sequences 179 | pop dx 180 | ret 181 | 182 | ; --------------------------------------- 183 | ; print: Print a string to screen 184 | ; 185 | ; Parameters: 186 | ; si: pointer to string 187 | ; --------------------------------------- 188 | print: 189 | .loop: 190 | lodsb 191 | cmp al, 0 192 | je .done 193 | call print_char 194 | jmp .loop 195 | .done: 196 | ret 197 | 198 | ; --------------------------------------------- 199 | ; print_char: Print a character to the screen 200 | ; 201 | ; Parameters: 202 | ; al: character 203 | ; --------------------------------------------- 204 | print_char: 205 | push bx 206 | push dx 207 | 208 | mov bl, al 209 | mov dx, UART_LSR 210 | .loop: 211 | in al, dx 212 | and al, UART_LSR_TFE 213 | cmp al, 0 214 | je .loop 215 | mov dx, UART_BASE 216 | mov al, bl 217 | out dx, al 218 | 219 | pop dx 220 | pop bx 221 | ret 222 | 223 | ; --------------------------------------------- 224 | ; clear_screen: Clear the screen 225 | ; --------------------------------------------- 226 | clear_screen: 227 | mov si, str_clear_screen 228 | call print 229 | ret 230 | 231 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 232 | ; Game helpers # 233 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 234 | 235 | ; -------------------------------------------- 236 | ; draw_title: Draw game title 237 | ; -------------------------------------------- 238 | draw_title: 239 | call clear_screen 240 | mov si, str_game_title 241 | call print 242 | ret 243 | 244 | ; -------------------------------------------- 245 | ; draw_sticks: Draw the sticks remaining 246 | ; -------------------------------------------- 247 | draw_sticks: 248 | xor cx, cx 249 | .l1: 250 | mov al, cl 251 | add al, 49 252 | call print_char 253 | mov si, str_stick_ddots 254 | call print 255 | mov si, cx 256 | shl si, 1 257 | add si, sticks 258 | mov si, ss:[si] 259 | cmp si, 0 260 | je .l2 261 | sub si, 1 262 | shl si, 1 263 | add si, str_sticks_vec 264 | mov si, [si] ; sticks vec is a constant in code seg! 265 | mov dx, si 266 | call print 267 | mov si, dx 268 | call print 269 | mov si, dx 270 | call print 271 | .l2: 272 | add cl, 1 273 | cmp cl, 4 274 | jl .l1 275 | ret 276 | 277 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 278 | ; Game entry point # 279 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 280 | 281 | ; 282 | ; Redraw the options 283 | ; 284 | draw_options: 285 | mov si, str_cursor_up_2x 286 | call print 287 | 288 | ; Check pos 289 | cmp dx, 0 290 | jne .arrow_me 291 | 292 | ; Arrow YOU 293 | .arrow_you: 294 | mov si, str_arrow 295 | call print 296 | mov si, str_you 297 | call print 298 | mov si, str_empty_space 299 | call print 300 | mov si, str_me 301 | call print 302 | jmp .out_drawing 303 | .arrow_me: 304 | mov si, str_empty_space 305 | call print 306 | mov si, str_you 307 | call print 308 | mov si, str_arrow 309 | call print 310 | mov si, str_me 311 | call print 312 | .out_drawing: 313 | ret 314 | 315 | ; 316 | ; Main screen, decides who starts to play 317 | ; 318 | main_screen: 319 | xor dx, dx 320 | 321 | call draw_title 322 | mov si, str_game_explanation 323 | call print 324 | 325 | mov si, str_who_starts 326 | call print 327 | 328 | call draw_options 329 | 330 | ; Reads who starts 331 | .who_starts: 332 | call read_kbd 333 | cmp al, KBD_UP 334 | jne .l1 335 | sub dx, 1 336 | and dx, 1 337 | jmp .l3 338 | .l1: 339 | cmp al, KBD_DOWN 340 | jne .l2 341 | add dx, 1 342 | and dx, 1 343 | jmp .l3 344 | .l2: 345 | cmp al, KBD_ENTER 346 | jne .who_starts 347 | jmp .out_who_starts 348 | .l3: 349 | ; Update options on screen 350 | call draw_options 351 | jmp .who_starts 352 | 353 | .out_who_starts: 354 | ; Save current turn 355 | mov ss:[curr_turn], dl 356 | 357 | game_loop: 358 | mov dl, ss:[sticks_count] 359 | cmp dl, 0 360 | jle game_loop_end 361 | call think 362 | jmp game_loop 363 | game_loop_end: 364 | 365 | ; Elects who win 366 | call draw_title 367 | mov dl, ss:[curr_turn] 368 | lose: 369 | cmp dl, COMPUTER_TURN 370 | jne win 371 | mov si, str_lose 372 | call print 373 | jmp wait_key 374 | win: 375 | mov si, str_win 376 | call print 377 | wait_key: 378 | mov si, str_play_again 379 | call print 380 | call read_kbd 381 | 382 | ; Reset everything 383 | call reset_data 384 | 385 | ; Start again 386 | call clear_screen 387 | jmp main_screen 388 | 389 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 390 | ; PC and Player movements # 391 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 392 | ; -------------------------------------------- 393 | ; think: PC and Player movements 394 | ; -------------------------------------------- 395 | think: 396 | call draw_title 397 | call draw_sticks 398 | 399 | .computer_turn: 400 | mov dl, ss:[curr_turn] 401 | cmp dl, COMPUTER_TURN 402 | jne .player_turn 403 | 404 | mov si, str_think_pc 405 | call print 406 | call read_kbd 407 | 408 | ; Count amnt of heaps > 1 409 | xor cx, cx 410 | xor dx, dx 411 | mov si, sticks 412 | .count_sticks: 413 | cmp word ss:[si+0], 1 414 | jg .sticks_gt_1 415 | jmp .sticks_lte_1 416 | .sticks_gt_1: 417 | add dx, 1 418 | .sticks_lte_1: 419 | add cx, 1 420 | add si, 2 421 | cmp cx, 4 422 | jl .count_sticks 423 | push dx 424 | mov bp, sp 425 | 426 | .nim_sum: 427 | ; Calculate NIM sum 428 | mov si, sticks 429 | mov dx, ss:[si+0] 430 | xor dx, ss:[si+2] 431 | xor dx, ss:[si+4] 432 | xor dx, ss:[si+6] 433 | 434 | ; Check if there is a good movement or not 435 | cmp dx, 0 436 | jne .recalculate_nim_sum 437 | 438 | .only_one: 439 | ; 440 | ; Too bad, if the player keeps playing like this the 441 | ; computer will lose. 442 | ; 443 | xor cx, cx 444 | .l1: 445 | mov ax, ss:[si] 446 | cmp ax, 0 447 | jne .l2 448 | add cx, 1 449 | add si, 2 450 | cmp cx, 4 451 | jl .l1 452 | .l2: 453 | sub word ss:[si], 1 454 | sub byte ss:[sticks_count], 1 455 | jmp .finish 456 | 457 | ; 458 | ; If NIM sum differs from 0, great, we *will* win. We just 459 | ; need to recalculate the amount of pieces. 460 | ; 461 | .recalculate_nim_sum: 462 | xor cx, cx 463 | mov si, sticks 464 | .recalc_loop: 465 | mov ax, ss:[si] 466 | mov bx, ax 467 | xor bx, dx ; bx = p ^ nimsum, ax = p, dx = nimsum 468 | cmp bx, ax 469 | jl .recalc_end 470 | add cx, 1 471 | add si, 2 472 | cmp cx, 4 473 | jl .recalc_loop 474 | .recalc_end: 475 | ; 476 | ; We have 2 options here: 477 | ; a) More than one heap with more than one stick 478 | ; b) Exactly one heap with more than one stick 479 | ; 480 | ; The option b) do not conforms with the maths as 481 | ; expected and we fall in a case were we remove 482 | ; n or n-1 sticks from that column. So is necessary 483 | ; to bifurcate these two scenarios here. 484 | ; 485 | cmp word [bp], 1 486 | jne .scenario_a 487 | 488 | .scenario_b: 489 | mov bl, ss:[sticks_count] 490 | sub bl, al 491 | mov ax, bx 492 | 493 | ; 494 | ; We need to leave a odd numbers of stickers to the player 495 | ; If odd: remove all the sticks from the line 496 | ; If even: remove all but one sticks from the line 497 | ; 498 | and al, 1 499 | cmp al, 0 500 | je .even 501 | .odd: 502 | mov word ss:[si], 0 503 | mov ss:[sticks_count], bl 504 | jmp .finish 505 | .even: 506 | mov word ss:[si], 1 507 | add bl, 1 508 | mov ss:[sticks_count], bl 509 | jmp .finish 510 | 511 | .scenario_a: 512 | ; Now we proceed as usual 513 | mov ss:[si], bx 514 | sub ax, bx 515 | sub ss:[sticks_count], ax 516 | 517 | ; Set turn to player 518 | .finish: 519 | mov byte ss:[curr_turn], PLAYER_TURN 520 | add sp, 2 521 | ret 522 | 523 | ; 524 | ; Player turn 525 | ; 526 | .player_turn: 527 | mov si, str_think_askrow 528 | call print 529 | 530 | ; Read rows 531 | .read_rows: 532 | mov cx, 1 533 | mov al, '1' 534 | call print_char 535 | .invalid_row: 536 | call read_number 537 | ; Check if selected row is valid 538 | cmp cx, 0 539 | je .invalid_row 540 | cmp cx, 4 541 | jg .invalid_row 542 | ; Check if selected row is not empty 543 | mov si, cx 544 | sub si, 1 545 | shl si, 1 546 | add si, sticks 547 | mov si, ss:[si] 548 | cmp si, 0 549 | je .invalid_row 550 | sub cx, 1 ; make row 0 based 551 | push cx 552 | mov bp, sp 553 | 554 | ; Read sticks amount 555 | .read_amnt: 556 | mov si, str_newline 557 | call print 558 | mov si, str_think_askstick 559 | call print 560 | mov cx, 1 561 | mov al, '1' 562 | call print_char 563 | .invalid_amnt: 564 | call read_number 565 | ; Check if selected amount is valid 566 | cmp cx, 0 567 | je .invalid_amnt 568 | ; Check if selected amount not exceeds the sticks amnt 569 | mov dx, [bp] 570 | mov si, dx 571 | shl si, 1 572 | add si, sticks 573 | mov si, ss:[si] 574 | cmp cx, si 575 | jg .invalid_amnt 576 | 577 | ; Decrement the row sticks 578 | mov dx, [bp] 579 | mov si, dx 580 | shl si, 1 581 | add si, sticks 582 | sub ss:[si], cx 583 | 584 | ; Decrement sticks count 585 | sub ss:[sticks_count], cx 586 | 587 | ; Set turn to computer 588 | mov byte ss:[curr_turn], COMPUTER_TURN 589 | 590 | ; 'Pop' cx and return 591 | add sp, 2 592 | ret 593 | 594 | ; 595 | ; Strings 596 | ; 597 | str_hide_cursor: db 0x1B, '[?25l', 0 598 | str_clear_screen: db 0x1B, '[2J', 0x1B, '[H', 0 ; Clear & jump to top (0,0) 599 | str_cursor_back: db 0x1B, '[D', 0 ; Cursor backwards 600 | str_cursor_up_2x: db 0x1B, '[2A', 0 ; Cursor upwards two times 601 | 602 | str_game_title: 603 | db ' _______ _______ _______ ', 10,13 604 | db '| | ||_ _|| | |', 10,13 605 | db '| | _| |_ | |', 10,13 606 | db '|__|____||_______||__|_|__| by Theldus', 10,13, 10,13, 0 607 | 608 | str_game_explanation: 609 | db 'The NIM game consists of removing the sticks from the table the amount you', 10,13 610 | db 'want, a single row at a time. The last to remove the sticks LOSES!!', 10,13 611 | db 'Good luck!!!', 10,13, 10,13, 0 612 | 613 | str_who_starts: 614 | db 'Who starts? (UP - k/DOWN - j, Enter select):', 10,13, 10,13, 10,13, 0 615 | str_you: 616 | db 'You', 10,13, 0 617 | str_me: 618 | db 'Me', 10,13, 0 619 | str_arrow: 620 | db '-> ', 0 621 | str_empty_space: 622 | db ' ', 0 623 | 624 | str_stick_ddots: 625 | db ': ', 10,13, 0 626 | str_stick_1: db ' ##', 10,13, 0 627 | str_stick_2: db ' ## ##', 10,13, 0 628 | str_stick_3: db ' ## ## ##', 10,13, 0 629 | str_stick_4: db ' ## ## ## ##', 10,13, 0 630 | str_stick_5: db ' ## ## ## ## ##', 10,13, 0 631 | str_stick_6: db ' ## ## ## ## ## ##', 10,13, 0 632 | str_stick_7: db ' ## ## ## ## ## ## ##', 10,13, 0 633 | str_sticks_vec: 634 | dw str_stick_1, str_stick_2, str_stick_3, str_stick_4 635 | dw str_stick_5, str_stick_6, str_stick_7 636 | 637 | str_think_pc: 638 | db 10,13, 'Thats my turn, can I play? (press ENTER) ', 0 639 | str_think_askrow: 640 | db 10,13, 'Choose the row you want (Up - k/Down - j, Enter to select): ', 0 641 | str_think_askstick: 642 | db 'How many sticks you want to remove? (Up - k/Down - j, Enter to select): ', 0 643 | 644 | str_newline: 645 | db 10,13, 0 646 | 647 | str_lose: 648 | db ' @@@ @@@ @@@@@@ @@@ @@@ @@@ @@@@@@ @@@@@@ @@@@@@@@', 10,13 649 | db ' @@! !@@ @@! @@@ @@! @@@ @@! @@! @@@ !@@ @@!', 10,13 650 | db ' !@!@! @!@ !@! @!@ !@! @!! @!@ !@! !@@!! @!!!:!', 10,13 651 | db ' !!: !!: !!! !!: !!! !!: !!: !!! !:! !!:', 10,13 652 | db ' .: : :. : :.:: : : ::.: : : :. : ::.: : : :: :::', 10,13, 10,13, 0 653 | str_win: 654 | db ' __ __ ___ __ __ __ __ ____ ____ ', 10,13 655 | db ` | | |/ \\| | | | |__| | | \\ `, 10,13 656 | db ` | | | | | | | | | || || _ |`, 10,13 657 | db ` | ~ | O | | | | | | || || | |`, 10,13 658 | db ` |___, | | : | | \` || || | |`, 10,13 659 | db ` | | | | \\ / | || | |`, 10,13 660 | db ` |____/ \\___/ \\__,_| \\_/\\_/ |____|__|__|`, 10,13,10,13, 0 661 | str_play_again: 662 | db ' =-=- Press ENTER to play again -=-=', 0 663 | 664 | ; 665 | ; Section game data 666 | ; (Our data should be located in the same place as our stack 667 | ; because the code section is not writtable!) 668 | ; 669 | 670 | section GAME_DATA follows=GAME_TEXT vstart=0 671 | 672 | curr_turn: db 0 ; default = 0xF 673 | sticks_count: db 0 ; default = 16 674 | sticks: dw 0,0,0,0 ; default = 1,3,5,7 675 | 676 | ; 677 | ; Constants 678 | ; 679 | 680 | ; Game 681 | PLAYER_TURN equ 0 682 | COMPUTER_TURN equ 1 683 | 684 | ; Keyboard scan codes 685 | KBD_UP equ 'k' 686 | KBD_DOWN equ 'j' 687 | KBD_ENTER equ 13 ; Carriage Return 688 | -------------------------------------------------------------------------------- /tools/find_asm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # MIT License 4 | # 5 | # Copyright (c) 2023 Davidson Francis 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | # Default amount of bytes to dump 26 | DUMP_BYTES=32 27 | 28 | # Help function 29 | show_help() { 30 | echo "Usage: $0 [-h] [-d ] " 31 | echo " -h Display this help message" 32 | echo " -d Amount of bytes to dump (default is $DUMP_BYTES)" 33 | echo " input_file Input file name" 34 | echo "" 35 | echo "Please note that amount of bytes to dump is only as estimation" 36 | echo "and the final instructions of the dump might be wrongly" 37 | echo "disassembled! To ensure the right dissassembling, adjust the" 38 | echo "'-d' parameter accordingly to your expectations." 39 | exit 0 40 | } 41 | 42 | # Parse parameters 43 | while getopts "hd:" opt; do 44 | case $opt in 45 | h) show_help ;; 46 | d) DUMP_BYTES="$OPTARG" ;; 47 | ?) show_help ;; 48 | esac 49 | done 50 | shift $((OPTIND - 1)) 51 | 52 | # Check if input file exists 53 | if [ ! -f "$1" ]; then 54 | echo "Error: Input file not found" 55 | show_help 56 | fi 57 | 58 | # Generate assembly code from input file 59 | input="[BITS 16]\n" 60 | while read line; do 61 | input="$input\n$line" 62 | done 63 | 64 | printf "$input" > ".temp.asm" 65 | nasm -fbin .temp.asm -o .temp.bin 66 | 67 | # Get hex escaped string 68 | esc=$(xxd -p .temp.bin | tr -d '\n' | sed 's/../\\x&/g') 69 | 70 | # Get occurrences and dump instructions 71 | LANG=C grep -PaUbo "$esc" "$1" | while read line; do 72 | offset=$(echo "$line" | cut -d: -f1) 73 | echo "Offset $offset:" 74 | dd if="$1" bs=1 skip=$offset count=$DUMP_BYTES 2>/dev/null | ndisasm -b16 - 75 | done 76 | 77 | rm -rf .temp.{asm,bin} 2>/dev/null 78 | -------------------------------------------------------------------------------- /tools/find_gaps/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | find_gaps 3 | 4 | -------------------------------------------------------------------------------- /tools/find_gaps/Makefile: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2023 Davidson Francis 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | CC ?= cc 24 | CFLAGS += -Wall -Wextra -pedantic -O2 25 | OBJ = find_gaps.o 26 | BIN = find_gaps 27 | 28 | .PHONY: all clean 29 | 30 | all: $(BIN) 31 | 32 | # C Files 33 | %.o: %.c Makefile 34 | $(CC) $< $(CFLAGS) -c -o $@ 35 | 36 | # Main 37 | $(BIN): $(OBJ) 38 | $(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@ 39 | 40 | clean: 41 | $(RM) $(OBJ) 42 | $(RM) $(BIN) 43 | -------------------------------------------------------------------------------- /tools/find_gaps/find_gaps.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2023 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | 28 | /* Error and exit */ 29 | #define errx(...) \ 30 | do { \ 31 | fprintf(stderr, __VA_ARGS__); \ 32 | exit(1); \ 33 | } while (0) 34 | 35 | /* Main. */ 36 | int main(int argc, char **argv) 37 | { 38 | FILE *fp; 39 | int pos; 40 | long offset; 41 | int curr_byte; 42 | int prev_byte; 43 | long seq_start; 44 | long min_length; 45 | long seq_length; 46 | 47 | if (argc < 4) 48 | errx("Usage: %s \n", argv[0]); 49 | 50 | offset = strtol(argv[1], NULL, 10); 51 | min_length = strtol(argv[2], NULL, 10); 52 | 53 | fp = fopen(argv[3], "rb"); 54 | if (fp == NULL) 55 | errx("Error: Could not open file %s\n", argv[3]); 56 | 57 | if (fseek(fp, offset, SEEK_SET) != 0) 58 | errx("Error: Could not seek to offset %ld in file %s\n", offset, 59 | argv[3]); 60 | 61 | pos = offset; 62 | seq_length = 0; 63 | seq_start = -1; 64 | curr_byte = 0; 65 | prev_byte = fgetc(fp); 66 | 67 | if (prev_byte == EOF) 68 | errx("Error: Could not read first byte from file %s\n", argv[3]); 69 | 70 | while ((curr_byte = fgetc(fp)) != EOF) 71 | { 72 | pos++; 73 | if (curr_byte == prev_byte && (curr_byte == 0 || curr_byte == 0xff)) 74 | { 75 | if (seq_start == -1) 76 | seq_start = pos - 1; 77 | seq_length++; 78 | } 79 | else 80 | { 81 | if (seq_length >= min_length) 82 | printf("Found sequence at offset %ld, length %ld bytes\n", 83 | seq_start, seq_length); 84 | 85 | seq_start = -1; 86 | seq_length = 0; 87 | } 88 | 89 | prev_byte = curr_byte; 90 | } 91 | 92 | if (seq_length >= min_length) 93 | printf("Found sequence at offset %ld, length %ld bytes\n", 94 | seq_start, seq_length); 95 | 96 | return (0); 97 | } 98 | -------------------------------------------------------------------------------- /tools/mdump/.bochsrc.txt: -------------------------------------------------------------------------------- 1 | megs: 16 2 | cpu: model=p4_willamette 3 | romimage: file="/usr/share/bochs/BIOS-bochs-latest" 4 | vgaromimage: file="/usr/share/bochs/VGABIOS-lgpl-latest" 5 | floppya: 1_44="mdump.img", status=inserted 6 | boot: a 7 | log: /dev/null 8 | mouse: enabled=0 9 | clock: sync=none, time0=utc 10 | display_library: x, options="gui_debug" 11 | magic_break: enabled=1 12 | com1: enabled=1, mode=socket-client, dev=localhost:2345 13 | -------------------------------------------------------------------------------- /tools/mdump/.gitignore: -------------------------------------------------------------------------------- 1 | *.img 2 | *.o 3 | mdump 4 | bx_enh_dbg.ini 5 | 6 | -------------------------------------------------------------------------------- /tools/mdump/Makefile: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2023 Davidson Francis 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | CC ?= cc 24 | CFLAGS += -Wall -Wextra -pedantic -O2 25 | OBJ = mdump.o 26 | BIN = mdump mdump.img 27 | 28 | ifeq ($(BOOTABLE),no) 29 | CFLAGS += -DBIOS 30 | ASMFLAGS += -DBIOS 31 | endif 32 | 33 | ifneq ($(DUMP_BYTES),) 34 | CFLAGS += -DDUMP_BYTES=$(DUMP_BYTES) 35 | ASMFLAGS += -DDUMP_BYTES=$(DUMP_BYTES) 36 | endif 37 | 38 | ifneq ($(BAUDRATE),) 39 | CFLAGS += -DBAUDRATE=B$(BAUDRATE) 40 | ASMFLAGS += -DBAUDRATE=$(BAUDRATE) 41 | endif 42 | 43 | ifeq ($(DISABLE_CRC),yes) 44 | CFLAGS += -DDISABLE_CRC 45 | ASMFLAGS += -DDISABLE_CRC 46 | endif 47 | 48 | .PHONY: all qemu clean 49 | 50 | all: $(BIN) 51 | 52 | # C Files 53 | %.o: %.c Makefile 54 | $(CC) $< $(CFLAGS) -c -o $@ 55 | 56 | # ASM Files 57 | %.img: %.asm 58 | nasm -fbin $< -o $@ $(ASMFLAGS) 59 | 60 | mdump.img: mdump.asm 61 | 62 | # Main 63 | mdump: $(OBJ) 64 | $(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@ 65 | 66 | # Run on Bochs and QEMU 67 | bochs: mdump.img 68 | bochs -q -f .bochsrc.txt 69 | qemu: mdump.img 70 | qemu-system-i386 -boot a -fda mdump.img \ 71 | -serial tcp:127.0.0.1:2345 -gdb tcp::5678 72 | 73 | clean: 74 | $(RM) $(OBJ) 75 | $(RM) $(BIN) 76 | -------------------------------------------------------------------------------- /tools/mdump/README.md: -------------------------------------------------------------------------------- 1 | # mdump 2 | `mdump` is a lightweight memory dump tool that can be used to dump the entire memory of a 3 | system over a serial cable. It is designed to be minimally invasive and can be booted or 4 | injected into a system to perform memory dumps. 5 | 6 | ## Features 7 | `mdump` is quite simple, and this brings several advantages: 8 | - Lightweight: only 512-byte. Fits inside a single disk sector 9 | - Bootable: No OS required, just burn the bootable image file and perform the 10 | memory dump. 11 | - Minimal memory footprint: Even if there are similar solutions for Linux or 12 | even FreeDOS, the memory usage of `mdump` is minimal, which means that it interferes 13 | minimally with the contents of the PC's memory. 14 | - Injectable: Its source code is made to be modular and easily injected into other 15 | environments, such as within the BIOS itself or even as a DOS program. 16 | - Position-Independent Code: Once built, mdump does not require specific offsets to 17 | work, just place your binary where you want to dump it and that's it =). 18 | 19 | ## Usage 20 | To use it is quite simple, something like: 21 | 22 | ```bash 23 | # Build 24 | $ git clone https://github.com/Theldus/AMI_BIOS_CodeInjection.git 25 | $ cd AMI_BIOS_CodeInjection/tools/mdump 26 | $ make 27 | 28 | # Copy the bootable image to your USB flash drive 29 | $ sudo dd if=mdump.img of=/dev/sdX 30 | 31 | # << PLUG THE SERIAL CABLE >> 32 | 33 | # Run the `mdump` tool, specifying the path of the serial device and 34 | # amount of memory. To dump 4MB, reading from /dev/ttyUSB0: 35 | $ ./mdump /dev/ttyUSB0 output_file.bin $((4<<20)) 36 | Waiting to read 4194304 bytes... 37 | Device /dev/ttyUSB0 opened for reading... 38 | Dumping memory: [################################] done =) 39 | Received CRC-32: ce18133e 40 | Calculated CRC-32: ce18133e, match?: yes 41 | Checking output file... 42 | Success! 43 | 44 | # << BOOT THE PC YOU WANT TO DUMP >> 45 | ``` 46 | A progress bar will indicate the status of the transfer. After the dump is complete, some 47 | sanity checks will be performed on the output file, and if everything is ok, chances are 48 | the dump is in good shape. 49 | 50 | ### Custom builds 51 | mdump has a number of variables that can be selected during the build and that customize its 52 | operation: 53 | 54 | `BOOTABLE`: `yes/no` (default: `yes`) 55 | 56 | If `yes`, the generated code is bootable and has the signature '`55 AA`' 57 | at the end of the binary. If `no`, the code no longer has the 512-byte padding and the prints 58 | are also removed, since it is assumed that the video may not be initialized/functional in 59 | other circumstances, such as inside the BIOS ROM. 60 | 61 | `BAUDRATE`: `9600, 19200, 38400, 115200...` (default: `115200`) 62 | 63 | Selects the data transmission speed. 64 | 65 | `DISABLE_CRC`: `yes/no` (defaut: `no`) 66 | 67 | By default mdump will calculate the CRC-32 of the transmitted data, however, the CRC 68 | calculation increases the final size of the binary (~399 bytes total) and makes 69 | considerable use of the stack (1kB). If this may be a problem for you, disable the use of CRC. 70 | 71 | #### Examples: 72 | 73 | ```bash 74 | # Build a non-bootable code without CRC-checking 75 | $ make clean 76 | $ make BOOTABLE=no DISABLE_CRC=yes 77 | 78 | # Build a bootable code with 9600-bauds of speed: 79 | $ make clean 80 | $ make BAUDRATE=9600 81 | 82 | # Default build equivalent: 83 | $ make clean 84 | $ make BOOTABLE=yes DISABLE_CRC=no BAUDRATE=115200 85 | ``` 86 | 87 | ## Limitations 88 | `mdump` has a few limitations: 89 | - Speed: Serial cable is naturally slow, so don't expect too much speed. A 4M dump (usually 90 | more than enough for BIOS analysis) takes about 6 minutes. A dump of 128M is expected to 91 | take ~3h12m. 92 | - No switch back: `mdump` jumps to protected mode but does not jump back to real mode... So 93 | you will have to restart your PC to use it again (if using a bootable media or DOS) or 94 | reflash the BIOS again, if you have put it inside the BIOS ROM. 95 | -------------------------------------------------------------------------------- /tools/mdump/mdump.asm: -------------------------------------------------------------------------------- 1 | ; MIT License 2 | ; 3 | ; Copyright (c) 2023 Davidson Francis 4 | ; 5 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 6 | ; of this software and associated documentation files (the "Software"), to deal 7 | ; in the Software without restriction, including without limitation the rights 8 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | ; copies of the Software, and to permit persons to whom the Software is 10 | ; furnished to do so, subject to the following conditions: 11 | ; 12 | ; The above copyright notice and this permission notice shall be included in all 13 | ; copies or substantial portions of the Software. 14 | ; 15 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | ; SOFTWARE. 22 | 23 | [BITS 16] 24 | [ORG 0x7C00] ; No need to change anything here 25 | 26 | ; -------------------------------- 27 | ; Macros 28 | ; -------------------------------- 29 | %macro dbg 0 30 | xchg bx, bx 31 | %endmacro 32 | 33 | %macro inputb 1 34 | mov dx, %1 35 | in al, dx 36 | %endmacro 37 | 38 | %macro outbyte 2 39 | mov ax, %2 40 | mov dx, %1 41 | out dx, al 42 | %endmacro 43 | 44 | %ifndef BAUDRATE 45 | %define BAUDRATE 115200 46 | %endif 47 | 48 | %ifndef BIOS 49 | %define SETUP_STACK 50 | %endif 51 | 52 | ; -------------------------------- 53 | ; Code 54 | ; -------------------------------- 55 | 56 | ; Disable ints 57 | cli 58 | 59 | %ifdef SETUP_STACK 60 | mov ax, 0x7C0 61 | add ax, 544 62 | mov ss, ax 63 | mov sp, 4096 64 | %endif 65 | 66 | ; 67 | ; Create our GDB on stack 68 | ; 69 | ; Code and data with 4G, base=0, 4k pages 70 | ; 71 | 72 | ; Entry 2, Data 73 | push dword 0x00CF9200 74 | push dword 0x0000FFFF 75 | ; Entry 1, Code 76 | push dword 0x00CF9A00 77 | push dword 0x0000FFFF 78 | ; Entry 0 79 | push dword 0 80 | push dword 0 81 | 82 | ; Phys addr 83 | xor eax, eax 84 | mov bp, sp 85 | mov ax, ss 86 | shl ax, 4 87 | add ax, bp ; Make AX point to the first element on stack 88 | 89 | ; GDT desc 90 | push eax 91 | push word 23 ; gdt_end-gdt-1 92 | 93 | ; Jump to protected mode 94 | mov bp, sp 95 | lgdt [ss:bp] 96 | 97 | mov eax, cr0 98 | or eax, 1 ; protecc mode 99 | mov cr0, eax 100 | 101 | ; Do our 'far jump' without absolute address =) 102 | push dword 0x08 103 | call protecc 104 | return: 105 | retfd 106 | 107 | ; 108 | ; Adjust return address: 109 | ; Skip protecc itself and the retfd word too 110 | ; 111 | protecc: 112 | ; Adjust EIP 113 | xor eax, eax 114 | pop ax 115 | add ax, protecc_len + 2 ; EIP 116 | ; Get Phys CS 117 | xor ebx, ebx 118 | push cs 119 | pop bx 120 | shl ebx, 4 ; Phys CS 121 | ; Final phys address 122 | add eax, ebx ; Phys address 123 | ; Push it and return 124 | push dword eax 125 | jmp return 126 | protecc_len equ $ - protecc 127 | 128 | [BITS 32] 129 | protected_mode: 130 | mov ax, 10h ; data seg 131 | mov ds, ax ; data seg 132 | mov es, ax ; data seg 133 | mov ss, ax ; stack seg as data seg 134 | mov esp, 090000h ; set stack pointer 135 | 136 | %define STCK_CRC_TBL_ADDR 12 137 | %define STCK_DUMP_VG_OFF 8 138 | %define STCK_CRC_VALUE 4 139 | %define STCK_DUMP_AMOUNT 0 140 | 141 | ; Reserve space to our CRC table (1024 bytes) 142 | sub esp, 1024 143 | 144 | ; Prepare our aux data 145 | push dword 0x0B8000 ; DUMP_VG_OFF 146 | push dword 0xFFFFFFFF ; CRC_VALUE 147 | push dword 0 ; DUMP_AMOUNT 148 | mov ebp, esp 149 | 150 | ; UART 151 | call setup_uart 152 | 153 | %ifndef DISABLE_CRC 154 | ; Create our CRC-32 table 155 | call build_crc32_table 156 | %endif 157 | 158 | ; Signal that we're ready 159 | mov ebx, 0xc001b00b 160 | call write_dword_serial 161 | 162 | %ifndef BIOS 163 | ; Clear entire screen 164 | mov edi, 0xB8000 165 | xor eax, eax 166 | mov ecx, 2*80*25 167 | repe stosb 168 | 169 | ; Initial text 170 | push dword 0x202e2e2e ; '... ' 171 | push dword 0x74696157 ; 'Wait' 172 | mov esi, esp 173 | call print_text 174 | add esp, 8 175 | %endif 176 | 177 | ; Wait for the length 178 | mov edi, ebp+STCK_DUMP_AMOUNT 179 | xor ecx, ecx 180 | mov cl, 4 181 | 182 | wait_len: 183 | inputb UART_LSR 184 | bt ax, 0 185 | jnc wait_len 186 | inputb UART_RB 187 | stosb 188 | loop wait_len 189 | 190 | ; 'Fix' our DUMP_AMOUNT by /4 191 | shr dword [ss:ebp+STCK_DUMP_AMOUNT], 2 192 | 193 | %ifndef BIOS 194 | ; Dump text 195 | push dword 0x2e2e2e70 ; 'p...' 196 | push dword 0x6d754420 ; ' Dum' 197 | mov esi, esp 198 | call print_text 199 | add esp, 8 200 | %endif 201 | 202 | ; ------- Memory dump ------------- 203 | cld 204 | xor esi, esi 205 | mov ecx, dword [ss:ebp+STCK_DUMP_AMOUNT] 206 | dump: 207 | ; Load 4-bytes and dump 208 | lodsd 209 | 210 | %ifndef DISABLE_CRC 211 | ; Update CRC-32 value 212 | call update_crc 213 | %endif 214 | 215 | ; Send over serial 216 | xchg ebx, eax 217 | call write_dword_serial 218 | loop dump 219 | 220 | ; 221 | ; Done, now we send our calculated CRC-32 value 222 | ; 223 | mov ebx, dword [ss:ebp+STCK_CRC_VALUE] 224 | not ebx 225 | call write_dword_serial 226 | 227 | %ifndef BIOS 228 | ; Done 229 | push dword 0x20202065 ; 'e ' 230 | push dword 0x6e6f4420 ; ' Don' 231 | mov esi, esp 232 | call print_text 233 | add esp, 8 234 | %endif 235 | 236 | hang: jmp hang 237 | 238 | %ifndef BIOS 239 | ; 240 | ; Write a NULL-terminated text into the screen 241 | ; Parameters: 242 | ; esi = text buffer 243 | ; 244 | print_text: 245 | mov edi, [ss:ebp+STCK_DUMP_VG_OFF] 246 | xor ecx, ecx 247 | mov cl, 8 248 | .loop: 249 | lodsb 250 | stosb 251 | mov al, 0x1B 252 | stosb 253 | loop .loop 254 | .out: 255 | ; Update curr vid memory 256 | mov [ss:ebp+STCK_DUMP_VG_OFF], edi 257 | ret 258 | %endif 259 | 260 | ; 261 | ; Configure our UART with the following parameters: 262 | ; 8 bits, no parity, one stop bit 263 | ; 264 | setup_uart: 265 | ; Calculate and set divisor 266 | outbyte UART_LCR, UART_LCR_DLA 267 | outbyte UART_DLB1, UART_DIVISOR & 0xFF 268 | outbyte UART_DLB2, UART_DIVISOR >> 8 269 | 270 | ; Set line control register: 271 | outbyte UART_LCR, UART_LCR_BPC_8 272 | 273 | ; Reset FIFOs and set trigger level to 14 bytes. 274 | outbyte UART_FCR, UART_FCR_CLRRECV | UART_FCR_CLRTMIT | UART_FCR_TRIG_14 275 | ret 276 | 277 | ; 278 | ; Write a single byte to UART 279 | ; Parameters: 280 | ; bl = byte to be sent 281 | ; 282 | write_byte_serial: 283 | mov dx, UART_LSR 284 | .loop: 285 | in al, dx 286 | and al, UART_LSR_TFE 287 | cmp al, 0 288 | je .loop 289 | mov dx, UART_BASE 290 | mov al, bl 291 | out dx, al 292 | ret 293 | 294 | ; 295 | ; Write a dword to UART 296 | ; 297 | ; Parameters: 298 | ; ebx = dword to be written 299 | ; 300 | write_dword_serial: 301 | call write_byte_serial 302 | shr ebx, 8 303 | call write_byte_serial 304 | shr ebx, 8 305 | call write_byte_serial 306 | shr ebx, 8 307 | call write_byte_serial 308 | ret 309 | 310 | %ifndef DISABLE_CRC 311 | ; 312 | ; Generate a CRC-32 table 313 | ; 314 | ; Based on: 315 | ; https://lxp32.github.io/docs/a-simple-example-crc32-calculation/ 316 | ; 317 | build_crc32_table: 318 | mov edi, ebp 319 | add edi, STCK_CRC_TBL_ADDR 320 | xor ecx, ecx 321 | ; ecx = counter/i/j 322 | ; eax = crc 323 | ; ebx = ch 324 | ; edx = b 325 | .outer: ; 256 326 | mov ebx, ecx ; ch = i 327 | xor eax, eax ; crc = 0 328 | push ecx ; backup outer counter 329 | xor ecx, ecx 330 | mov cl, 8 ; ecx = j = 0 331 | .inner: ; 8 332 | mov edx, ebx ; b = ch 333 | xor edx, eax ; b = b^crc 334 | and edx, 1 ; b = b & 1 335 | shr eax, 1 ; crc >>= 1 336 | cmp edx, 0 ; if (b) 337 | je .skip 338 | xor eax, 0xEDB88320 ; crc = crc^0xEDB88320 339 | .skip: 340 | shr ebx, 1 ; ch >>= 1 341 | loop .inner 342 | pop ecx ; restore outer counter 343 | stosd ; crc32_table[i] = crc/eax 344 | inc ecx 345 | cmp ecx, 256 346 | jl .outer 347 | ret 348 | 349 | ; 350 | ; Update the CRC-32 value for a given dword 351 | ; 352 | ; Parameters: 353 | ; eax = dword to be crc'ed 354 | ; Register usage: 355 | ; ebx = t 356 | ; edx = crc 357 | ; eax = dword crc'ed 358 | ; 359 | update_crc: 360 | push eax 361 | push ecx 362 | xor ecx, ecx 363 | mov cl, 4 364 | mov edx, dword [ss:ebp+STCK_CRC_VALUE] 365 | .crc_calc_loop: 366 | movzx ebx, al ; t = s[0] 367 | xor ebx, edx ; ch ^ crc 368 | and ebx, 0xFF ; & 0xFF 369 | shr edx, 8 ; crc>>8 370 | xor edx, dword [ss:ebp+STCK_CRC_TBL_ADDR+(ebx*4)] 371 | shr eax, 8 372 | loop .crc_calc_loop 373 | ; Save our new CRC 374 | mov dword [ss:ebp+STCK_CRC_VALUE], edx 375 | pop ecx 376 | pop eax 377 | ret 378 | %endif 379 | 380 | ; UART Constants 381 | ; -------------- 382 | UART_CLOCK_SIGNAL equ 1843200 383 | UART_BASE equ 0x3F8 384 | UART_BAUD equ BAUDRATE ; 9600 if things go wrong 385 | UART_DIVISOR equ UART_CLOCK_SIGNAL / (UART_BAUD << 4) 386 | UART_RB equ UART_BASE + 0 ; Receiver Buffer (R). 387 | UART_FCR equ UART_BASE + 2 ; FIFO Control Register (W). 388 | UART_LCR equ UART_BASE + 3 ; Line Control Register (RW). 389 | UART_LSR equ UART_BASE + 5 ; Line Status Register (R). 390 | 391 | ; Line Control Register values 392 | UART_LCR_DLA equ 0x80 ; Divisor Latch Access. 393 | UART_LCR_BPC_8 equ 0x3 ; 8 bits per character. 394 | 395 | ; Divisor register 396 | UART_DLB1 equ UART_BASE + 0 ; Divisor Latch LSB (RW). 397 | UART_DLB2 equ UART_BASE + 1 ; Divisor Latch MSB (RW). 398 | 399 | ; FIFO Control Register bits. 400 | UART_FCR_CLRRECV equ 0x1 ; Clear receiver FIFO. 401 | UART_FCR_CLRTMIT equ 0x2 ; Clear transmitter FIFO. 402 | 403 | ; FIFO Controle Register bit 7-6 values 404 | UART_FCR_TRIG_14 equ 0x0 ; Trigger level 14-byte. 405 | 406 | ; Line status register 407 | UART_LSR_TFE equ 0x20 ; Transmitter FIFO Empty. 408 | 409 | %ifndef BIOS 410 | ; Some magic number 411 | times 506-($-$$) db 0 412 | dd 0xB16B00B5 413 | 414 | ; Signature 415 | dw 0xAA55 416 | %endif 417 | -------------------------------------------------------------------------------- /tools/mdump/mdump.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2023 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #define _GNU_SOURCE 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #ifndef BAUDRATE 43 | #define BAUDRATE B115200 44 | #endif 45 | 46 | /* Progress amount of bars. */ 47 | #define AMNT_BARS 32 48 | 49 | /* Serial fd. */ 50 | static int is_serial; 51 | static int serial_fd; 52 | static struct termios savetty; 53 | 54 | /* Mmaped outfile file. */ 55 | static uint8_t *out_file; 56 | static off_t out_size; 57 | static int out_fd; 58 | 59 | /* CRC-32 table. */ 60 | uint32_t crc32_table[256]; 61 | 62 | /* Receive buffer. */ 63 | struct recv_buffer 64 | { 65 | uint8_t buff[128]; 66 | size_t cur_pos; 67 | size_t amt_read; 68 | } recv_buffer = {0}; 69 | 70 | /* Error and exit */ 71 | #define errx(...) \ 72 | do { \ 73 | fprintf(stderr, __VA_ARGS__); \ 74 | exit(1); \ 75 | } while (0) 76 | 77 | 78 | /* Error */ 79 | #define err(...) fprintf(stderr, __VA_ARGS__) 80 | 81 | /* Error and goto */ 82 | #define errto(lbl, ...) \ 83 | do { \ 84 | fprintf(stderr, __VA_ARGS__); \ 85 | goto lbl; \ 86 | } while (0) 87 | 88 | /* Restore the tty/device while exiting. */ 89 | static void restore_settings(void) 90 | { 91 | if (is_serial && serial_fd) 92 | { 93 | tcsetattr(serial_fd, TCSANOW, &savetty); 94 | close(serial_fd); 95 | } 96 | if (out_fd) 97 | close(out_fd); 98 | if (out_file) 99 | munmap(out_file, out_size); 100 | } 101 | 102 | /** 103 | * @brief Read a chunk of bytes and return the next byte 104 | * belonging to the frame. 105 | * 106 | * @return Returns the byte read, or -1 if error. 107 | */ 108 | static inline int next_byte(void) 109 | { 110 | ssize_t n; 111 | 112 | /* If empty or full. */ 113 | if (recv_buffer.cur_pos == 0 || 114 | recv_buffer.cur_pos == recv_buffer.amt_read) 115 | { 116 | if ((n = read(serial_fd, recv_buffer.buff, 117 | sizeof(recv_buffer.buff))) <= 0) 118 | { 119 | return (-1); 120 | } 121 | recv_buffer.amt_read = (size_t)n; 122 | recv_buffer.cur_pos = 0; 123 | } 124 | return (recv_buffer.buff[recv_buffer.cur_pos++]); 125 | } 126 | 127 | #ifndef DISABLE_CRC 128 | /** 129 | * @brief Creates the CRC-32 table 130 | */ 131 | void build_crc32_table(void) 132 | { 133 | uint32_t i, j; 134 | uint32_t crc; 135 | uint32_t ch; 136 | uint32_t b; 137 | 138 | for (i = 0; i < 256; i++) 139 | { 140 | ch = i; 141 | crc = 0; 142 | for (j = 0; j < 8; j++) 143 | { 144 | b = (ch ^ crc) & 1; 145 | crc >>= 1; 146 | if (b) 147 | crc = crc ^ 0xEDB88320; 148 | ch >>= 1; 149 | } 150 | crc32_table[i] = crc; 151 | } 152 | } 153 | 154 | /** 155 | * @brief Calculates the CRC-32 checksum for the 156 | * given buffer @p buff. 157 | * 158 | * @param buff Input buffer. 159 | * @param length Buffer length. 160 | * 161 | * @return Returns the calculated CRC-32. 162 | */ 163 | uint32_t do_crc32(const uint8_t *buff, size_t length) 164 | { 165 | uint32_t crc; 166 | uint32_t t; 167 | size_t i; 168 | 169 | crc = 0xFFFFFFFF; 170 | for (i = 0; i < length; i++) 171 | { 172 | t = (buff[i] ^ crc) & 0xFF; 173 | crc = (crc >> 8) ^ crc32_table[t]; 174 | } 175 | return (~crc); 176 | } 177 | #endif 178 | 179 | /** 180 | * @brief Configure a TCP server to listen to the 181 | * specified port @p port. 182 | * 183 | * @param port Port to listen. 184 | * 185 | * @return Returns the client fd. 186 | */ 187 | int setup_server_and_listen(uint16_t port) 188 | { 189 | struct sockaddr_in server; 190 | int srv_fd; 191 | int reuse; 192 | 193 | reuse = 1; 194 | 195 | printf("Waiting to launch VM...\n"); 196 | 197 | srv_fd = socket(AF_INET, SOCK_STREAM, 0); 198 | if (srv_fd < 0) 199 | errx("Unable to open socket!\n"); 200 | 201 | setsockopt(srv_fd, SOL_SOCKET, SO_REUSEADDR, 202 | (const char *)&reuse, sizeof(reuse)); 203 | 204 | /* Prepare the sockaddr_in structure. */ 205 | memset((void*)&server, 0, sizeof(server)); 206 | server.sin_family = AF_INET; 207 | server.sin_addr.s_addr = INADDR_ANY; 208 | server.sin_port = htons(port); 209 | 210 | /* Bind. */ 211 | if (bind(srv_fd, (struct sockaddr *)&server, sizeof(server)) < 0) 212 | errx("Bind failed"); 213 | 214 | /* Listen. */ 215 | listen(srv_fd, 1); 216 | 217 | /* Blocks in accept() until someone connects. */ 218 | serial_fd = accept(srv_fd, NULL, NULL); 219 | if (serial_fd < 0) 220 | errx("Failed to accept connection!\n"); 221 | 222 | /* Close server. */ 223 | close(srv_fd); 224 | return (serial_fd); 225 | } 226 | 227 | /** 228 | * @brief Initial setup for the serial device. 229 | * 230 | * @param sdev Serial device path, like: /dev/ttyUSB0 231 | * 232 | * @return Returns the serial file descriptor. 233 | */ 234 | int setup_serial(const char *sdev) 235 | { 236 | struct termios tty; 237 | 238 | is_serial = 1; 239 | 240 | /* Open device. */ 241 | if ((serial_fd = open(sdev, O_RDWR | O_NOCTTY)) < 0) 242 | errx("Failed to open: %s, (%s)", sdev, strerror(errno)); 243 | 244 | /* Attributes. */ 245 | if (tcgetattr(serial_fd, &tty) < 0) 246 | errx("Failed to get attr: (%s)", strerror(errno)); 247 | 248 | savetty = tty; 249 | cfsetospeed(&tty, (speed_t)BAUDRATE); 250 | cfsetispeed(&tty, (speed_t)BAUDRATE); 251 | cfmakeraw(&tty); 252 | 253 | /* TTY settings. */ 254 | tty.c_cc[VMIN] = 1; 255 | tty.c_cc[VTIME] = 10; 256 | tty.c_cflag &= ~CSTOPB; 257 | tty.c_cflag &= ~CRTSCTS; /* no HW flow control? */ 258 | tty.c_cflag |= CLOCAL | CREAD; 259 | 260 | printf("Device %s opened for reading...\n", sdev); 261 | 262 | if (tcsetattr(serial_fd, TCSANOW, &tty) < 0) 263 | errx("Failed to set attr: (%s)", strerror(errno)); 264 | 265 | return (serial_fd); 266 | } 267 | 268 | #if !defined(DISABLE_CRC) || !defined(BIOS) 269 | /** 270 | * @brief Open the output file to read and get its size. 271 | * 272 | * @return Returns 0 if success, -1 otherwise. 273 | */ 274 | int mmap_outfile(void) 275 | { 276 | struct stat st = {0}; 277 | 278 | /* Mmap the output file. */ 279 | lseek(out_fd, 0, SEEK_SET); 280 | fstat(out_fd, &st); 281 | out_size = st.st_size; 282 | 283 | out_file = mmap(0, out_size, PROT_READ, MAP_PRIVATE, out_fd, 0); 284 | if (out_file == MAP_FAILED) { 285 | err("ERROR: Failed to mmap: %s\n", strerror(errno)); 286 | return (-1); 287 | } 288 | 289 | return (0); 290 | } 291 | #endif 292 | 293 | /** 294 | * @brief Perform some sanity checks on the output 295 | * file. 296 | * 297 | * Passing these checks does not mean that the file 298 | * is (with 100% of certainty) fine, but is very 299 | * likely to. 300 | * 301 | * @param amnt_bytes Output file expected size. 302 | * 303 | * @return Returns 0 if success, -1 otherwise. 304 | */ 305 | #ifndef BIOS 306 | static int check_output(size_t amnt_bytes) 307 | { 308 | uint8_t *out_found = NULL; 309 | struct stat st = {0}; 310 | int shift = 0; /* Amount of bytes to shift. */ 311 | int ret = -1; 312 | 313 | #ifdef DISABLE_CRC 314 | /* Mmap the output file. */ 315 | if (mmap_outfile() < 0) 316 | goto out0; 317 | #endif 318 | 319 | if ((size_t)out_size != amnt_bytes) 320 | err("WARNING: Output size (%zu) differs from expected: " 321 | "%zu bytes!\n", st.st_size, amnt_bytes); 322 | 323 | /* If not a boot sector code, we cant proceed. */ 324 | if (!stat("mdump.img", &st) && st.st_size != 512) 325 | errto(out0, "INFO: mdump.img file is not a bootable file!\n" 326 | " I'm unable to check for consistency"); 327 | 328 | /* Check if there is room to check. */ 329 | if (out_size < 0x7c00 + 512) 330 | goto out0; 331 | 332 | /* Check for alignment. */ 333 | if (out_file[0x7c00 + 510] == 0x55 && 334 | out_file[0x7c00 + 511] == 0xaa) 335 | { 336 | goto check_a20; /* is aligned, do not align this file. */ 337 | } 338 | 339 | err("WARNING: Output is not properly aligned!\n"); 340 | 341 | /* Try to find our magic number: 0xB16B00B5 (little endian). */ 342 | out_found = memmem(out_file, out_size, "\xb5\x00\x6b\xb1", 4); 343 | if (!out_found) 344 | errto(out0, "ERROR: Magic number not found, output file should be " 345 | "discarded!\n"); 346 | 347 | shift = ((out_found - out_file) - 0x7c00) - 506; 348 | 349 | err("WARNING: Found magic number!, output file is %+d bytes " 350 | "shifted!\n", shift); 351 | 352 | check_a20: 353 | if ((out_size >= 0x7c00 + 512 + (1<<20) + shift) && 354 | out_file[0x7c00 + 510 + (1<<20) + shift] == 0x55 && 355 | out_file[0x7c00 + 511 + (1<<20) + shift] == 0xaa) 356 | { 357 | errto(out0, "WARNING: A20 line looks like its *not* enabled!\n"); 358 | } 359 | 360 | ret = 0; 361 | printf("Success!\n"); 362 | out0: 363 | return (ret); 364 | } 365 | #endif 366 | 367 | /** 368 | * @brief Write @p len bytes from @p buf to @p conn. 369 | * 370 | * Contrary to send(2)/write(2) that might return with 371 | * less bytes written than specified, this function 372 | * attempts to write the entire buffer, because... 373 | * thats the most logical thing to do... 374 | * 375 | * @param conn Target file descriptor. 376 | * @param buf Buffer to be sent. 377 | * @param len Amount of bytes to be sent. 378 | * 379 | * @return Returns 0 if success, -1 otherwise. 380 | */ 381 | ssize_t send_all( 382 | int conn, const void *buf, size_t len) 383 | { 384 | const char *p; 385 | ssize_t ret; 386 | 387 | if (conn < 0) 388 | return (-1); 389 | 390 | p = buf; 391 | while (len) 392 | { 393 | ret = write(conn, p, len); 394 | if (ret == -1) 395 | return (-1); 396 | p += ret; 397 | len -= ret; 398 | } 399 | return (0); 400 | } 401 | 402 | #ifndef DISABLE_CRC 403 | /** 404 | * @brief Receives the CRC-32 sent from the dumper 405 | * and compares with the output file. 406 | * 407 | * A clean transmission should have matching CRCs, 408 | * otherwise, the output file is not exactly as sent 409 | * and might be very (or not) wrong. 410 | */ 411 | static void wait_and_check_crc(void) 412 | { 413 | uint32_t crc32; 414 | size_t i = 0; 415 | int c = 0; 416 | 417 | union crc { 418 | uint32_t crc32; 419 | uint8_t crc8[4]; 420 | } crc; 421 | 422 | /* Create our table. */ 423 | build_crc32_table(); 424 | 425 | /* Wait to our received crc. */ 426 | for (i = 0; i < sizeof crc.crc8; i++) 427 | { 428 | c = next_byte(); 429 | if (c < 0) 430 | errto(out0, "Unable to receive CRC-32!\n"); 431 | 432 | crc.crc8[i] = c; 433 | } 434 | 435 | /* Mmap the output file. */ 436 | if (mmap_outfile() < 0) 437 | goto out0; 438 | 439 | /* Get CRC. */ 440 | crc32 = do_crc32(out_file, out_size); 441 | 442 | printf("Received CRC-32: %08x\n", crc.crc32); 443 | printf("Calculated CRC-32: %08x, match?: %s\n", crc32, 444 | (crc32 == crc.crc32 ? "yes" : "no")); 445 | 446 | out0: 447 | return; 448 | } 449 | #endif 450 | 451 | /** 452 | * @brief Wait until the dumper is ready to talk 453 | */ 454 | static int wait_ready() 455 | { 456 | static const uint8_t magic[] = {0x0b,0xb0,0x01,0xc0}; 457 | int c = 0; 458 | int idx = 0; 459 | 460 | while (1) 461 | { 462 | c = next_byte(); 463 | if (c < 0) 464 | return (-1); 465 | 466 | if (c == magic[idx]) 467 | { 468 | idx++; 469 | if (idx == sizeof magic) 470 | return (0); 471 | } 472 | else 473 | idx = 0; 474 | } 475 | 476 | /* Never reaches. */ 477 | return (-1); 478 | } 479 | 480 | /** 481 | * @brief Wait the dumper to be ready and then 482 | * send the amountof bytes to be dumped. 483 | * 484 | * @param serial_fd Serial file descriptor (or socket). 485 | * @param length Amount of bytes to dump. 486 | */ 487 | static void wait_and_send_length(uint32_t length) 488 | { 489 | union len { 490 | uint32_t len32; 491 | uint8_t len8[4]; 492 | } len; 493 | 494 | len.len32 = length; 495 | 496 | /* Wait serial device to be ready. */ 497 | if (wait_ready() < 0) 498 | errx("Unable to receive message from dbg!\n"); 499 | 500 | /* Send amount of bytes. */ 501 | if (send_all(serial_fd, len.len8, 4) < 0) 502 | errx("Unable to send amount of bytes!\n"); 503 | } 504 | 505 | /** 506 | * @brief Usage 507 | * @param prg_name Program name 508 | */ 509 | static void usage(const char *prg_name) 510 | { 511 | errx( 512 | "Usage: %s [-s|/serial/path] output_file output_file_length\n" 513 | "Arguments:\n" 514 | " -s: Uses a TCP connection, listening to 2345\n" 515 | " /serial/path: Uses a serial cable for the given path\n" 516 | "Example:\n" 517 | " # Dumps 4M listening to a socket: \n" 518 | " %s -s dump4M.img $((4<<20))\n\n" 519 | " # Dumps 4M using a serial cable: \n" 520 | " %s /dev/ttyUSB0 dump4M.img $((4<<20))\n\n" 521 | "Note:\n" 522 | " must be divisible by 4!!", 523 | prg_name, prg_name, prg_name); 524 | } 525 | 526 | /** 527 | * @brief Handle program arguments 528 | * 529 | * @param argc Argument count. 530 | * @param argv Argument list. 531 | * @param out_len Dump length. 532 | */ 533 | static void handle_arguments(int argc, char **argv, 534 | ssize_t *out_len) 535 | { 536 | char *out; 537 | 538 | if (argc != 4) 539 | usage(argv[0]); 540 | 541 | out = argv[2]; 542 | 543 | /* Dump length. */ 544 | *out_len = atoi(argv[3]); 545 | if (*out_len <= 0 || (*out_len % 4 != 0)) 546 | { 547 | err("Output length must be multiple of 4!\n"); 548 | usage(argv[0]); 549 | } 550 | 551 | /* Open output file. */ 552 | if ((out_fd = open(out, O_CREAT|O_RDWR|O_TRUNC, 0644)) < 0) 553 | errx("Failed to open: %s, (%s)", out, strerror(errno)); 554 | 555 | printf("Waiting to read %zd bytes...\n", *out_len); 556 | 557 | /* Setup socket and/or serial. */ 558 | if (strcmp(argv[1],"-s") != 0) 559 | serial_fd = setup_serial(argv[1]); 560 | else 561 | serial_fd = setup_server_and_listen(2345); 562 | } 563 | 564 | /* Main =). */ 565 | int main(int argc, char **argv) 566 | { 567 | ssize_t out_len = 0; 568 | size_t rdbytes; 569 | size_t amnt; 570 | size_t i, j; 571 | int c; 572 | 573 | handle_arguments(argc, argv, &out_len); 574 | rdbytes = 0; 575 | amnt = out_len / AMNT_BARS; 576 | 577 | /* Wait to dump. */ 578 | wait_and_send_length(out_len); 579 | 580 | /* Restore tty and stuff at exit. */ 581 | atexit(restore_settings); 582 | 583 | /* Dump. */ 584 | fputs("Dumping memory: [ ]\r", stderr); 585 | fputs("Dumping memory: [", stderr); 586 | 587 | for (i = 0; i < (size_t)out_len; i++) 588 | { 589 | if ((c = next_byte()) < 0) 590 | errx("Failed to read from device! (%s)\n", strerror(errno)); 591 | 592 | if (write(out_fd, &c, 1) <= 0) 593 | errx("Unable to write to the output file!\n"); 594 | 595 | rdbytes++; 596 | 597 | /* Update progress bar. */ 598 | if (rdbytes >= amnt) 599 | { 600 | for (j = 0; j < rdbytes / amnt; j++) 601 | fputc('#', stderr); 602 | rdbytes = 0; 603 | } 604 | } 605 | 606 | fputs("] done =)\n", stderr); 607 | 608 | #ifndef DISABLE_CRC 609 | /* Check for CRC. */ 610 | wait_and_check_crc(); 611 | #endif 612 | 613 | #ifndef BIOS 614 | puts("Checking output file..."); 615 | check_output(out_len); 616 | #endif 617 | 618 | return (0); 619 | } 620 | -------------------------------------------------------------------------------- /tools/nim/.bochsrc.txt: -------------------------------------------------------------------------------- 1 | megs: 16 2 | cpu: model=p4_willamette 3 | romimage: file="/usr/share/bochs/BIOS-bochs-latest" 4 | vgaromimage: file="/usr/share/bochs/VGABIOS-lgpl-latest" 5 | floppya: 1_44="bootable.img", status=inserted 6 | boot: a 7 | log: /dev/null 8 | mouse: enabled=0 9 | clock: sync=none, time0=utc 10 | display_library: x, options="gui_debug" 11 | magic_break: enabled=1 12 | com1: enabled=1, mode=socket-client, dev=localhost:2345 13 | debug: none -------------------------------------------------------------------------------- /tools/nim/.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.swp 3 | *.swo 4 | *.o 5 | *.img 6 | *.bin 7 | bridge 8 | bochsout.txt 9 | bx_enh_dbg.ini 10 | -------------------------------------------------------------------------------- /tools/nim/Makefile: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2023 Davidson Francis 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | AS ?= as 24 | OBJ = nim.o boot.o 25 | BIN = nim.bin boot.bin bootable.img 26 | LDFLAGS = -T linker.ld 27 | 28 | .PHONY: all bochs qemu clean 29 | 30 | all: $(BIN) 31 | 32 | # ASM Files 33 | %.o: %.S 34 | $(AS) $^ -o $@ 35 | 36 | nim.bin: nim.o 37 | $(LD) $(LDFLAGS) -Ttext=0x8210 --oformat binary $^ -o $@ 38 | boot.bin: boot.o 39 | $(LD) $(LDFLAGS) -Ttext=0x7c00 --oformat binary $^ -o $@ 40 | 41 | bootable.img: boot.bin nim.bin 42 | cat boot.bin nim.bin > bootable.img 43 | 44 | # Run on Bochs and QEMU 45 | bochs: bootable.img 46 | bochs -q -f .bochsrc.txt 47 | qemu: bootable.img 48 | qemu-system-i386 -boot a -fda bootable.img -soundhw pcspk 49 | 50 | clean: 51 | $(RM) $(OBJ) 52 | $(RM) $(BIN) 53 | $(RM) bx_enh_dbg.ini 54 | -------------------------------------------------------------------------------- /tools/nim/boot.S: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2023 Davidson Francis 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | .code16 24 | 25 | boot_init: 26 | cli 27 | mov $0x7C0, %ax 28 | add $544, %ax 29 | mov %ax, %ss 30 | mov $4096, %sp 31 | 32 | # Read 10 sectors 33 | mov $0xFFFF, %ax # segment 34 | mov %ax, %es 35 | mov $0x8210, %bx # offset addr 36 | mov $10, %al # num sectors to read 37 | mov $2, %cl # from sector 2 (1-based) 38 | call read_sectors 39 | 40 | # Far call to our module 41 | lcall $0xFFFF, $0x8A10 42 | 43 | # Hang 44 | 1: jmp 1b 45 | 46 | read_sectors: 47 | mov $2, %ah 48 | mov $0, %ch 49 | mov $0, %dh 50 | int $0x13 51 | jc .again 52 | ret 53 | .again: 54 | xor %ax, %ax 55 | int $0x13 56 | jmp read_sectors 57 | ret 58 | 59 | .fill 510-(.-boot_init), 1, 0 60 | .word 0xAA55 61 | -------------------------------------------------------------------------------- /tools/nim/linker.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT(binary) 2 | SECTIONS 3 | { 4 | .text : 5 | { 6 | *(.text) 7 | } 8 | 9 | /DISCARD/ : 10 | { 11 | *(*) 12 | KEEP(*(.text)) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tools/nim/nim.S: -------------------------------------------------------------------------------- 1 | # 2 | # MIT License 3 | # 4 | # Copyright (c) 2021-23 Davidson Francis 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | # 24 | 25 | .code16 26 | 27 | .macro dbg 28 | xchgw %bx, %bx 29 | .endm 30 | 31 | .macro delay_ms ms 32 | mov $(\ms*1000)>>16, %cx 33 | mov $(\ms*1000)&0xFFFF, %dx 34 | call delay2 35 | .endm 36 | 37 | # Our 'logo' 38 | .fill 2048, 1, 0x0A # >> CHANGE_HERE << 39 | 40 | ############################################################################### 41 | # System Helpers & Boot stuff # 42 | ############################################################################### 43 | 44 | # Backup registers and flags 45 | _start: 46 | pushfl 47 | pushal 48 | push %ds 49 | push %es 50 | push %fs 51 | 52 | # Define proper segment registers 53 | mov %cs, %ax 54 | mov %ax, %ds 55 | mov %ax, %es 56 | mov %ax, %fs 57 | 58 | # Set video mode: 80x25, 16-color text 59 | mov $0x0003, %ax 60 | int $0x10 61 | 62 | # Play siren 63 | mov $3, %cx 64 | ambulance: 65 | push %cx 66 | mov $4000, %bx 67 | call sound 68 | call delay 69 | mov $5000, %bx 70 | call sound 71 | call delay 72 | pop %cx 73 | loop ambulance 74 | 75 | # Mute 76 | call mute 77 | 78 | # Jump to game 79 | jmp main_screen 80 | 81 | # Let the player use their PC 82 | player_win_exit: 83 | call well_done_sound 84 | 85 | # Restore register 86 | pop %fs 87 | pop %es 88 | pop %ds 89 | popal 90 | popfl 91 | 92 | # Restore old instructions 93 | mov $0x4F02, %ax # >> CHANGE_HERE << 94 | int $0x10 95 | retf 96 | 97 | # ---------------------------------------------------- 98 | # sound: Make a sound with buzzer for a given pitch 99 | # 100 | # Parameters: 101 | # bx: Pitch 102 | # ---------------------------------------------------- 103 | sound: 104 | mov $182, %al 105 | out %al, $0x43 106 | mov %bx, %ax # Pitch 107 | out %al, $0x42 # Lower value 108 | mov %ah, %al 109 | out %al, $0x42 # Higher value 110 | in $0x61, %al 111 | or $0x3, %al 112 | out %al, $0x61 113 | ret 114 | 115 | # ---------------------------------------------------- 116 | # well_done_sound: Plays a well done/you win sound 117 | # ---------------------------------------------------- 118 | well_done_sound: 119 | mov $2111, %bx 120 | call sound 121 | delay_ms 80 122 | call mute 123 | 124 | delay_ms 200 125 | 126 | mov $2811, %bx 127 | call sound 128 | delay_ms 20 129 | call mute 130 | 131 | delay_ms 150 132 | 133 | mov $2111, %bx 134 | call sound 135 | delay_ms 300 136 | call mute 137 | 138 | delay_ms 1000 139 | ret 140 | 141 | # ---------------------------------------------------- 142 | # delay: Sleeps/delay for 500ms 143 | # ---------------------------------------------------- 144 | delay: 145 | mov $0x8600, %ax 146 | mov $0x3, %cx 147 | mov $0xD090, %dx 148 | int $0x15 149 | ret 150 | 151 | # ---------------------------------------------------- 152 | # delay: Sleeps/delay for a given time in CX:DS us 153 | # ---------------------------------------------------- 154 | delay2: 155 | mov $0x8600, %ax 156 | int $0x15 157 | ret 158 | 159 | # ---------------------------------------------------- 160 | # mute: Mute current sound 161 | # ---------------------------------------------------- 162 | mute: 163 | in $0x61, %al 164 | and $0xFC, %al # Turn off the speaker 165 | out %al, $0x61 166 | ret 167 | 168 | # ---------------------------------------------------- 169 | # read_number: Read a number with the arrow keys (up 170 | # and down) from 0 to 7, Enter selects the number. 171 | # 172 | # Returns: 173 | # cx: Number read. 174 | # ---------------------------------------------------- 175 | read_number: 176 | call read_kbd 177 | cmp $KBD_UP, %ah 178 | jne 1f 179 | add $1, %cx 180 | and $7, %cx 181 | jmp 3f 182 | 1: 183 | cmp $KBD_DOWN, %ah 184 | jne 2f 185 | sub $1, %cx 186 | and $7, %cx 187 | jmp 3f 188 | 2: 189 | cmp $KBD_ENTER, %ah 190 | jne read_number 191 | jmp 4f 192 | 3: 193 | # Get cursor position 194 | push %cx 195 | xor %bx, %bx 196 | mov $3, %ah 197 | int $0x10 198 | # Adjust column 199 | sub $1, %dl 200 | mov $2, %ah 201 | int $0x10 202 | # Draw again 203 | pop %cx 204 | mov %cl, %al 205 | add $'0', %al 206 | call print_char 207 | 208 | jmp read_number 209 | 4: 210 | ret 211 | 212 | # --------------------------------------------- 213 | # read_kbd: Reads a keypress from the keyboard 214 | # 215 | # This routine uses a polling approach because 216 | # the BIOS at this stage does not have keyboard 217 | # ints yet! (and I'm too lazy to setup one) 218 | # 219 | # Returns: 220 | # ah: Scan code 221 | # al: ASCII Character code 222 | # --------------------------------------------- 223 | read_kbd: 224 | .wait_ready_and_read_again: 225 | in $0x64, %al 226 | andb $1, %al 227 | cmp $0, %al 228 | je .wait_ready_and_read_again 229 | 230 | # Got something let us check 231 | in $0x60, %al 232 | cmp $0xE0, %al 233 | je .wait_ready_and_read_again 234 | 235 | # Nice, let us check if its key pressed (not released!) 236 | test $0x80, %al 237 | jz .wait_read_release 238 | jmp .out 239 | 240 | # We need to ignore the release event as well! 241 | .wait_read_release: 242 | mov %ax, %bx # Got our key, backup it 243 | jmp .wait_ready_and_read_again 244 | 245 | .out: 246 | # If release, we already have our key 247 | mov %bl, %ah 248 | ret 249 | 250 | # --------------------------------------- 251 | # print: Print a string to screen 252 | # 253 | # Parameters: 254 | # si: pointer to string 255 | # --------------------------------------- 256 | print: 257 | mov $0xE, %ah 258 | mov $0x0007, %bx 259 | .print_char: 260 | lodsb 261 | cmp $0, %al 262 | je .done 263 | int $0x10 264 | jmp .print_char 265 | .done: 266 | ret 267 | 268 | # --------------------------------------------- 269 | # print_char: Print a character to the screen 270 | # 271 | # Parameters: 272 | # al: character 273 | # --------------------------------------------- 274 | print_char: 275 | mov $0xE, %ah 276 | mov 0x0007, %bx 277 | int $0x10 278 | ret 279 | 280 | # --------------------------------------------- 281 | # clear_screen: Clear the screen 282 | # --------------------------------------------- 283 | clear_screen: 284 | # Clear screen 285 | push %ax 286 | push %bx 287 | push %cx 288 | push %dx 289 | mov $0x0700, %ax # Scroll down, whole window 290 | mov $0x07, %bh # White on black 291 | mov $0x0000, %cx # Row 0, Col 0 292 | mov $0x184f, %dx # Row 24, Col 79 293 | int $0x10 294 | # Set cursor to top 295 | mov $0x2, %ah 296 | mov $0x0, %bh 297 | mov $0x0000, %dx 298 | int $0x10 299 | pop %ax 300 | pop %bx 301 | pop %cx 302 | pop %dx 303 | ret 304 | 305 | ############################################################################### 306 | # Game helpers # 307 | ############################################################################### 308 | # -------------------------------------------- 309 | # draw_title: Draw game title 310 | # -------------------------------------------- 311 | draw_error: 312 | push %bp 313 | mov $0x1301, %ax # ah=13 (write string), al = 01, advance cursor 314 | mov $0x00CF, %bx # C = red back, F = white text 315 | mov $str_error_len, %cx 316 | mov $0, %dx # dh row, dl col 317 | mov $str_error, %bp 318 | int $0x10 319 | pop %bp 320 | ret 321 | 322 | # -------------------------------------------- 323 | # draw_title: Draw game title 324 | # -------------------------------------------- 325 | draw_title: 326 | call clear_screen 327 | call draw_error 328 | mov $str_game_title, %si 329 | call print 330 | ret 331 | 332 | # -------------------------------------------- 333 | # draw_sticks: Draw the sticks remaining 334 | # -------------------------------------------- 335 | draw_sticks: 336 | xor %cx, %cx 337 | 1: 338 | mov %cl, %al 339 | add $49, %al 340 | call print_char 341 | mov $str_stick_ddots, %si 342 | call print 343 | mov %cx, %si 344 | shl $1, %si 345 | add $sticks, %si 346 | mov (%si), %si 347 | cmp $0, %si 348 | je 2f 349 | sub $1, %si 350 | shl $1, %si 351 | add $str_sticks_vec, %si 352 | mov (%si), %si 353 | mov %si, %dx 354 | call print 355 | mov %dx, %si 356 | call print 357 | mov %dx, %si 358 | call print 359 | 2: 360 | add $1, %cl 361 | cmp $4, %cl 362 | jl 1b 363 | ret 364 | 365 | ############################################################################### 366 | # Game entry point # 367 | ############################################################################### 368 | main_screen: 369 | call draw_title 370 | mov $str_game_explanation, %si 371 | call print 372 | 373 | mov $str_who_starts, %si 374 | call print 375 | mov $str_arrow, %si 376 | call print 377 | mov $str_you, %si 378 | call print 379 | mov $str_empty_space, %si 380 | call print 381 | mov $str_me, %si 382 | call print 383 | 384 | # Reads who starts 385 | xor %dx, %dx 386 | who_starts: 387 | call read_kbd 388 | cmp $KBD_UP, %ah 389 | jne 1f 390 | sub $1, %dx 391 | and $1, %dx 392 | jmp 3f 393 | 1: 394 | cmp $KBD_DOWN, %ah 395 | jne 2f 396 | add $1, %dx 397 | and $1, %dx 398 | jmp 3f 399 | 2: 400 | cmp $KBD_ENTER, %ah 401 | jne who_starts 402 | jmp .out_who_starts 403 | 3: 404 | # Get cursor position 405 | push %dx 406 | xor %bx, %bx 407 | mov $3, %ah 408 | int $0x10 409 | # Adjust row 410 | sub $2, %dh 411 | mov $2, %ah 412 | int $0x10 413 | # Draw again 414 | pop %dx 415 | mov %dx, %si 416 | shl $2, %si 417 | add $str_arrow, %si 418 | call print 419 | mov $str_you, %si 420 | call print 421 | mov %dx, %si 422 | add $1, %si 423 | and $1, %si 424 | shl $2, %si 425 | add $str_arrow, %si 426 | call print 427 | mov $str_me, %si 428 | call print 429 | jmp who_starts 430 | .out_who_starts: 431 | 432 | # Save current turn 433 | movb %dl, curr_turn 434 | 435 | game_loop: 436 | movb sticks_count, %dl 437 | cmp $0, %dl 438 | jle game_loop_end 439 | call think 440 | jmp game_loop 441 | game_loop_end: 442 | 443 | # Elects who win 444 | call draw_title 445 | movb curr_turn, %dl 446 | lose: 447 | cmp $COMPUTER_TURN, %dl 448 | jne win 449 | mov $str_lose, %si 450 | call print 451 | jmp wait_key 452 | win: 453 | mov $str_win, %si 454 | call print 455 | jmp player_win_exit 456 | wait_key: 457 | mov $str_play_again, %si 458 | call print 459 | call read_kbd 460 | 461 | # Reset everything 462 | movb $16, sticks_count 463 | mov $sticks, %si 464 | movw $1, 0(%si) 465 | movw $3, 2(%si) 466 | movw $5, 4(%si) 467 | movw $7, 6(%si) 468 | 469 | # Start again 470 | call clear_screen 471 | jmp main_screen 472 | 473 | ############################################################################### 474 | # PC and Player movements # 475 | ############################################################################### 476 | # -------------------------------------------- 477 | # think: PC and Player movements 478 | # -------------------------------------------- 479 | think: 480 | call draw_title 481 | call draw_sticks 482 | 483 | .computer_turn: 484 | mov curr_turn, %dl 485 | cmp $COMPUTER_TURN, %dl 486 | jne .player_turn 487 | 488 | mov $str_think_pc, %si 489 | call print 490 | 491 | call read_kbd 492 | 493 | # Count amnt of heaps > 1 494 | xor %cx, %cx 495 | xor %dx, %dx 496 | mov $sticks, %si 497 | .count_sticks: 498 | cmpw $1, 0(%si) 499 | jg 1f 500 | jmp 2f 501 | 1: 502 | add $1, %dx 503 | 2: 504 | add $1, %cx 505 | add $2, %si 506 | cmp $4, %cx 507 | jl .count_sticks 508 | push %dx 509 | mov %sp, %bp 510 | 511 | .nim_sum: 512 | # Calculate NIM sum 513 | mov $sticks, %si 514 | mov 0(%si), %dx 515 | xor 2(%si), %dx 516 | xor 4(%si), %dx 517 | xor 6(%si), %dx 518 | 519 | # Check if there is a good movement or not 520 | cmp $0, %dx 521 | jne .recalculate_nim_sum 522 | 523 | .only_one: 524 | # 525 | # Too bad, if the player keeps playing like this the 526 | # computer will lose. 527 | # 528 | xor %cx, %cx 529 | 1: 530 | mov (%si), %ax 531 | cmp $0, %ax 532 | jne 2f 533 | add $1, %cx 534 | add $2, %si 535 | cmp $4, %cx 536 | jl 1b 537 | 2: 538 | subw $1, (%si) 539 | subb $1, sticks_count 540 | jmp .finish 541 | 542 | # 543 | # If NIM sum differs from 0, great, we *will* win. We just 544 | # need to recalculate the amount of pieces. 545 | # 546 | .recalculate_nim_sum: 547 | xor %cx, %cx 548 | mov $sticks, %si 549 | 1: 550 | mov (%si), %ax 551 | mov %ax, %bx 552 | xor %dx, %bx # bx = p ^ nimsum, ax = p, dx = nimsum 553 | cmp %ax, %bx 554 | jl 2f 555 | add $1, %cx 556 | add $2, %si 557 | cmp $4, %cx 558 | jl 1b 559 | 2: 560 | # 561 | # We have 2 options here: 562 | # a) More than one heap with more than one stick 563 | # b) Exactly one heap with more than one stick 564 | # 565 | # The option b) do not conforms with the maths as 566 | # expected and we fall in a case were we remove 567 | # n or n-1 sticks from that column. So is necessary 568 | # to bifurcate these two scenarios here. 569 | # 570 | cmpw $1, (%bp) 571 | jne .scenario_a 572 | 573 | .scenario_b: 574 | movb sticks_count, %bl 575 | subb %al, %bl 576 | mov %bx, %ax 577 | 578 | # 579 | # We need to leave a odd numbers of stickers to the player 580 | # If odd: remove all the sticks from the line 581 | # If even: remove all but one sticks from the line 582 | # 583 | andb $1, %al 584 | cmpb $0, %al 585 | je .even 586 | .odd: 587 | movw $0, (%si) 588 | movb %bl, sticks_count 589 | jmp .finish 590 | .even: 591 | movw $1, (%si) 592 | addb $1, %bl 593 | movb %bl, sticks_count 594 | jmp .finish 595 | 596 | .scenario_a: 597 | # Now we proceed as usual 598 | mov %bx, (%si) 599 | sub %bx, %ax 600 | sub %ax, sticks_count 601 | 602 | # Set turn to player 603 | .finish: 604 | movb $PLAYER_TURN, curr_turn 605 | add $2, %sp 606 | ret 607 | 608 | .player_turn: 609 | mov $str_think_askrow, %si 610 | call print 611 | 612 | # Read rows 613 | .read_rows: 614 | mov $1, %cx 615 | mov $'1', %al 616 | call print_char 617 | 1: 618 | call read_number 619 | # Check if selected row is valid 620 | cmp $0, %cx 621 | je 1b 622 | cmp $4, %cx 623 | jg 1b 624 | # Check if selected row is not empty 625 | mov %cx, %si 626 | sub $1, %si 627 | shl $1, %si 628 | add $sticks, %si 629 | mov (%si), %si 630 | cmp $0, %si 631 | je 1b 632 | sub $1, %cx # make row 0 based 633 | push %cx 634 | mov %sp, %bp 635 | 636 | # Read sticks amount 637 | .read_amnt: 638 | mov $str_newline, %si 639 | call print 640 | mov $str_think_askstick, %si 641 | call print 642 | mov $1, %cx 643 | mov $'1', %al 644 | call print_char 645 | 2: 646 | call read_number 647 | # Check if selected amount is valid 648 | cmp $0, %cx 649 | je 2b 650 | # Check if selected amount not exceeds the sticks amnt 651 | mov (%bp), %dx 652 | mov %dx, %si 653 | shl $1, %si 654 | add $sticks, %si 655 | mov (%si), %si 656 | cmp %si, %cx 657 | jg 2b 658 | 659 | # Decrement the row sticks 660 | mov (%bp), %dx 661 | mov %dx, %si 662 | shl $1, %si 663 | add $sticks, %si 664 | sub %cx, (%si) 665 | 666 | # Decrement sticks count 667 | sub %cx, sticks_count 668 | 669 | # Set turn to computer 670 | movb $COMPUTER_TURN, curr_turn 671 | 672 | # 'Pop' cx and return 673 | add $2, %sp 674 | ret 675 | 676 | # 677 | # Strings 678 | # 679 | str_error: 680 | .ascii " ERROR " 681 | .ascii " This computer is only allowed to run for people who win the following game: \r\n" 682 | .equ str_error_len, .-str_error 683 | 684 | str_game_title: 685 | .ascii " _______ _______ _______ \r\n" 686 | .ascii "| | ||_ _|| | |\r\n" 687 | .ascii "| | _| |_ | |\r\n" 688 | .asciz "|__|____||_______||__|_|__| by Theldus\r\n\r\n" 689 | 690 | str_game_explanation: 691 | .ascii "The NIM game consists of removing the sticks from the table, \r\n" 692 | .ascii "the amount you want, a single row at a time. The last to \r\n" 693 | .asciz "remove the sticks LOSES!! Good luck!!!\r\n\r\n" 694 | 695 | str_who_starts: 696 | .asciz "Who starts? (UP/DOWN, Enter select): \r\n" 697 | str_you: 698 | .asciz "You\r\n" 699 | str_me: 700 | .asciz "Me\r\n" 701 | str_arrow: 702 | .asciz "-> " 703 | str_empty_space: 704 | .asciz " " 705 | 706 | str_stick_ddots: 707 | .asciz ": \r\n" 708 | str_stick_1: .asciz " \xdb\xdb\r\n" 709 | str_stick_2: .asciz " \xdb\xdb \xdb\xdb\r\n" 710 | str_stick_3: .asciz " \xdb\xdb \xdb\xdb \xdb\xdb\r\n" 711 | str_stick_4: .asciz " \xdb\xdb \xdb\xdb \xdb\xdb \xdb\xdb\r\n" 712 | str_stick_5: .asciz " \xdb\xdb \xdb\xdb \xdb\xdb \xdb\xdb \xdb\xdb\r\n" 713 | str_stick_6: .asciz " \xdb\xdb \xdb\xdb \xdb\xdb \xdb\xdb \xdb\xdb \xdb\xdb\r\n" 714 | str_stick_7: .asciz " \xdb\xdb \xdb\xdb \xdb\xdb \xdb\xdb \xdb\xdb \xdb\xdb \xdb\xdb\r\n" 715 | str_sticks_vec: 716 | .word str_stick_1, str_stick_2, str_stick_3, str_stick_4 717 | .word str_stick_5, str_stick_6, str_stick_7 718 | 719 | str_think_pc: 720 | .asciz "\r\nThats my turn, can I play? (press any key) " 721 | str_think_askrow: 722 | .asciz "\r\nChoose the row you want (Up/Down arrows, Enter to select): " 723 | str_think_askstick: 724 | .asciz "How many sticks you want to remove? (Up/Down arrows, Enter to select): " 725 | 726 | str_newline: 727 | .asciz "\r\n" 728 | 729 | str_lose: 730 | .ascii " @@@ @@@ @@@@@@ @@@ @@@ @@@ @@@@@@ @@@@@@ @@@@@@@@\r\n" 731 | .ascii " @@! !@@ @@! @@@ @@! @@@ @@! @@! @@@ !@@ @@!\r\n" 732 | .ascii " !@!@! @!@ !@! @!@ !@! @!! @!@ !@! !@@!! @!!!:!\r\n" 733 | .ascii " !!: !!: !!! !!: !!! !!: !!: !!! !:! !!:\r\n" 734 | .asciz " .: : :. : :.:: : : ::.: : : :. : ::.: : : :: :::\r\n\r\n" 735 | str_win: 736 | .ascii " __ __ ___ __ __ __ __ ____ ____ \r\n" 737 | .ascii " | | |/ \\| | | | |__| | | \\ \r\n" 738 | .ascii " | | | | | | | | | || || _ |\r\n" 739 | .ascii " | ~ | O | | | | | | || || | |\r\n" 740 | .ascii " |___, | | : | | ` ' || || | |\r\n" 741 | .ascii " | | | | \\ / | || | |\r\n" 742 | .asciz " |____/ \\___/ \\__,_| \\_/\\_/ |____|__|__|\r\n\r\n" 743 | str_play_again: 744 | .asciz " =-=- Press any key to play again -=-=" 745 | 746 | # 747 | # Game data 748 | # 749 | curr_turn: .byte 0xF 750 | sticks_count: .byte 16 751 | sticks: .word 1,3,5,7 752 | 753 | # Keyboard 754 | got_released: .byte 0x0 755 | 756 | # 757 | # Constants 758 | # 759 | 760 | # Game 761 | .equ PLAYER_TURN, 0 762 | .equ COMPUTER_TURN, 1 763 | 764 | # Keyboard scan codes 765 | .equ KBD_UP, 0x48 766 | .equ KBD_DOWN, 0x50 767 | .equ KBD_ENTER, 0x1C 768 | -------------------------------------------------------------------------------- /tools/sdtlist/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | sdt 3 | -------------------------------------------------------------------------------- /tools/sdtlist/Makefile: -------------------------------------------------------------------------------- 1 | # MIT License 2 | # 3 | # Copyright (c) 2023 Davidson Francis 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in all 13 | # copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | # SOFTWARE. 22 | 23 | CC ?= cc 24 | CFLAGS += -Wall -Wextra -pedantic -O2 25 | OBJ = sdt.o 26 | BIN = sdt 27 | 28 | .PHONY: all clean 29 | 30 | all: $(BIN) 31 | 32 | # C Files 33 | %.o: %.c Makefile 34 | $(CC) $< $(CFLAGS) -c -o $@ 35 | 36 | # Main 37 | $(BIN): $(OBJ) 38 | 39 | clean: 40 | $(RM) $(OBJ) 41 | $(RM) $(BIN) 42 | -------------------------------------------------------------------------------- /tools/sdtlist/README.md: -------------------------------------------------------------------------------- 1 | # sdtlist 2 | `sdtlist` is a tool that, given the 1B module of an AMIBIOS8 BIOS, lists all the 3 | SMM handlers (i.e., the SMM Dispatch Table) present in that module and their 4 | respective physical positions in the file. 5 | 6 | The main idea is to be able to explore the existing modules and modify them 7 | freely as needed. 8 | 9 | ## Building & Usage 10 | Build and execute as follows: 11 | 12 | ```bash 13 | # Build 14 | $ make 15 | cc sdt.c -Wall -Wextra -pedantic -O2 -c -o sdt.o 16 | cc sdt.o -o sdt 17 | 18 | # Execute 19 | ./sdt amibody.1b 20 | >> Found SMI dispatch table! 21 | Size: 360 bytes 22 | Entries: 15 23 | Start file offset: 0x65cb1 24 | 25 | >> SMI dispatch table entries: 26 | Entry 0: 27 | Name: $SMICA 28 | Handle SMI ptr: 0xa8c86e4b 29 | Handle SMI foff: 0x47969 30 | Entry 1: 31 | Name: $SMISS 32 | Handle SMI ptr: 0xa8c885b5 33 | Handle SMI foff: 0x490d3 34 | Entry 2: 35 | Name: $SMIPA 36 | Handle SMI ptr: 0xa8c8891c 37 | Handle SMI foff: 0x4943a 38 | Entry 3: 39 | Name: $SMISI 40 | Handle SMI ptr: 0xa8c89f7b 41 | Handle SMI foff: 0x4aa99 42 | Entry 4: 43 | Name: $SMIX5 44 | Handle SMI ptr: 0xa8c89fd2 45 | Handle SMI foff: 0x4aaf0 46 | Entry 5: 47 | Name: $SMIBP 48 | 49 | ``` 50 | 51 | ## References 52 | [.:: A Real SMM Rootkit ::.](http://phrack.org/issues/66/11.html) 53 | -------------------------------------------------------------------------------- /tools/sdtlist/sdt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2023 Davidson Francis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #define _GNU_SOURCE 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | struct smi_disp_table { 40 | char sign[6]; /* $SMIxx. */ 41 | uint16_t flags; 42 | uint32_t ptr0; 43 | uint32_t ptr1; 44 | uint32_t ptr2; 45 | uint32_t handle_smi_ptr; 46 | } __attribute__((packed)); 47 | 48 | static struct smi_disp_table *sdt; 49 | static size_t num_entries; 50 | 51 | /** 52 | * @brief For a given input file @p file_buff of size @p file_size, 53 | * find the SMI Dispatch Table and allocates a new structure with 54 | * all its data. 55 | * 56 | * @param file_buff MMaped file buffer (1B module). 57 | * @apram file_size File size (1B module). 58 | * 59 | * @return Returns 0 if success, -1 otherwise. 60 | */ 61 | static int fill_sdt(const char *file_buff, size_t file_size) 62 | { 63 | const char *ptr1, *ptr2, *first_smi, *def; 64 | size_t rem_size; 65 | ptrdiff_t size; 66 | 67 | ptr1 = file_buff; 68 | rem_size = file_size; 69 | 70 | ptr2 = memmem(ptr1, rem_size, "$APMEND$", 8); 71 | if (!ptr2) 72 | return (-1); 73 | ptr2 += 8; 74 | ptr1 = ptr2; 75 | rem_size -= 8; 76 | 77 | /* Find first $SMI. */ 78 | ptr2 = memmem(ptr1, rem_size, "$SMI", 4); 79 | if (!ptr2) 80 | return (-1); 81 | 82 | first_smi = ptr2; 83 | ptr2 += 4; 84 | ptr1 = ptr2; 85 | rem_size -= 4; 86 | 87 | ptr2 = memmem(ptr1, rem_size, "$DEF", 4); 88 | if (!ptr2) 89 | return (-1); 90 | 91 | def = ptr2 + sizeof (struct smi_disp_table); 92 | size = def - first_smi; 93 | 94 | if (size % (sizeof (struct smi_disp_table))) 95 | return (-1); 96 | 97 | num_entries = (size_t)size/(sizeof(struct smi_disp_table)); 98 | 99 | printf( 100 | ">> Found SMI dispatch table!\n" 101 | "Size: %td bytes\n" 102 | "Entries: %td\n" 103 | "Start file offset: 0x%tx\n", 104 | size, 105 | num_entries, 106 | first_smi - file_buff 107 | ); 108 | 109 | /* Allocate the table and save it. */ 110 | sdt = calloc(1, (size_t)size); 111 | if (!sdt) 112 | return (-1); 113 | 114 | memcpy(sdt, first_smi, (size_t)size); 115 | return (0); 116 | } 117 | 118 | /** 119 | * @brief Gets a printable char or '?'. 120 | * 121 | * @param c Input character 122 | * 123 | * @return Returns the same character or a '?' if its not 124 | * printable. 125 | */ 126 | static inline int get_char(int c) { 127 | return (isprint(c) ? c : '?'); 128 | } 129 | 130 | /** 131 | * @brief Given the already filled SDT and the @p file_buff, 132 | * dumps all the SMI handlers and its respective addresses 133 | * and file offset. 134 | * 135 | * @param file_buff MMaped file buffer (1B module). 136 | * @apram file_size File size (1B module). 137 | */ 138 | static void dump_sdt(const char *file_buff, size_t file_size) 139 | { 140 | size_t i; 141 | char *handle_smied; 142 | uint32_t smied_ptr; 143 | uint32_t smied_handle_foff; 144 | 145 | /* Find '$SMIED' handle ptr addr. */ 146 | for (i = 0; i < num_entries; i++) 147 | if (!memcmp(sdt[i].sign, "$SMIED", 6)) 148 | break; 149 | 150 | if (i == num_entries) { 151 | fprintf(stderr, "$SMIED not present, aborting!\n"); 152 | return; 153 | } 154 | 155 | smied_ptr = sdt[i].handle_smi_ptr; 156 | 157 | /* 158 | * We need to find some known handler, and once found, 159 | * it's possible to know the offset of all the others. 160 | * 161 | * From my (brief) research, several AMIBIOS 8 have the 162 | * '$SMIED' and the handler of all of them are the same, 163 | * so the 'memmem' below searches for its initial 164 | * instructions... 165 | */ 166 | handle_smied = memmem(file_buff, file_size, 167 | "\x0e" /* push cs */ 168 | "\xe8\xe9\xff" /* call 0xb2fd */ 169 | "\xb8\x01\x00" /* mov ax, 0x1 */ 170 | "\x72\x45" /* jc 0xb35e */ 171 | "\x66\x60" /* pushad */ 172 | "\x1e" /* push ds */ 173 | "\x06" /* push es */ 174 | "\x68\x00\x70" /* push 0x7000 */, 175 | 16 176 | ); 177 | 178 | if (!handle_smied) { 179 | fprintf(stderr, "Unable to find the handle for $SMIED, aborting...\n"); 180 | return; 181 | } 182 | 183 | smied_handle_foff = (uint32_t)((ptrdiff_t)(handle_smied - file_buff)); 184 | 185 | printf("\n>> SMI dispatch table entries:\n"); 186 | 187 | for (i = 0; i < num_entries; i++) 188 | { 189 | printf("Entry %zu:\n", i); 190 | printf(" Name: %c%c%c%c%c%c\n", 191 | get_char(sdt[i].sign[0]), 192 | get_char(sdt[i].sign[1]), 193 | get_char(sdt[i].sign[2]), 194 | get_char(sdt[i].sign[3]), 195 | get_char(sdt[i].sign[4]), 196 | get_char(sdt[i].sign[5]) 197 | ); 198 | printf(" Handle SMI ptr: 0x%x\n", sdt[i].handle_smi_ptr); 199 | printf(" Handle SMI foff: 0x%x\n", 200 | (int32_t)smied_handle_foff + 201 | ((int32_t)sdt[i].handle_smi_ptr - (int32_t)smied_ptr) 202 | ); 203 | } 204 | } 205 | 206 | /* Main =). */ 207 | int main(int argc, char **argv) 208 | { 209 | int fd; 210 | char *file; 211 | struct stat s; 212 | 213 | if (argc < 2) 214 | errx(1, "Usage: %s <1b-module>\n", argv[0]); 215 | 216 | fd = open(argv[1], O_RDONLY); 217 | if (fd < 0) 218 | errx(1, "Error while opening!\n"); 219 | 220 | fstat(fd, &s); 221 | 222 | file = mmap(NULL, s.st_size, PROT_READ, MAP_SHARED, fd, 0); 223 | if (file == MAP_FAILED) 224 | errx(1, "Failed to mmap!\n"); 225 | 226 | if (fill_sdt(file, s.st_size) < 0) 227 | errx(1, "Unable to Fill SDT!\n"); 228 | 229 | dump_sdt(file, s.st_size); 230 | 231 | free(sdt); 232 | munmap(file, s.st_size); 233 | close(fd); 234 | return (0); 235 | } 236 | -------------------------------------------------------------------------------- /tools/skip_strings.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # MIT License 4 | # 5 | # Copyright (c) 2023 Davidson Francis 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | # Default threshold value 26 | THRESHOLD=8 27 | 28 | # Help function 29 | show_help() { 30 | echo "Usage: $0 [-h] [-t ] " 31 | echo " -h Display this help message" 32 | echo " -t Set the threshold value (default is $THRESHOLD)" 33 | echo " input_file Input file name" 34 | exit 0 35 | } 36 | 37 | # Parse parameters 38 | while getopts "ht:" opt; do 39 | case $opt in 40 | h) show_help ;; 41 | t) THRESHOLD="$OPTARG" ;; 42 | ?) show_help ;; 43 | esac 44 | done 45 | shift $((OPTIND - 1)) 46 | 47 | # Check if input file exists 48 | if [ ! -f "$1" ]; then 49 | echo "Error: Input file not found" 50 | show_help 51 | fi 52 | 53 | input_file="$1" 54 | output_file="$(basename "$input_file").dump" 55 | 56 | # Generate skips string 57 | strings=$(strings "$input_file" -n"$THRESHOLD" -td) 58 | skips="" 59 | while IFS= read -r line; do 60 | offset=$(echo "$line" | awk '{print $1}') 61 | string=$(echo "$line" | awk '{print substr($0, index($0, $2))}') 62 | length=${#string} 63 | skips="$skips -k$offset,$length" 64 | done <<< "$strings" 65 | 66 | # Generate output file 67 | ndisasm -b16 "$input_file" $skips > "$output_file" 68 | echo "Output file: $output_file" 69 | --------------------------------------------------------------------------------