Paragon gpt_loader.sys BSOD analysis and fix

By dose | March 28, 2014
Under: Uncategorized
Comments: 53 Comments »

Update 27.09.2019

I forgot to patch an entry in the relocation table, causing a 0xc000001d bugcheck,
as soon as the driver is relocated. This is fixed now, sorry for the inconvenience.

Update 29.03.2014

I found a better solution that the one published 2 days ago which should really
fix the problem. In case that you downloaded and used the patcher earlier,
please copy back the gpt_loader.sys.bak that the patcher generated in your
SYSTEM32\Drivers directory to gpt_loader.sys, download the new patcher and
patch again.

The Problem

I recently came across a Bugcheck 0x00000024 (NTFS_FILE_SYSTEM) in
gpt_loader.sys on a friend’s PC.
gpt_loader.sys is a driver that belongs to Paragon GPT Loader
which allows a Windows XP user to use drives with a capacity over
2TB (which isn’t normally possible). This software also comes bundled
with Hiatchi-drives
.

This driver hasn’t been updated since the end of 2010 (Version 8.0.1.0) and
unfortunately has a very nasty bug:
On high I/O loads on the target drive, the driver sometimes crashes with
a BSOD, if read/write-Operations are performed at sectors above the
2TB Limit, as it seems. That would explain, why the problem hasn’t occured
earlier but starts to get problematic with the drive being filled more and
more:

BUGCHECK
0x00000024  (0x001902FE, 0xB73A5D00,0xB73A59FC,0xBA92E9E2)
BA92E92E base at BA928000

It seems that there are more people with this problem, but until now,
no fix has been found :

The analysis

So I was forced to analyze the probem myself using a minidump. I loaded
it into WinDbg and found the following interesting parts:

eax=00000000 ebx=89d69e70 ecx=ae1d5c38 edx=00000000 esi=89d69f4c edi=8af0f978
eip=b810e9e6 esp=ae1d5bac ebp=ae1d5bc4 iopl=0         nv up ei pl nz na po nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010202

gpt_lo_1+0x69e6:
b810e9e6 80480301        or      byte ptr [eax+3],1         ds:0023:00000003=??
DEFAULT_BUCKET_ID:  NULL_CLASS_PTR_DEREFERENCE
STACK_TEXT:
WARNING: Stack unwind information not available. Following frames may be wrong.
ae1d5bc4 b810ea5d ae1d5c38 89d69e70 ae1d5d34 gpt_lo_1+0x69e6
ae1d5bd4 b810f3d7 ae1d5c38 89d69e70 89d69e70 gpt_lo_1+0x6a5d
ae1d5d34 b7781411 89d69f4c 89d69e70 89eb2950 gpt_lo_1+0x73d7
ae1d5e00 804ef1f9 8af769e0 89d69f4c 89bc5bf8 fltsrv+0x4411
...

So this is a classic NULL-Pointer dereferencing bug.
I therefore loaded the driver into the disassembler and found the
following code in the function where it crashed:

Now let’s have a look at the position where it crashed:

.text:00016942 ; int __stdcall Z3TbGptLoader__irpReadWrite(int, PIRP Irp)
.text:00016942 sub_16942       proc near               ; DATA XREF: .rdata:0001897Co
.text:00016942                                         ; .rdata:00018A1Co
.text:00016942
.text:00016942 var_C           = dword ptr -0Ch
.text:00016942 var_8           = dword ptr -8
.text:00016942 var_1           = byte ptr -1
.text:00016942 arg_0           = dword ptr  8
.text:00016942 Irp         = byte ptr  0Ch
.text:00016942
.text:00016942                 mov     edi, edi
.text:00016944                 push    ebp
.text:00016945                 mov     ebp, esp
.text:00016947                 sub     esp, 0Ch
.text:0001694A                 push    ebx
.text:0001694B                 mov     ebx, dword ptr [ebp+Irp]
.text:0001694E                 push    esi
.text:0001694F                 mov     esi, [ebx+60h]
.text:00016952                 cmp     byte ptr [esi], 3
.text:00016955                 push    edi
.text:00016956                 setz    [ebp+var_1]
.text:0001695A                 mov     edi, ecx
.text:0001695C                 xor     ecx, ecx
.text:0001695E                 xor     eax, eax
.text:00016960                 mov     [ebp+var_C], ecx
.text:00016963                 cmp     [edi+7Ch], ecx
.text:00016966                 jz      short loc_1698B
.text:00016968                 mov     ebx, [edi+7Ch]
.text:0001696B                 push    ecx
.text:0001696C                 push    ebx
.text:0001696D                 push    dword ptr [esi+10h]
.text:00016970                 push    dword ptr [esi+0Ch]
.text:00016973                 call    _alldiv
.text:00016978                 mov     ecx, edx
.text:0001697A                 mov     [ebp+var_C], eax
.text:0001697D                 mov     eax, [esi+4]
.text:00016980                 xor     edx, edx
.text:00016982                 div     ebx
.text:00016984                 mov     ebx, dword ptr [ebp+Irp]
.text:00016987                 test    ecx, ecx
.text:00016989                 jnz     short loc_1699E
.text:0001698B
.text:0001698B loc_1698B:                              ; CODE XREF: sub_16942+24j
.text:0001698B                 xor     edx, edx
.text:0001698D                 add     eax, [ebp+var_C]
.text:00016990                 adc     edx, ecx
.text:00016992                 mov     [ebp+var_8], edx
.text:00016995                 js      short loc_169A7
.text:00016997                 jg      short loc_1699E
.text:00016999                 cmp     eax, 0FFFFFFFFh
.text:0001699C                 jbe     short loc_169A7
.text:0001699E
.text:0001699E loc_1699E:                              ; CODE XREF: sub_16942+47j
.text:0001699E                                         ; sub_16942+55j
.text:0001699E                 cmp     byte ptr [edi+0E2h], 0
.text:000169A5                 jnz     short loc_169C1
.text:000169A7
.text:000169A7 loc_169A7:                              ; CODE XREF: sub_16942+53j
.text:000169A7                                         ; sub_16942+5Aj
.text:000169A7                 cmp     [ebp+var_1], 0
.text:000169AB                 push    ebx
.text:000169AC                 push    [ebp+arg_0]
.text:000169AF                 mov     ecx, edi
.text:000169B1                 jz      short loc_169BA
.text:000169B3                 call    sub_12B96
.text:000169B8                 jmp     short loc_16A37
.text:000169BA ; ---------------------------------------------------------------------------
.text:000169BA
.text:000169BA loc_169BA:                              ; CODE XREF: sub_16942+6Fj
.text:000169BA                 call    sub_12B96
.text:000169BF                 jmp     short loc_16A37
.text:000169C1 ; ---------------------------------------------------------------------------
.text:000169C1
.text:000169C1 loc_169C1:                              ; CODE XREF: sub_16942+63j
.text:000169C1                 lea     eax, [ebp+Irp]
.text:000169C4                 push    eax             ; Irp
.text:000169C5                 lea     ecx, [edi+0FCh]
.text:000169CB                 call    sub_166B2
.text:000169D0                 test    al, al
.text:000169D2                 jz      short loc_169F4
.text:000169D4                 lea     ecx, [edi+20h]  ; Tag
.text:000169D7                 call    sub_10A04
.text:000169DC                 mov     eax, [ebx+60h]
.text:000169DF                 mov     ecx, [ebp+arg_0]
.text:000169E2                 or      byte ptr [eax+3], 1 ; CRASH!!!
.text:000169E6                 add     edi, 11Ch
.text:000169EC                 push    edi
.text:000169ED                 call    sub_11B14
.text:000169F2                 jmp     short loc_16A37
.text:000169F4 ; ---------------------------------------------------------------------------
.text:000169F4
.text:000169F4 loc_169F4:                              ; CODE XREF: sub_16942+90j
.text:000169F4                 mov     eax, dword_19024
.text:000169F9                 mov     esi, [eax]
.text:000169FB                 mov     [ebp+var_8], eax
.text:000169FE                 cmp     esi, eax
.text:00016A00                 jz      short loc_16A21

