├── ffxiii ├── 01_Archives.md ├── 02_Crypto_and_Classfiles.md ├── 03_JNI_and_Patching.md └── 03_images │ ├── blank_prompt.png │ ├── fixed_strings.png │ └── obfuscated_strings.png └── windows-ci ├── assets ├── cantverifypublisher.png ├── certificatechain.png ├── roottablestruct.png └── success.png └── part1_on_disk_patching.md /ffxiii/01_Archives.md: -------------------------------------------------------------------------------- 1 | Inside of the `white_data\sys` folder there are a couple of large `.bin` files. `filelist_*` and `white_*`. Searching the binary for strings containing `filelist` and then xrefing on that brings us to the game's main init function and game loop, located at `0x004054D0`. The first references we see to filelist strings are determining what language the game should be using by checking the name of the filelist file. 2 | 3 | ![Language determination disasm](http://i.imgur.com/ETygoY4.png) 4 | 5 | After performing some other initializations we don't really care about the game loads the entire contents of `sys/filelist%LANGCODE.%PLATFORM.bin` into memory, and then passes the contents of the file along with the name of it's corresponding data file, `sys/white_img%LANGCODE.%PLATFORM.bin` to a function I have called `FileController::AddBinFile` which is located at `0x00827C00`. 6 | 7 | ![hex-rays is cheating](http://i.imgur.com/EZQkFPu.png) 8 | 9 | The file controller is an object that handles all file-related operations for the game and calls the underlying platform specific code. The AddBinFile function adds an entry to the file controller that causes it to look inside of that bin file when trying to open a file, and fixes some offsets inside of the filelist header. 10 | 11 | ![hex-rays is like heroin, try it once and you're hooked for life](http://i.imgur.com/3XiZoE0.png) 12 | 13 | That function gives us a good idea of what the header of the filelist looks like: 14 | 15 | ![Why do things manually when you have a debugger that can make the app do things for you?](http://i.imgur.com/r2Nwodx.png) 16 | 17 | The first two DWORDs are pointers to somewhere further in the file. The third DWORD is the number of files contained in the archive. For each file there are four WORDs. At this point I don't know what the first two are, but the third is the subpackage number, and the fourth is the location of the file info within the subpackage info. 18 | 19 | That very first DWORD is a pointer to info used to retrieve subpackage info. For each subpackage there are three DWORDs: 20 | 21 | ![subpackage compression info](http://i.imgur.com/yje9Eca.png) 22 | 23 | The first DWORD is the size of the subpackage info, the second is the size of the compressed subpackage info, and the third is a pointer to the zlib-compressed subpackage info. 24 | 25 | After creating a couple of structs to reflect what we've learned so far 26 | ![filelist structs](http://i.imgur.com/gXBL230.png) 27 | that offset fixing function that I mentioned earlier now looks like: 28 | 29 | ![offset fixing function part 2: electric-boogaloo](http://i.imgur.com/Dzil5mF.png) 30 | 31 | The zlib-decompressed subpackage data for the first subpackage looks like: 32 | 33 | ![subpackage data](http://i.imgur.com/97dkoSO.jpg) 34 | 35 | It's a series of NULL-terminated strings deliminated by colons. Each string is split into four parts: 36 | 37 | 1. Index into the data archive file(`white_*.bin`) multiply by `0x800` to get an offset to the zlib data. 38 | 2. Uncompressed size 39 | 3. Compressed size 40 | 4. File name 41 | 42 | If the uncompressed size and compressed size are the same, then it is raw data and zlib decompression should not be applied. 43 | 44 | I wrote some terrible hacked-together code to extract archives. If you'd like to play around with it it's available here: http://pastebin.com/XbyAsFZ1. -------------------------------------------------------------------------------- /ffxiii/02_Crypto_and_Classfiles.md: -------------------------------------------------------------------------------- 1 | ### Intro 2 | 3 | Looking through the strings I found a lot of references to a JVM. Looking further into strings related to class loading such as `class file load count max over!\n`, `class info error`, and `class name too long` led me to the function which loads classes. The `*.clb` files within archives are a container for java classes and have optional crypto. 4 | 5 | ### Classfile Parsing 6 | 7 | The function which parses class files is located at `0x00BEC3C0` and I am calling it `ParseCLBFile`. CLB files are actually very very similar to oracle/sun jvm classfiles. The major differences are that CLB files are little-endian and have a fixed-size header. Please refer to the [official JVM documentation on classfiles](http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1) as I will be referring to the names of the fields in the ClassFile structure heavily. 8 | 9 | #### Magic 10 | 11 | There are three different code paths relating to the first four bytes of the file. 12 | 13 | * `'SSSS'` - Normal CLB file 14 | * `'TTTT'` - Encrypted CLB file 15 | * Anything else - Assumed to be an Oracle/Sun classfile. These are parsed by converting them to CLB files internally. 16 | 17 | #### Major and minor version 18 | 19 | These directly follow the magic, as in the official jvm classfile, and are never encrypted. 20 | 21 | #### Crypto 22 | 23 | Everything beyond the major and minor version can optionally be encrypted. The code for crypto is really messy within hex-rays because the executable is 32-bit and the crypto code makes heavy use of 64-bit numbers. For example, take a look at how the `key` variable is always indexed `2*x` and `2*x+1` in hex-rays' decompilation of this function. 24 | 25 | ![messy crypto code](http://i.imgur.com/ScpLM0U.png) 26 | 27 | Forcing the type of the `key` variable to `unsigned __int64*` only causes hex-rays to cast it to a `DWORD*`. I don't really want to get into the internals of how the crypto works, partially because it's messy, and partially because I don't understand it. However, a high level overview of the algorithm is: 28 | 29 | * Block cipher 30 | * 64-bit blocks 31 | * 64-bit key 32 | * Expanded to 32 64-bit numbers internally. 33 | * Two-step 34 | * S-box 35 | * Whatever it's doing in that screenshot above 36 | * Built-in checksum that's appended to the end of the encrypted data 37 | 38 | This same algorithm is also used for the encryption and decryption of savegames. Partially manually decompiled and slightly cleaned-up decryption code is included in the class converter down below. The key used for the crypto of CLB files are the first four bytes of the file, so `'TTTT'` and then the major and minor versions. 39 | 40 | After decryption the magic is changed to `'SSSS'` in the internal CLBFile structure. 41 | 42 | #### Fixed-size header 43 | 44 | Unlike official JVM classfiles, CLB files have a fixed-size header. General structure is a bunch of field counts, and then a bunch of pointers within the file to field data. The fact that the `ParseCLBFile` function has code to convert java classfiles to clb files helped greatly with figuring out this structure. 45 | 46 | ```cpp 47 | struct CLBFile 48 | { 49 | int magic; 50 | __int16 minor_version; 51 | __int16 major_version; 52 | __int16 constant_pool_count; 53 | __int16 access_flags; 54 | __int16 interfaces_count; 55 | __int16 fields_count; 56 | __int16 methods_count; 57 | __int16 reserved1; 58 | __int16 this_classname_hash; 59 | __int16 reserved2; 60 | constant_pool_info *pConstant_pool_info; 61 | constant_pool_info *pThis_class; 62 | void *pThis_classname; 63 | constant_pool_info *pSuper_class; 64 | void *pInterface_info; 65 | field_info *pField_info; 66 | method_info *pMethod_info; 67 | void *zero; 68 | }; 69 | ``` 70 | 71 | #### Constant Pool 72 | 73 | In official jvm classfiles constant pool entries are of a variable size depending on the type(`tag`). They are of a fixed size within CLB files. 74 | 75 | ```cpp 76 | struct constant_pool_info 77 | { 78 | int tag; 79 | int reserved; 80 | int tagInfoA; 81 | int tagInfoB; 82 | }; 83 | ``` 84 | 85 | The `tag` field matches up with the offical jvm, and `reserved` is always zero. `tagInfoA` and `tagInfoB` depend on the tag type. For the full details please see the parsing code in the class converter below. 86 | 87 | #### Attributes 88 | 89 | The one remaining major difference between official jvm classfiles and CLB files are how attributes are encoded. CLB files only allows two different attributes, `Code` attributes and `ConstValue` attributes. Methods are only allowed to have `Code` attributes, and fields are only allowed to have `ConstValue` attributes. Nothing else is allowed to have any type of attribute. 90 | 91 | ### Class converter code 92 | 93 | I hacked together some code to convert `*.clb` files to jvm class files so I could throw them in existing java decompilers. As before, the code is mostly terrible and it's all in one source file: http://pastebin.com/XkVh90Bt. 94 | 95 | ### Interesting classes 96 | 97 | The game's main class is `WhiteResident` and is located at `sys/script/WhiteResident.clb`. The main function of the game is called `WhiteMain`. A decompiled version is available here: http://pastebin.com/qwG8aPB2. All classes within the `fake` package are almost 100% jni bindings. However, there are a few helper functions that contain actual java bytecode. 98 | 99 | ### Next steps 100 | 101 | If you looked within the decompiled `WhiteResident` you may have noticed some debug menu options such as `i = Window.askChoice(str1, "FFXIII起動|ロケセレクタ|フィールドテスト|バトルテスト|ロールテスト|イベントテスト|ムービーテスト|キャラテスト|モーションテスト|CrystalToolsと通信|オートキャプチャ|オプション");`. I'm going to patch the binary so that `White.isUserBoot()` returns false and try to access these options. Spoiler: You totally can. I'll write up the post about it in a day or two when I'm feeling less lazy. -------------------------------------------------------------------------------- /ffxiii/03_JNI_and_Patching.md: -------------------------------------------------------------------------------- 1 | ## JNI 2 | 3 | Like I said last time, I'm going to patch the function `White.isUserBoot()` to return false, 4 | so that the debugging options appear. Inside of the `White` class that function looks like 5 | 6 | ```java 7 | public class White 8 | { 9 | public static native boolean isUserBoot(); 10 | } 11 | ``` 12 | 13 | ### Binding 14 | 15 | It's marked native so I to have to reverse how the engine handles JNI functions. 16 | Inside of the code that loads clb/class files there is a for loop that iterates over all 17 | of the methods in a class and calls a function that associates that method with jni info. 18 | 19 | Hex-rays'd loop: 20 | 21 | ```c++ 22 | for ( jj = 0; jj < a1a->methods_count; ++jj ) 23 | { 24 | RegisterJNIForMethod(a1a, &a1a->pMethod_info[jj]); 25 | v23 = GetUtf8StringFromConstantPool(a1a, a1a->pMethod_info[jj].name_index); 26 | a1a->pMethod_info[jj].name_hash = calcJvmHash(v23); 27 | v24 = GetUtf8StringFromConstantPool(a1a, a1a->pMethod_info[jj].descriptor_index); 28 | a1a->pMethod_info[jj].descriptor_hash = calcJvmHash(v24); 29 | a1a->pMethod_info[jj].flags = 0; 30 | if ( sub_BEE5E0(&a1a->pMethod_info[jj], a1a, a1a->pMethod_info[jj].name_hash, "main") ) 31 | a1a->pMethod_info[jj].flags |= 1u; 32 | else 33 | a1a->pMethod_info[jj].flags &= 0xFFFEu; 34 | } 35 | ``` 36 | 37 | `a1a` is an instance of the `CLBFile` class I described in the last write-up. 38 | Hex-rays of RegisterJNIForMethod: 39 | 40 | ```c++ 41 | signed int __cdecl RegisterJNIForMethod(CLBFile *a1, method_info *methodInfo) 42 | { 43 | signed int j; // [esp+0h] [ebp-320h]@10 44 | signed int k; // [esp+0h] [ebp-320h]@14 45 | CHAR *v5; // [esp+4h] [ebp-31Ch]@8 46 | char v6; // [esp+8h] [ebp-318h]@8 47 | int i; // [esp+20Ch] [ebp-114h]@1 48 | char v8[132]; // [esp+210h] [ebp-110h]@17 49 | int v9; // [esp+294h] [ebp-8Ch]@1 50 | char v10[132]; // [esp+298h] [ebp-88h]@13 51 | 52 | v9 = 0; 53 | for ( i = 0; *(a1->pThis_classname + i); ++i ) 54 | { 55 | if ( *(a1->pThis_classname + i) == 47 ) 56 | v9 = i + 1; 57 | } 58 | if ( v9 >= 512 ) 59 | { 60 | printf("class name too long\n"); 61 | sub_BEBA60("class name too long"); 62 | } 63 | strcpy(&v6, a1->pThis_classname + v9, 512); 64 | v5 = GetUtf8StringFromConstantPool(a1, methodInfo->name_index); 65 | for ( i = 0; ; ++i ) 66 | { 67 | if ( !*jniBindings[i].pClassName ) 68 | { 69 | *&methodInfo->reserved[0] = 0; 70 | return 1; 71 | } 72 | for ( j = 0; jniBindings[i].pClassName[j] && j < 127; ++j ) 73 | v10[j] = jniBindings[i].pClassName[j] ^ 0xAB; 74 | v10[j] = 0; 75 | for ( k = 0; jniBindings[i].pFuncName[k] && k < 127; ++k ) 76 | v8[k] = jniBindings[i].pFuncName[k] ^ 0xAB; 77 | v8[k] = 0; 78 | if ( !strcmp(&v6, v10) && !strcmp(v5, v8) ) 79 | break; 80 | } 81 | if ( methodInfo->access_flags & 0x100 ) 82 | { 83 | *&methodInfo->reserved[0] = jniBindings[i].pFunc; 84 | } 85 | else 86 | { 87 | printf("can't find native method [%s/%s]\n", &v6, v8); 88 | sub_BEBA60("can't find native method"); 89 | } 90 | return 1; 91 | } 92 | ``` 93 | 94 | First it loops over all of the entires in jniBindings, which I'll get to in a second, 95 | and compares the method name and class name associated with the binding to the method 96 | name and class name of the method that it was called with. Notice the `0xAB` xor, 97 | that'll become important in a minute. If a match is found and the access_flags has 98 | bit `0x100(ACC_NATIVE)` set, then it sets a field in the method info to a pointer to 99 | the binding's associated native code function. There's a problem though, look at 100 | the data contained in `jniBindings`: 101 | 102 | ![screenshot of IDA showing obfuscated strings in the jniBindingsData](03_images/obfuscated_strings.png) 103 | 104 | It's all fucked up. Thankfully it's just a simple xor with `0xAB` and can be fixed 105 | easily by an idc script 106 | 107 | ```c 108 | static fixString(ea) 109 | { 110 | auto needsfix = 0, i = 0; 111 | while(1) 112 | { 113 | auto c = Byte(ea+i); 114 | if(c == 0) 115 | { 116 | break; 117 | } 118 | if(c & 0x80) 119 | { 120 | needsfix = 1; 121 | break; 122 | } 123 | i = i + 1; 124 | } 125 | 126 | if(needsfix) 127 | { 128 | i = 0; 129 | while(1) 130 | { 131 | auto c2 = Byte(ea+i); 132 | if(c2 == 0) 133 | { 134 | break; 135 | } 136 | PatchByte(ea+i, c2^0xAB); 137 | i = i + 1; 138 | } 139 | } 140 | MakeStr(ea, -1); 141 | } 142 | 143 | static fixJni() 144 | { 145 | auto ea = 0x026CBF20; 146 | while(1) 147 | { 148 | auto pClassname, pFuncname, pFunc; 149 | pClassname = Dword(ea); 150 | pFuncname = Dword(ea+4); 151 | pFunc = Dword(ea+8); 152 | 153 | if(pClassname == 0) 154 | { 155 | break; 156 | } 157 | 158 | fixString(pClassname); 159 | fixString(pFuncname); 160 | 161 | if(strstr(Name(pFunc), "sub_") == 0) 162 | { 163 | MakeNameEx(pFunc, sprintf("JNI_%08X", pFunc), 0x20); 164 | } 165 | 166 | ea = ea + 12; 167 | } 168 | } 169 | ``` 170 | 171 | This script also replaces IDA's default `sub_` prefix with `JNI_` to show that 172 | it's a function called from jni when you're looking at the random xrefs of whatever. 173 | 174 | ![Screenshot of IDA showing fixed jniBindings struct](03_images/fixed_strings.png) 175 | 176 | Isn't that much better? It's now easy to figure out which native code function is 177 | associated with `White.isUserBoot()`. 178 | 179 | ### Patching 180 | 181 | ```c++ 182 | BOOL __cdecl JNI_White_isUserBoot(int a1) 183 | { 184 | BOOL v1; // ST04_4@1 185 | BOOL result; // eax@1 186 | 187 | v1 = (pAppGame->bootMode & BOOT_UserMode) != 0; 188 | *(a1 + 276) -= 8; 189 | **(a1 + 276) = 1; 190 | result = v1; 191 | *(*(a1 + 276) + 4) = v1; 192 | return result; 193 | } 194 | ``` 195 | 196 | `a1` is some weird JNI object that I don't really care about, but you can see 197 | that the return value is set based off of whether or not the `BOOT_UserMode(0x200)` 198 | flag is set in `pAppGame->bootMode`. `pAppGame` is a pointer to the global game 199 | object. I didn't know this at the time so in hex-rays it showed up along the lines 200 | of `(dword_XXXXXXXX + offset)`. The easy way to change the return value of this 201 | function would be to patch this it to always return 0, but that wouldn't exactly work 202 | because there could be other functions that also check the bootMode member. The way I 203 | got around this is by patching it where it's initially set. I did an immediate search 204 | for all instances of the offset from `pAppGame`, `408h`, and then inspected all of the 205 | functions until I found one that was setting the boot mode. I found a function I'm calling 206 | `AppGame::Initializer` that or'd the bootmode with `0x200`. It doesn't look like 207 | their compiler's optimizer is very good though, take a look at the assembly that 208 | ors it with `0x200`: 209 | 210 | ```assembly 211 | .text:0040989B ; 184: v23->bootMode |= BOOT_UserMode; 212 | .text:0040989B 1A0 mov edx, [ebp+var_138] 213 | .text:004098A1 1A0 add edx, 408h 214 | .text:004098A7 1A0 mov [ebp+var_C0], edx 215 | .text:004098AD 1A0 mov eax, 9 216 | .text:004098B2 1A0 shr eax, 5 217 | .text:004098B5 1A0 mov ecx, 9 218 | .text:004098BA 1A0 and ecx, 1Fh 219 | .text:004098BD 1A0 mov edx, 1 220 | .text:004098C2 1A0 shl edx, cl 221 | .text:004098C4 1A0 mov ecx, [ebp+var_C0] 222 | .text:004098CA 1A0 mov eax, [ecx+eax*4] 223 | .text:004098CD 1A0 or eax, edx 224 | .text:004098CF 1A0 mov ecx, 9 225 | .text:004098D4 1A0 shr ecx, 5 226 | .text:004098D7 1A0 mov edx, [ebp+var_C0] 227 | .text:004098DD 1A0 mov [edx+ecx*4], eax 228 | ``` 229 | 230 | It does a bunch of shifting around nonsense, I don't see why the compiler didn't 231 | replace those with immediate constants but I'm not a compiler engineer so, 232 | ¯\\\_(ツ)\_/¯. Nopping out the `or eax, edx` is sufficient to prevent the 233 | usermode boot flag from being set. 234 | 235 | If you take a look at the decompiled code of the [WhiteResident](http://pastebin.com/qwG8aPB2) 236 | class again you can see that besdies `White.isUserBoot()`, `White.getPlayMode()` 237 | is also checked. That can be patched using a similar method to `isUserBoot`. 238 | Thankfully, the White class actually defines all the possible play modes. 239 | 240 | 241 | ```java 242 | public class White 243 | { 244 | public static final int PlayModeDebug = 0; 245 | public static final int PlayModeRelease = 1; 246 | public static final int PlayModeAlpha = 2; 247 | public static final int PlayModeBeta = 3; 248 | public static final int PlayModeTgs = 4; 249 | public static final int PlayModeE3 = 5; 250 | } 251 | ``` 252 | 253 | I patched my copy of the game to set the play mode to 0, or debug. 254 | 255 | So now I'm ready to boot up the game and get into the debug menu, right? Wrong. 256 | 257 | ![Screenshot of game with blank prompt](03_images/blank_prompt.png) 258 | 259 | I can see the title of what's supposed to be a menu prompt, "FinalFantasyXIII 起動メニュー", 260 | set by `common.sfMesMenuNoSync(440, 140, 35, 5, "FinalFantasyXIII 起動メニュー", true);`, but 261 | there are no options. If I use the arrow keys, dpad, or scroll my mousewheel I can hear 262 | the 'ding' that you hear normally when scrolling through options, and random things happen 263 | when I click or hit enter but it's kind of hard having no idea what's actually happening. 264 | 265 | I'm not sure if the prompt options are supposed to show up in a connected instance of 266 | Crystal Tools(their game engine and editor), or if the code for displaying options on this 267 | kind of menu were just stripped out of the release build. To get around this I'm going to 268 | write a dll that'll hook into `Window.askChoice` and display and let you select from the 269 | options in a console window so that you know wtf is actually goin' on. 270 | 271 | If you see any mistakes in this post or have any questions, feel free to pm me on 272 | [reddit, /u/Avery3R](https://reddit.com/user/Avery3R) -------------------------------------------------------------------------------- /ffxiii/03_images/blank_prompt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Avery3R/re-writeups/4d8d690a80af69e337546bfa1e270e1766bda061/ffxiii/03_images/blank_prompt.png -------------------------------------------------------------------------------- /ffxiii/03_images/fixed_strings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Avery3R/re-writeups/4d8d690a80af69e337546bfa1e270e1766bda061/ffxiii/03_images/fixed_strings.png -------------------------------------------------------------------------------- /ffxiii/03_images/obfuscated_strings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Avery3R/re-writeups/4d8d690a80af69e337546bfa1e270e1766bda061/ffxiii/03_images/obfuscated_strings.png -------------------------------------------------------------------------------- /windows-ci/assets/cantverifypublisher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Avery3R/re-writeups/4d8d690a80af69e337546bfa1e270e1766bda061/windows-ci/assets/cantverifypublisher.png -------------------------------------------------------------------------------- /windows-ci/assets/certificatechain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Avery3R/re-writeups/4d8d690a80af69e337546bfa1e270e1766bda061/windows-ci/assets/certificatechain.png -------------------------------------------------------------------------------- /windows-ci/assets/roottablestruct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Avery3R/re-writeups/4d8d690a80af69e337546bfa1e270e1766bda061/windows-ci/assets/roottablestruct.png -------------------------------------------------------------------------------- /windows-ci/assets/success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Avery3R/re-writeups/4d8d690a80af69e337546bfa1e270e1766bda061/windows-ci/assets/success.png -------------------------------------------------------------------------------- /windows-ci/part1_on_disk_patching.md: -------------------------------------------------------------------------------- 1 | # Patching Yourself into Windows Code Integrity, Part 1: On-Disk Patching 2 | 3 | ## My Machine, My Code 4 | 5 | I started this whole thing because I wanted to run my own kernel-mode code while still having access games protected by 6 | anti-cheat that don't allow test signing, and I didn't want to shell out the time and money required to get an EV 7 | certificate. Really what I wanted to do was use PCI passthrough with a consumer NVIDIA GPU, but to do that you need to 8 | patch their driver, and to do that you need to be able to re-sign it. Even if you do have a valid code signing 9 | certificate there are still some restrictions on the types of drivers you can create, for example you can't create an 10 | early boot driver(these can only be signed certificates that Microsoft won't give to anyone except themselves and large 11 | A/V companies). The "traditional" way to do this is to use something like 12 | [UPGDSED](https://github.com/hfiref0x/UPGDSED), but while this allows you to run your own code it also allows any other 13 | malicious code to run, and greatly reduces security by disabling KPP(patchguard). It doesn't solve the early boot 14 | driver problem either. The root of trust has to be established somewhere, so why don't we just add ourselves to it? 15 | 16 | As far as I can tell, at least [one](http://www.alex-ionescu.com/?p=24) other person has done this before me, but 17 | nobody has publicly documented the process. 18 | 19 | I'm going to start out by patching binaries on disk, but the end result will be a UEFI application that patches all 20 | binaries in memory. 21 | 22 | ## A note on UEFI Secure Boot and Hypervisor Based Code Integrity 23 | This series is going to focus on normal/old code integrity, not that new fancy hypervisor based stuff. Mostly because 24 | I don't have a good hardware platform to test/debug it with. For the same reason I'm not going to focus on UEFI/Secure 25 | Boot. I will say though that there are many intel motherboard manufacturers that don't blow the fuses on the PCH, 26 | allowing you to flash your own firmware onto the board and run whatever you want if you so desire. You can also find 27 | the intel system tools if you search around, so in theory you could create your own firmware signing key, sign your 28 | patched firmware, and then blow the fuses yourself. You can also put your own secure boot keys in the firmware setup 29 | utility if it allows you to do so. 30 | 31 | ## PKI 32 | 33 | Before we go about signing anything we need to setup a PKI. Let's look at the certificate chain used to sign 34 | `bootmgfw.efi`. 35 | 36 | ![certificate chain](assets/certificatechain.png) 37 | 38 | They have a root CA, an intermediate CA, and then a code signing certificate. The root CA is a run-of-the-mill 39 | SHA256 RSA4096 certificate, and we can create our own by running: 40 | 41 | ``` 42 | makecert -pe -n "CN=NotNSA Root CA" -sv NotNSARoot2.pvk -a sha256 -sky signature -r -cy authority -len 4096 -e 01/01/2021 NotNSARoot2.crt 43 | ``` 44 | 45 | The code signing certificate has two EKUs defined: 46 | 47 | * `1.3.6.1.4.1.311.10.3.6` - Windows System Component Verification 48 | * `1.3.6.1.5.5.7.3.3` - Code Signing 49 | 50 | We can create a code signing certificate signed by our CA using the following command: 51 | 52 | ``` 53 | makecert -pe -n "CN=NotNSA Windows" -sv NotNSAWindows2.pvk -a sha256 -sky signature -cy end -len 2048 -e 01/01/2020 -ic NotNSARoot2.crt -iv NotNSARoot2.pvk NotNSAWindows2.crt 54 | ``` 55 | 56 | ## bootmgfw.efi and winload.efi 57 | 58 | Bootmgfw is the first windows component that will run on a computer, and therefore is the root of trust(ignoring the 59 | UEFI firmware). 60 | 61 | Both bootmgfw and winload use the same signature verification code, so if you figure out how to patch one you can patch 62 | the other very easily. 63 | 64 | The first thing I did was search for the subject name of the root certificate. That brought me to an ASN.1 encoded blob 65 | that had the name(after demangling) `unsigned char const near * const rgbMicrosoftRoot4_Name`. Following the cross 66 | references I was brought to a "root table", with the name `struct _ROOT_INFO const * const RootTable`. The _ROOT_INFO 67 | type was not defined in the public PDB so I had to figure it out myself, it was a list of pointers to ASN.1 encoded 68 | subject names, ASN.1 encoded public keys, and some numbers. After some playing around (pressing the `d` key in IDA 69 | and seeing what stuck), I came up with the following structure: 70 | 71 | ```c 72 | typedef struct _ROOT_INFO 73 | { 74 | QWORD cbName; 75 | unsigned char const* pbName; 76 | QWORD cbPubKeyInfo; 77 | unsigned char const* pbPubKeyInfo 78 | DWORD dwFlags; 79 | DWORD dwCertID; 80 | } ROOT_INFO, *PROOT_INFO; 81 | ``` 82 | 83 | ![root table struct](assets/roottablestruct.png) 84 | 85 | The `dwCertID` field seemed to match up with the "well-known" certificates used by the [Windows 10 S System Integrity 86 | policy](https://tyranidslair.blogspot.com/2017/07/device-guard-on-windows-10-s_20.html). 87 | 88 | Because of the way that the table was structured it wouldn't be that trivial to add another entry to it, so I decided 89 | to replace one of the testing roots instead. I replaced the name and public key with those from my root certificate, 90 | and then set the `dwFlags` field to the same value as the Microsoft Root 4. I signed the patched file and tried to boot, 91 | and hey, it worked! Bootmgfw checks it's own integrity so if it's signed incorrectly it will either bluescreen or print 92 | a warning message to WinDbg's log depending on whether or not a debugger is attached. Bootmgfw then verifies and starts 93 | winload, which verifies the kernel and all early boot drivers. After patching winload, I can sign my own kernel and 94 | early boot drivers and libraries that they depend on, such as CI.dll. 95 | 96 | ## Initial Load Attempt 97 | 98 | I wanted to see what would happen if I tried to load a driver signed with my cert without patching anything else, so I 99 | could get a hint of what I needed to patch next. I make a KMDF hello world driver and then signed it with the same cert 100 | I signed the bootloader with. I attached a kernel debugger to my VM and tried to load the driver with devcon. I was 101 | presented with the following screen: 102 | 103 | ![cantverifypublisher](assets/cantverifypublisher.png) 104 | 105 | ## wintrust and crypt32 106 | 107 | Nothing printed to my kernel debugger, so I assumed that this check was happening in user-mode somewhere and I tried to 108 | track it down. I watched what dlls were loaded into the devcon procress and `wintrust.dll` caught my eye. Wintrust 109 | pointed me to a function called `CertVerifyCertificateChainPolicy` in `crypt32.dll`. I looked around the strings of 110 | crypt32 and found a resource called `AUTHROOTS` in some binary format that contained a bunch of certificates. I figured 111 | out that windows has it's own certificate store binary format and you can use 112 | [certmgr](https://docs.microsoft.com/en-us/windows/desktop/seccrypto/using-certmgr) to manipulate these files. 113 | 114 | I added my root CA to the store and replaced the resource using 115 | [Resource Hacker](http://www.angusj.com/resourcehacker/). I wasn't able to get the edited file signed at first, I kept 116 | getting a "Bad exe format" error, but after some debugging I figured out that if you try to edit a signed PE file with 117 | resource hacker it will end up corrupting the file. You have to strip the signature out with signtool first. 118 | 119 | ## CI.dll 120 | 121 | After editing, I signed the modified crypt32 with my certificate, attached a kernel debugger, and attempted to boot. I 122 | was given the following error in the output of my kernel debugger: 123 | 124 | ``` 125 | ****************************************************************** 126 | * This break indicates this binary is not signed correctly: \Device\HarddiskVolume4\Windows\System32\crypt32.dll 127 | * and does not meet the system policy. 128 | * The binary was attempted to be loaded in the process: \Device\HarddiskVolume4\Windows\System32\smss.exe 129 | * This is not a failure in CI, but a problem with the failing binary. 130 | * Please contact the binary owner for getting the binary correctly signed. 131 | ****************************************************************** 132 | ``` 133 | 134 | This message was printed by `CI.dll`. I searched its strings for certificate subject names and found two root tables. 135 | They were both very similar to the root table in the bootloader, but different from it and from each other. I did the 136 | same sort of patching to them as I did to the bootloader and then it booted with my modified crypt32. 137 | 138 | It turns out modifying crypt32 wasn't necessary at all. I was trying to get it to implicitly trust my root CA so I 139 | wouldn't have to manually add it to the trusted root store, but it didn't work. Code Integrity was also throwing 140 | errors into the event log because while I signed crypt32 for boot, I didn't catalog sign it which is required after 141 | the system has been booted. I restored the original crypt32 binary and then manually added my root certificate to the 142 | trusted roots store the normal way(double clicking the cert and clicking install certificate etc etc). After doing 143 | that I tried installing my driver and hey, it worked! 144 | 145 | ![success screenshot](assets/success.png) 146 | 147 | Notice the lack of the test mode watermark on the bottom right. 148 | 149 | ## Conclusions 150 | 151 | Writing a UEFI app that will persist through the boot path is going to be a pain. One thing that I would like to look 152 | into but haven't yet is the "Secure Boot Policy", which has nothing to do with UEFI secure boot confusingly enough. 153 | You may recognize the term, a secure boot policy is the "golden key" that unlocked Windows RT devices. It's also 154 | possible that device guard is an evolution of secure boot policies, so device guard documentation may aid in reversing. 155 | The default secure boot policy is stored as a resource of bootmgfw, and from an initial glance doesn't have a signature 156 | (a signature is present when the policy is an external file). So if UEFI will boot bootmgfw, then whatever secure boot 157 | policy is embedded in it will be used unless another one is provided. Some strings and symbols have hinted to me that 158 | it may be possible to add an additional signer certificate using a secure boot policy. As far as I know too, nobody has 159 | publicly documented the secure boot policy format, so that might be fun to do anyways. 160 | 161 | If you have any questions or comments, feel free to get in touch with me on reddit 162 | [/u/Avery3R](https://reddit.com/r/avery3r) or twitter [@Avery3R](https://twitter.com/avery3r). --------------------------------------------------------------------------------