Gigabyte GA-C1037UN UEFI/BIOS mod: how to load default/optimized settings in case of low CMOS battery


I understand that completely. I really appreciate your help, and it’s not my intetion neither to test your patient nor to take advante of your help. However, I don’a agree with the last sentence. These small details helped me to continue this work a bit more independently so that’s not a waste of time from my point of view, but a huge advantage.
Without your help I would still be at the beginning of this task, but without my contribution it becomes your work, not mine.


Well, I tried to open AMIBCP > “Extract Strings”, I can see the handle/token is 80h (absolute 00B2h) but it’s in the GUID [B1DA0ADF-4F77-4070-A88E-BFFE1C60529A] (the AMIBCP one), not in E609E497C14CD91181F6000000000000.

I tried on UEFITool and I found:

1
 
Unicode text "bios has been reset" found in 97E409E6-4CC1-11D9-81F6-000000000000 at offset 175Ch
 

And that's ok, but not in E609E497C14CD91181F6000000000000

However, our point 6:
Here's the list of CHECKPOINT it prints before the message:
--, [too fast, 0x18 I think - I filmed it with the smartphone but that's too fast even for it], 0x15, 0x3B, 0x32, 0x4F, [2-3 very fast], 0x62, 0x88, 0x72, [...], 0x99, 0x88, 0x9C, [...], 0xB2, 0x24, 0xA2, 0xA9, 0xAB. Some where so fast that I weren't able to film them. However, it freezes at 0xAB, it displays the message and waits for the input.
On the screen, it does not display absolutely nothing. Before the message, black screen.

EDIT:
I've got CORE_DXE source. Do you think it's reasonable to look in the source before, and in the disassembled code then? OEM should not customize this module, right? The code is >here< in Core > CORE_DXE

The more time I spend on CORE_DXE, the more I belive that the module we need to modify is this.

CORE_DXE contains HII (human interface infrastructure) implementation, the UEFI protocol that manages with strings and user interface in general (AMITSE does another thing in my opinion - it only displays the strings). Here’a an >explanation of HIIS<. The author said:



Here’s the HII String Protocol (the explanation of the specification): >link<
Here’s the HII Database: >link<
And here’s the code in HiiString.c, that shows the way CORE_DXE module > HII functions retrives the string

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
 
//----------------------------------------------------------------------------
// Procedure: AmiHiiGetString
//
// Description:
// This function is HII String protocol function GetString implementation
//
// Input:
// IN EFI_HII_STRING_PROTOCOL *This - Pointer to EFI_HII_STRING_PROTOCOL structure
// IN CHAR8 *Language - Language definition
// IN EFI_HII_HANDLE PackageList - Handle of package list
// IN EFI_STRING_ID StringId - String Id
// OUT EFI_STRING String - Pointer to output string
// IN OUT UINTN *StringSize - Pointer to string size value
// OUT EFI_FONT_INFO *StringFontInfo - Pointer to returned string font info
//
// Output:
// EFI_SUCCESS - Function executed successfully
// EFI_INVALID_PARAMETER - String or StringSize or Language is NULL
// EFI_NOT_FOUND - String not found
// EFI_BUFFER_TOO_SMALL - Allocated buffer too small
//
//----------------------------------------------------------------------------
//<AMI_PHDR_END>
EFI_STATUS AmiHiiGetString(
IN CONST EFI_HII_STRING_PROTOCOL *This,
IN CONST CHAR8 *Language,
IN EFI_HII_HANDLE PackageList,
IN EFI_STRING_ID StringId,
OUT EFI_STRING String,
IN OUT UINTN *StringSize,
OUT EFI_FONT_INFO *StringFontInfo OPTIONAL)
{
PACKAGE_LIST_RECORD *Record = (PACKAGE_LIST_RECORD *)PackageList;
STRING_RECORD DbKey;
INT8 Vicinity;
STRING_RECORD *Found;
UINTN *FindRecord;
UINT32 Size;
EFI_STATUS Status;
LANGUAGE_LINK *Ptr;
UINT32 i;
UINT16 SaveLanguage;
 
if (Language == NULL || StringId < 1 || StringSize == NULL || PackageList == NULL)
return EFI_INVALID_PARAMETER;
 
if (String == NULL && *StringSize != 0)
return EFI_INVALID_PARAMETER;
 
if (Record->Signature != PACKAGE_LIST_RECORD_SIGNATURE)
return EFI_NOT_FOUND;
 
DbKey.StringId = StringId;
Status = GetLanguageId(Record, Language, &(DbKey.LanguageId), &Ptr);
if(EFI_ERROR(Status))
return EFI_INVALID_LANGUAGE;
 
Status = DbeLocateKey(&(Record->StringDb), 0, &DbKey, &FindRecord, &Vicinity, NULL);
if(EFI_ERROR(Status))
{
//string not found in database, try different languages
SaveLanguage = DbKey.LanguageId;
for(i = 0; i < Record->LanguageList.Size; i++)
{
if (i == SaveLanguage)
continue;
DbKey.LanguageId = (UINT16)i;
Status = DbeLocateKey(&(Record->StringDb), 0, &DbKey, &FindRecord, &Vicinity, NULL);
if(!EFI_ERROR(Status))
return EFI_INVALID_LANGUAGE; //string exists but with different language
}

return EFI_NOT_FOUND;
}
 
Found = (STRING_RECORD *)(UINTN)(*FindRecord);
 
Size = StrSize16(Found->String);

if(Size > *StringSize)
{
*StringSize = Size;
return EFI_BUFFER_TOO_SMALL;
}
 
StrCpy16(String, Found->String);
*StringSize = Size;
 
return EFI_SUCCESS;
}
 


Another part of CORE_DXE, ConSplitter (I don't know what the name means) seems to expose high-level function to get data from keyboard (CSReadKeyStrokeEx, CSWaitForKey, ...)

Finally, I think I found the the code/macros that should print the breakpoints. Look at here (at the notes):
Function : checkpoint
File: x64AsmLib.asm
Description: VOID checkpoint(IN UINT8 c) writes the value c to port 0x80.
Input: IN UINT8 c
Notes: This routine should only be used if the PROGRESS_CODE or PEI_PROGRESS_CODE macros are unavailable.

While I agree that providing extra details can help you to follow / participate in this research, or even help you to solve / finding a trace to the solution, it still doesn’t change the fact that I have to reply to questions that seem elementary in my book.

B1DA0ADF-4F77-4070-A88E-BFFE1C60529A is AMITSE. The name is just for human readability, the machine only knows and cares about the GUID. All strings are placed in sections with GUID 97E409E6-4CC1-11D9-81F6-000000000000, how it would help to have that repeating X times in the saved strings? The parent file is more important and unique in a volume, thus it is used. 97E409E6-4CC1-11D9-81F6-000000000000 is E609E497C14CD91181F6000000000000 in hex string, how else do you store that GUID in binary form? The questions to the above answers is the exact thing I tried to avoid. They can be easily answered with simple logic and EFI basics, they bring nothing new or helpful to the table.

The codes 0xB2 - Legacy Option ROM Initialization, 0xA2 - IDE Detect, 0xA9 = Start of Setup can be used to trace the code a little better than 0xAB - Setup Input Wait, because those codes are linked to more complex and better documented actions. However, here is the bad news. Without having an immediate and secure hook to base my research, I will have to decline or heavily postpone this task. Having the source code is not as important as it sounds, because: a) I have to read it thoroughly and b) understand it. Which is not the case, as: a) I’m deeply covered in some personal projects and b) I can understand C code, but not cover complex sources. Even if I had the time and skill to cover this first step, I would hit wall number two: trying to match this code to some assembly instructions. Which is not the case, again, as I don’t have: a) enough time and b) enough knowledge. I can understand basic instructions, but not convert from C code to assembly or vice-versa.