eax is obviously NULL from what we can see from the WinDbg output.
eax in turn is the pointer found at ebx+60h.
ebx is dword ptr [ebp+Irp], as we can also verify from
the Windbg output regarding registers and stack.
When checking the call stack, we can soon find out that this is
a handler function for the IRP_MJ_WRITE request.
Now we can apply the IRP-Structure from NTDDK to it and we find
out that ebx+60h actually is the member:

struct _IO_STACK_LOCATION *CurrentStackLocation;

So basically this resembles the call to IoGetCurrentIrpStackLocation().
Byte 3 there is the Control-Member, so the OR here tries to add
1 (SL_PENDING_RETURNED) to the Flags. This is what the DDK macro IoMarkIrpPending does.
When reading the docs to IRP_MJ_WRITE, we are told that this member
has to be set upon this IRP, so why the heck is it NULL there?
The next thing, that can be checked, is, if it was really NULL upon
function entry. As it gets already dereferenced at 00016952, it must
have been sane there, otherwise the crash would have occured there:

.text:0001694F                 mov     esi, [ebx+60h]
.text:00016952                 cmp     byte ptr [esi], 3

So the reason for the crash must be somewhere within that location
and 000169E2 where the crash finally occurs.
The only reference that we can see here which does not just read
the contents of the CurrentStackLocation is at 000169C1, where a
pointer to it gets loaded into register eax and then passed to the
function sub_166B2 via the stack:

.text:000169C1                 lea     eax, [ebp+Irp]
.text:000169C4                 push    eax             ; Irp
.text:000169C5                 lea     ecx, [edi+0FCh]
.text:000169CB                 call    sub_166B2

Now let’s see what this function does:

.text:000166B2 ; int __stdcall AddIRPToList(KIRQL NewIrql)
.text:000166B2 sub_166B2       proc near               ; CODE XREF: sub_16942+8Dp
.text:000166B2                                         ; sub_16B7E+20p
.text:000166B2
.text:000166B2 NewIrql         = byte ptr  8
.text:000166B2
.text:000166B2                 mov     edi, edi
.text:000166B4                 push    ebp
.text:000166B5                 mov     ebp, esp
.text:000166B7                 push    esi
.text:000166B8                 push    edi
.text:000166B9                 push    0               ; PoolType
.text:000166BB                 push    0Ch             ; NumberOfBytes
.text:000166BD                 mov     esi, ecx
.text:000166BF                 call    sub_16E36       ; ExAllocatePoolWithTag
.text:000166C4                 pop     ecx
.text:000166C5                 pop     ecx
.text:000166C6                 test    eax, eax
.text:000166C8                 jz      short loc_166D6
.text:000166CA                 mov     ecx, dword ptr [ebp+NewIrql]
.text:000166CD                 mov     ecx, [ecx]
.text:000166CF                 mov     [eax+8], ecx
.text:000166D2                 mov     edi, eax
.text:000166D4                 jmp     short loc_166D8
.text:000166D6 ; ---------------------------------------------------------------------------
.text:000166D6
.text:000166D6 loc_166D6:                              ; CODE XREF: sub_166B2+16j
.text:000166D6                 xor     edi, edi
.text:000166D8
.text:000166D8 loc_166D8:                              ; CODE XREF: sub_166B2+22j
.text:000166D8                 test    edi, edi
.text:000166DA                 jnz     short loc_166E0
.text:000166DC                 xor     al, al
.text:000166DE                 jmp     short loc_16712
.text:000166E0 ; ---------------------------------------------------------------------------
.text:000166E0
.text:000166E0 loc_166E0:                              ; CODE XREF: sub_166B2+28j
.text:000166E0                 push    ebx
.text:000166E1                 lea     ebx, [esi+1Ch]
.text:000166E4                 mov     ecx, ebx        ; SpinLock
.text:000166E6                 call    sub_1631C       ; AcquireSpinLock
.text:000166EB                 mov     [ebp+NewIrql], al
.text:000166EE                 mov     eax, [esi+4]
.text:000166F1                 push    dword ptr [ebp+NewIrql] ; NewIrql
.text:000166F4                 mov     [edi], esi
.text:000166F6                 mov     [edi+4], eax
.text:000166F9                 mov     [eax], edi
.text:000166FB                 mov     ecx, ebx        ; SpinLock
.text:000166FD                 mov     [esi+4], edi
.text:00016700                 call    sub_1634C       ; ReleaseSpinLock
.text:00016705                 push    0               ; int
.text:00016707                 lea     ecx, [esi+8]    ; Semaphore
.text:0001670A                 call    sub_16398       ; ReleaseSemaphore
.text:0001670F                 mov     al, 1
.text:00016711                 pop     ebx
.text:00016712
.text:00016712 loc_16712:                              ; CODE XREF: sub_166B2+2Cj
.text:00016712                 pop     edi
.text:00016713                 pop     esi
.text:00016714                 pop     ebp
.text:00016715                 retn    4
.text:00016715 sub_166B2       endp

