struct _SID_AND_ATTRIBUTES * get_current_sid_a(ULONG *sid_a_size) // must be called at PASSIVE_LEVEL! { NTSTATUS status; HANDLE token; ULONG size; SID_AND_ATTRIBUTES *sid_a; *sid_a_size = 0; // open thread token status = ZwOpenThreadToken(CURRENT_THREAD, TOKEN_QUERY, FALSE, &token); if (status == STATUS_NO_TOKEN) { // open process token status = ZwOpenProcessToken(CURRENT_PROCESS, TOKEN_QUERY, &token); } if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] get_current_sid_a: ZwOpen{Thread|Process}Token: 0x%x!\n")); return NULL; } size = sizeof(*sid_a) + 100; // default size sid_a = (SID_AND_ATTRIBUTES *)malloc_np(size); if (sid_a == NULL) { KdPrint(("[tdi_fw] get_current_sid_a: malloc_np!\n")); goto done; } status = ZwQueryInformationToken(token, TokenUser, sid_a, size, &size); if (status == STATUS_BUFFER_TOO_SMALL) { free(sid_a); sid_a = (SID_AND_ATTRIBUTES *)malloc_np(size); if (sid_a == NULL) { KdPrint(("[tdi_fw] get_current_sid_a: malloc_np!\n")); goto done; } status = ZwQueryInformationToken(token, TokenUser, sid_a, size, &size); } if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] get_current_sid_a: ZwQueryInformationToken: 0x%x!\n")); free(sid_a); sid_a = NULL; goto done; } // got sid & attributes! *sid_a_size = size; done: ZwClose(token); return sid_a; }
NTSTATUS proc_init(void) { g_proc_hash = (proc_entry_t **)malloc_np(sizeof(*g_proc_hash) * HASH_SIZE); if (!g_proc_hash) { return STATUS_NO_MEMORY; } RtlZeroMemory(g_proc_hash, sizeof(*g_proc_hash) * HASH_SIZE); KeInitializeSpinLock(&g_proc_hash_guard); return STATUS_SUCCESS; }
NTSTATUS set_sid_list(char *buf, ULONG size) { KIRQL irql; NTSTATUS status; ULONG pos; int i; KeAcquireSpinLock(&g_sids.guard, &irql); // first, free information if (g_sids.buf != NULL) { free(g_sids.buf); g_sids.buf = NULL; } memset(g_sids.list, 0, sizeof(g_sids.list)); g_sids.count = 0; if (size != 0) { // copy buffer g_sids.buf = (char *)malloc_np(size); if (g_sids.buf == NULL) { KdPrint(("[tdi_fw] set_sid_list: malloc_np!\n")); status = STATUS_INSUFFICIENT_RESOURCES; goto done; } memcpy(g_sids.buf, buf, size); // parse buffer and find struct sid_nfo for (pos = 0, i = 0; pos + sizeof(struct sid_nfo) < size && i < MAX_SIDS_COUNT; i++) { struct sid_nfo *nfo = (struct sid_nfo *)&g_sids.buf[pos]; if (pos + sizeof(*nfo) + nfo->sid_len > size) break; g_sids.list[i] = nfo; pos += sizeof(*nfo) + nfo->sid_len; } g_sids.count = i; if (pos == size) status = STATUS_SUCCESS; else status = STATUS_INVALID_PARAMETER; } else status = STATUS_SUCCESS; done: KeReleaseSpinLock(&g_sids.guard, irql); return status; }
NTSTATUS proc_add(CONST ULONG pid, CONST ProcessData *pProcInfo, CONST TIME seen) { ULONG hash = CALC_HASH(pid); KIRQL irql; proc_entry_t *ote, *old_next; NTSTATUS status; int i; KeAcquireSpinLock(&g_proc_hash_guard, &irql); for (ote = g_proc_hash[hash]; ote; ote = ote->next) if (ote->pid == pid) break; if (!ote) { ote = (proc_entry_t *)malloc_np(sizeof(*ote)); if (!ote) { status = STATUS_NO_MEMORY; goto done; } memset(ote, 0, sizeof(*ote)); ote->next = g_proc_hash[hash]; g_proc_hash[hash] = ote; ote->pid = pid; } else { struct proc_entry *saved_next; ULONG saved_pid; DBGOUT(("proc_add: reuse pid 0x%x\n", pid)); // set all fields to zero except "next" and "fileobj" (cleanup listen/conn_entry if any) saved_next = ote->next; saved_pid = ote->pid; memset(ote, 0, sizeof(*ote)); ote->next = saved_next; ote->pid = saved_pid; } ote->lastseen = seen; ote->pProcInfo = pProcInfo; status = STATUS_SUCCESS; done: KeReleaseSpinLock(&g_proc_hash_guard, irql); RemoveUnseenProcesses(); return status; }
NTSTATUS ot_init(void) { g_ot_hash = (struct ot_entry **)malloc_np(sizeof(*g_ot_hash) * HASH_SIZE); if (g_ot_hash == NULL) { KdPrint(("[tdi_fw] ot_init: malloc_np\n")); return STATUS_INSUFFICIENT_RESOURCES; } memset(g_ot_hash, 0, sizeof(*g_ot_hash) * HASH_SIZE); KeInitializeSpinLock(&g_ot_hash_guard); g_cte_hash = (struct ctx_entry **)malloc_np(sizeof(*g_cte_hash) * HASH_SIZE); if (g_cte_hash == NULL) { KdPrint(("[tdi_fw] ot_init: malloc_np\n")); free(g_ot_hash); return STATUS_INSUFFICIENT_RESOURCES; } memset(g_cte_hash, 0, sizeof(*g_cte_hash) * HASH_SIZE); KeInitializeSpinLock(&g_cte_hash_guard); return STATUS_SUCCESS; }
int add_adapter(const wchar_t *name) { KIRQL irql; struct adapter_entry *adapter; int result; KeAcquireSpinLock(&g_list_guard, &irql); __try { // first try to find adapter by name if (g_first != NULL) { for (adapter = g_first, result = 1; adapter != NULL; adapter = adapter->next, result++) if (wcscmp(adapter->name, name) == 0) __leave; } // not found: add adapter to list adapter = (struct adapter_entry *)malloc_np(sizeof(*adapter) + (wcslen(name) + 1) * sizeof(wchar_t)); if (adapter == NULL) { KdPrint(("[ndis_hk] add_adapter: malloc_np!\n")); result = 0; __leave; } adapter->next = NULL; wcscpy(adapter->name, name); if (g_first == NULL) g_first = g_last = adapter; else { g_last->next = adapter; g_last = adapter; } result = ++g_count; } __finally { KeReleaseSpinLock(&g_list_guard, irql); } return result; }
struct _SID_AND_ATTRIBUTES * copy_sid_a(SID_AND_ATTRIBUTES *sid_a, ULONG sid_a_size) { SID_AND_ATTRIBUTES *result; if (sid_a == NULL) return NULL; result = (SID_AND_ATTRIBUTES *)malloc_np(sid_a_size); if (result == NULL) return NULL; memcpy(result, sid_a, sid_a_size); result->Sid = (char *)result + ((char *)(sid_a->Sid) - (char *)sid_a); return result; }
NTSTATUS datapipe_push(datapipe_t *dp, const char *data, ULONG size) { struct datapipe_entry *de; KIRQL irql; de = (struct datapipe_entry *)malloc_np(sizeof(*de) + size); if (!de) return STATUS_NO_MEMORY; de->next = NULL; de->size = size; memcpy(de->data, data, size); KeAcquireSpinLock(&dp->guard, &irql); if (dp->last) dp->last->next = de; else dp->last = de; if (!dp->first) dp->first = dp->last; KeReleaseSpinLock(&dp->guard, irql); return STATUS_SUCCESS; }
NTSTATUS ot_add_conn_ctx(PFILE_OBJECT addrobj, CONNECTION_CONTEXT conn_ctx, PFILE_OBJECT connobj) { ULONG hash = CALC_HASH_2(addrobj, conn_ctx); KIRQL irql; struct ctx_entry *cte; NTSTATUS status; KeAcquireSpinLock(&g_cte_hash_guard, &irql); for (cte = g_cte_hash[hash]; cte != NULL; cte = cte->next) if (cte->addrobj == addrobj && cte->conn_ctx == conn_ctx) break; if (cte == NULL) { KdPrint(("[tdi_fw] ot_add_fileobj: reuse addrobj 0x%x, conn_ctx 0x%x\n", addrobj, conn_ctx)); cte = (struct ctx_entry *)malloc_np(sizeof(*cte)); if (cte == NULL) { KdPrint(("[tdi_fw] ot_add_conn_ctx: malloc_np\n")); status = STATUS_INSUFFICIENT_RESOURCES; goto done; } cte->next = g_cte_hash[hash]; g_cte_hash[hash] = cte; cte->addrobj = addrobj; cte->conn_ctx = conn_ctx; } cte->connobj = connobj; status = STATUS_SUCCESS; done: KeReleaseSpinLock(&g_cte_hash_guard, irql); return status; }
NTSTATUS tdi_event_connect( IN PVOID TdiEventContext, IN LONG RemoteAddressLength, IN PVOID RemoteAddress, IN LONG UserDataLength, IN PVOID UserData, IN LONG OptionsLength, IN PVOID Options, OUT CONNECTION_CONTEXT *ConnectionContext, OUT PIRP *AcceptIrp) { TDI_EVENT_CONTEXT *ctx = (TDI_EVENT_CONTEXT *)TdiEventContext; TA_ADDRESS *remote_addr = ((TRANSPORT_ADDRESS *)RemoteAddress)->Address, *local_addr; struct ot_entry *ote_addr = NULL, *ote_conn = NULL; KIRQL irql; struct flt_request request; struct flt_rule rule; int result = FILTER_DENY; NTSTATUS status; PIO_STACK_LOCATION irps = NULL; struct accept_param *param = NULL; memset(&request, 0, sizeof(request)); KdPrint(("[tdi_fw] tdi_event_connect: addrobj 0x%x\n", ctx->fileobj)); ote_addr = ot_find_fileobj(ctx->fileobj, &irql); if (ote_addr == NULL) { KdPrint(("[tdi_fw] tdi_event_connect: ot_find_fileobj(0x%x)\n", ctx->fileobj)); goto done; } local_addr = (TA_ADDRESS *)(ote_addr->local_addr); KdPrint(("[tdi_fw] tdi_event_connect(pid:%u): %x:%u -> %x:%u\n", ote_addr->pid, ntohl(((TDI_ADDRESS_IP *)(remote_addr->Address))->in_addr), ntohs(((TDI_ADDRESS_IP *)(remote_addr->Address))->sin_port), ntohl(((TDI_ADDRESS_IP *)(local_addr->Address))->in_addr), ntohs(((TDI_ADDRESS_IP *)(local_addr->Address))->sin_port))); /* * request quick filter */ request.struct_size = sizeof(request); request.type = TYPE_CONNECT; request.direction = DIRECTION_IN; request.proto = IPPROTO_TCP; request.pid = ote_addr->pid; // get user SID & attributes! if ((request.sid_a = copy_sid_a(ote_addr->sid_a, ote_addr->sid_a_size)) != NULL) request.sid_a_size = ote_addr->sid_a_size; memcpy(&request.addr.from, &remote_addr->AddressType, sizeof(struct sockaddr)); memcpy(&request.addr.to, &local_addr->AddressType, sizeof(struct sockaddr)); request.addr.len = sizeof(struct sockaddr_in); result = quick_filter(&request, &rule); memcpy(request.log_rule_id, rule.rule_id, RULE_ID_SIZE); // log request later if (result == FILTER_DENY) goto done; result = FILTER_DENY; // leave spinlock before calling original handler KeReleaseSpinLock(&g_ot_hash_guard, irql); ote_addr = NULL; /* * run original handler */ status = ((PTDI_IND_CONNECT)(ctx->old_handler)) (ctx->old_context, RemoteAddressLength, RemoteAddress, UserDataLength, UserData, OptionsLength, Options, ConnectionContext, AcceptIrp); if (status != STATUS_MORE_PROCESSING_REQUIRED || *AcceptIrp == NULL) { KdPrint(("[tdi_fw] tdi_event_connect: status from original handler: 0x%x\n", status)); goto done; } /* * reinitialize connobj */ irps = IoGetCurrentIrpStackLocation(*AcceptIrp); KdPrint(("[tdi_fw] tdi_event_connect: connobj 0x%x\n", irps->FileObject)); // patch *AcceptIrp to change completion routine param = (struct accept_param *)malloc_np(sizeof(*param)); if (param == NULL) { KdPrint(("[tdi_fw] tdi_event_connect: malloc_np!\n")); status = STATUS_INSUFFICIENT_RESOURCES; goto done; } param->old_cr = irps->CompletionRoutine; param->old_context = irps->Context; param->fileobj = irps->FileObject; param->old_control = irps->Control; // can't use IoSetCompletionRoutine because it uses next not current stack location irps->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL; irps->CompletionRoutine = tdi_evconn_accept_complete; irps->Context = param; param = NULL; // find connobj for changing ote_conn = ot_find_fileobj(irps->FileObject, &irql); if (ote_conn == NULL) { KdPrint(("[tdi_fw] tdi_event_connect: ot_find_fileobj(0x%x)\n", irps->FileObject)); status = STATUS_OBJECT_NAME_NOT_FOUND; goto done; } ASSERT(ote_conn->type == FILEOBJ_CONNOBJ); // connobj must be associated with addrobj! if (ote_conn->associated_fileobj != ctx->fileobj) { KdPrint(("[tdi_fw] tdi_event_connect: 0x%x != 0x%x\n", ote_conn->associated_fileobj, ctx->fileobj)); status = STATUS_INVALID_PARAMETER; goto done; } // change conn_ctx (if needed) if (ote_conn->conn_ctx != *ConnectionContext) { // update (conn_ctx, addrobj)->connobj status = ot_del_conn_ctx(ote_conn->associated_fileobj, ote_conn->conn_ctx); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] tdi_event_connect: ot_del_conn_ctx: 0x%x\n", status)); goto done; } ote_conn->conn_ctx = *ConnectionContext; status = ot_add_conn_ctx(ote_conn->associated_fileobj, ote_conn->conn_ctx, irps->FileObject); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] tdi_event_connect: ot_add_conn_ctx: 0x%x\n", status)); goto done; } } // clear listen & conn entries in connobj (fileobject can be reused) ASSERT(ote_conn->listen_entry == NULL); if (ote_conn->listen_entry != NULL) del_listen_obj(ote_conn->listen_entry, FALSE); // free build case if (ote_conn->conn_entry != NULL) { if (ote_conn->ipproto == IPPROTO_TCP && ote_conn->log_disconnect) log_disconnect(ote_conn); del_tcp_conn_obj(ote_conn->conn_entry, FALSE); } // clear bytes count ote_conn->bytes_in = ote_conn->bytes_out = 0; // setup log_disconnect flag from rule ote_conn->log_disconnect = (rule.log >= RULE_LOG_COUNT); // sanity check if (local_addr->AddressLength != remote_addr->AddressLength) { KdPrint(("[tdi_fw] tdi_event_connect: different addr lengths! (%u != %u)\n", local_addr->AddressLength, remote_addr->AddressLength)); status = STATUS_INFO_LENGTH_MISMATCH; goto done; } // associate remote address with connobj if (remote_addr->AddressLength > sizeof(ote_conn->remote_addr)) { KdPrint(("[tdi_fw] tdi_event_connect: address too long! (%u)\n", remote_addr->AddressLength)); status = STATUS_BUFFER_TOO_SMALL; goto done; } memcpy(ote_conn->remote_addr, remote_addr, remote_addr->AddressLength); // associate local address with connobj if (local_addr->AddressLength > sizeof(ote_conn->local_addr)) { KdPrint(("[tdi_fw] tdi_event_connect: address too long! (%u)\n", local_addr->AddressLength)); status = STATUS_BUFFER_TOO_SMALL; goto done; } memcpy(ote_conn->local_addr, local_addr, local_addr->AddressLength); // create connection with "SYN_RCVD" state status = add_tcp_conn(ote_conn, TCP_STATE_SYN_RCVD); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] tdi_event_connect: add_tcp_conn: 0x%x\n", status)); goto done; } result = FILTER_ALLOW; done: // if logging is needed log request if (rule.log >= RULE_LOG_LOG) { if (result != FILTER_ALLOW && rule.result == FILTER_ALLOW) { request.type = TYPE_CONNECT_ERROR; // error has been occured request.status = status; } log_request(&request); } if (result != FILTER_ALLOW) { // deny incoming connection KdPrint(("[tdi_fw] tdi_event_connect: deny on reason 0x%x\n", status)); if (irps != NULL) { // delete connection if (ote_conn != NULL && ote_conn->conn_entry != NULL) { del_tcp_conn_obj(ote_conn->conn_entry, FALSE); ote_conn->conn_entry = NULL; } // release spinlock before IoCompleteRequest to avoid completion call inside spinlock if (ote_addr != NULL || ote_conn != NULL) { KeReleaseSpinLock(&g_ot_hash_guard, irql); ote_addr = NULL; ote_conn = NULL; } // destroy accepted IRP (*AcceptIrp)->IoStatus.Status = STATUS_UNSUCCESSFUL; IoCompleteRequest(*AcceptIrp, IO_NO_INCREMENT); } *AcceptIrp = NULL; status = STATUS_CONNECTION_REFUSED; } else status = STATUS_MORE_PROCESSING_REQUIRED; // cleanup if (ote_addr != NULL || ote_conn != NULL) KeReleaseSpinLock(&g_ot_hash_guard, irql); if (param != NULL) free(param); if (request.sid_a != NULL) free(request.sid_a); return status; }
/* this completion routine queries address and port from address object */ NTSTATUS tdi_create_addrobj_complete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) { NTSTATUS status; PIO_STACK_LOCATION irps = IoGetCurrentIrpStackLocation(Irp); PIRP query_irp = (PIRP)Context; PDEVICE_OBJECT devobj; TDI_CREATE_ADDROBJ2_CTX *ctx = NULL; PMDL mdl = NULL; KdPrint(("[tdi_fw] tdi_create_addrobj_complete: devobj 0x%x; addrobj 0x%x\n", DeviceObject, irps->FileObject)); if (Irp->IoStatus.Status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] tdi_create_addrobj_complete: status 0x%x\n", Irp->IoStatus.Status)); status = Irp->IoStatus.Status; goto done; } // query addrobj address:port ctx = (TDI_CREATE_ADDROBJ2_CTX *)malloc_np(sizeof(TDI_CREATE_ADDROBJ2_CTX)); if (ctx == NULL) { KdPrint(("[tdi_fw] tdi_create_addrobj_complete: malloc_np\n")); status = STATUS_INSUFFICIENT_RESOURCES; goto done; } ctx->fileobj = irps->FileObject; ctx->tai = (TDI_ADDRESS_INFO *)malloc_np(TDI_ADDRESS_INFO_MAX); if (ctx->tai == NULL) { KdPrint(("[tdi_fw] tdi_create_addrobj_complete: malloc_np!\n")); status = STATUS_INSUFFICIENT_RESOURCES; goto done; } mdl = IoAllocateMdl(ctx->tai, TDI_ADDRESS_INFO_MAX, FALSE, FALSE, NULL); if (mdl == NULL) { KdPrint(("[tdi_fw] tdi_create_addrobj_complete: IoAllocateMdl!\n")); status = STATUS_INSUFFICIENT_RESOURCES; goto done; } MmBuildMdlForNonPagedPool(mdl); devobj = get_original_devobj(DeviceObject, NULL); // use original devobj! if (devobj == NULL) { KdPrint(("[tdi_fw] tdi_create_addrobj_complete: get_original_devobj!\n")); status = STATUS_INVALID_PARAMETER; goto done; } TdiBuildQueryInformation(query_irp, devobj, irps->FileObject, tdi_create_addrobj_complete2, ctx, TDI_QUERY_ADDRESS_INFO, mdl); status = IoCallDriver(devobj, query_irp); query_irp = NULL; mdl = NULL; ctx = NULL; if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] tdi_create_addrobj_complete: IoCallDriver: 0x%x\n", status)); goto done; } status = STATUS_SUCCESS; done: // cleanup if (mdl != NULL) IoFreeMdl(mdl); if (ctx != NULL) { if (ctx->tai != NULL) free(ctx->tai); free(ctx); } if (query_irp != NULL) IoCompleteRequest(query_irp, IO_NO_INCREMENT); Irp->IoStatus.Status = status; if (status != STATUS_SUCCESS) { // tdi_create failed - remove fileobj from hash ot_del_fileobj(irps->FileObject, NULL); } return tdi_generic_complete(DeviceObject, Irp, Context); }
/* * Dispatch routines call this function to complete their processing. * They _MUST_ call this function anyway. */ NTSTATUS tdi_dispatch_complete(PDEVICE_OBJECT devobj, PIRP irp, int filter, PIO_COMPLETION_ROUTINE cr, PVOID context) { PIO_STACK_LOCATION irps = IoGetCurrentIrpStackLocation(irp); NTSTATUS status; if (filter == FILTER_DENY) { /* * DENY: complete request with status "Access violation" */ KdPrint(("[tdi_fw] tdi_dispatch_complete: [DROP!]" " major 0x%x, minor 0x%x for devobj 0x%x; fileobj 0x%x\n", irps->MajorFunction, irps->MinorFunction, devobj, irps->FileObject)); if (irp->IoStatus.Status == STATUS_SUCCESS) { // change status status = irp->IoStatus.Status = STATUS_ACCESS_DENIED; } else { // set IRP status unchanged status = irp->IoStatus.Status; } IoCompleteRequest (irp, IO_NO_INCREMENT); } else if (filter == FILTER_ALLOW) { /* * ALLOW: pass IRP to the next driver */ #ifndef USE_TDI_HOOKING PDEVICE_OBJECT old_devobj = get_original_devobj(devobj, NULL); if (old_devobj == NULL) { KdPrint(("[tdi_fw] tdi_send_irp_to_old_driver: Unknown DeviceObject 0x%x!\n", devobj)); status = irp->IoStatus.Status = STATUS_UNSUCCESSFUL; IoCompleteRequest (irp, IO_NO_INCREMENT); return status; } #endif KdPrint(("[tdi_fw] tdi_dispatch_complete: [ALLOW.]" " major 0x%x, minor 0x%x for devobj 0x%x; fileobj 0x%x\n", irps->MajorFunction, irps->MinorFunction, devobj, irps->FileObject)); #ifndef USE_TDI_HOOKING if (cr == NULL || irp->CurrentLocation <= 1) { /* * we use _THIS_ way of sending IRP to old driver * a) to avoid NO_MORE_STACK_LOCATIONS * b) and if we haven't our completions - no need to copy stack locations! */ // stay on this location after IoCallDriver IoSkipCurrentIrpStackLocation(irp); #endif if (cr != NULL) { /* * set completion routine (this way is slow) */ // save old completion routine and context TDI_SKIP_CTX *ctx = (TDI_SKIP_CTX *)malloc_np(sizeof(*ctx)); if (ctx == NULL) { KdPrint(("[tdi_fw] tdi_send_irp_to_old_driver: malloc_np\n")); status = irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; IoCompleteRequest(irp, IO_NO_INCREMENT); return status; } ctx->old_cr = irps->CompletionRoutine; ctx->old_context = irps->Context; ctx->new_cr = cr; ctx->new_context = context; ctx->fileobj = irps->FileObject; ctx->new_devobj = devobj; ctx->old_control = irps->Control; IoSetCompletionRoutine(irp, tdi_skip_complete, ctx, TRUE, TRUE, TRUE); } #ifndef USE_TDI_HOOKING } else { PIO_STACK_LOCATION irps = IoGetCurrentIrpStackLocation(irp), next_irps = IoGetNextIrpStackLocation(irp); memcpy(next_irps, irps, sizeof(*irps)); if (cr != NULL) { /* * this way for completion is more quicker than used above */ IoSetCompletionRoutine(irp, cr, context, TRUE, TRUE, TRUE); } else IoSetCompletionRoutine(irp, tdi_generic_complete, NULL, TRUE, TRUE, TRUE); } #endif /* call original driver */ #ifndef USE_TDI_HOOKING status = IoCallDriver(old_devobj, irp); #else status = g_old_DriverObject.MajorFunction[irps->MajorFunction](devobj, irp); #endif } else { /* FILTER_UNKNOWN */ /* * UNKNOWN: just complete the request */ status = irp->IoStatus.Status = STATUS_SUCCESS; // ??? IoCompleteRequest (irp, IO_NO_INCREMENT); } return status; }
int tdi_connect(PIRP irp, PIO_STACK_LOCATION irps, struct completion *completion) { PTDI_REQUEST_KERNEL_CONNECT param = (PTDI_REQUEST_KERNEL_CONNECT)(&irps->Parameters); TA_ADDRESS *remote_addr = ((TRANSPORT_ADDRESS *)(param->RequestConnectionInformation->RemoteAddress))->Address; PFILE_OBJECT addrobj; NTSTATUS status; TA_ADDRESS *local_addr; int result = FILTER_DENY, ipproto; struct ot_entry *ote_conn = NULL, *ote_addr; KIRQL irql; struct flt_request request; struct flt_rule rule; memset(&request, 0, sizeof(request)); KdPrint(("[tdi_fw] tdi_connect: connobj 0x%x, to address %x:%u\n", irps->FileObject, ntohl(((TDI_ADDRESS_IP *)(remote_addr->Address))->in_addr), ntohs(((TDI_ADDRESS_IP *)(remote_addr->Address))->sin_port))); // check device object: TCP or UDP if (irps->DeviceObject != g_tcpfltobj && irps->DeviceObject != g_udpfltobj) { KdPrint(("[tdi_fw] tdi_connect: unknown DeviceObject 0x%x!\n", irps->DeviceObject)); goto done; } ote_conn = ot_find_fileobj(irps->FileObject, &irql); if (ote_conn == NULL) { KdPrint(("[tdi_fw] tdi_connect: ot_find_fileobj(0x%x)!\n", irps->FileObject)); goto done; } if (get_original_devobj(irps->DeviceObject, &ipproto) == NULL || (ipproto != IPPROTO_TCP && ipproto != IPPROTO_UDP)) { // invalid device object! KdPrint(("[tdi_fw] tdi_connect: invalid device object 0x%x!\n", irps->DeviceObject)); goto done; } if (ipproto == IPPROTO_TCP) { /* * For TCP: get addrobj by connobj and get local address by it */ addrobj = ote_conn->associated_fileobj; if (addrobj == NULL) { KdPrint(("[tdi_fw] tdi_connect: empty addrobj!\n")); goto done; } ote_addr = ot_find_fileobj(addrobj, NULL); // we're already in spinlock if (ote_addr == NULL) { KdPrint(("[tdi_fw] tdi_connect: ot_find_fileobj(0x%x)!\n", addrobj)); goto done; } } else { /* * For UDP: connobj and addrobj are the same */ KdPrint(("[tdi_fw] tdi_connect: connected UDP socket detected\n")); // for connected UDP sockets connobj and addrobj are the same addrobj= irps->FileObject; ote_addr = ote_conn; } local_addr = (TA_ADDRESS *)(ote_addr->local_addr); // sanity check if (local_addr->AddressLength != remote_addr->AddressLength) { KdPrint(("[tdi_fw] tdi_connect: different addr lengths! (%u != %u)\n", local_addr->AddressLength, remote_addr->AddressLength)); goto done; } // set remote address with connobj if (remote_addr->AddressLength > sizeof(ote_conn->remote_addr)) { KdPrint(("[tdi_fw] tdi_connect: address too long! (%u)\n", remote_addr->AddressLength)); goto done; } memcpy(ote_conn->remote_addr, remote_addr, remote_addr->AddressLength); // set local address with connobj if (local_addr->AddressLength > sizeof(ote_conn->local_addr)) { KdPrint(("[tdi_fw] tdi_connect: address to long! (%u)\n", local_addr->AddressLength)); goto done; } memcpy(ote_conn->local_addr, local_addr, local_addr->AddressLength); KdPrint(("[tdi_fw] tdi_connect(pid:%u/%u): %x:%u -> %x:%u (ipproto = %d)\n", ote_conn->pid, PsGetCurrentProcessId(), ntohl(((TDI_ADDRESS_IP *)(local_addr->Address))->in_addr), ntohs(((TDI_ADDRESS_IP *)(local_addr->Address))->sin_port), ntohl(((TDI_ADDRESS_IP *)(remote_addr->Address))->in_addr), ntohs(((TDI_ADDRESS_IP *)(remote_addr->Address))->sin_port), ipproto)); /* * Call quick_filter */ request.struct_size = sizeof(request); request.type = TYPE_CONNECT; request.direction = DIRECTION_OUT; request.proto = ipproto; // don't use ote_conn->pid because one process can create connection object // but another one can connect request.pid = (ULONG)PsGetCurrentProcessId(); if (request.pid == 0) { // avoid idle process pid (XXX do we need this?) request.pid = ote_addr->pid; } // get user SID & attributes (can't call get_current_sid_a at DISPATCH_LEVEL) if ((request.sid_a = copy_sid_a(ote_addr->sid_a, ote_addr->sid_a_size)) != NULL) request.sid_a_size = ote_addr->sid_a_size; memcpy(&request.addr.from, &local_addr->AddressType, sizeof(struct sockaddr)); memcpy(&request.addr.to, &remote_addr->AddressType, sizeof(struct sockaddr)); request.addr.len = sizeof(struct sockaddr_in); memset(&rule, 0, sizeof(rule)); result = quick_filter(&request, &rule); memcpy(request.log_rule_id, rule.rule_id, RULE_ID_SIZE); if (result == FILTER_ALLOW && ipproto == IPPROTO_TCP) { struct flt_request *context_req = NULL; // add connection with state "SYN_SENT" status = add_tcp_conn(ote_conn, TCP_STATE_SYN_SENT); if (status != STATUS_SUCCESS) { KdPrint(("[tdi_fw] tdi_connect: add_conn: 0x%x!\n", status)); result = FILTER_DENY; goto done; // don't log this failure } if (rule.log >= RULE_LOG_LOG) { // set ote_conn->log_disconnect ote_conn->log_disconnect = (rule.log >= RULE_LOG_COUNT); // copy request for completion (LOG success or not) context_req = (struct flt_request *)malloc_np(sizeof(*context_req)); if (context_req != NULL) { memcpy(context_req, &request, sizeof(*context_req)); // don't free SID request.sid_a = NULL; // don't log request in this time rule.log = RULE_LOG_NOLOG; } } // set completion to add connection info to connection table completion->routine = tdi_connect_complete; completion->context = context_req; } // if logging is needed log request if (rule.log >= RULE_LOG_LOG) log_request(&request); done: // cleanup if (ote_conn != NULL) KeReleaseSpinLock(&g_ot_hash_guard, irql); if (request.sid_a != NULL) free(request.sid_a); if (result != FILTER_ALLOW) { irp->IoStatus.Status = STATUS_REMOTE_NOT_LISTENING; // set fake status } return result; }
/* query local address and port for connection */ void update_conn_info(PDEVICE_OBJECT devobj, PFILE_OBJECT connobj) { PIRP query_irp; PMDL mdl = NULL; struct uci_param *uci_param = NULL; // MUST be executed at PASSIVE_LEVEL if (KeGetCurrentIrql() != PASSIVE_LEVEL) { // do it a bit later :-) struct delayed_ucn_param *ucn_param = (struct delayed_ucn_param *)malloc_np(sizeof(*ucn_param)); if (ucn_param != NULL) { memset(ucn_param, 0, sizeof(*ucn_param)); ucn_param->devobj = devobj; ucn_param->fileobj = connobj; ExInitializeWorkItem(&ucn_param->item, delayed_ucn, ucn_param); ExQueueWorkItem(&ucn_param->item, DelayedWorkQueue); // DelayedWorkQueue a good value? } else { KdPrint(("[ndis_hk] tdi_connect_complete: malloc_np!\n")); // so we'll live without known local address :-( } return; } // we're at PASSIVE_LEVEL query_irp = TdiBuildInternalDeviceControlIrp(TDI_QUERY_INFORMATION, devobj, connobj, NULL, NULL); if (query_irp == NULL) { KdPrint(("[tdi_fw] update_conn_info: TdiBuildInternalDeviceControlIrp!\n")); goto done; } uci_param = (struct uci_param *)malloc_np(sizeof(*uci_param) + TDI_ADDRESS_INFO_MAX); if (uci_param == NULL) { KdPrint(("[tdi_fw] update_conn_info: malloc_np!\n")); goto done; } memset(uci_param, 0, sizeof(*uci_param) + TDI_ADDRESS_INFO_MAX); uci_param->connobj = connobj; mdl = IoAllocateMdl(uci_param->address, TDI_ADDRESS_INFO_MAX, FALSE, FALSE, NULL); if (mdl == NULL) { KdPrint(("[tdi_fw] update_conn_info: IoAllocateMdl!\n")); goto done; } MmBuildMdlForNonPagedPool(mdl); TdiBuildQueryInformation(query_irp, devobj, connobj, update_conn_info_complete, uci_param, TDI_QUERY_ADDRESS_INFO, mdl); IoCallDriver(devobj, query_irp); query_irp = NULL; mdl = NULL; uci_param = NULL; done: // cleanup if (mdl != NULL) IoFreeMdl(mdl); if (uci_param != NULL) ExFreePool(uci_param); if (query_irp != NULL) IoCompleteRequest(query_irp, IO_NO_INCREMENT); }
NTSTATUS tdi_event_receive( IN PVOID TdiEventContext, IN CONNECTION_CONTEXT ConnectionContext, IN ULONG ReceiveFlags, IN ULONG BytesIndicated, IN ULONG BytesAvailable, OUT ULONG *BytesTaken, IN PVOID Tsdu, OUT PIRP *IoRequestPacket) { TDI_EVENT_CONTEXT *ctx = (TDI_EVENT_CONTEXT *)TdiEventContext; PFILE_OBJECT connobj = ot_find_conn_ctx(ctx->fileobj, ConnectionContext); NTSTATUS status; KdPrint(("[tdi_fw] tdi_event_receive: addrobj 0x%x; connobj: 0x%x; %u/%u; flags: 0x%x\n", ctx->fileobj, connobj, BytesIndicated, BytesAvailable, ReceiveFlags)); status = ((PTDI_IND_RECEIVE)(ctx->old_handler)) (ctx->old_context, ConnectionContext, ReceiveFlags, BytesIndicated, BytesAvailable, BytesTaken, Tsdu, IoRequestPacket); KdPrint(("[tdi_fw] tdi_event_receive: status 0x%x; BytesTaken: %u; Irp: 0x%x\n", status, *BytesTaken, *IoRequestPacket)); if (*BytesTaken != 0) { struct ot_entry *ote_conn; KIRQL irql; ote_conn = ot_find_fileobj(connobj, &irql); if (ote_conn != NULL) { ULONG bytes = *BytesTaken; ote_conn->bytes_in += bytes; // traffic stats KeAcquireSpinLockAtDpcLevel(&g_traffic_guard); g_traffic[TRAFFIC_TOTAL_IN] += bytes; if (ote_conn->log_disconnect) g_traffic[TRAFFIC_COUNTED_IN] += bytes; KeReleaseSpinLockFromDpcLevel(&g_traffic_guard); KeReleaseSpinLock(&g_ot_hash_guard, irql); } } if (*IoRequestPacket != NULL) { // got IRP. replace completion. struct tdi_client_irp_ctx *new_ctx; PIO_STACK_LOCATION irps = IoGetCurrentIrpStackLocation(*IoRequestPacket); new_ctx = (struct tdi_client_irp_ctx *)malloc_np(sizeof(*new_ctx)); if (new_ctx != NULL) { new_ctx->connobj = connobj; if (irps->CompletionRoutine != NULL) { new_ctx->completion = irps->CompletionRoutine; new_ctx->context = irps->Context; new_ctx->old_control = irps->Control; } else { // we don't use IoSetCompletionRoutine because it uses next not current location new_ctx->completion = NULL; new_ctx->context = NULL; } irps->CompletionRoutine = tdi_client_irp_complete; irps->Context = new_ctx; irps->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL; } } return status; }
NTSTATUS ot_add_fileobj(PDEVICE_OBJECT devobj, PFILE_OBJECT fileobj, int fileobj_type, int ipproto, CONNECTION_CONTEXT conn_ctx) // must be called at PASSIVE_LEVEL! { ULONG hash = CALC_HASH(fileobj); KIRQL irql; struct ot_entry *ote; NTSTATUS status; int i; SID_AND_ATTRIBUTES *sid_a; ULONG sid_a_size; if (fileobj == NULL) return STATUS_INVALID_PARAMETER_2; // while we're at PASSIVE_LEVEL get SID & attributes sid_a = get_current_sid_a(&sid_a_size); KeAcquireSpinLock(&g_ot_hash_guard, &irql); for (ote = g_ot_hash[hash]; ote != NULL; ote = ote->next) if (ote->fileobj == fileobj) break; if (ote == NULL) { ote = (struct ot_entry *)malloc_np(sizeof(*ote)); if (ote == NULL) { KdPrint(("[tdi_fw] ot_add_fileobj: malloc_np\n")); status = STATUS_INSUFFICIENT_RESOURCES; goto done; } memset(ote, 0, sizeof(*ote)); ote->next = g_ot_hash[hash]; g_ot_hash[hash] = ote; ote->fileobj = fileobj; for (i = 0; i < MAX_EVENT; i++) ote->ctx[i].fileobj = fileobj; } else { KdPrint(("[tdi_fw] ot_add_fileobj: reuse fileobj 0x%x\n", fileobj)); ot_cleanup_ote(ote); } ote->signature = 'OTE '; ote->pid = (ULONG)PsGetCurrentProcessId(); // save SID & attributes ote->sid_a = sid_a; ote->sid_a_size = sid_a_size; sid_a = NULL; ote->devobj = devobj; ote->type = fileobj_type; ote->ipproto = ipproto; ote->conn_ctx = conn_ctx; status = STATUS_SUCCESS; done: // cleanup KeReleaseSpinLock(&g_ot_hash_guard, irql); if (sid_a != NULL) free(sid_a); return status; }