Your pointer arithmetic is wrong. (ULONG *)pe + ... and (ULONGLONG *)pe + ... don't do what you think they do.
When you advance a pointer T* by an integer N, the address stored in the pointer is advanced by N number of T elements - ie by N * sizeof(T) number of bytes.
When you type-cast pe to ULONG* and then add importDir.VirtualAddress to it, you are advancing the address stored in pe by sizeof(ULONG) * VirtualAddress number of bytes, rather than by VirtualAddress number of bytes.
Likewise, when you type-cast pe to ULONGLONG* and then add importDescriptor[0].Name to it, you are advancing the address in pe by sizeof(ULONGLONG) * Name number of bytes, rather than by Name number of bytes.
That is not what you want in this situation. Virtual addresses in a PE are absolute offsets from the base address, so you need to advance the base address by N bytes, not multiples of N bytes, so any T* used for such a type-cast needs to be a pointer to a 1-byte data type, such as BYTE or char.
So, in your example, to advance your base address pe by N number of bytes, use BYTE* for the type cast, eg:
PIMAGE_IMPORT_DESCRIPTOR importDescriptor =
(PIMAGE_IMPORT_DESCRIPTOR)(((LPBYTE)pe) + importDir.VirtualAddress);
...
printf("%s\n", (char*)(((LPBYTE)pe) + importDescriptor[0].Name));
Alternatively, you can use integer arithmetic instead of pointer arithmetic, by type-casting your base address to an integer such as ULONG_PTR (a pointer-sized ULONG), eg:
PIMAGE_IMPORT_DESCRIPTOR importDescriptor =
(PIMAGE_IMPORT_DESCRIPTOR)(((ULONG_PTR)pe) + importDir.VirtualAddress);
...
printf("%s\n", (char*)(((ULONG_PTR)pe) + importDescriptor[0].Name));