Don’t get confused by the misleading name NewIrql, in fact it’s still our
PIRP pointer.
It also has 2 parameters, not one. First one is a pointer to the start
of a linked IRP-list, partly seems the be a structure like this:

typedef struct tag_PENDINGIRPS
{
  LIST_ENTRY ListHead;
  KSEMAPHORE  Semaphore;    // +8
  KSPIN_LOCK Lock;        // +28
} PENDINGIRPS;

And the second parameter is our PIRP.
What this function basically does (here as C-Code for easier understanding):

typedef struct tag_IRPLIST
{
  LIST_ENTRY ListHead;
  PIRP *Irp; // +8
} IRPLIST, *PIRPLIST;

BOOL AddIRPToList(PENDINGIRPS *pIrps, PIRP **ppMyIrp)
{
  PIRPLIST pList;

  if (pList = ExAllocatePoolWithTag(sizeof(IRPLIST), NonPagedPool))
  {
    KIRQL irql;

    pList->Irp = *ppMyIrp;
    irql = AcquireSpinlock(&pIrps->Lock);
    InsertTailList(&pIrps->ListHead, pList);
    ReleaseSpinLock(&pIrps->Lock, irql);
    KeReleaseSemaphore(&pIrps->Semaphore, 1, 1, FALSE);
    return TRUE;
  }
  return FASE;
}

So this function acquires a mutex and then adds our PIRP to some
linked list, presumably for processing by another worker.
As adding to the list is done secured by spinlocks, this tells us
that we are not the only one messing with the list at the same time.
Additionally there is a Semaphore that gets triggered after adding, which makes
us believe that this finally fires up the processing of the IRP.
As the bugcheck clearly shows that CurrentStackLocation member of our
IRP is NULL, we must assume that the worker is consuming and
completing our IRP from the list, before we can add 1 to the
CurrentStackLocation->Control member resulting in the given NULL pointer
dereference.

Upon further investigation, we can actually find the routine that is
processing the entries in the list ( I named some functions to
better specify what they are doing):

.text:00016A6A IRPProcessingLoop proc near             ; DATA XREF: .rdata:00018980o
.text:00016A6A                                         ; .rdata:00018A20o
.text:00016A6A                 mov     edi, edi
.text:00016A6C                 push    ebx
.text:00016A6D                 push    esi
.text:00016A6E                 mov     esi, ecx
.text:00016A70                 lea     ebx, [esi+0FCh]
.text:00016A76                 mov     ecx, ebx
.text:00016A78                 call    GetNextIRPFromList
.text:00016A7D                 test    eax, eax
.text:00016A7F                 jz      short loc_16AAC
.text:00016A81                 push    edi
.text:00016A82                 lea     edi, [esi+20h]
.text:00016A85
.text:00016A85 loc_16A85:                              ; CODE XREF: IRPProcessingLoop+3Fj
.text:00016A85                 mov     edx, [esi]
.text:00016A87                 push    eax
.text:00016A88                 mov     ecx, esi
.text:00016A8A                 call    dword ptr [edx+8Ch]
.text:00016A90                 cmp     byte ptr [edi+18h], 0
.text:00016A94                 jnz     short loc_16AA0
.text:00016A96                 push    18h             ; RemlockSize
.text:00016A98                 push    edi             ; Tag
.text:00016A99                 push    edi             ; RemoveLock
.text:00016A9A                 call    ds:IoReleaseRemoveLockEx
.text:00016AA0
.text:00016AA0 loc_16AA0:                              ; CODE XREF: IRPProcessingLoop+2Aj
.text:00016AA0                 mov     ecx, ebx
.text:00016AA2                 call    GetNextIRPFromList
.text:00016AA7                 test    eax, eax
.text:00016AA9                 jnz     short loc_16A85
.text:00016AAB                 pop     edi
.text:00016AAC
.text:00016AAC loc_16AAC:                              ; CODE XREF: IRPProcessingLoop+15j
.text:00016AAC                 pop     esi
.text:00016AAD                 xor     eax, eax
.text:00016AAF                 pop     ebx
.text:00016AB0                 retn
.text:00016AB0 IRPProcessingLoop endp
.text:00016AB0
.text:00016AB0 ; ---------------------------------------------------------------------------
.text:00016AB1                 db 5 dup(0CCh)

If we transform the function to C-Code, this would read like:

int __fastcall IRPProcessingLoop(my_class *this)
{
  PIRP irp;

  for (irp = GetNextIRPFromList(this->PendingIRPs); irp; irp = GetNextIRPFromList(this->PendingIRPs) )
  {
    this->lpVtbl->processIRP(this, irp);
    if ( !this->b56)
      IoReleaseRemoveLock(&this->IORemoveLock, &this->IORemoveLock);
  }
  return 0;
}

So now take a look at the function that I call GetNextIRPFromList:

.text:0001665E GetNextIRPFromList proc near            ; CODE XREF: FreeIRPList+14p
.text:0001665E                                         ; IRPProcessingLoop+Ep ...
.text:0001665E
.text:0001665E var_8           = dword ptr -8
.text:0001665E NewIrql         = byte ptr -4
.text:0001665E
.text:0001665E                 mov     edi, edi
.text:00016660                 push    ebp
.text:00016661                 mov     ebp, esp
.text:00016663                 push    ecx
.text:00016664                 push    ecx
.text:00016665                 push    ebx
.text:00016666                 push    esi
.text:00016667                 push    edi
.text:00016668                 mov     esi, ecx
.text:0001666A                 lea     edi, [esi+8]
.text:0001666D                 push    edi             ; Semaphore
.text:0001666E                 call    ds:KeReadStateSemaphore
.text:00016674                 mov     ecx, edi        ; Object
.text:00016676                 call    WaitForSingleObject
.text:0001667B                 lea     ebx, [esi+1Ch]
.text:0001667E                 mov     ecx, ebx        ; SpinLock
.text:00016680                 call    AcquireSpinlock
.text:00016685                 mov     edi, [esi]
.text:00016687                 mov     [ebp+NewIrql], al
.text:0001668A                 mov     eax, [edi+8]
.text:0001668D                 push    dword ptr [ebp+NewIrql] ; NewIrql
.text:00016690                 mov     [ebp+var_8], eax
.text:00016693                 mov     eax, [edi]
.text:00016695                 mov     [esi], eax
.text:00016697                 mov     ecx, ebx        ; SpinLock
.text:00016699                 mov     [eax+4], esi
.text:0001669C                 call    ReleaseSpinlock
.text:000166A1                 push    edi             ; NewIrql
.text:000166A2                 call    FreePoolWithTag
.text:000166A7                 mov     eax, [ebp+var_8]
.text:000166AA                 pop     ecx
.text:000166AB                 pop     edi
.text:000166AC                 pop     esi
.text:000166AD                 pop     ebx
.text:000166AE                 leave
.text:000166AF                 retn
.text:000166AF GetNextIRPFromList endp

