0f 20

Aug 20, 2008 07:48


I wish to continue the campaign on utilizing the ideas of AV researchers. This is the second attempt, the previous one was the Linux.42 virus (using new SSE4.2 instruction CRC32B in polymorphic decryptor). This time I wish to show you how the CPU decoding "anomaly" discovered by Peter Ferrie might be used to fool the emulators.

Let's read the original description of the feature:

There's another anomaly, when we play with these opcodes:
  • 0f 20
  • 0f 21
  • 0f 22
  • 0f 23

They are documented as accepting only register encodings ("The 2 bits in the mod field are always 11B"). Therefore, anything else should cause an exception, but that's not what happens. In fact, they support the full range of encodings, but in a special way. The quote should actually say, "The 2 bits in the mod field are always interpreted as 11B". That is, no matter what value is in the mod field, the instruction always decodes to a register access, not a memory access.

The opcodes mentioned above is a MOV commands to/from control/debugging registers. Privileged ones. So, it's time to move our tiny experiment to the kernel space. We will construct an «invalid» mov instruction which looks like it is accessing memory:

0f 20 05 40 40 40 40
While real CPU will treat it as «mov ebp, cr0» and will execute the subsequent four bytes which are not the part of instruction. 0x40 means inc eax. Suppose the following kernel module:

#include int __init init_module(void) { int r; asm ( "pushl %%ebp\n" "xorl %%eax, %%eax\n" ".byte 0x0f,0x20,0x05\n" ".long 0x40404040\n" "popl %%ebp\n":"=a"(r)); printk(KERN_ERR "EAX=%d\n", r); return 0; } void __exit cleanup_module(void) { } MODULE_LICENSE("GPL");
And correspoding Makefile:

obj-m := test_0f20.o KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
Let's try it on a real machine:

[root@****** src]# make make -C /lib/modules/2.6.************/build SUBDIRS=************************/0f20/src modules make[1]: Entering directory `/lib/modules/2.6.************/build' CC [M] ************************/0f20/src/test_0f20.o Building modules, stage 2. MODPOST CC ************************/0f20/src/test_0f20.mod.o LD [M] ************************/0f20/src/test_0f20.ko make[1]: Leaving directory `/lib/modules/2.6.************/build' [root@****** src]# insmod ./test_0f20.ko [root@****** src]# tail -1 /var/log/messages Aug 19 18:04:25 ****** kernel: EAX=4 [root@****** src]# rmmod test_0f20
And now in qemu:



I think that the following piece of qemu's code will explain everything:

5841 case 0x120: /* mov reg, crN */ 5842 case 0x122: /* mov crN, reg */ 5843 if (s->cpl != 0) { 5844 gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); 5845 } else { 5846 modrm = ldub_code(s->pc++); 5847 if ((modrm & 0xc0) != 0xc0) 5848 goto illegal_op;
And should be replaced with something like modrm |= 0xc0.

Not only emulators affected, but disassemblers too:

objdump (or objdumb?) can not decide which of two possible interpretations to choose:

3: 55 push %ebp 4: 31 c0 xor %eax,%eax 6: 0f mov %cr0,(bad) 7: 20 05 40 40 40 40 and %al,0x40404040 d: 5d pop %ebp
IDA/Linux is fooled too:

.init.text:08000013 push ebp .init.text:08000014 xor eax, eax .init.text:08000014 ; --------------------------------------------------------------------------- .init.text:08000016 dw 200Fh .init.text:08000018 ; --------------------------------------------------------------------------- .init.text:08000018 add eax, 40404040h .init.text:0800001D pop ebp только что посмотрел. в qemu из svn та же фигня.
Previous post Next post
Up