[Back Home]





Asus Probe Hacking

If you have a asus motherboard with unknown resistors before sensor chips, and you need correct computation formula, this document is for you.

Many (nearly all) mainboards manufactures are known not to provide necessary conversion formulas, thus rendering voltage readings for example from
lm_sensors project useless. Fortunately there are two quite painfull ways how to get the formulas.

First way is to hack the BIOS image and disassemble right parts and reconstruct the formulas from assembly code.

Second way is to hack the manufacturer monitoring software such as Asus Probe, or PC Alert software.

Word hack means to use Interactive Disassembler and get the assembly code of the binary execeutables and find the place where computrations of voltage readings is performed I already made some BIOS hacking as well as ASUS Probe hacking. I will stop providing the info when asus will tell us the formulas on
request.


ASUS SYSTEM MONITOR INTERNALS

Before I will present rest I will say few words about windows DLL.

First it is possible to use the objdump utillity to see the Import/Export symbols. Second some DLLs are loaded runtime. In $indows there are function similar to dlopen and dlsym - LoadlibraryA and Getprocaddress. These are called right in init routine of dll which is usually first function in codesegment of DLL.

Code will look like this:

10001017                 mov     edi, ds:LoadLibraryA
1000101D push offset aAsmihwio_dll
10001022 call edi
10001024 test eax, eax ;test if zero
10001026 mov dword_1000D950, eax ;store
1000102B jnz short loc_10001047 ;if not zero jump

EDI is a pointer to LoadLibraryA function, one param on stack is string name of DLL that is to be loaded. If success in EAX is handle to opened DLL. Note that:

 mov     dword_1000D950, eax


Should be:

 mov     [dword_1000D950], eax


The IDA FW does not put [] there, So DLL is opened now load some functions:

10001047 loc_10001047:           ; CODE XREF: dllmain+2B^Xj
10001047 mov esi, ds:GetProcAddress
1000104D push offset aInport
10001052 push eax
10001053 call esi
10001055 mov dword_1000D960, eax

offset aInport was some address. I pressed enter on it and have seen like:

(another text)

1000F58C                 db  47h ; G
1000F58D db 65h ; e
1000F58E db 74h ; t
1000F58F db 4Ch ; L
1000F590 db 6Fh ; o
1000F591 db 63h ; c
1000F592 db 61h ; a
1000F593 db 6Ch ; l
1000F594 db 65h ; e
1000F595 db 49h ; I
1000F596 db 6Eh ; n
1000F597 db 66h ; f
1000F598 db 6Fh ; o
1000F599 db 57h ; W

If you press a key it will become ascii text

1000A13C aInport         db 'InPort',0           ; DATA XREF: dllmain+4D.o

By pressing ESC you will return back to push instruction. If you press o key it will become

1000104D                 push    offset aInport

I think later IDA versions do this automatically After the function proceed,
10001055                 mov     dword_1000D960, eax
In that memory location will be the address of fuction
so if you
call dword_1000D960
Inport will be called. By pressing ENTER on dword_1000D960, you can see referencies where the Inport is called. If you press n you can dword_1000D960 rename dword_1000D960  to something else like call_inport.

Lets go and DIG to ASUS stuff :)

And now for something completly different

Asus is known not to provide their computation formulas to anyone. Lets see how their monitorin software works :)

I downloaded the instalation files. And fire up the instalation with wine. To some point it went fine. (Of course it failed on SYS driver loading) I just grab bunch of dll in /tmp directory just before it finally chrashed. I started from ASUS.DLL which was quite promissing. I just used objdump -x ASUS.DLL | less and found following exported symbols:

        [   5] GetVoltIN0
        [   6] GetVoltIN1
        [   7] GetVoltIN2
        [   8] GetVoltIN3
        [   9] GetVoltIN4
        [  10] GetVoltIN5
        [  11] GetVoltIN6

Lets have look whats inside (just use IDA37FW)