Translating it to more readable C-Code reveals how it works:

struct _IO_STACK_LOCATION *__fastcall GetNextIRPFromList(PENDINGIRPS *pIrps)
{
  KIRQL irql;
  IRPLIST Entry;
  PIRP irp;

  KeReadStateSemaphore(&pIrps->Semaphore);
  WaitForSingleObject(&pIrps->Semaphore);
  irql = AcquireSpinlock(&pIrps->Lock);
  Entry = RemoveHeadList(&pIrps->ListHead);
  irp = Entry->IrpStackLocation;
  ReleaseSpinLock(&pIrps->Lock, irql);
  FreePoolWithTag(Entry);
  return irp;
}

So it’s clearly waiting for the Semaphore that is set in AddIRPToList.
Now for the sake of completeness, let’s also translate the Z3TbGptLoader__irpReadWrite
function to some sort of readable C-code so tht we can finally see
what’s going on here and how to fix it. I didn’t do a full translation
of this, as we are only interested in the relevant parts:

int __thiscall Z3TbGptLoader__irpReadWrite(my_class *this, int a2, PIRP Irp)
{
  if (this->dw226 && this->dw124 &&
  /* Some unnown division, no time to find out what it does, doesn't matter
for us */
  Irp->CurrentStackLocation->Parameters.Write.ByteOffset / this->dw124
  ! (Irp->CurrentStackLocation->Parameters.Write.ByteOffset.LowPart == FILE_WRITE_TO_END_OF_FILE &&
  Irp->CurrentStackLocation->Parameters.Write.ByteOffset.HighPart == -1)
  {
    if (AddIRPToList(this->PendingIRPs, &Irp))
    {
      AcquireRemoveLock(&this->IORemoveLock);
      IoMarkIrpPending(Irp);
      EndThis(arg2, this->lpVtbl->Func284);
    }
    else
    {
      AddErrorToSyslog("Z3TbGptLoader::irpReadWrite", ...);
      Irp->IoStatus.Status = STATUS_NO_MEMORY;
      EndThis(arg2, this->lpVtbl->IRPProcessingLoop);
    }
  }
  else
  {
    EndThis(arg2, this->lpVtbl->PassOn);
  }
}

So putting one and one together, we see what’s going wrong here:
AddIRPToList triggers the semaphore to process the IRP, after
it has added it to the list, but in irpReadWrite, it is attempted to
modify the IRP afterwards (mark IRP pending by setting control-member)
and acquiring a remove lock.

Fixing it

Now how to fix the bug?
First we can see from WinDbg output, that ESI still holds a pointer to
the CurrentStackLocation, so wo could just change

.text:000169E2                 or      byte ptr [eax+3], 1

to

.text:000169E2                 or      byte ptr [esi+3], 1

But that wouldn’t be a proper fix, because maybe the pointer may not
be valid anymore at that time and additionally, the intention of the
code is to set that control flag before the IRP gets processed by the worker
thread.
So we must think about a different solution.
First guess would be that we could just set it in AddIRPToList.
But this doesn’t work out well, because the function is also called
at in another funtion, so no luck with that. Additionally, we learned
from above that there is also a RemoveLock in place which also should
be set before the IRP is being processed.
Let’s have a look at this other function that is calling AddIRPToList
which I called close_driver:

.text:00016B82 close_driver    proc near               ; CODE XREF: sub_15DC2+Ep
.text:00016B82                                         ; sub_16C60+8p
.text:00016B82
.text:00016B82 NewIrql         = byte ptr -4
.text:00016B82
.text:00016B82                 mov     edi, edi
.text:00016B84                 push    ebp
.text:00016B85                 mov     ebp, esp
.text:00016B87                 push    ecx
.text:00016B88                 and     dword ptr [ebp+NewIrql], 0
.text:00016B8C                 push    esi
.text:00016B8D                 mov     esi, ecx
.text:00016B8F                 push    edi
.text:00016B90                 lea     eax, [ebp+NewIrql]
.text:00016B93                 lea     edi, [esi+0FCh]
.text:00016B99                 push    eax             ; NewIrql
.text:00016B9A                 mov     ecx, edi
.text:00016B9C                 mov     dword ptr [esi], offset off_18998
.text:00016BA2                 call    AddIRPToList
.text:00016BA7                 test    al, al
.text:00016BA9                 jz      short loc_16BB6
.text:00016BAB                 lea     ecx, [esi+128h]
.text:00016BB1                 call    DoWaitForSingleObject
.text:00016BB6
.text:00016BB6 loc_16BB6:                              ; CODE XREF: close_driver+27j
.text:00016BB6                 lea     ecx, [esi+148h]
.text:00016BBC                 call    sub_16722
.text:00016BC1                 lea     ecx, [esi+128h]
.text:00016BC7                 call    DoCloseHandle
.text:00016BCC                 lea     ecx, [esi+11Ch]
.text:00016BD2                 call    sub_16722
.text:00016BD7                 mov     ecx, edi
.text:00016BD9                 call    FreeIRPList
.text:00016BDE                 mov     ecx, esi
.text:00016BE0                 call    sub_12BB2
.text:00016BE5                 pop     edi
.text:00016BE6                 pop     esi
.text:00016BE7                 leave
.text:00016BE8                 retn
.text:00016BE8 close_driver    endp
.text:00016BE8 ; ---------------------------------------------------------------------------
.text:00016BE9                 db 5 dup(0CCh)

Now the following idea arises:
1) Remove the KeReleaseSemaphore from AddIRPToList.
2) Release the Semaphore in irpReadWrite AFTER PIRP was
setup correctly.
3) Modify close_driver to also do a manual KeReleaseSemaphore
after AddIRPToList so that the call to it really can be removed
from that function.

Let’s see if there is sufficient space for this. Have a look at AddIRPToList
and check what’s done there:

.text:00016705                 push    0               ; int
.text:00016707                 lea     ecx, [esi+8]    ; Semaphore
.text:0001670A                 call    sub_16398       ; ReleaseSemaphore

So semaphore is loaded into ECX, The PUSH 0 takes 2 bytes,
the CALL to ReleaseSemaphore takes 5 bytes, in total 7 bytes provided
that we can reuse ecx after function call, which fortuntely is the case
as ECX doesn’t get restored at function end of AddIRPToList.

