2011-06-16 14:46:56

by Rafał Miłecki

[permalink] [raw]
Subject: Faking MMIO ops? Fooling a driver

I analyze MMIO dumps of closed source driver and found such a place:
W 2 3855.911536 9 0xb06003fc 0x810 0x0 0
R 2 3855.911540 9 0xb06003fe 0x0 0x0 0
W 2 3855.911541 9 0xb06003fe 0x0 0x0 0

After translation:
phy_read(0x0810) -> 0x0000
phy_write(0x0810) <- 0x0000

So it's quite obvious, the driver is reading PHY register, masking it
and writing masked value. Unfortunately from just looking at such
place we can not guess the mask driver uses.

I'd like to fake value read from 0xb06003fe to be 0xFFFF.
Is there some ready method for doing such a trick?

Dump comes from Kernel hacking → Tracers → MMIO and ndiswrapper.

--
Rafał


2011-06-16 18:07:49

by Larry Finger

[permalink] [raw]
Subject: Re: Faking MMIO ops? Fooling a driver

On 06/16/2011 12:20 PM, Rafał Miłecki wrote:
> W dniu 16 czerwca 2011 16:44 użytkownik Rafał Miłecki
> <[email protected]> napisał:
>> I analyze MMIO dumps of closed source driver and found such a place:
>> W 2 3855.911536 9 0xb06003fc 0x810 0x0 0
>> R 2 3855.911540 9 0xb06003fe 0x0 0x0 0
>> W 2 3855.911541 9 0xb06003fe 0x0 0x0 0
>>
>> After translation:
>> phy_read(0x0810) -> 0x0000
>> phy_write(0x0810)<- 0x0000
>>
>> So it's quite obvious, the driver is reading PHY register, masking it
>> and writing masked value. Unfortunately from just looking at such
>> place we can not guess the mask driver uses.
>>
>> I'd like to fake value read from 0xb06003fe to be 0xFFFF.
>> Is there some ready method for doing such a trick?
>>
>> Dump comes from Kernel hacking → Tracers → MMIO and ndiswrapper.
>
> I can see values in MMIO trace struct are filled in
> arch/x86/mm/mmio-mod.c in "pre" and "post". However still no idea how
> to hack the returned value.
>
> Should I try hacking read[bwl] instead? :|

Probably. I do not see any way to trace and modify the results for a particular
address without special code.

FYI, my reference driver for reverse engineering has no instance of a
read/modify/write for PHY register 0x810. Is the code in question for a PHY type
> 6?

Larry

2011-06-16 21:52:44

by Rafał Miłecki

[permalink] [raw]
Subject: Re: Faking MMIO ops? Fooling a driver

W dniu 16 czerwca 2011 21:34 użytkownik Pekka Paalanen <[email protected]> napisał:
> On Thu, 16 Jun 2011 21:19:04 +0200
> Rafał Miłecki <[email protected]> wrote:
>
>> W dniu 16 czerwca 2011 20:07 użytkownik Larry Finger
>> <[email protected]> napisał:
>> > On 06/16/2011 12:20 PM, Rafał Miłecki wrote:
>> >>
>> >> W dniu 16 czerwca 2011 16:44 użytkownik Rafał Miłecki
>> >> <[email protected]>  napisał:
>> >>>
>> >>> I analyze MMIO dumps of closed source driver and found such a
>> >>> place: W 2 3855.911536 9 0xb06003fc 0x810 0x0 0
>> >>> R 2 3855.911540 9 0xb06003fe 0x0 0x0 0
>> >>> W 2 3855.911541 9 0xb06003fe 0x0 0x0 0
>> >>>
>> >>> After translation:
>> >>>  phy_read(0x0810) ->  0x0000
>> >>> phy_write(0x0810)<- 0x0000
>> >>>
>> >>> So it's quite obvious, the driver is reading PHY register,
>> >>> masking it and writing masked value. Unfortunately from just
>> >>> looking at such place we can not guess the mask driver uses.
>> >>>
>> >>> I'd like to fake value read from 0xb06003fe to be 0xFFFF.
>> >>> Is there some ready method for doing such a trick?
>> >>>
>> >>> Dump comes from Kernel hacking → Tracers → MMIO and
>> >>> ndiswrapper.
>> >>
>> >> I can see values in MMIO trace struct are filled in
>> >> arch/x86/mm/mmio-mod.c in "pre" and "post". However still no
>> >> idea how to hack the returned value.
>
> If you want to do it that way, the idea is to overwrite
> the right CPU register in mmio-mod.c:post(). You would test for
> the address you want to mess with, and then "invert"
> get_ins_reg_val() to overwrite the register with your own value.