I’m not sure that following generic functions like the ones from HiiString or ConSplitter would help. Even if you find them in IDA, how would you link them to your issue? You should search something that it is as close as possible to that 0xAB checkpoint, while still being easily traceable in IDA (or your favourite disassembler). It gets even more complicated when you don’t know which module to inspect: is it AMITSE, CORE_DXE, any other involved? For instance, AMIBCP is loading the tree (most likely) from the third section of AMITSE. The keywords to search next in sources is STRING_TOKEN and $SPF. But as I said, you need someone that knows C/C++, knows the sources, knows assembly, has the time and will to implement a patch. Humanity’s last hope may be in CodeRush. And the joke is not so much of a joke, as there are only a few independent researchers investigating UEFI (to my knowledge), even fewer that would provide a patch for a specific issue, for a specific user. This brings me to what should have been my first question: have you tried asking Gigabyte for a patched BIOS?


Yes, I asked both AMI and Gigabyte. AMI replied that they cannot help me, while Gigayte didn’t answer.


I knew from the beginning that this is the hardest task I’ve ever have to deal with. I completely understand why you have to decline this task. I have nothing to say other than thank you for your time - most of the people would not even have started this task. I think I’ll continue to try, maybe I’ll still continue to publish my progresses on this thread.

Just another question: I’ve another idea, probably my last trump card. Tell me (@lordkag ) if in your opinion, this makes sense:
I thought I might add some INT 3h (breakpoint) in modules AMITSE and CORE_DXE. I will first put one breakpoint early in the code and try to boot:
1. if does not show the message
1.A. remove breakpoint
1.B. add breakpoint later in the code
2. flash & try until you see the message. That will be the point involved
What do you think?

