Linux Kernel programming: trying to get vm_area_struct->vm_start crashes kernel - process

this is for an assignment at school, where I need to determine the size of the processes on the system using a system call. My code is as follows:
...
struct task_struct *p;
struct vm_area_struct *v;
struct mm_struct *m;
read_lock(&tasklist_lock);
for_each_process(p) {
printk("%ld\n", p->pid);
m = p->mm;
v = m->mmap;
long start = v->vm_start;
printk("vm_start is %ld\n", start);
}
read_unlock(&tasklist_lock);
...
When I run a user level program that calls this system call, the output that I get is:
1
vm_start is 134512640
2
EIP: 0073:[<0806e352>] CPU: 0 Not tainted ESP: 007b:0f7ecf04 EFLAGS: 00010246
Not tainted
EAX: 00000000 EBX: 0fc587c0 ECX: 081fbb58 EDX: 00000000
ESI: bf88efe0 EDI: 0f482284 EBP: 0f7ecf10 DS: 007b ES: 007b
081f9bc0: [<08069ae8>] show_regs+0xb4/0xb9
081f9bec: [<080587ac>] segv+0x225/0x23d
081f9c8c: [<08058582>] segv_handler+0x4f/0x54
081f9cac: [<08067453>] sig_handler_common_skas+0xb7/0xd4
081f9cd4: [<08064748>] sig_handler+0x34/0x44
081f9cec: [<080648b5>] handle_signal+0x4c/0x7a
081f9d0c: [<08066227>] hard_handler+0xf/0x14
081f9d1c: [<00776420>] 0x776420
Kernel panic - not syncing: Kernel mode fault at addr 0x0, ip 0x806e352
EIP: 0073:[<400ea0f2>] CPU: 0 Not tainted ESP: 007b:bf88ef9c EFLAGS: 00000246
Not tainted
EAX: ffffffda EBX: 00000000 ECX: bf88efc8 EDX: 080483c8
ESI: 00000000 EDI: bf88efe0 EBP: bf88f038 DS: 007b ES: 007b
081f9b28: [<08069ae8>] show_regs+0xb4/0xb9
081f9b54: [<08058a1a>] panic_exit+0x25/0x3f
081f9b68: [<08084f54>] notifier_call_chain+0x21/0x46
081f9b88: [<08084fef>] __atomic_notifier_call_chain+0x17/0x19
081f9ba4: [<08085006>] atomic_notifier_call_chain+0x15/0x17
081f9bc0: [<0807039a>] panic+0x52/0xd8
081f9be0: [<080587ba>] segv+0x233/0x23d
081f9c8c: [<08058582>] segv_handler+0x4f/0x54
081f9cac: [<08067453>] sig_handler_common_skas+0xb7/0xd4
081f9cd4: [<08064748>] sig_handler+0x34/0x44
081f9cec: [<080648b5>] handle_signal+0x4c/0x7a
081f9d0c: [<08066227>] hard_handler+0xf/0x14
081f9d1c: [<00776420>] 0x776420
The first process (pid = 1) gave me the vm_start without any problems, but when I try to access the second process, the kernel crashes. Can anyone tell me what's wrong, and maybe how to fix it as well? Thanks a lot!
(sorry for the bad formatting....)
edit: This is done in a Fedora 2.6 core in an uml environment.

Some kernel threads might not have mm filled - check p->mm for NULL.

Changed the code to check for null pointers:
m = p->mm;
if (m != 0) {
v = m->mmap;
if (v != 0) {
long start = v->vm_start;
printk("vm_start is %ld\n", start);
}
}

