0

I'm trying to code a driver that implements a singly linked list for md5 hashes.

The code I have so far is:

Signature.h

#ifndef SIGNATURE_H
#define SIGNATURE_H

typedef struct {
    char hash[33]; // last byte for null terminator
    SINGLE_LIST_ENTRY next;
} SIGNATURE, *PSIGNATURE;

#endif

SignatureList.h

#define CopyCharArrays(src, dest, len)      \
do {                                    \
    for (int i = 0; i < len; i++)       \
        dest[i] = src[i];               \
    dest[len] = '\0';                   \
} while(0)           

/* Return TRUE if strings are equal */
static BOOLEAN IsEqual(char* str1, char* str2, int len)
{
    for (int i = 0; i < len; i++)
        if (str1[i] != str2[i])
            return FALSE;

    return TRUE;
}
SINGLE_LIST_ENTRY Head;

static void AddToSignatureList(char hash[32]) {
    PSIGNATURE Sig = ExAllocatePool(NonPagedPoolNx, sizeof(SIGNATURE)); // allocate memory to store a SIGNATURE node

    CopyCharArrays(hash, Sig->hash, 32); // place the hash in our new allocated node (puts null terminator at position 33)

    DbgPrintEx(0, DPFLTR_ERROR_LEVEL, "Hash: %s\n", Sig->hash);
    PushEntryList(&Head, &Sig->next); // insert the new node in our list
}

/*Returns TRUE if the hash is in the library*/
static BOOLEAN SignatureScan(char hash[32])
{
    if (IsListEmpty(Head)) { // if there is no signature in the index
        return FALSE;
    }
    else {
        PSINGLE_LIST_ENTRY pSig = Head; // this is the pointer we will use to iterate
        PSIGNATURE Sig;

        do {
            Sig = CONTAINING_RECORD(pSig, SIGNATURE, next); // get the PSIGNATURE on the current pointer

            DbgPrintEx(0, DPFLTR_ERROR_LEVEL, "%s - %s\n", Sig->hash, hash);
            if (!IsEqual(Sig->hash, hash, 32)) {
                return TRUE;
            }

            pSig = &Sig->next; // go to the next node
        } while (pSig != Head);

        return FALSE;
    }
}

Main.c

AddToSignatureList("2d75cc1bf8e57872781f9cd04a529256");
AddToSignatureList("00f538c3d410822e241486ca061a57ee");
AddToSignatureList("3f066dd1f1da052248aed5abc4a0c6a1");
AddToSignatureList("781770fda3bd3236d0ab8274577dddde");
AddToSignatureList("86b6c59aa48a69e16d3313d982791398");

DbgPrintEx(0, DPFLTR_ERROR_LEVEL, "Should return TRUE: %d\n", SignatureScan("3f066dd1f1da052248aed5abc4a0c6a1"));
DbgPrintEx(0, DPFLTR_ERROR_LEVEL, "Should return FALSE: %d\n", SignatureScan("3f066dd1f1da052248aed5abc4a0c6a2"));
DbgPrintEx(0, DPFLTR_ERROR_LEVEL, "Should return FALSE: %d\n", SignatureScan("526ae1b5f10eccfa069f4fd1c8b18097"));

I can't really figure out.. It doesn't crash and the WinDBG output is: WinDBG output

I have read this blog post and this msdn article but I still don't know what is wrong in my driver