EDIT: I forgot INT 3h is a software breakpoint. Is it better to use hardware breakpoint (DR0-DR3)?

How do you add INT 3 to a compiled file and where do you add it, in your specific case? This is not so much a question towards you, but rather a rhetorical one, as I have no knowledge of doing that.


I’ve extracted the module. I substitute with an hex editor one instruction in the .code section with 0xCC + NOPs. Then I insert the modified module in the bios, flash & try.
Where? First in the start function in CORE_DXE (probably at this point my message is not shown yet), then I move the breakpoint later in the code until I see my message. It would be the point, right?

EDIT: Well, there’a problem. I flash the BIOS with breakpoint. It stops before showing the message and (obviously) before booting. Result? I won’t be able to flash it again.
However, C1037UN has dual bios, I might force a “BIOS recovery” to return to the original one.

Going on with the project:
1. Before continuing, I tried to force a DualBIOS recovery. You just need to play a little with the power button and the reset button, and a prompt will appear on the screen saing “bios is corrupted…” and it will restore the backup version it has (F1 or F2, I don’t know). I’m sure I’ll need it.
2. The strategy

The strategy
This method will be applied to the modules CORE_DXE and AMITSE
Do you know PE code injection? Let me make an example:

1
2
3
4
5
 
Before:
0: instruction_0
2: instruction_1
6: instruction_2
...
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
Later:
0: jmp myroutine
2: instruction_1
6: instruction_2
...
myroutine:
move dx, mycode ; display a checkpoint defined by me on port 80
out 80h, dx
move eax, 0x7FFFFFFF ; add a sort of delay - I want 10 second more or less, I'll have to guess this number
myroutine_delay_loop:
sub eax, 1
jnz myroutine_delay_loop
; delay ended
instruction_0 ; do the instruction(s) you removed from the top
jmp 2; jump back to the code
 

Now, where to put myroutine? There's no "free space" in both modules, right?
But in both modules there are some strings in the .data section. Substituting the strings with random bytes is not a big problem, I suppose. In the worst case, I will see bad-formatted strings. If the platform does not check the permissions (.data should be r/w but not x) this can work. What do you think guys?

At the same time, I'm having a look at the Intel PCH NM70 datasheet. Probably there's a register memory mapped or I/O mapped that reads if the CMOS battery is present or not. Finding the address/port could bring me to the solution.