ASUS.DLL: GetVoltINXX      proc near 
100016E7 push ebp
I100016E8 mov ebp, esp ; EBP=ESP
I100016EA sub esp, 10h ;ESP=ESP-10h
I100016ED mov dword ptr [ebp-8], 100XXh
I100016F4 lea eax, [ebp-0Ch]
I100016F7 push eax
I100016F8 push 4
I100016FA lea ecx, [ebp-4]
I100016FD push ecx
I100016FE push 0
I10001700 push 0
I10001702 mov edx, [ebp-8]
I10001705 push edx
I10001706 call j_AsmiCtrl_1
I1000170B add esp, 18h
I1000170E test eax, eax
I10001710 jz short loc_1000172D
10001712 cmp dword ptr [ebp-4], 0
I10001716 jle short loc_10001720
I10001718 mov eax, [ebp-4]
I1000171B mov [ebp-10h], eax
I1000171E jmp short loc_10001728
10001720 loc_10001720: ; CODE XREF: GetVoltIN1+2F^j %
I10001720 xor ecx, ecx
I10001722 sub ecx, [ebp-4]
I10001725 mov [ebp-10h], ecx
I10001728
I10001728 loc_10001728: ; CODE XREF:GetVoltIN1+37^j %
I10001728 mov eax, [ebp-10h]
I1000172B jmp short loc_1000172F
1000172D loc_1000172D: ; CODE XREF:GetVoltIN1+29^j %
I1000172D xor eax, eax
I1000172F
I1000172F loc_1000172F: ; CODE XREF: GetVoltIN1+44^j %
I1000172F mov esp, ebp
I10001731 pop ebp
I10001732 retn
I10001732 GetVoltIN1 endp

This function allocates some local structure on stack and calls AsmiCtrl. Which is a another DLL. Lets use IDA again and see what is there. If you take a look to the ctrl function you will find that it is again only a wrapper for a function call called lmcontrol. If you use objdump again you will find that some DLL contain this function some not. How are these DLL loaded?

Answer is initialization routine in AsusCtrl.DLL. It just calls AsmiEnum.dll(AsusIoControl), and it will provide handler for right DLL.

ASUSCTRL:

100010FD mov eax, [ebp+1Ch] ;retval
I10001100 push eax
I10001101 mov ecx, [ebp+18h]
I10001104 push ecx
I10001105 mov edx, [ebp+14h]
I10001108 push edx
I10001109 mov eax, [ebp+10h]
I1000110C push eax
I1000110D mov ecx, [ebp+0Ch]
I10001110 push ecx
I10001111 mov edx, [ebp+8]
I10001114 push edx
I10001115 call lmControl

So we have now this chain of DLLs

ASUS.DLL -> AsmiCtrl.DLL -> AsmiEnum.Dll

AsmiEnum.dll just tries to find out what mainboard the running system has. It uses PCI IDs and DMI information.

I stated later that some DLL do has lmcontrol some not. How does it work? Well Chips that use ISA access to monitoring functions has lmcontrol function, rest of DLLs are Smbus drivers. This drivers are internally used by ASMIASPM.DLL which contains  smbus engine and lmcontrol function. I still dont know how are differnt monitoring chips supported by this DLL.

I own Asus Motherboard with ITE8705 chip so I was curious about the ASMI8705.DLL. Now we know that lmcontrol function is present in this dll.

If you trace the arguments in DLL chain you will get following:

EBP-0
EBP-2
EBP-4 = ?
EBP-8 = 100XX
EBP-C = ?
EBP-E
EBP-10 = ESP

ASUSCTRL.DLL versus ASUSDLL versus ASMI8705 param mapping
[ebp+1C] EBP-C ESP+18 ESP+28 retval
[ebp+18] 4 ESP+14 ESP+24 size?
[ebp+14] EBP-4 ESP+10 ESP+20 value
[ebp+10] 0 ESP+C ESP+1C
[ebp+0C] 0 ESP+8 ESP+18
[ebp+8] = EBP-8 control_code ESP+4 ESP+4+10 = ESP+14;

LMCONTROL prototype will be something like this:
Lmcontrol(int control_code,0,0,int *val,int size,int *retval);

Now big mystery will be shown. Where are the conversion formulas for voltages readings? There are right in this DLL. There is simple case consruct that according to control_code will return right inX value. Access to hardware is made via ASMIHWIO.DLL (it contains Inport, Outport etc) Let see what inX lines are monitored by Asus. If it does not contain any conversion formulas no ASM list is provided.

IT8705:

    label in0 "VCore 1" ; monitored by ASUS