David Gomes
  • 650
  • 2
  • 10
  • 34
  • 1
    Sounds like you need to use a debugger. – aschepler Apr 13 '17 at 22:47
  • @aschepler I'm using WinDBG.. but you mean use it to see memory space? – David Gomes Apr 13 '17 at 22:49
  • 1
    To step through your code a bit at a time and see where the values are/aren't what you expect. – aschepler Apr 13 '17 at 22:50
  • 1
    `char[32]` ==> `char[33]`. Your terminated strings container 32 characters **and** a nullchar character, which you're not providing space for. Probability is high the initial copy to your nodes is breaching your buffer and invoking UB. – WhozCraig Apr 13 '17 at 22:53
  • @WhozCraig really? I thought char[32] would already allocate '\0' in the 32th position – David Gomes Apr 13 '17 at 22:54
  • 1
    @L3n You need to account space for the terminator *yourself*. I also question your use of double-indirection, `pSig = &Sig->next`. That has a code smell on its own. It may just be a thing I'm not used to seeing, but it looks odd. Regardless, the buffer size is *definitely* a problem. – WhozCraig Apr 13 '17 at 22:54
  • @WhozCraig ok just updated my question, now it doesn't output random characters but the Sig->hash is empty still.. – David Gomes Apr 13 '17 at 23:15
  • @WhozCraig what do you mean by breaching my buffer and invoking UB? – David Gomes Apr 13 '17 at 23:19
  • You overran the end of the buffer, breaching it. Doing so invokes Undefined Behaviour, UB. UB is one of the most feared things in all programming. It is code that has no standard-defined behaviour, so the compiler is allowed to generate a program that does anything from exactly what you expected to the utterly fanciful like banishing all that is evil from the world. – user4581301 Apr 13 '17 at 23:36
  • There are some really cool double pointer linked list tricks. [Here's one.](http://stackoverflow.com/questions/12914917/using-pointers-to-remove-item-from-singly-linked-list) It does not look like it's being applied correctly here, though. – user4581301 Apr 13 '17 at 23:41
  • @user4581301 I can't do that in windows drivers. – David Gomes Apr 13 '17 at 23:49
  • @WhozCraig how am I reaching the end of the buffer? – David Gomes Apr 13 '17 at 23:49
  • Can't do what in a Windows driver? I've found there are a great many things you can do in a Windows driver. In fact the number positively dwarfs the number of things you should do in a Windows driver. – user4581301 Apr 13 '17 at 23:52
  • @user4581301 AFAIK I can't do a normal linked list in kernel mode... I mean why would microsoft give coders this functions and structures if you could do a normal one? – David Gomes Apr 13 '17 at 23:54
  • So you wouldn't goof up the linked list implementation writing it yourself and crash Windows. SO is littered with failed linked list implementations. As it is, this looks dodgy, but it isn't. Looks like WhozCraig and I got sucked in by the name `next`. That variable isn't a reference to the next node, it's the list node's book-keeping structure. It contains a `next` that points to the next node. – user4581301 Apr 14 '17 at 00:13
  • @user4581301 this is way harder than normal linkedlist! So can I really implement a normal one? – David Gomes Apr 14 '17 at 00:51
  • @WhozCraig: I'm not certain about the implementation here but the double indirection is very common in linked lists. It allows using the initial HEAD pointer in exactly the same way as the node->next pointers and makes the code more generic. – Zan Lynx Apr 14 '17 at 02:53
  • Are you compiling this with maximum warning levels? Because I think you are assigning the wrong pointers. If pSig is a pointer to a list entry, and Head is a list entry, pSig = Head should be wrong. – Zan Lynx Apr 14 '17 at 02:56
  • @ZanLynx yes but I chose to not take warnings as errors since there are warnings in nfdef.h(microsoft's code) – David Gomes Apr 14 '17 at 03:22
  • 1
    @ZanLynx, I think the code as currently posted in the question is a combination of old and new code. `Head` was originally a `PSINGLE_LINKED_LIST`. – Harry Johnston Apr 14 '17 at 21:54
  • If you're talking about warning C4201, Microsoft's sample code disables that particular warning before including the headers. That might be preferable to changing the warnings-as-errors setting. – Harry Johnston Apr 14 '17 at 21:55
  • 1
    Note that your latest change, replacing `strcmp` with `IsEqual`, introduces a bug: it should be `if (IsEqual()) return TRUE` not `if (!IsEqual)`. – Harry Johnston Apr 15 '17 at 20:41
  • @HarryJohnston I know, I had removed the ! – David Gomes Apr 16 '17 at 10:24

1 Answers1

2

I see several likely issues.

  • Your loop isn't being initialized correctly.

    PSINGLE_LIST_ENTRY pSig = Head;
    PSIGNATURE Sig;
    
    do {
        Sig = CONTAINING_RECORD(pSig, SIGNATURE, next);
    

The first time through this loop, pSig is pointing at Head which is not part of a SIGNATURE structure, so the call to CONTAINING_RECORD results in a meaningless pointer.

  • You're not exiting the loop correctly either. The end of a singly-linked list is represented by a null pointer, not by a pointer to the header.

  • And, going for the triple, you aren't incrementing the list properly either:

    pSig = &Sig->next; // go to the next node
    

Sig->next points to the current node, not the next node.

  • IsListEmpty is for doubly-linked lists, not singly-linked lists. That code shouldn't even compile.

  • The choice of PSINGLE_LIST_ENTRY rather than SINGLE_LIST_ENTRY for Head is odd, and the code that initializes Head is missing.

  • The argument hash is declared as a 32-character array, it should be 33 characters to allow for the null terminator.

I think the function should look more like this:

SINGLE_LIST_ENTRY Head = {NULL};

static BOOLEAN SignatureScan(char hash[33])
{
    PSINGLE_LIST_ENTRY pSig = Head->Next;
    PSIGNATURE Sig;

    while (pSig != NULL)
    {
        Sig = CONTAINING_RECORD(pSig, SIGNATURE, next);

        DbgPrintEx(0, DPFLTR_ERROR_LEVEL, "%s - %s\n", Sig->hash, hash);

        if (!strcmp(Sig->hash, hash)) return TRUE;

        pSig = pSig->Next;
    }

    return FALSE;
}

Also, note that it is more usual for the SINGLE_LINKED_LIST to be the first element of the structure, and that calling it next is misleading. Something like this might be preferable:

typedef struct {
    SINGLE_LIST_ENTRY list_node;
    char hash[33]; // last byte for null terminator
} SIGNATURE, *PSIGNATURE;
Harry Johnston
  • 35,639
  • 6
  • 68
  • 158
  • thanks for the effort, btw in the parameters I can put 32 length right? also is AddToSignatureList right? – David Gomes Apr 14 '17 at 01:25
  • 1
    You're using `strcmp` so the `hash` array must allow space for the null terminator. If you used `memcmp` instead you might not need one, although you'd also have to update the debug statement accordingly. I don't see anything wrong in `AddToSignatureList`, assuming that `CopyCharArrays` does the right thing. – Harry Johnston Apr 14 '17 at 01:31
  • Added to the signaturelist.h btw I used a do {} while(0) uselessly because I was getting a compiler error and after googling I concluded that I need that. – David Gomes Apr 14 '17 at 01:36
  • Btw it isn't working still, I added a IsEqual function to the question(and changed the code I have with your updates but won't put it in the answer) and it doesn't work still! But now the DbgPrintEx for Sig->hash and hash just don't show! – David Gomes Apr 14 '17 at 02:28
  • Meaning that it never enters that while loop so there must be a problem with my add method – David Gomes Apr 14 '17 at 02:31
  • 1
    Easy enough to add in debug statements to check, at the end of the add method and the beginning of the scan method, whether or not `Head->Next` is null. – Harry Johnston Apr 14 '17 at 05:01
  • I used Head.Next since Head is SINGLE_LIST_ENTRY and not a pointer as you said it would look better this way and it showed that it is null even after pushing... – David Gomes Apr 14 '17 at 16:20
  • 1
    Perhaps the SIGNATURE structure is being tightly packed so the pointer is misaligned? Add some code to verify that the address of `Sig->next` is a multiple of eight. – Harry Johnston Apr 14 '17 at 22:06
  • First one is on: 0x58d58898, second 0x56d29ef8, third 5857ecf8 so yea they're all multiple of eight. – David Gomes Apr 15 '17 at 12:57
  • Since this question was meant for iterating and I got it working without using microshit's api I'll consider this has correct answer, thanks a lot for your effort and your time. – David Gomes Apr 18 '17 at 16:49