First let’s check the possibility for close_driver:
We have 5 bytes in alignment space AFTER the function end.
Too bad, that’s not enough.. Now what to do.. Fortunately
the MS C ompiler always generates a pointless 2 Byte mov edi, edi
instruction at the start of each function whic actually is meant
to be used for a detour hook in case someone wants to patch the
function during update -> Success, we have 7 bytes!

Now we can move up the function by 2 bytes, then insert our
call to ReleseSemaphore and we’re set.

Next, let’s chec irpReadWrite:
We can save 2 bytes by the fact that

.text:000169DC                 mov     eax, [ebx+60h]

is unnecessary, as we already have the pointer to it in esi.
Not enough, though. But let’s have a look above that function for a moment:

.text:000169AF                 mov     ecx, edi
.text:000169B1                 jz      short loc_169BA
.text:000169B3                 call    sub_12B96
.text:000169B8                 jmp     short loc_16A37
.text:000169BA ; ---------------------------------------------------------------------------
.text:000169BA
.text:000169BA loc_169BA:                              ; CODE XREF: sub_16942+6Fj
.text:000169BA                 call    sub_12B96
.text:000169BF                 jmp     short loc_16A37

Now we can agree that this is really useless code! So the idea is to move
up the following code, relocate the calls, and insert our ReleaseSemaphore
function code. Plenty of space for us, hoorray! So with adjustments
to acommodate the length of near jumps to the function exit point, our
code will finally look like this:

.text:00016942 ; int __stdcall Z3TbGptLoader__irpReadWrite(int, KIRQL NewIrql)
.text:00016942 Z3TbGptLoader__irpReadWrite proc near   ; DATA XREF: .rdata:0001897Co
.text:00016942                                         ; .rdata:00018A1Co
.text:00016942
.text:00016942 var_C           = dword ptr -0Ch
.text:00016942 var_8           = dword ptr -8
.text:00016942 var_1           = byte ptr -1
.text:00016942 arg_0           = dword ptr  8
.text:00016942 NewIrql         = byte ptr  0Ch
.text:00016942
.text:00016942                 mov     edi, edi
.text:00016944                 push    ebp
.text:00016945                 mov     ebp, esp
.text:00016947                 sub     esp, 0Ch
.text:0001694A                 push    ebx
.text:0001694B                 mov     ebx, dword ptr [ebp+NewIrql]
.text:0001694E                 push    esi
.text:0001694F                 mov     esi, [ebx+60h]
.text:00016952                 cmp     byte ptr [esi], 3
.text:00016955                 push    edi
.text:00016956                 setz    [ebp+var_1]
.text:0001695A                 mov     edi, ecx
.text:0001695C                 xor     ecx, ecx
.text:0001695E                 xor     eax, eax
.text:00016960                 mov     [ebp+var_C], ecx
.text:00016963                 cmp     [edi+7Ch], ecx
.text:00016966                 jz      short loc_1698B
.text:00016968                 mov     ebx, [edi+7Ch]
.text:0001696B                 push    ecx
.text:0001696C                 push    ebx
.text:0001696D                 push    dword ptr [esi+10h]
.text:00016970                 push    dword ptr [esi+0Ch]
.text:00016973                 call    _alldiv
.text:00016978                 mov     ecx, edx        ; Semaphore
.text:0001697A                 mov     [ebp+var_C], eax
.text:0001697D                 mov     eax, [esi+4]
.text:00016980                 xor     edx, edx
.text:00016982                 div     ebx
.text:00016984                 mov     ebx, dword ptr [ebp+NewIrql]
.text:00016987                 test    ecx, ecx
.text:00016989                 jnz     short loc_1699E
.text:0001698B
.text:0001698B loc_1698B:                              ; CODE XREF: Z3TbGptLoader__irpReadWrite+24j
.text:0001698B                 xor     edx, edx
.text:0001698D                 add     eax, [ebp+var_C]
.text:00016990                 adc     edx, ecx
.text:00016992                 mov     [ebp+var_8], edx
.text:00016995                 js      short loc_169A7
.text:00016997                 jg      short loc_1699E
.text:00016999                 cmp     eax, 0FFFFFFFFh
.text:0001699C                 jbe     short loc_169A7
.text:0001699E
.text:0001699E loc_1699E:                              ; CODE XREF: Z3TbGptLoader__irpReadWrite+47j
.text:0001699E                                         ; Z3TbGptLoader__irpReadWrite+55j
.text:0001699E                 cmp     byte ptr [edi+0E2h], 0
.text:000169A5                 jnz     short loc_169B8
.text:000169A7
.text:000169A7 loc_169A7:                              ; CODE XREF: Z3TbGptLoader__irpReadWrite+53j
.text:000169A7                                         ; Z3TbGptLoader__irpReadWrite+5Aj
.text:000169A7                 push    ebx
.text:000169A8                 push    [ebp+arg_0]
.text:000169AB                 mov     ecx, edi
.text:000169AD                 call    sub_12B96
.text:000169B2                 nop
.text:000169B3                 nop
.text:000169B4                 nop
.text:000169B5                 nop
.text:000169B6                 jmp     short loc_16A37 ; JMP here so that short JMP is enough
.text:000169B8 ; ---------------------------------------------------------------------------
.text:000169B8
.text:000169B8 loc_169B8:                              ; CODE XREF: Z3TbGptLoader__irpReadWrite+63j
.text:000169B8                 lea     eax, [ebp+NewIrql]
.text:000169BB                 push    eax             ; NewIrql
.text:000169BC                 lea     ecx, [edi+0FCh]
.text:000169C2                 call    AddIRPToList
.text:000169C7                 test    al, al
.text:000169C9                 jz      short loc_169F4
.text:000169CB                 push    ecx
.text:000169CC                 lea     ecx, [edi+20h]  ; Tag
.text:000169CF                 call    AcquireRemoveLock
.text:000169D4                 or      byte ptr [esi+3], 1
.text:000169D8                 pop     ecx
.text:000169D9                 push    0               ; int
.text:000169DB                 call    ReleaseSemaphore
.text:000169E0                 mov     ecx, [ebp+arg_0]
.text:000169E3                 nop
.text:000169E4                 nop
.text:000169E5                 nop
.text:000169E6                 add     edi, 11Ch
.text:000169EC                 push    edi
.text:000169ED                 call    sub_11B14
.text:000169F2                 jmp     short loc_16A37
.text:000169F4 ; ---------------------------------------------------------------------------
.text:000169F4
.text:000169F4 loc_169F4:                              ; CODE XREF: Z3TbGptLoader__irpReadWrite+87j
.text:000169F4                 mov     eax, dword_19024
.text:000169F9                 mov     esi, [eax]
.text:000169FB                 mov     [ebp+var_8], eax
.text:000169FE                 cmp     esi, eax
.text:00016A00                 jz      short loc_16A21
.text:00016A02
.text:00016A02 loc_16A02:                              ; CODE XREF: Z3TbGptLoader__irpReadWrite+DDj
.text:00016A02                 mov     ecx, [esi+0Ch]
.text:00016A05                 mov     eax, [ecx]
.text:00016A07                 push    0
.text:00016A09                 push    0
.text:00016A0B                 push    offset aZ3tbgptloaderI ; "Z3TbGptLoader::irpReadWrite"
.text:00016A10                 push    1
.text:00016A12                 push    0C0050007h
.text:00016A17                 call    dword ptr [eax+8]
.text:00016A1A                 mov     esi, [esi]
.text:00016A1C                 cmp     esi, [ebp+var_8]
.text:00016A1F                 jnz     short loc_16A02
.text:00016A21
.text:00016A21 loc_16A21:                              ; CODE XREF: Z3TbGptLoader__irpReadWrite+BEj
.text:00016A21                 mov     ecx, [ebp+arg_0]
.text:00016A24                 add     edi, 8Ch
.text:00016A2A                 push    edi
.text:00016A2B                 mov     dword ptr [ebx+18h], 0C0000017h
.text:00016A32
.text:00016A32 loc_16A32:
.text:00016A32                 call    sub_11B14
.text:00016A37
.text:00016A37 loc_16A37:                              ; CODE XREF: Z3TbGptLoader__irpReadWrite+74j
.text:00016A37                                         ; Z3TbGptLoader__irpReadWrite+B0j
.text:00016A37                 mov     eax, [ebp+arg_0]
.text:00016A3A                 pop     edi
.text:00016A3B                 pop     esi
.text:00016A3C                 pop     ebx
.text:00016A3D                 leave
.text:00016A3E                 retn    8
.text:00016A3E Z3TbGptLoader__irpReadWrite endp