label in1 "VCore 2" ; monitored by ASUS
label in2 "+3.3V" ; monitored by ASUS

I100015CB and eax, 0FFh
I100015D0 add esp, 4
I100015D3 mov ecx, eax
I100015D5 shl eax, 3
I100015D8 sub eax, ecx
I100015DA lea ecx, [eax+eax*2]
I100015DD mov eax, 51EB851Fh
I100015E2 shl ecx, 6
I100015E5 mul ecx
I100015E7 mov eax, [esp+4]
I100015EB shr edx, 4
I100015EE mov [eax], edx

EAX = 8bit value

ECX=EAX
ECX=ECX*8
EAX=EAX-ECX
ECX=EAX*2+EAX
EAX=0x51EB851F
ECX=ECX*64
EDX:EAX=EAX*ECX
EDX=EDX/16 => retval

label in3 "+5V" is monitored by ASUS

all motherboards except ASUSD1
return EAX*64

ASUSD1:
ECX=EAX*8+EAX
EDX=EAX+ECX*2
EAX=0xAAAAAAAB
EDX=EDX*16
EDX:EAX=EAX*EDX
EDX=EDX/2
reurn EDX

label in4 "+12V" not monitored
label in5 "-12V" not monitored
label in6 "-5V" monitored
ECX=0xE8000
EAX=EAX*9
EAX=EAX*512
ECX=ECX-EAX
EAX=0x24924925
EDX:EAX=ECX*EAX
EDX=EDX-ECX
ECX=ECX/2
ECX=ECX+EDX
ECX=ECX/32
?????

label in7 "Stdby" not monitored
label in8 "VBat" not monitored



Some InX routines contains multiple formulas. In init function of this DLL
are strings compares for this motherboards:

I1000A0A4 aP4s533X        db 'P4S533-X',0         ; DATA XREF: dllmain+264^o
I1000A0B0 aP4s533mx db 'P4S533MX',0 ; DATA XREF: dllmain+22C^o
I1000A0BC aP4sgxMx db 'P4SGX-MX',0 ; DATA XREF: dllmain+1F4^o

1000A0CC aA7sc db 'A7SC',0 ; DATA XREF: dllmain+17A^o
I1000A0D4 aA7s266u2 db 'A7S266U2',0 ; DATA XREF: dllmain+146^o
I1000A0E0 aA7s266vm db 'A7S266VM',0 ; DATA XREF: dllmain+109^

A0C8 aD1 db 'D1',0 ; DATA XREF: dllmain+1B8^o

If some of these  motherboards is found special boolean variable is set. This variable is tested just after raw value is read. If are set, different formula is used. Results are stored is some fixed point format which is still unknown to me because I still dont have AsusProb.exe.

The only thing I tried is just calling modified original functions and see responces for 0 to 0xFF. For this purpose I just copied InX functions replaced the readings with mov eax,[esp+4] (This would read first ARG of created func, computation formulas are left original, and in EAX is stored result. So I have created

This C prototype int in2(unsigned int)


And asm code:

section .text
extern dword_1000D980
extern dword_1000A06C
global in2
in2:
; proc near ;
mov eax,[esp+4]
and eax, 0FFh
mov ecx, eax
shl eax, 3
sub eax, ecx
lea ecx, [eax+eax*2]
mov eax, 0x51EB851F
shl ecx, 6
mul ecx
;mov eax, [esp+4] ;originally the arg is pointer to int*
shr edx, 4
;mov [eax], edx ;in EDX is stored the result and it should be
;written to *EAX.
;mov eax, 1 ;original return value is 1
mov eax,edx ;but we want to return the value.
retn

This could be compiled with nasm compiler. nasm -f elf code.asm

And C part is simple:

unsigned int dword_1000D980=0;
unsigned int dword_1000A06C=0~
;
int in2(unsigned int);
int in3(unsigned int);
int in6(unsigned int);

int main(void) {
int vysl;
int i;
for (i=0;i<0x100;i++) {
vysl=in3(i);
printf("%d:%d\n",i,vysl);
}
}

gcc dec.c code.o -o test

Please note that in negative voltage computations there are two temporary
variables.

That is all I have for now. It is first version and bit chaotic But I can answer  quetions you might have.

Rudolf Marek

EOF
blinking cursor