All process related information can be found at /proc filesystem at the userspace level. Inside the kernel, these information are generated via fs/proc/*.c
http://lxr.linux.no/linux+v3.2.4/fs/proc/
Looking at the file task_mmu.c, which printing all the vm_start information u can observe that all handling of vm_start field always require the mmap_sem to be locked:
down_read(&mm->mmap_sem);
for (vma = mm->mmap; vma; vma = vma->vm_next) {
clear_refs_walk.private = vma;
...
walk_page_range(vma->vm_start, vma->vm_end,
&clear_refs_walk);

For kernel threads mm will be null. So whenever you read the mm do it in the following manner.
down_read(&p->mm->mmap_sem)
if(mm) {
/* read the contents of mm*/
}
up_read(&p->mm->mmap_sem)
Also you may use get_task_mm(). With get_task_mm() you need not acquire the lock. Here is how you use it :
struct mm_struct *mm;
mm = get_task_mm(p);
if (mm) {
/* read the mm contents */
}

Related

ESP32 crashes when I call WiFi.mode(WIFI_MODE_APSTA) from within a Interrupt ISR

Summary:
On my ISR routine I call a function that tries to set the WiFi mode to APSTA in a ESP32, and it crashes with the following dump:
Scanning for Modules...
OK1
Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1)
Core 1 register dump:
PC : 0x4008b7cc PS : 0x00060d34 A0 : 0x8008a9a7 A1 : 0x3ffbe530
A2 : 0x3ffb595c A3 : 0x3ffb8074 A4 : 0x00000001 A5 : 0x00000001
A6 : 0x00060d23 A7 : 0x00000000 A8 : 0x3ffb8074 A9 : 0x3ffb8074
A10 : 0x00000018 A11 : 0x00000018 A12 : 0x00000001 A13 : 0x00000001
A14 : 0x00060d21 A15 : 0x00000000 SAR : 0x00000020 EXCCAUSE: 0x00000006
EXCVADDR: 0x00000000 LBEG : 0x4000c2e0 LEND : 0x4000c2f6 LCOUNT : 0xffffffff
Core 1 was running in ISR context:
EPC1 : 0x400e176c EPC2 : 0x00000000 EPC3 : 0x00000000 EPC4 : 0x4008b7cc
Backtrace: 0x4008b7cc:0x3ffbe530 0x4008a9a4:0x3ffbe550 0x40088c3f:0x3ffbe570 0x400d504d:0x3ffbe5b0 0x400ed002:0x3ffbe5d0 0x400e1adf:0x3ffbe5f0 0x400d1991:0x3ffbe610 0x40081002:0x3ffbe710 0x400811c5:0x3ffbe790 0x4008125d:0x3ffbe7b0 0x40084a2d:0x3ffbe7d0 0x400d223a:0x3ffb1fb0 0x40088e39:0x3ffb1fd0
Core 0 register dump:
PC : 0x40089e2e PS : 0x00060034 A0 : 0x8008b005 A1 : 0x3ffb5500
A2 : 0x3ffbf300 A3 : 0x0000cdcd A4 : 0xb33fffff A5 : 0x00000001
A6 : 0x00060023 A7 : 0x0000abab A8 : 0x0000abab A9 : 0x3ffb5500
A10 : 0x00000000 A11 : 0x3ffba744 A12 : 0x000002cc A13 : 0x3ffbbc44
A14 : 0x3ffba8e9 A15 : 0x00000000 SAR : 0x00000001 EXCCAUSE: 0x00000006
EXCVADDR: 0x00000000 LBEG : 0x4000c2e0 LEND : 0x4000c2f6 LCOUNT : 0x00000000
Backtrace: 0x40089e2e:0x3ffb5500 0x4008b002:0x3ffb5530 0x40088b9f:0x3ffb5550 0x40088ca9:0x3ffb5590 0x400819ad:0x3ffb55b0 0x400e27c1:0x3ffb55d0 0x400f157d:0x3ffb55f0 0x400e2b3e:0x3ffb5620 0x400e2dc5:0x3ffb5650 0x400ecb31:0x3ffb56a0 0x400e9136:0x3ffb56d0 0x4008f83f:0x3ffb56f0 0x40088e39:0x3ffb5730
Rebooting...
ets Jun 8 2016 00:22:57
Detailed info:
In my project I have 2 buttons that call a different ISR (void IRAM_ATTR ScanForModule1() and void IRAM_ATTR ScanForModule2()) each, and within either ISR I call the same function with a different argument (void IRAM_ATTR ScanForModules(uint8_t moduleNumber)).
This new function call for WiFi mode to be set to WiFi.mode(WIFI_MODE_APSTA) and that makes the ESP32 crash.
I added some tracing "OK"s for debugging before and after the Wifi call and it only shows the one before.
Any idea that may help me?
Thank you
(non compilable snippet)
// ISR Scan for modules 1 in AP mode
void IRAM_ATTR ScanForModule1()
{
Serial.println("Enrolling Module 1...");
module1_addr = NULL;
ScanForModules(1);
}
// ISR Scan for modules 2 in AP mode
void IRAM_ATTR ScanForModule2()
{
Serial.println("Enrolling Module 2...");
module2_addr = NULL;
ScanForModules(2);
}
// Scan for modules in AP mode
void IRAM_ATTR ScanForModules(uint8_t moduleNumber)
{
detachInterrupt(ENROLL_BUTTON1_PIN);
detachInterrupt(ENROLL_BUTTON2_PIN);
Serial.println("Scanning for Modules...");
// Switch the ESP to AP and start broadcasting
//configDeviceAP();
Serial.println("OK1");
WiFi.mode(WIFI_MODE_APSTA); //Crashes here <---------------------
Serial.println("OK2");
The reason for the crash is that your WiFi mode change takes longer to process than an interrupt service routine is allowed to execute (well, technically the watchdog doesn't get reset because the task responsible for it gets blocked by ISR). Which is the expected outcome. Interrupt handlers are not meant for doing any serious work, but rather for registering an event and poking a responsible task to process it.
In this case you should have a task which simply sits and waits for the button press. When a button is pressed, the ISR doesn't do anything but wake up the processing task and say which button was pressed. You can build your own "notification system" with a simple shared bool and busywaiting; or use the FreeRTOS event groups to implement this. I find the Mastering the FreeRTOS Real Time Kernel – a Hands On Tutorial Guide to be an excellent resource for beginners.

USB Kernel Module not probing

I've written a simple usb device driver but it's not getting probed when the device is connected to the system, instead the kernel is calling the usb-storage module instead.
Code for my kernel module:
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/device.h>
#define USB_IT8951_VENDOR_ID 0x048d //ITE Vendor ID
#define USB_IT8951_PRODUCT_ID 0x0220
static int it8951_usb_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
pr_info("test_string 2\n");
return 0;
}
static void it8951_usb_disconnect(struct usb_interface *interface)
{
pr_info("Disconnect enter\n");
}
static const struct usb_device_id it8951_usb_devices [] = {
{ USB_DEVICE(USB_IT8951_VENDOR_ID, USB_IT8951_PRODUCT_ID) },
{},
};
MODULE_DEVICE_TABLE(usb, it8951_usb_devices);
static struct usb_driver it8951_usb_driver_struct = {
.name = "it8951_usb",
.probe = it8951_usb_probe,
.disconnect = it8951_usb_disconnect,
//.fops = &skel_fops,
.id_table = it8951_usb_devices,
};
static int __init it8951_usb_init(void)
{
int result;
pr_info("test_string 1\n");
result = usb_register(&it8951_usb_driver_struct);
if (result < 0) {
pr_err("usb_register failed for the "__FILE__ "driver."
"Error number %d", result);
return -1;
}
return 0;
}
module_init(it8951_usb_init);
static void __exit it8951_usb_exit(void)
{
usb_deregister(&it8951_usb_driver_struct);
}
module_exit(it8951_usb_exit);
Dmesg when the device is connnected:
[ 668.940000] usb 1-2: new high-speed USB device number 3 using atmel-ehci
[ 669.130000] usb 1-2: New USB device found, idVendor=048d, idProduct=0220
[ 669.130000] usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 669.140000] usb 1-2: Product: Digi-Photo-Frame
[ 669.140000] usb 1-2: Manufacturer: Smedia inc.
[ 669.140000] usb 1-2: SerialNumber: 14024689FA08
[ 669.160000] usb-storage 1-2:1.0: USB Mass Storage device detected
[ 669.170000] scsi host0: usb-storage 1-2:1.0
[ 670.240000] scsi 0:0:0:0: Direct-Access Generic Storage RamDisc 1.00 PQ: 0 ANSI: 0 CCS
[ 670.260000] sd 0:0:0:0: [sda] 1 512-byte logical blocks: (512 B/512 B)
[ 670.270000] sd 0:0:0:0: [sda] Write Protect is off
[ 670.270000] sd 0:0:0:0: [sda] Mode Sense: 03 00 00 00
[ 670.270000] sd 0:0:0:0: [sda] No Caching mode page found
[ 670.270000] sd 0:0:0:0: [sda] Assuming drive cache: write through
[ 670.320000] sd 0:0:0:0: [sda] Attached SCSI removable disk
I can't remove usb-storage support from the system, so is there a way to force the kernel to use my module instead of usb-storage? Or can I write a udev rule to re-assign the device?
Thanks in advance.

