linux-wireless.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Faking MMIO ops? Fooling a driver
@ 2011-06-16 14:44 Rafał Miłecki
  2011-06-16 17:20 ` Rafał Miłecki
  0 siblings, 1 reply; 7+ messages in thread
From: Rafał Miłecki @ 2011-06-16 14:44 UTC (permalink / raw)
  To: linux-wireless, Linux Kernel Mailing List

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ł

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Faking MMIO ops? Fooling a driver
  2011-06-16 14:44 Faking MMIO ops? Fooling a driver Rafał Miłecki
@ 2011-06-16 17:20 ` Rafał Miłecki
  2011-06-16 18:07   ` Larry Finger
  0 siblings, 1 reply; 7+ messages in thread
From: Rafał Miłecki @ 2011-06-16 17:20 UTC (permalink / raw)
  To: linux-wireless, Linux Kernel Mailing List; +Cc: Pekka Paalanen

W dniu 16 czerwca 2011 16:44 użytkownik Rafał Miłecki
<zajec5@gmail.com> 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ł

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Faking MMIO ops? Fooling a driver
  2011-06-16 17:20 ` Rafał Miłecki
@ 2011-06-16 18:07   ` Larry Finger
  2011-06-16 19:19     ` Rafał Miłecki
  0 siblings, 1 reply; 7+ messages in thread
From: Larry Finger @ 2011-06-16 18:07 UTC (permalink / raw)
  To: Rafał Miłecki
  Cc: linux-wireless, Linux Kernel Mailing List, Pekka Paalanen

On 06/16/2011 12:20 PM, Rafał Miłecki wrote:
> W dniu 16 czerwca 2011 16:44 użytkownik Rafał Miłecki
> <zajec5@gmail.com>  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

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Faking MMIO ops? Fooling a driver
  2011-06-16 18:07   ` Larry Finger
@ 2011-06-16 19:19     ` Rafał Miłecki
  2011-06-16 19:34       ` Pekka Paalanen
  0 siblings, 1 reply; 7+ messages in thread
From: Rafał Miłecki @ 2011-06-16 19:19 UTC (permalink / raw)
  To: Larry Finger; +Cc: linux-wireless, Linux Kernel Mailing List, Pekka Paalanen

W dniu 16 czerwca 2011 20:07 użytkownik Larry Finger
<Larry.Finger@lwfinger.net> napisał:
> On 06/16/2011 12:20 PM, Rafał Miłecki wrote:
>>
>> W dniu 16 czerwca 2011 16:44 użytkownik Rafał Miłecki
>> <zajec5@gmail.com>  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ł

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: Faking MMIO ops? Fooling a driver
  2011-06-16 19:19     ` Rafał Miłecki
@ 2011-06-16 19:34       ` Pekka Paalanen
  2011-06-16 21:47         ` Rafał Miłecki
  0 siblings, 1 reply; 7+ messages in thread
From: Pekka Paalanen @ 2011-06-16 19:34 UTC (permalink / raw)
  To: Rafał Miłecki
  Cc: Larry Finger, linux-wireless, Linux Kernel Mailing List

On Thu, 16 Jun 2011 21:19:04 +0200
Rafał Miłecki <zajec5@gmail.com> wrote:

> W dniu 16 czerwca 2011 20:07 użytkownik Larry Finger
> <Larry.Finger@lwfinger.net> napisał:
> > On 06/16/2011 12:20 PM, Rafał Miłecki wrote:
> >>
> >> W dniu 16 czerwca 2011 16:44 użytkownik Rafał Miłecki
> >> <zajec5@gmail.com>  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/


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Faking MMIO ops? Fooling a driver
  2011-06-16 19:34       ` Pekka Paalanen
@ 2011-06-16 21:47         ` Rafał Miłecki
  2011-06-16 21:53           ` Rafał Miłecki
  0 siblings, 1 reply; 7+ messages in thread
From: Rafał Miłecki @ 2011-06-16 21:47 UTC (permalink / raw)
  To: Pekka Paalanen; +Cc: Larry Finger, linux-wireless, Linux Kernel Mailing List

W dniu 16 czerwca 2011 21:34 użytkownik Pekka Paalanen <pq@iki.fi> napisał:
> On Thu, 16 Jun 2011 21:19:04 +0200
> Rafał Miłecki <zajec5@gmail.com> wrote:
>
>> W dniu 16 czerwca 2011 20:07 użytkownik Larry Finger
>> <Larry.Finger@lwfinger.net> napisał:
>> > On 06/16/2011 12:20 PM, Rafał Miłecki wrote:
>> >>
>> >> W dniu 16 czerwca 2011 16:44 użytkownik Rafał Miłecki
>> >> <zajec5@gmail.com>  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ł

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Faking MMIO ops? Fooling a driver
  2011-06-16 21:47         ` Rafał Miłecki