Good, idea thanks!


>> >> Should I try hacking read[bwl] instead? :|
>> >
>> > Probably. I do not see any way to trace and modify the results
>> > for a particular address without special code.
>>
>> Did you success with writing some special code? Following patch
>> does not seem to work for me:
>>
>>
>> diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
>> index e0ffa3d..448e4ff 100644
>> --- a/include/asm-generic/io.h
>> +++ b/include/asm-generic/io.h
>> @@ -23,6 +23,8 @@
>>  #define mmiowb() do {} while (0)
>>  #endif
>>
>> +static int zajec = 0;
>> +
>>  /*****************************************************************************/
>>  /*
>>   * readX/writeX() are used to access memory mapped devices. On
>> some @@ -40,6 +42,11 @@ static inline u8 __raw_readb(const
>> volatile void __iomem *addr)
>>  #ifndef __raw_readw
>>  static inline u16 __raw_readw(const volatile void __iomem *addr)
>>  {
>> +     if (zajec++ < 10)
>> +             printk(KERN_INFO "[ZAJEC] %d\n", *addr);
>> +     if (*addr == 0xfaafc000)
>> +             printk(KERN_INFO "[ZAJEC] Bingo!\n");
>> +     //0x1381a8d8
>>       return *(const volatile u16 __force *) addr;
>>  }
>>  #endif
>>
>> Should I modify different readw? Path include/asm-generic/io.h
>> sounds sane to me, so I modified this one.
>
> Are you sure that code is ever used on your arch?
> For instance, it seems that arch/x86/include/asm/io.h
> defines a different __raw_readw.

You're right, I was using wrong file. Anyway it's too low level stuff
to use printk here. So thank you for the other suggested method!

--
Rafał

2011-06-16 21:53:19

by Rafał Miłecki

[permalink] [raw]
Subject: Re: Faking MMIO ops? Fooling a driver

W dniu 16 czerwca 2011 23:47 użytkownik Rafał Miłecki
<[email protected]> napisał:
> W dniu 16 czerwca 2011 21:34 użytkownik Pekka Paalanen <[email protected]> napisał:
>> On Thu, 16 Jun 2011 21:19:04 +0200
>> Rafał Miłecki <[email protected]> wrote:
>>
>>> W dniu 16 czerwca 2011 20:07 użytkownik Larry Finger
>>> <[email protected]> napisał:
>>> > On 06/16/2011 12:20 PM, Rafał Miłecki wrote:
>>> >>
>>> >> W dniu 16 czerwca 2011 16:44 użytkownik Rafał Miłecki
>>> >> <[email protected]>  napisał:
>>> >>>
>>> >>> I analyze MMIO dumps of closed source driver and found such a
>>> >>> place: W 2 3855.911536 9 0xb06003fc 0x810 0x0 0
>>> >>> R 2 3855.911540 9 0xb06003fe 0x0 0x0 0
>>> >>> W 2 3855.911541 9 0xb06003fe 0x0 0x0 0
>>> >>>
>>> >>> After translation:
>>> >>>  phy_read(0x0810) ->  0x0000
>>> >>> phy_write(0x0810)<- 0x0000
>>> >>>
>>> >>> So it's quite obvious, the driver is reading PHY register,
>>> >>> masking it and writing masked value. Unfortunately from just
>>> >>> looking at such place we can not guess the mask driver uses.
>>> >>>
>>> >>> I'd like to fake value read from 0xb06003fe to be 0xFFFF.
>>> >>> Is there some ready method for doing such a trick?
>>> >>>
>>> >>> Dump comes from Kernel hacking → Tracers → MMIO and
>>> >>> ndiswrapper.
>>> >>
>>> >> I can see values in MMIO trace struct are filled in
>>> >> arch/x86/mm/mmio-mod.c in "pre" and "post". However still no
>>> >> idea how to hack the returned value.
>>
>> If you want to do it that way, the idea is to overwrite
>> the right CPU register in mmio-mod.c:post(). You would test for
>> the address you want to mess with, and then "invert"
>> get_ins_reg_val() to overwrite the register with your own value.
>
> Good, idea thanks!

Implementation attached.
Now I only need to track writes to 0xfaafc3fc (that register is for
addressing to-follow PHY read/write) and wait for 0xfaafc3fe which is
read of PHY register value.

--
Rafał


Attachments:
mmio.fake.0xfaafc000.patch (2.90 kB)

2011-06-16 19:19:05

by Rafał Miłecki

[permalink] [raw]
Subject: Re: Faking MMIO ops? Fooling a driver

W dniu 16 czerwca 2011 20:07 użytkownik Larry Finger
<[email protected]> napisał:
> On 06/16/2011 12:20 PM, Rafał Miłecki wrote:
>>
>> W dniu 16 czerwca 2011 16:44 użytkownik Rafał Miłecki
>> <[email protected]>  napisał:
>>>
>>> I analyze MMIO dumps of closed source driver and found such a place:
>>> W 2 3855.911536 9 0xb06003fc 0x810 0x0 0
>>> R 2 3855.911540 9 0xb06003fe 0x0 0x0 0
>>> W 2 3855.911541 9 0xb06003fe 0x0 0x0 0
>>>
>>> After translation:
>>>  phy_read(0x0810) ->  0x0000
>>> phy_write(0x0810)<- 0x0000
>>>
>>> So it's quite obvious, the driver is reading PHY register, masking it
>>> and writing masked value. Unfortunately from just looking at such
>>> place we can not guess the mask driver uses.
>>>
>>> I'd like to fake value read from 0xb06003fe to be 0xFFFF.
>>> Is there some ready method for doing such a trick?
>>>
>>> Dump comes from Kernel hacking → Tracers → MMIO and ndiswrapper.
>>
>> I can see values in MMIO trace struct are filled in
>> arch/x86/mm/mmio-mod.c in "pre" and "post". However still no idea how
>> to hack the returned value.
>>
>> Should I try hacking read[bwl] instead? :|
>
> Probably. I do not see any way to trace and modify the results for a
> particular address without special code.

Did you success with writing some special code? Following patch does
not seem to work for me:


diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
index e0ffa3d..448e4ff 100644
--- a/include/asm-generic/io.h
+++ b/include/asm-generic/io.h
@@ -23,6 +23,8 @@
#define mmiowb() do {} while (0)
#endif

+static int zajec = 0;
+
/*****************************************************************************/
/*
* readX/writeX() are used to access memory mapped devices. On some
@@ -40,6 +42,11 @@ static inline u8 __raw_readb(const volatile void
__iomem *addr)
#ifndef __raw_readw
static inline u16 __raw_readw(const volatile void __iomem *addr)
{
+ if (zajec++ < 10)
+ printk(KERN_INFO "[ZAJEC] %d\n", *addr);
+ if (*addr == 0xfaafc000)
+ printk(KERN_INFO "[ZAJEC] Bingo!\n");
+ //0x1381a8d8
return *(const volatile u16 __force *) addr;
}
#endif

Should I modify different readw? Path include/asm-generic/io.h sounds
sane to me, so I modified this one.


> FYI, my reference driver for reverse engineering has no instance of a
> read/modify/write for PHY register 0x810. Is the code in question for a PHY
> type > 6?

This is for PHY type 7, AKA "HT".

--
Rafał

2011-06-16 19:35:07

by Pekka Paalanen

[permalink] [raw]
Subject: Re: Faking MMIO ops? Fooling a driver

On Thu, 16 Jun 2011 21:19:04 +0200
Rafał Miłecki <[email protected]> wrote:

> W dniu 16 czerwca 2011 20:07 użytkownik Larry Finger
> <[email protected]> napisał:
> > On 06/16/2011 12:20 PM, Rafał Miłecki wrote:
> >>
> >> W dniu 16 czerwca 2011 16:44 użytkownik Rafał Miłecki
> >> <[email protected]>  napisał:
> >>>
> >>> I analyze MMIO dumps of closed source driver and found such a
> >>> place: W 2 3855.911536 9 0xb06003fc 0x810 0x0 0
> >>> R 2 3855.911540 9 0xb06003fe 0x0 0x0 0
> >>> W 2 3855.911541 9 0xb06003fe 0x0 0x0 0
> >>>
> >>> After translation:
> >>>  phy_read(0x0810) ->  0x0000
> >>> phy_write(0x0810)<- 0x0000
> >>>
> >>> So it's quite obvious, the driver is reading PHY register,
> >>> masking it and writing masked value. Unfortunately from just
> >>> looking at such place we can not guess the mask driver uses.
> >>>
> >>> I'd like to fake value read from 0xb06003fe to be 0xFFFF.
> >>> Is there some ready method for doing such a trick?
> >>>
> >>> Dump comes from Kernel hacking → Tracers → MMIO and
> >>> ndiswrapper.
> >>
> >> I can see values in MMIO trace struct are filled in
> >> arch/x86/mm/mmio-mod.c in "pre" and "post". However still no
> >> idea how to hack the returned value.

If you want to do it that way, the idea is to overwrite
the right CPU register in mmio-mod.c:post(). You would test for
the address you want to mess with, and then "invert"
get_ins_reg_val() to overwrite the register with your own value.

> >> Should I try hacking read[bwl] instead? :|
> >
> > Probably. I do not see any way to trace and modify the results
> > for a particular address without special code.
>
> Did you success with writing some special code? Following patch
> does not seem to work for me:
>
>
> diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
> index e0ffa3d..448e4ff 100644
> --- a/include/asm-generic/io.h
> +++ b/include/asm-generic/io.h
> @@ -23,6 +23,8 @@
> #define mmiowb() do {} while (0)
> #endif
>
> +static int zajec = 0;
> +
> /*****************************************************************************/
> /*
> * readX/writeX() are used to access memory mapped devices. On
> some @@ -40,6 +42,11 @@ static inline u8 __raw_readb(const
> volatile void __iomem *addr)
> #ifndef __raw_readw
> static inline u16 __raw_readw(const volatile void __iomem *addr)
> {
> + if (zajec++ < 10)
> + printk(KERN_INFO "[ZAJEC] %d\n", *addr);
> + if (*addr == 0xfaafc000)
> + printk(KERN_INFO "[ZAJEC] Bingo!\n");
> + //0x1381a8d8
> return *(const volatile u16 __force *) addr;
> }
> #endif
>
> Should I modify different readw? Path include/asm-generic/io.h
> sounds sane to me, so I modified this one.

Are you sure that code is ever used on your arch?
For instance, it seems that arch/x86/include/asm/io.h
defines a different __raw_readw.


Cheers.

--
Pekka Paalanen
http://www.iki.fi/pq/


2011-06-16 17:20:13

by Rafał Miłecki

[permalink] [raw]
Subject: Re: Faking MMIO ops? Fooling a driver

W dniu 16 czerwca 2011 16:44 użytkownik Rafał Miłecki
<[email protected]> napisał:
> I analyze MMIO dumps of closed source driver and found such a place:
> W 2 3855.911536 9 0xb06003fc 0x810 0x0 0
> R 2 3855.911540 9 0xb06003fe 0x0 0x0 0
> W 2 3855.911541 9 0xb06003fe 0x0 0x0 0
>
> After translation:
>  phy_read(0x0810) -> 0x0000
> phy_write(0x0810) <- 0x0000
>
> So it's quite obvious, the driver is reading PHY register, masking it
> and writing masked value. Unfortunately from just looking at such
> place we can not guess the mask driver uses.
>
> I'd like to fake value read from 0xb06003fe to be 0xFFFF.
> Is there some ready method for doing such a trick?
>
> Dump comes from Kernel hacking → Tracers → MMIO and ndiswrapper.

I can see values in MMIO trace struct are filled in
arch/x86/mm/mmio-mod.c in "pre" and "post". However still no idea how
to hack the returned value.

Should I try hacking read[bwl] instead? :|

--
Rafał