How to understand the difference between Offset and VirAddr in Program Headers in elf?

There is a shared library elf file, I use readelf -l to see the program headers, the output is:
Elf file type is DYN (Shared object file)
Entry point 0x0
There are 11 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x00000034 0x00000034 0x00100 0x00100 R 0x4
INTERP 0x000194 0x00000194 0x00000194 0x00013 0x00013 R 0x1
[Requesting program interpreter: /system/bin/linker]
LOAD 0x000000 0x00000000 0x00000000 0x3aa8c4 0x3aa8c4 R E 0x1000
LOAD 0x3ab1cc 0x003ac1cc 0x003ac1cc 0x062c0 0x25ee4 RW 0x1000
LOAD 0x3b2000 0x003d3000 0x003d3000 0x02561 0x02561 R E 0x1000
LOAD 0x3b4e8c 0x003d6e8c 0x003d6e8c 0x00298 0x00299 RW 0x1000
LOAD 0x3b5268 0x003d8268 0x003d8268 0x00128 0x00128 RW 0x1000
DYNAMIC 0x3b5268 0x003d8268 0x003d8268 0x00128 0x00128 RW 0x4
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0
EXIDX 0x2e71e8 0x002e71e8 0x002e71e8 0x0b558 0x0b558 R 0x4
GNU_RELRO 0x3ab1cc 0x003ac1cc 0x003ac1cc 0x01e34 0x01e34 RW 0x4
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .dynsym .dynstr .hash .gnu.version .gnu.version_d .rel.dyn .plt .text .ARM.extab .ARM.exidx .rodata
03 .data.rel.ro.local .fini_array .data.rel.ro .got .data .bss
04 .rel.plt
05 .init_array
06 .dynamic
07 .dynamic
08
09 .ARM.exidx
10 .data.rel.ro.local .fini_array .data.rel.ro .got
if the following struct represents an program header:
typedef struct {
uint32_t p_type;
Elf32_Off p_offset;
Elf32_Addr p_vaddr;
Elf32_Addr p_paddr;
uint32_t p_filesz;
uint32_t p_memsz;
uint32_t p_flags;
uint32_t p_align;
} Elf32_Phdr;
Then my question is: How to understand the difference between p_offset and p_vaddr which corresponds to Offset and VirtAddr in output of readelf -l? Will them always be the same? And will them be changed by the procedure of dynamic loading?
How to understand the difference between p_offset and p_vaddr which corresponds to Offset and VirtAddr in output of readelf -l?
The runtime loader will mmap a set of pages at offset .p_offset (rounded down to pagesize) at virtual address .p_vaddr (similarly rounded down; that address will actually have some large multiple-page offset added to it for ET_DYN object).
Will them always be the same?
They aren't the same even in your example:
LOAD 0x3ab1cc 0x003ac1cc
0x3ab1 != 0x3ac1. What is guaranteed is that .p_offset % pagesize == .p_vaddr % pagesize (otherwise mmap will become impossible).
Generally speaking-
p_offset - offset within the elf file
p_vaddr - address of section after loaded to memory (say, after c runtime initialization finished)
They will not always be the same, those addresses can be configured using linker script for example. Refer to this.
As for the shared library addresses after library loaded into a process address space - this depends on process addresses space, ASLR, and more, but its safe to say that the dynamic loader will set new addresses (p_vaddr, aka execution address)