@ 2011-06-16 21:53           ` Rafał Miłecki
  0 siblings, 0 replies; 7+ messages in thread
From: Rafał Miłecki @ 2011-06-16 21:53 UTC (permalink / raw)
  To: Pekka Paalanen; +Cc: Larry Finger, linux-wireless, Linux Kernel Mailing List

[-- Attachment #1: Type: text/plain, Size: 1998 bytes --]

W dniu 16 czerwca 2011 23:47 użytkownik Rafał Miłecki
<zajec5@gmail.com> napisał:
> W dniu 16 czerwca 2011 21:34 użytkownik Pekka Paalanen <pq@iki.fi> napisał:
>> On Thu, 16 Jun 2011 21:19:04 +0200
>> Rafał Miłecki <zajec5@gmail.com> wrote:
>>
>>> W dniu 16 czerwca 2011 20:07 użytkownik Larry Finger
>>> <Larry.Finger@lwfinger.net> napisał:
>>> > On 06/16/2011 12:20 PM, Rafał Miłecki wrote:
>>> >>
>>> >> W dniu 16 czerwca 2011 16:44 użytkownik Rafał Miłecki
>>> >> <zajec5@gmail.com>  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ł

[-- Attachment #2: mmio.fake.0xfaafc000.patch --]
[-- Type: application/octet-stream, Size: 2974 bytes --]

diff --git a/arch/x86/mm/mmio-mod.c b/arch/x86/mm/mmio-mod.c
index 3adff7d..7f71884b 100644
--- a/arch/x86/mm/mmio-mod.c
+++ b/arch/x86/mm/mmio-mod.c
@@ -219,6 +219,11 @@ static void post(struct kmmio_probe *p, unsigned long condition,
 		BUG();
 	}
 
+	if (my_trace->phys == 0xfaafc000) {
+		pr_info("ZAJEC: got it\n");
+		set_ins_reg_val(my_reason->ip, regs, 0x13816666);
+	}
+
 	switch (my_reason->type) {
 	case REG_READ:
 		my_trace->value = get_ins_reg_val(my_reason->ip, regs);
diff --git a/arch/x86/mm/pf_in.c b/arch/x86/mm/pf_in.c
index 9f0614d..86449ed 100644
--- a/arch/x86/mm/pf_in.c
+++ b/arch/x86/mm/pf_in.c
@@ -461,6 +461,99 @@ err:
 	return 0;
 }
 
+static void set_reg_w32(int no, struct pt_regs *regs, u32 val)
+{
+	switch (no) {
+	case arg_AX:
+		regs->ax = val;
+		break;
+	case arg_BX:
+		regs->bx = val;
+		break;
+	case arg_CX:
+		regs->cx = val;
+		break;
+	case arg_DX:
+		regs->dx = val;
+		break;
+	case arg_SP:
+		regs->sp = val;
+		break;
+	case arg_BP:
+		regs->bp = val;
+		break;
+	case arg_SI:
+		regs->si = val;
+		break;
+	case arg_DI:
+		regs->di = val;
+		break;
+#ifdef __amd64__
+	case arg_R8:
+		regs->r8 = val;
+		break;
+	case arg_R9:
+		regs->r9 = val;
+		break;
+	case arg_R10:
+		regs->r10 = val;
+		break;
+	case arg_R11:
+		regs->r11 = val;
+		break;
+	case arg_R12:
+		regs->r12 = val;
+		break;
+	case arg_R13:
+		regs->r13 = val;
+		break;
+	case arg_R14:
+		regs->r14 = val;
+		break;
+	case arg_R15:
+		regs->r15 = val;
+		break;
+#endif
+	default:
+		printk(KERN_ERR "mmiotrace: Error reg no# %d\n", no);
+	}
+}
+
+void set_ins_reg_val(unsigned long ins_addr, struct pt_regs *regs, u32 val)
+{
+	unsigned int opcode;
+	int reg;
+	unsigned char *p;
+	struct prefix_bits prf;
+	int i;
+
+	p = (unsigned char *)ins_addr;
+	p += skip_prefix(p, &prf);
+	p += get_opcode(p, &opcode);
+	for (i = 0; i < ARRAY_SIZE(reg_rop); i++)
+		if (reg_rop[i] == opcode)
+			goto do_work;
+
+	for (i = 0; i < ARRAY_SIZE(reg_wop); i++)
+		if (reg_wop[i] == opcode)
+			goto do_work;
+
+	printk(KERN_ERR "mmiotrace: Not a register instruction, opcode "
+							"0x%02x\n", opcode);
+	return;
+
+do_work:
+	/* for STOS, source register is fixed */
+	if (opcode == 0xAA || opcode == 0xAB) {
+		reg = arg_AX;
+	} else {
+		unsigned char mod_rm = *p;
+		reg = ((mod_rm >> 3) & 0x7) | (prf.rexr << 3);
+	}
+
+	set_reg_w32(reg, regs, val);
+}
+
 unsigned long get_ins_imm_val(unsigned long ins_addr)
 {
 	unsigned int opcode;
diff --git a/arch/x86/mm/pf_in.h b/arch/x86/mm/pf_in.h
index e05341a..90b43ff 100644
--- a/arch/x86/mm/pf_in.h
+++ b/arch/x86/mm/pf_in.h
@@ -34,6 +34,7 @@ enum reason_type {
 enum reason_type get_ins_type(unsigned long ins_addr);
 unsigned int get_ins_mem_width(unsigned long ins_addr);
 unsigned long get_ins_reg_val(unsigned long ins_addr, struct pt_regs *regs);
+void set_ins_reg_val(unsigned long ins_addr, struct pt_regs *regs, u32 val);
 unsigned long get_ins_imm_val(unsigned long ins_addr);
 
 #endif /* __PF_H_ */

^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2011-06-16 21:53 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-06-16 14:44 Faking MMIO ops? Fooling a driver Rafał Miłecki
2011-06-16 17:20 ` Rafał Miłecki
2011-06-16 18:07   ` Larry Finger
2011-06-16 19:19     ` Rafał Miłecki
2011-06-16 19:34       ` Pekka Paalanen
2011-06-16 21:47         ` Rafał Miłecki
2011-06-16 21:53           ` Rafał Miłecki

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).