Finally, NOP out the calls to ReleaseSemaphore in AddIRPToList
(Remove the PUSH 0 and the CALL) and we’re finished patching it.

I made this modification and until now the driver seems to remain stable
(crossing fingers that it remains stable and I fixed the bug 😉 )

Patch

As manually patching this is a real pain and I’m also not allowed to
redistribute a patched copy of the file. So I wrote a little patcher that
patches the driver accordingly. Just run it and
if it patched successfully, reboot the system to load the fixed version of the
driver.
Feel free to try it and if you are also suffering from this problem, you can
leave a comment if this actually fixes it for you too.

For those who use crappy Antivirus programs like Antivir, don’t get fooled by the generic Antivirus signature-match for compressed Executables XPACK.GEN, you can check with Virustotal.
If you have such an Antivirus program, use this build instead which is a larger executable but isn’t subject to false positives.

“Server ist Ausgelastet” bei Systemstart mit Avira Antivirus

By dose | December 17, 2013
Under: Uncategorized
Comments: 2 Comments »

Heute hatte ich ein Notebook in Behandlung, das zeigte beim Systemstart immer “Server is ausgelastet” an und dann “Wechseln zu, Warten, …”. Dabei handelt es sich eindeutig um eine COM-Fehlermeldung, wo vergeblich auf die Antwort eines Servers gewartet wird.
Nun ist mir aufgefallen, dass der Spuk immer genau dann zu Ende ist, wenn der Echtzeitschutz von Avira Antivirus fertig gestartet ist. Also lag die Vermutung nahem, dass es am Antivirus liegt, zumal es hier auch bereits einen Thread im Avira-Supportforum dazu gibt. Nun ist mir weiters aufgefallen, dass bis zu diesem Zeitpunkt keine weiteren Programme geladen werden, die beim Systemstart mitstarten sollten.

Als Erstes hatte ich einmal den Avira Antivirus in Verdacht und habe ihn deinstalliert, besagte Fehlermeldung ist damit auch wirklich verschwunden. Die Startverzögerung beeinflusste die Deinstallation des Programms allerdings nicht, es war nach wie vor ca. eine zweiminĂŒtige Pause beim Systemstart beim Laden der Anwendungen zu beobachten, die sich insofern unangenehm bemerkbar machte, als dass der Explorer in dieser Zeit unzureichend bzw. garnicht reagierte und man damit auch nicht wirklich arbeiten konnte.

Nach nĂ€herer Analyse des Umstands habe ich festgestellt, dass der Arbeitsstationsdienst scheinbar genau diese 2 Minuten lang, in denen sich nichts tut, beim Start hĂ€ngen bleibt. Deaktiviert man den Arbeitsstationsdienst, so ist die Startverzögerung weg. Das ist natĂŒrlich keine Lösung fĂŒr das Problem, denn der Dienst wird ja benötigt. Startet man den deaktivierten Dienst nach dem Startcorgang manuell, so startet dieser hingegen in akzeptabler Startzeit. HĂ€nger beim Start des Arbeitsstationsdienstes haben meistens etwas mit den Netzwerkverbindungen zu tun, also habe ich durch Schrittweisene deaktivieren und aktivieren der verfĂŒgbaren Netzwerkverbindungen herausgefunden, dass es scheinbar mit der WLAN-Verbindung zusammenhĂ€ngt. Dabei ist unerheblich, ob eine WLAN-VErbindung besteht oder das WLAN-Modu physisch am Rechner deaktiviert ist. Sobald man den WLAN-Adapter deaktiviert, startete der Arbeitsstationsdienst wieder normal.

Nun war im Notebook eine Intel Pro Wireless Karet verbaut mit entsprechender Verbindungsverwaltungssoftware von Intel. Diese Software ersetzt die Windowseigene WLAN-Konfiguration, was mich Schließlich auf den Schuldigen Dienst bracht, der da alles aufhielt: Der Dienst “Konfigurationsfreie drahtlose Verbindung“, welcher eigentlich von Windows benutzt wird, blockiert scheinbar den Intel-Dienst fĂŒr die Konfiguration fĂŒr 2 Minuten beim Start, was wiederum den Arbeitsstationsdienst beim Start so lange aufhĂ€lt und die 2 Minuten “HĂ€nger” beim Starten verursacht. Ich habe daher den Dienst deaktiviert, und siehe da, der Rechner startete wieder normal, die WLAN-Verbindung wurde schneller beim Start aufgebaut und Avira Antivirus war nicht mehr ausgelastet, somit war die störende Fehlermeldung damit auch beseitigt.

pgrouting Dijksta Travelling Salesman Problem (TSP) with OpenStreetmap