Retrieving IOCTL Input Buffer Content From Crash Dump + Windbg[BSOD]

We know user mode applications can pass IOCTL code and data buffer to kernel device drivers by calling DeviceIoControl() API.
BOOL WINAPI DeviceIoControl(
_In_ HANDLE hDevice,
_In_ DWORD dwIoControlCode, <--Control Code
_In_opt_ LPVOID lpInBuffer, <- Input buffer pointer
_In_ DWORD nInBufferSize, <- Input buffer size
_Out_opt_ LPVOID lpOutBuffer,
_In_ DWORD nOutBufferSize,
_Out_opt_ LPDWORD lpBytesReturned,
_Inout_opt_ LPOVERLAPPED lpOverlapped
);
I've a situation, where an user mode application sometime passing an IOCTL buffer to a Kernel driver and which is causing BSOD again and again. Every time i'm getting kernel memory dump for BSOD.
So my question is, is it possible to find the exact malformed input buffer and IOCTL code which causes the BSOD from the Kernel memory dump so that I can reproduce the BSOD using simple C prog.
As you can find from the stack trace, its crashing just after ntDeviceIoContrilFile call.
kd> kb
ChildEBP RetAddr Args to Child
b8048798 805246fb 00000050 ffff0000 00000001 nt!KeBugCheckEx+0x1b
b80487e4 804e1ff1 00000001 ffff0000 00000000 nt!MmAccessFault+0x6f5
b80487e4 804ed0db 00000001 ffff0000 00000000 nt!KiTrap0E+0xcc
b80488b4 804ed15a 88e23a38 b8048900 b80488f4 nt!IopCompleteRequest+0x92
b8048904 806f2c0a 00000000 00000000 b804891c nt!KiDeliverApc+0xb3
b8048904 806ed0b3 00000000 00000000 b804891c hal!HalpApcInterrupt2ndEntry+0x31
b8048990 804e59ec 88e23a38 88e239f8 00000000 hal!KfLowerIrql+0x43
b80489b0 804ed174 88e23a38 896864c8 00000000 nt!KeInsertQueueApc+0x4b
b80489e4 f7432123 8960e9d8 8980b300 00000000 nt!IopfCompleteRequest+0x1d8
WARNING: Stack unwind information not available. Following frames may be wrong.
b80489f8 804e3d77 0000001c 0000001c 806ed070 NinjaDriver+0x1123
b8048a08 8056a9ab 88e23a8c 896864c8 88e239f8 nt!IopfCallDriver+0x31
b8048a1c 8057d9f7 89817030 88e239f8 896864c8 nt!IopSynchronousServiceTail+0x60
b8048ac4 8057fbfa 00000090 00000000 00000000 nt!IopXxxControlFile+0x611
b8048af8 b6e6a06f 00000090 00000000 00000000 nt!NtDeviceIoControlFile+0x2a
b8048b8c b6e6a5c3 00000001 00000090 00000000 Ninja+0x506f
b8048c80 b6e6ab9b 00000001 88da9898 00000090 Ninja+0x55c3
b8048d34 804df06b 00000090 00000000 00000000 Ninja+0x5b9b
b8048d34 7c90ebab 00000090 00000000 00000000 nt!KiFastCallEntry+0xf8
00f8fd7c 00000000 00000000 00000000 00000000 0x7c90ebab
Thanks in Advance,
You would need the function signature for nt!NtDeviceIoControlFile. With that info unassemble backwards from nt!NtDeviceIoControlFile's return address with ub b6e6a06f. This will show you how Ninja sets up the arguments for its call to nt!NtDeviceIoControlFile. Find the args that correspond to the ioctl code and buffer and then dump their contents.
Note that registers will have been reused so you may need to dig back further in the disassembly to get the correct values from the non-volatile registers which will have been saved on the stack before the function call.
In the windbg help file (debugger.chm) there is a very useful page titled "x86 Architecture". In this case, you may want to read the sections titled "Registers" and "Calling Conventions".