By dose | November 17, 2013
Under: Uncategorized
Comments: 10 Comments »

I recently had to educate myself about Route Optimization due to an upcoming project. One of the interesting features was to solve the so-called “Travelling Salesman Problem“: To find the correct ordering of places to visit when driving on a road in order to minimize the number of km to drive (so cost=length of route in this case).

I wanted to do the Optimazation on freely available data, therefore I choose OpenStreetMap as a data source. I then tried to find out which program is suited best to do such kind of optimization under Linux. I first stumbled upon OsmSharp and took the pain of all the Mono compilation woes, but soon learned, that it does not suit well for the imposed task, because it has to load all data before doing optimization and so it doesn’t work well in a timely manner, if you for example want to do an optimization for a lot of points within Austria.

So I learned about pgrouting which seems to be a PostgreSQL database designed specially for Geospatial processing. There is a simple guide on how to import OSM data to pgrouting.  libgaul isn’t needed anymore, as the TSP extention dependency on it was removed in version 2.0.

I found out, that there are simple TSP-Solutions in pgrouting using the pgr_tsp function, however, they only do euclidean distance checking and that wasn’t what I wanted (even tough the help for the function mentions that euclidean distance should be sufficient in most cases – for me, it wasn’t good enough). I wanted to find the correct order of points using the given road network. In order to do this, you have to take all your points you want to visit and then calculate the distance between all of them forming a distance matrix. As the way between all points is the same in both directions, you end up calculating nÂČ/2-n ways (where n is the number of points to visit). Then you have to do a TSP-calculation over that matrix and as a result, you get the correct order.
To calculate the fastest route between 2 points, you can use the pgr_kdijkstraCost SQL function of pgrouting (as the name implies, it’s using the Dijkstra algorithm).

I am an absolute newbie on PostgreSQL, therefore my solution may not be the cleanest, but here is how I solved this problem:

1) Install my pgrouting Dijkstra functions, which basically offer you 2 functions:

pgr_tspDijkstra    - Does a TSP optimization using Dijkstra distances
pgr_tspDijkstraLen - Does the same, but implies that cost = length of route

2) My functions are designed so that you have to create a table containing all the points that you want to visit on your route. So create this table:

create table my_route (id serial, lat double precision, lon double precision,
  node integer);

3) Now you most probably have the points you want to visit as Longitude/Latitude points, so just insert your points into that table:

insert into my_route(lat,lon) values (48.30609, 14.28642);
...

4) Now you have to calculate the correct nodes for all the points you entered, because pgrouting is only accepting node-IDs. This can be done with the folowing statement:

update my_route g set node=(
  SELECT source FROM (
    SELECT source, distance(the_geom,
      GeometryFromText('POINT('||g.lon||' '||g.lat||')', 4326)) AS dist
    FROM ways
    WHERE the_geom && setsrid(
     ('BOX3D('||g.lon-0.1||' '||g.lat-0.1||','||g.lon+0.1||' '||g.lat+0.1||')'
     )::box3d, 4326)
    order by dist LIMIT 1
  ) as foo
);

5) Now that you have all the nodes in your table, it’s time to do the real calculation. If length is the only cost of a route for you, simply use the pgr_tspDijkstraLen function. You have to find out the node ID of the starting node from your my_route table and pass it to the function, so that it knows where to start. The syntax for the function is:

function pgr_tspDijkstraLen(thetbl text, start_id integer,
   end_id integer default (-1))
thetbl   - Name of your table that contains all the nodes to visit
start_id - Node ID of the starting point
end_id   - Node ID of the last point to visit. If not given, it's a round trip
           ending at the starting point.

Therefore it’s as simple as i.e.

select * from pgr_tspDijkstraLen('my_route', 189064);

As a result, you get a table with the folowing columns:

 seq | id1 |  id2   |       cost
-----+-----+--------+------------------
   0 |   2 |  64186 | 11.1983268959869
...
seq  - row sequence number in the resulting table.
id1  - internal index into the distance matrix
id2  - id of the node
cost - cost to traverse from the current node to the next node.

So theoretically, you can use the following statement to get your lon/lat points ordered:

select id,lon,lat from pgr_tspDijkstraLen('my_route', 189064
) dj, my_route rt where dj.id2=rt.node;

Now there may be the case that you have your own slightly trickier definition of the cost, not just the length of the route. In this case, you can use the pgr_tspDijkstra function.
Its syntax is:

function pgr_tspDijkstra(thetbl text, sql text, start_id integer, 
   end_id integer default (-1))

thetbl   - Name of your table that contains all the nodes to visit
sql      - SQL statement that returns a pgr_costResult to do TSP optimization on
start_id - Node ID of the starting point
end_id   - Node ID of the last point to visit. If not given, it's a round trip
           ending at the starting point.

So basically, it’s just the same as the pgr_tspDijkstraLen function, but hsa the additional parameter sql, which lets you define your own costresult to do TSP optimization on. For an example on how to use this, just have a look at what pgr_tspDijkstraLen returns:

     return query SELECT * FROM pgr_tspDijkstra(thetbl,
          'SELECT gid AS id, source::integer, target::integer,
           length::double precision AS cost FROM ways',
           start_id, end_id);

I hope that this function is useful to anybody and feedback is appreciated.

svchost.exe using 100% CPU because of Windows Update (wuauclt) in XP

By dose | October 21, 2013
Under: Uncategorized
Comments: 2 Comments »

Recently I had a Windows XP machine where CPU usage stayed at 100% for approx. 10-15 Minutes after startup. So I used process analyzer to check which thread was using the CPU and (as usual) it turned out to be Windows Update.

It seems that some recent Windows XP updates broke the system once again (after already having issues with an update ruining DOS high memory last year). The solution to the problem is to install the updates KB2879017 and KB2870699 manually. After installing these and a reboot, CPU usage of svchost.exe went back to normal.

Edit: Here is an explanation from Microsoft why this is happening.

“Speichern unter” Dialog hĂ€ngt

By dose | October 14, 2013
Under: Uncategorized
Comments: No Comments »

Hatte heute wieder einen Fall, dass eine Shell-Extension auf einem Rechner Probleme machte und den Speicher unter Dialog zum HĂ€ngen brachte (besonders Ă€rgerlich in MS Office, da will man speichern….)

Hier die Lösung:
http://superuser.com/questions/378296/windows-7-save-dialog-hang-any-solutions

War genau wie dort angegeben. Verursacher war die Erweiterung “Share-to-Web” von HP. Mit ShellExView von nirSoft deaktiviert und er hĂ€ngt nun nicht mehr.

Doodle Alternative

By dose | October 9, 2013
Under: Uncategorized
Comments: 2 Comments »

Nachdem Doodle fĂŒr mich mittlerweile unbrauchbar geworden ist, da es

1) Mit allen etwas Àlteren Browser-Versionen nicht mehr funktioniert und die mobile Version quasi nicht verwendbar ist
2) Es mit HTTP-Proxies ĂŒberhaupt nicht funktioniert

war ich auf der Suche nach einem brauchbaren Ersatz.
Und hier ist das Ergebnis meiner Recherche: Dudle!

Windows XP von IDE-Festplatte auf SATA-Festplatte migrieren

By dose | October 9, 2013
Under: Uncategorized
Comments: No Comments »

UnlĂ€ngst hatte ich die Problemstellung, ein Windows XP von einem alten Rechner mit IDE-Festplatte auf einen neuen Rechner mit SATA-Festplatte zu migrieren. Ansich keine große Sache, man kopiert mittels Diskimager (wie z.B. dem kostenlosen Macrium Reflect) ein Image der alten Platte auf die neue Platte, indem man diese z.B. mittels eine USB-Adapters an den alten Rechner anhĂ€ngt und stellt anschließend im BIOS den SATA-Mode der SATA-Platte im neune Rechner von AHCI auf IDE. So weit, so gut, das bekannte Procedere…

Das Problem hierbei war nur, dass die Kopie von Windows XP nur Treiber fĂŒr den im alten ZielgerĂ€t verwendeten IDE-Treiber installiert hatte und damit mit dem SATA IDE-Modus am neuen Rechner nicht zurechtkam, was im bekannten STOP 0x7B (INACCESSIBLE_BOOT_DEVICE) endet.
Nun gibt es in der Microsoft Knowledge Base ja den Artikel KB314082, welcher die Vorgehensweise in so einem Fall erklĂ€rt. Das Problem ist nur, dass der Artikel dort davon ausgeht, dass man den gleichen Festplattentyp (sprich: IDE) von einem GerĂ€t zum Anderen ĂŒbertrĂ€gt, also dass es damit quasi möglich ist, von der kopierten Platte im alten System zu Booten und Anpassungen am dort laufenden System durhczufĂŒhren. In diesem Fall war dies allerdings nicht möglich, da ich ja auf eine SATA-Platte kopiert hatte, von welcher ich im alten System nicht booten konnte. Die Lösung des Problems ist dennoch relativ trivial.
Man hĂ€ngt die kopierte Platte z.B. mittels eines USB-Adapterkabels an ein beliebiges Windows-System an (in unserme Fall einfach an den laten Rechner, wo wir die Platte ja auch schon zum Kopieren hĂ€ngen hatten) und fĂŒhrt folgende Schritte analog zur Anleitung des KB-Artikels aus:

  1. Extrahieren Sie aus der Datei “%SystemRoot%\Driver Cache\I386\Driver.cab” die Dateien “Atapi.sys”, “Intelide.sys”, “Pciide.sys” und “Pciidex.sys”, oder kopieren Sie diese Dateien in den Ordner “%SystemRoot%\System32\Drivers” der Zielplatte.
  2. Öffnen Sie den Registrierungseditor (Start/AusfĂŒhren/regedit) , gehen Sie auf den SchlĂŒssel HKEY_LOCAL_MACHINE und gehen Sie im MenĂŒ Datei auf  “Struktur laden…” und öffnen Sie die Datei %SystemRoot%\System32\config\SYSTEM der Zielplatte. Benennen Sie den EinhĂ€ngpunkt fĂŒr die Struktur z.B. dest
    Damit ist die Registry der kopireten Windows-Installation unter HKEY_LOCAL_MACHINE\dest eingehÀngt.
  3. Laden Sie die mergeide.reg vom Knowledgebase-Artikel in einem Texteditor und Àndern Sie mittels Suchen & Ersetzen (CRTL+H) den String
    \SYSTEM\CurrentControlSet

    in

    \dest\ControlSet001

    Damit wird die .reg Datei angewiesen, nicht in die Registry des aktuellen Systems zu schreiben, sondern in die eingehÀngte Struktur des Zielsystems.

  4. Doppelklicken Sie auf die .reg Datei, um sie mit der Registrierung zusammenzufĂŒhren.
  5. Markieren Sie den HKEY_LOCAL_MACHINE\dest Zweig, wo Sie die Remote-Registrierung eingehĂ€ngt haben und gehen Sie auf Datei/Struktur entfernen… um die Remote-Registrierung wieder zu entladen.
  6. HĂ€ngen Sie nun die Zielplatte aus und geben Sie diese in den neuen Rechner und schon sollte dieser wie gewohnt von der Festplatte booten.

Mithilfe dieses einfachen Procederes sollte es möglich sein, Windows XP auch problemlos von IDE auf SATA-Platten umzukopieren.

Replicate directory timestamps

By dose | July 25, 2013
Under: Uncategorized
Comments: No Comments »

Yesterday I copied a huge amount of data from one harddrive to another. The timestamps of the files were preserverd correctly, but the timestamps of the directories were changed to the date of the copying. I know that there are utilities that may preserve the timestamps, like presumably robocopy, but I didn’t want to copy all files again as this took half a day.

So I wrote a little utility that replicates the directory Timestamps from one directory to the other. If you are in the same situation as me, you may want to give it a try, get it here.

VMWare 9 – PC Speaker bug

By dose | July 3, 2013
Under: Uncategorized
Comments: No Comments »

Unter VMware 9 funktioniert aufgrund eines Bugs der PC Speaker nicht mehr, von einem Upgrade wird daher abgeraten, bis der Fehler beseitigt wurde, was bis Dato leider noch nicht der Fall ist.

IBM Access Connections findet kein WLAN mehr

By dose | July 3, 2013
Under: Uncategorized
Comments: No Comments »

Hatte unlÀngst den Fall, dass IBM Access Connections auf einem Thinkpad kein WLAN mehr gefunden hat. Stellte man die WLAN-Steuerung von Access Connections auf Windows um, fand Windows die Netze aber problemlos.

Des RĂ€tsels Lösung war, dass sich in Windows-Verzeichnis eine veraltete ssleay32.dll von OpenSSL befunden hat, was scheinbar dazu gefĂŒhrt hat, dass Access connections keine Verbindung mehr mit dem Interface des WLAN Treibers herstellen konnte. Habe die Datei dann gelöscht und durch eine neuere aus einem der IBM-Verzeichnisse ersetzt,  neu gestartet und siehe da, es funktioniert wieder.