ring0 APC DLL injection crash target process on win7

I am trying to implement a ring0 dll injector driver and implement by APC injection. the code work perfectly on win XP. by on win7, it keeps crashing the target process.
here is the code:
NTSTATUS InjectDll( IN ULONG ulProcessId, HANDLE hEvent, IN TCHAR * pszDllPath )
{
NTSTATUS ntStatus;
HANDLE hProcess = NULL;
OBJECT_ATTRIBUTES ObjectAttributes;
CLIENT_ID ClientId;
PKEVENT pevtWait;
PKPROCESS pPcb = NULL;
PVOID pArg = NULL;
ULONG ulSize = 0;
KAPC_STATE ApcState;
PKAPC pApc;
PVOID pTcb;
PVOID pfnLoadLibrary;
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
LARGE_INTEGER nTimeout;
nTimeout.QuadPart = (LONGLONG) -300000000;
do
{
InitializeObjectAttributes(
&ObjectAttributes,
NULL,
0,
NULL,
NULL)
ClientId.UniqueProcess = (HANDLE)ulProcessId;
ClientId.UniqueThread = 0;
ntStatus = ZwOpenProcess(
&hProcess,
PROCESS_ALL_ACCESS,
&ObjectAttributes,
&ClientId
);
if ( NT_ERROR(ntStatus) )
{
DbgPrint("Open process %d failed - error 0x%X\r\n", ulProcessId, ntStatus);
break;
}
ulSize = MAX_FILENAME_LEN * sizeof(WCHAR);
ntStatus = ZwAllocateVirtualMemory(
hProcess,
&pArg,
0,
&ulSize,
MEM_RESERVE|MEM_COMMIT,
PAGE_READWRITE);
if ( NT_ERROR(ntStatus) )
{
DbgPrint("ZwAllocateVirtualMemory failed - error 0x%X\r\n", ntStatus);
// thus, make it leak in target process space!
break;
}
ntStatus = ObReferenceObjectByHandle(
hProcess,
PROCESS_ALL_ACCESS,
0,//should be PsProcessType,
PreviousMode,
(PVOID*)&pPcb,
NULL);
if ( NT_ERROR(ntStatus) )
{
DbgPrint("ObReferenceObjectByHandle 0x%X failed - error 0x%X\r\n", hProcess, ntStatus);
break;
}
//enter target process space
KeStackAttachProcess(pPcb,&ApcState);
pfnLoadLibrary = GetLoadLibraryAddress(pPcb);
if (!pfnLoadLibrary)
{
DbgPrint("Failed to get address of LoadLibrary\r\n");
// leave target process space
KeUnstackDetachProcess(&ApcState);
break;
}
else
{
DbgPrint("Get address of LoadLibrary : 0x%X\r\n", pfnLoadLibrary);
}
RtlCopyMemory( pArg, pszDllPath, MAX_FILENAME_LEN * sizeof(TCHAR));
// leave target process space
KeUnstackDetachProcess(&ApcState);
// get target thread
pTcb = GetThreadByProcess(pPcb);
if (!pTcb)
{
DbgPrint("Get thread failed!\r\n");
ntStatus = STATUS_UNSUCCESSFUL;
break;
}
// start inject
pApc = (PKAPC)ExAllocatePoolWithTag(NonPagedPool,sizeof(KAPC),POOL_TAG);
if (!pApc)
{
DbgPrint("Failed to allocate memory for the APC structure\r\n");
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
break;
}
KeInitializeApc(
pApc,
(PETHREAD)pTcb,
OriginalApcEnvironment,
&ApcKernelRoutine,
NULL,
(PKNORMAL_ROUTINE)pfnLoadLibrary,
UserMode,
pArg);
if (!KeInsertQueueApc(pApc,NULL,NULL,0))
{
DbgPrint("Failed to insert APC\r\n");
ntStatus = STATUS_UNSUCCESSFUL;
ExFreePool(pApc);
break;
}
} while (0);
if ( pPcb )
{
ObDereferenceObject(pPcb);
}
if ( hProcess)
{
ZwClose(hProcess);
}
DbgPrint("InjectDll end - status = 0x%X\r\n", ntStatus);
return ntStatus;
}
injectDll is called by DeviceIoControl serivce routine.
DRIVER_DISPATCH InjectorDeviceControl;
NTSTATUS NTAPI InjectorDeviceControl(
IN PDEVICE_OBJECT pDeviceObject,
PIRP pIrp)
{
PIO_STACK_LOCATION pStack;
PINJECT_DLL_PARAM pParam;
NTSTATUS ntStatus;
DbgPrint("call InjectorDeviceControl...\r\n");
/* Get the stack location and parameters */
pStack = IoGetCurrentIrpStackLocation(pIrp);
pParam = (PINJECT_DLL_PARAM)pIrp->AssociatedIrp.SystemBuffer;
if (pStack->Parameters.DeviceIoControl.IoControlCode != IOCTL_INJECTDLL)
{
/* Unsupported command */
ntStatus = STATUS_NOT_IMPLEMENTED;
}
else
{
/* Validate the input buffer length */
if (pStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(INJECT_DLL_PARAM))
{
/* Invalid buffer */
ntStatus = STATUS_INVALID_PARAMETER;
}
else
{
/* Inject dll */
ntStatus = InjectDll(pParam->ulProcessId, pParam->hEvent, pParam->DllName);
}
}
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return ntStatus;
}
here is the crash stacktrace of target process.
FAULTING_IP:
ntdll!RtlActivateActivationContextUnsafeFast+9c
7770f59a 8933 mov dword ptr [ebx],esi
EXCEPTION_RECORD: ffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 7770f59a (ntdll!RtlActivateActivationContextUnsafeFast+0x0000009c)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000001
Parameter[1]: 00000000
Attempt to write to address 00000000
DEFAULT_BUCKET_ID: NULL_POINTER_WRITE
PROCESS_NAME: QQProtect.exe
ERROR_CODE: (NTSTATUS) 0xc0000005 - 0x%08lx
EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - 0x%08lx
EXCEPTION_PARAMETER1: 00000001
EXCEPTION_PARAMETER2: 00000000
WRITE_ADDRESS: 00000000
FOLLOWUP_IP:
ntdll!RtlActivateActivationContextUnsafeFast+9c
7770f59a 8933 mov dword ptr [ebx],esi
NTGLOBALFLAG: 0
FAULTING_THREAD: 00000300
PRIMARY_PROBLEM_CLASS: NULL_POINTER_WRITE
BUGCHECK_STR: APPLICATION_FAULT_NULL_POINTER_WRITE
LAST_CONTROL_TRANSFER: from 77740baa to 7770f59a
STACK_TEXT:
021df8a0 77740baa 75655a2c 00000000 02790000 ntdll!RtlActivateActivationContextUnsafeFast+0x9c
021df91c 77740461 002b5cd0 021dfab8 756559bc ntdll!LdrpProcessStaticImports+0x1b8
021dfa8c 7774232c 021dfaec 021dfab8 00000000 ntdll!LdrpLoadDll+0x314
021dfac0 759088ee 0026f074 021dfb00 021dfaec ntdll!LdrLoadDll+0x92
021dfaf8 75dc3c12 00000000 00000000 00000001 KERNELBASE!LoadLibraryExW+0x15a
021dfb0c 77726f7d 02790000 00000000 00000000 kernel32!LoadLibraryW+0x11
021dff88 75dc3c45 00000000 021dffd4 777437f5 ntdll!KiUserApcDispatcher+0x25
021dff94 777437f5 00295fe8 75655ce4 00000000 kernel32!BaseThreadInitThunk+0xe
021dffd4 777437c8 7770fd0f 00295fe8 00000000 ntdll!__RtlUserThreadStart+0x70
021dffec 00000000 7770fd0f 00295fe8 00000000 ntdll!_RtlUserThreadStart+0x1b
STACK_COMMAND: ~1s; .ecxr ; kb
SYMBOL_STACK_INDEX: 0
SYMBOL_NAME: ntdll!RtlActivateActivationContextUnsafeFast+9c
FOLLOWUP_NAME: MachineOwner
MODULE_NAME: ntdll
IMAGE_NAME: ntdll.dll
DEBUG_FLR_IMAGE_TIMESTAMP: 4ce7b96e
FAILURE_BUCKET_ID: NULL_POINTER_WRITE_c0000005_ntdll.dll!RtlActivateActivationContextUnsafeFast
BUCKET_ID: APPLICATION_FAULT_NULL_POINTER_WRITE_ntdll!RtlActivateActivationContextUnsafeFast+9c
WATSON_STAGEONE_URL: http://watson.microsoft.com/StageOne/QQProtect_exe/3_0_1_3629/50a06369/ntdll_dll/6_1_7601_17514/4ce7b96e/c0000005/0002f59a.htm?Retriage=1
Followup: MachineOwner
any idea?
I've being working on some dll-injetion by apc and got this exact same problem.
After performing some reverse-engeenering, I found why it occured.
This is caused when the current thread's TEB does not have a ActivationContextStackPointer member:
ntdll!_TEB
+0x000 NtTib : _NT_TIB
+0x01c EnvironmentPointer : (null)
+0x020 ClientId : _CLIENT_ID
+0x028 ActiveRpcHandle : (null)
+0x02c ThreadLocalStoragePointer : (null)
+0x030 ProcessEnvironmentBlock : 0x7ffd8000 _PEB
+0x034 LastErrorValue : 0
+0x038 CountOfOwnedCriticalSections : 0
+0x03c CsrClientThread : (null)
+0x040 Win32ThreadInfo : (null)
+0x044 User32Reserved : [26] 0
+0x0ac UserReserved : [5] 0
+0x0c0 WOW32Reserved : (null)
+0x0c4 CurrentLocale : 0x804
+0x0c8 FpSoftwareStatusRegister : 0
+0x0cc SystemReserved1 : [54] (null)
+0x1a4 ExceptionCode : 0n0
+0x1a8 ActivationContextStackPointer : (null) <---it's null
The RtlActivateActivationContextUnsafeFast function tries to insert a node in the ActivationContext list, but the ActivationContextStackPointer is null, so an ACCESS_VIOLATION is raised.
mov dword ptr [ebx],esi //ebx = teb->ActivationContextStackPointer which is 000000000 esi = node
The ActivationContext is kinda related to the manifest of DLL.
You can get more information about this in the Microsoft help.
When loading a dll with a manifest file, the RtlActivateActivationContextUnsafeFast is called.
In case of that, I've solved the problem by disabling the generation of my dll's manifest file:
linker - > (/MANIFEST:NO)
or you can try to enforce the system to allocate a ActivationContextStack for target thread by making the target thread to call a ActivateActCtx function (I'm not sure if it is possible?).
Hope this helps you.
I stumbled upon the same problem, with a slightly different crash on Windows 10 (invalid address access, but not NULL pointer dereference). I didn't like 王云路's solution since I wanted to be able to load any DLL. What worked for me is calling NtQueueApcThread directly instead of using QueueUserAPC. In this case, RtlDispatchAPC is bypassed and the activation functions are not called.
Reference:
https://repnz.github.io/posts/apc/user-apc/#queueuserapc-kernelbase-dll-layer