C++DebuggingMFCProgrammierenMartin Richter - Sa 23 Jul 2011 18:16

Dann will ich mal das Problem lüften, dass sich mit diesem Code ergibt, dein ich meinem letzten Artikel vorgestellt habe:

template <class T>
void SecureClearString(T &strText)
{
  ::SecureZeroMemory(strText.GetBuffer(0),strText.GetAllocLength());
  strText.Empty();
}

Zuerst einmal liegt es nicht daran, dass es hier Template verwendet wurde.
Ein Template wurde verwendet, weil in dem Code nicht nur CString, sondern implizit CStringA und CStringW verwendet wurde. Der Code sollte also mit beiden Typen funktionieren.

Und damit sind wir bei Problem 1, das auch gelöst wurde:
Wenn ein CStringW verwendet wird, dann wird nur die Hälfte des Strings gelöscht, und nicht alles.

Das Szenario, dass zu einem miesen Crash führen kann, will ich nun in den einzelnen Schritten schildern (es wurde ja vermutet, dass es mit GetBuffer zusammenhängt und die Vermutung ist richtig):

  1. Der CString der mit diesem template behandelt wurde enthielt einen größeren CString und anschließend wurde ein kürzerer CString zugewiesen. Damit ist GetAllocLength>GetLength.
  2. Dieser CString wird nun an eine weitere Variable zugewiesen. Durch die Referenzzählung wird keine volle Kopie erzeugt.
  3. Nun kommt unsere schöne Funktion ins Spiel und einer der beiden Strings wird mit dieser Template Funktion behandelt.
  4. Die Funktion hat zwei Argumente, die von rechts nach links berechnet und auf den Stack geschoben werden.
  5. D.h. Zuerst wird GetAllocLength ausgeführt. Und dies ergibt einen Wert für die Länge, der ursprünglich einmal in diese Variable passte.
  6. Als zweites erfolgt nun der Aufruf von GetBuffer. Da wir aber einen CString haben, der mehrfach benutzt wird, muss nun ein Copy on Write erfolgen. D..h. der String wird kopiert und mit der jetzt benötigten Länge neu alloziert und der Zeiger auf diesen Speicher wird zurückgegeben, dieser ist aber eben kürzer als der ursprüngliche Puffer.
  7. Und nun erfolgt der memset, auf einen Speicher der nur noch so groß ist wie der kurze String. Folgerichtig wird der Heap zerstört, weil der Speicher hinter dem String überschrieben wird.
  8. Peng :!: Wir haben hier einen ganz miesen Seiteneffekt.

Hier der Code, mit dem man den Crash gezielt nachbauen kann:

void Crash()
{
  CString str1 = _T("12345678901234567890");
  str1 = _T("123");
  CString str2 = str1;
  SecureClearString(str1); // Crash
  SecureClearString(str2);
}

Der Vollständigkeit halber will ich aber auch noch ein Stück Code zegen, der es richtig macht:

template <class T>
void SecureClearString(T &strText)
{
  // We need this only if there is a private buffer
  if (strText.GetAllocLength()!=0)
  {
    // Execute GetBuffer first. This might cause a fork and may change
    // GetAllocLength.
    T::XCHAR *pBuffer = strText.GetBuffer(0);
    size_t iLen =strText.GetAllocLength();
    ::SecureZeroMemory(pBuffer,iLen*sizeof(T::XCHAR));
  }
  strText.Empty();
}

PS: Der Leser kann sich denken, dass mich dieser Bug und die entsprechende Reproduktion einige Nerven gekostet haben.  Denn es war nicht einfach die Vorbedingung (erst langer String, dann kurzer String, dann Zuweisung) zu ermitteln. Und wie es oft so ist führen Heap-Fehler erst sehr verzögert zu einem Problem.
Wen es genau interessiert: Ich habe ca 7 Stunden an dem Fall geknobelt und hatte 3 verschiedene Crashdumps zur Verfügung. Selbst konnte ich diesen Fehler in unserem Testfeld zuvor nicht erzeugen, weil eben nie alle Bedingungen erfüllt waren. Erst als mir klar war wo das Problem lag, gelang es mir natürlich auch sofort Eingaben zu erzeugen, die den Crash reproduzierten.

C++ProgrammierenVS 2008VS 2010Martin Richter - So 17 Jul 2011 12:25

Folgender Code wurde in einem Programmteil von uns eingebaut:

template <class T>
void SecureClearString(T &strText)
{
  ::SecureZeroMemory(strText.GetBuffer(0),strText.GetAllocLength());
  strText.Empty();
}

Der Sinn und Zweck sollte sein, dass der Inhalt einer CString Variable durch diesen Code überschrieben und anschließend freigegeben wird, damit zum Beispiel ein Kennwort oder ein Benutzername nicht mehr im Speicher lesbar bleibt.
Die Anwendung sieht in etwa so aus (war allerdings noch in einer Klasse gekapselt):

CString strPassword;
...
// Fill password and use it
...
SecureClearString(strPassword);

Doch leider ist was faul mit dem Code… zwei Probleme gibt es mit diesem Stück Code.
Meine Frage an meine Leser lautet nun was :?:

C++ProgrammierenWindows APIMartin Richter - Sa 25 Jun 2011 18:46

Wir haben ein Stück Code, dass verhindern soll, dass ein Programm zweimal gestartet werden kann.
Dieser basiert auf einem Mutex und einer Memory Mapped File, mit der man sich auch das Fenster-Handle einer bereits gestarteten Instanz besorgen kann.

Nun gelang es einem unserer Händler aber dennoch dieses Programm zweimal in einer Session zu starten und zwar auf folgendem Weg:

  1. Er startet die Software mit dem normalen Link auf dem Desktop, der durch das Installationsprogramm angelegt wurde.
  2. Er öffnen eine Console mit CMD.EXE und wechselt in das Verzeichnis, gibt den Programmnamen ein und das Programm startet erneut. :eek:

Die Ursache ist war wie folgt:

  1. Der Mutex den wir intern verwendet haben nutzte den Dateinamen der EXE. Der Name des Mutex wird unter Anderem auch durch GetModuleFileName ermittelt.
  2. Der Dateiname der EXE, wenn sie als Verknüpfung gestartet wird ist “XYZ.exe” (so wie die Datei auch auf der Festplatte heißt) und das liefert auch GetModuleFileName als Ergebnis.
  3. Der Dateiname, den GetModuleFileName liefert, wenn man das Programm aus CMD.EXE startest ist exakt so wie man es eintippt, also z.B. “xyz.exe”. Erstaunlich.
  4. Da der Mutex einen Namen case sensitiv behandelt (was ich nicht vermutet hätte und erst mit staunenden Augen nachgelesen habe), wurde das bereits gestartete Programm nicht erkannt und eine zweite Instanz gestartet.

Was schreiben wir uns also hinter die Löffel für die Zukunft:
a) GetModuleFileName liefert nicht den “exakten” Dateinamen (obwohl ich es anders erwartet hätte)!
b) Mutexe sind case sensitiv wie auch Events (obwohl ich hier eine Behandlung wie bei einem Dateinamen erwartet habe)!
c) Manche Erwartungen trügen… ;)

C++ProgrammierenVS 2008Martin Richter - Sa 18 Jun 2011 10:14

Die nachfolgenden 4 Sicherheitsupdates wurden am 14.06.2011 von Microsoft herausgegeben:

Sicherheitsupdate für Microsoft Visual C++ 2005 Service Pack 1 Redistributable Package (KB2538242)
Sicherheitsupdate für Microsoft Visual Studio 2005 Service Pack 1 (KB2538218)
Sicherheitsupdate für Microsoft Visual C++ 2008 Service Pack 1 Redistributable Package (KB2538243)
Sicherheitsupdate für Microsoft Visual Studio 2008 Service Pack 1 (KB2538241)

Eigentlich beheben Sie nur das, was am Patchday vom 13.04.2011 hätte behoben werden sollen. Ich habe dazu ja mehrere Artikel geschrieben (1, 2, 3, 4, 5). Nach Vergleich der entsprechenden Sourcen machen Sie Änderungen jetzt das, was sie sollen.

Auch das Problem, dass mit dem letzten Sicherheitsupdate, die Größe der Executables immens angewachsen ist wurde behoben.
Schade, dass dieses Problem in VS-2010 weiter bestehen bleibt.
Insbesondere funktioniert diese neue Runtime für VS-2005 auch auf Windows 2000. Eines der Hauptprobleme vom Patchday im April.

Anzumerken wäre hier eigentlich nur noch die neuen Versionen der Runtime die mit diesem Sicherheitsupdate veröffentlicht werden, d.h. auch, dass man nun die neuen Runtimes auch in sein Setup einbauen sollte, bzw. dass man die neueste passende VCRedist_x86 nun auch mit ausliefern muss. D.h. auch, dass sich die entsprechenden Manifeste wieder ändern, sofern man diese manuell angepasst hat:

  • Die neuen Runtimedateien von VS-2005 haben die Versionsnummer 8.0.50727.6195
    Die neue Runtime für VS-2005 gibt es hier zum Download.
  • Die neuen Runtimedateien von VS-2008 haben die Versionsnummer 9.0.30729.6161
    Die neue Runtime für VS-2008 gibt es hier zum Download.

Weitere und vollständige Infos zu diesem Sicherheitsupdate finden sich im VC-Blog:
http://blogs.msdn.com/b/vcblog/archive/2011/06/17/10175518.aspx

Ich jetzt kann nur allen Entwicklern raten diese neuen Sicherheitsupdates auch zu installieren und zu nutzen!

MFCProgrammierenVista / Windows 7VS 2008Martin Richter - Do 14 Apr 2011 17:30

This is the English translation of the already published German article:
BUG: Schwarzer Patchday für alle OS XP und später 3. – MFC 8.0 (VC-2005) oder MFC 9.0 (VC-2008) die dynamisch gelinkt wurden finden die MFC Sprach-DLLs evtl. nicht mehr nach Installation der Sicherheitspatches vom 12.04.2011

Affected are:

  • All programs created with MFC 8.0 and MFC 9.0 that link dynamically to the MFC DLLs .
  • All operating systems from Windows XP and later. 32bit as 64bit
  • Al programs that do not use an application local installation (program directory, see note at the bottom of the article). So all programs that use and depend on WinSxS and VCRedist_x86.exe ( VCRedist_x64.exe).
  • All programs that are localized and use the MFC90xxx.DLL or. MFC80xxx.DLL language-DLLs and the OS system language is not set to English.

It is affected due to the security fixes offered April 12th, 2011:

For VS-2005 SP1 http://support.microsoft.com/kb/2465367 and http://support.microsoft.com/kb/2467175
For VS-2008 SP1 http://support.microsoft.com/kb/2465361 and http://support.microsoft.com/kb/2467174

Failure description:

The MFC language DLLs (satellite DLLs) are not loaded any longer. Parts of the application appear in English and not the selected language from the OS.

Background:

To prevent loading of wrong satellite DLLs (Binary Planting), an internal function in appcore.cpp named _AfxLoadLangDLL was changed. It checks if an activation context is active or not and if the DLLs should be loaded using this context. If there is an activation context active it is safe to load the satellite DLLs(MFCDEUxxx.DLL etc.) without defining a full path. If no activation context is active the path of the current application is used to load and find the satellite DLLs. The DLLs are loaded with a call to LoadLibrary.

The code used looks like this (empty lines removed):

...
TCHAR *pszFilename = ::PathFindFileName(szLangDLL);
ACTCTX_SECTION_KEYED_DATA data;
if (FindActCtxSectionString(
    FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX,
    NULL,
    ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION,
    pszFilename,
    &data) )
{
    // Load using the dll name only...
    hInstance = ::LoadLibraryEx(pszFilename, NULL, 0);
}
else
{
    // Load using the full path...
    hInstance = ::LoadLibraryEx(szLangDLL, NULL, 0);
}
...

The code looks OK.  And it is conform to the documentation of FindActCtxSectionString where the last parameter is defined as __out.

BOOL FindActCtxSectionString(
  __in   DWORD dwFlags,
  __in   const GUID *lpExtensionGuid,
  __in   ULONG ulSectionId,
  __in   LPCTSTR lpStringToFind,
  __out  PACTCTX_SECTION_KEYED_DATA ReturnedData
);

But the documentation of ACTCTX_SECTION_KEYED_DATA tells a different story:

Callers should initialize the ACTCTX_SECTION_KEYED_DATA structure as such:
“ACTCTX_SECTION_KEYED_DATA askd = { sizeof(askd) };”
which initializes all members to zero/null except the size field which is set correctly.

(BTW: In my eyes a documentation failure)

So what we see is that the code misses this: data.cbSize isn’t initialized :!:
Now we have 3 possible scenarios what can happen with a  randomly initialized data.cbSize field:

  1. data.cbSize is larger than sizeof(ACTCTX_SECTION_KEYED_DATA):
    In this case the activation context is correctly detected. The program executes normal.  With an activation context no full path is needed. The MFC90xxx.DLL will be loaded from the WinSxS (Side by Side) or found over the common search path.
  2. data.cbSize is less than  sizeof(ACTCTX_SECTION_KEYED_DATA):
    In this case FindActCtxSectionString returns with an error. The DLL is now loaded with a full path name constructed from the application directory to prevent Binary Planting. Butthe problemis that with a normal installation the searched files are all in WinSxS, and the application directory has no such data. The DLL is not loaded.
    If the application local assemblies are used and placed in sub directories they aren’t found either.
  3. A future problem.
    If an OS will use a larger ACTCTX_SECTION_KEYED_DATA and data.cbSize has a greater value than the corresponding sizeof(…):
    We have a buffer-overrun!

I always recommend to use private and application local assemblies for the CRT and MFC DLLs. And to install all this files local to the application.
Years ago I wrote an article for this scenario that was published on CodeProject and a hotfix for VS-2008 is also available :!:
Create projects easily with private MFC, ATL and CRT assemblies
Hotfix für UseMSPrivateAssemblies.h und VC-2008

What to do?

Uninstall all of the mentioned security fixes with the specified article IDs.
Runtime-2005: KB2467175, Runtime-2008: KB2467174
VS-2007 SP1: KB2465367, VS-2008 SP1: KB2465361).

Further notes:

The affected C/C++ Runtimes of Visual Studio have the following version numbers:
- VC-2005 8.0.50727.5592 (KB2467175)
- VC-2008 9.0.30729.5570 (KB2467174)

My comment to tis issue:
It was easier to live with the DLL-hell. :(

Many thanks to my Co-MVP Mike Ryan who helped me to discover this problems with the latest security patches:!:

What Do I mean with “application local”?
Some people ship the MFC files in the application directory. In such a case this DLLs are not loaded if a newer version can be found in the WinSxS directory. This is not application local for me!
So if the manifest file in the program directory still have a publicKey entry, the local files will be used  in case of the here described bug. Even if the activation context was not detected, so the local files are a kind of fallback and help prevent get around the problem.
My articles describe how to make your application really application local in removingthe publicKey tokens from the manifest files. Such programs will never fail on such broken security patches. (Just read my article at Codeproject). (Thanks for Co-MVP David Ching who asked me for a clarification)

MFCProgrammierenSoftwareWindowsMartin Richter - Do 14 Apr 2011 10:30

Hintergrund siehe hier:
BUG: Schwarzer Patchday für Windows 2000 – MFC 8.0 (VC-2005) und MFC 9.0 (VC-2008) DLLs sind nicht mehr lauffähig nach Installation von KB2467174 bzw. KB2467175
BUG: Schwarzer Patchday für Windows 2000 2.- MFC 8.0 (VC-2005) und MFC 9.0 (VC-2008) Static Libraries erzeugen auch inkompatiblen Code für Windows 2000 durch KB2465367 bzw. KB2465361

Unter Windows 2000 kann man wie folgt vorgehen und das Problem beheben:

  1. Am Besten macht man das nachdem man das System neu gestartet hat und noch keine Anwendung gestartet hat.
  2. Alle betreffenden Hotfixe entfernen (für Runtime-2005 KB2467175, Runtime-2008 KB2467174, für VS-2007 SP1: KB2465367, VS-2008 SP1: KB2465361).
    Die betroffenen C/C++ Runtimes des Visual Studio, die deinstalliert werden müssen, haben die folgenden Versionsnummern
    - VC-2005 8.0.50727.5592 (KB2467175)
    - VC-2008 9.0.30729.5570 (KB2467174)
    Um VS-2005/2008 wiederherzustellen ist zwingend eine Deinstallation des Patches nötig.
    Die Dateien für das Visual Studio sollten dann wieder denen des letzten Fix aus 2005/2008 entsprechen.
  3. Eigentlich sollte die Deinstallation des Patches genügen.
    Sofern es sich nur um ein Problem mit den Runtimes handelt und sich das Problem nicht behoben hat kann man mit den nächsten Schritten weiter machen und versuchen die alten Dateien wieder herzustellen.
    (
    Man kann diese Schritte auch ohne Deinstallation durchführen)
  4. Für VS-2008: Die Dateien für aus dem letzten Sicherheitsupdate müssten in dem folgenden Verzeichnis unter C:\WinNT\winsxs\ liegen:
    a. x86_microsoft.vc90.mfc_1fc8b3b9a1e18e3b_9.0.30729.4974_…
    b. x86_microsoft.vc90.mfc_1fc8b3b9a1e18e3b_9.0.30729.4148_…
    c. x86_microsoft.vc90.mfc_1fc8b3b9a1e18e3b_9.0.30729.1_…
    Man wählt das Verzeichnis, dass man zuerst findet.
  5. Für VS-2005: Die Dateien für aus dem letzten Sicherheitsupdate müssten in dem folgenden Verzeichnis unter C:\WinNT\winsxs\ liegen:
    a. x86_microsoft.vc80.mfc_1fc8b3b9a1e18e3b_8.0.50727.4053_…
    b. x86_microsoft.vc80.mfc_1fc8b3b9a1e18e3b_8.0.50727.4027_…
    c. x86_microsoft.vc80.mfc_1fc8b3b9a1e18e3b_8.0.50727.1833_…
    d. x86_microsoft.vc80.mfc_1fc8b3b9a1e18e3b_8.0.50727.762_…
    Man wählt das Verzeichnis, dass man zuerst findet.
  6. Alle Dateien aus diesen gefundenen Verzeichnissen in das C:\WinNT\System32 Verzeichnis kopieren.

Hope that helps :!:

PS: Ich habe den Artikel mehrfach überarbeitet während er bereits veröffentlicht war und immer neue Infos eingebaut bzw. die Vorgehensweise besser erklärt.

MFCProgrammierenVista / Windows 7VS 2008Martin Richter - Do 14 Apr 2011 10:12

Betroffen sind:

  • Alle Programme die mit MFC 8.0 oder MFC 9.0 erzeugt wurden und dynamisch an die MFC DLLs gelinkt sind.
  • Alle Betriebssysteme ab Windows XP aufwärts. 32bit wie 64bit
  • Alle Programme, die nicht die MFC und CRT DLLs applikationsnah (d.h. im Programmverzeichnis, siehe dazu auch die Fußnote in meinem Artikel) installiert haben. Also alle Programme die WinSxS benutzen und die VCRedist_x86.exe ( VCRedist_x64.exe)  mit ausliefern.
  • Alle Programme, die lokalisiert sind und die MFC90xxx.DLL bzw. MFC80xxx.DLL Sprach-DLLs verwenden und das OS nicht auf Englisch eingestellt ist

Betrifft die folgenden Fixes vom 12.04.2011:

Für VS-2005 SP1 http://support.microsoft.com/kb/2465367 und http://support.microsoft.com/kb/2467175
Für VS-2008 SP1 http://support.microsoft.com/kb/2465361 und http://support.microsoft.com/kb/2467174

Effekt:

Die MFC Statelite DLLs werden nicht geladen. Teile der Anwendung erscheinen in englischer Sprache.

Hintergrund:

Um das Laden von falschen Satelite DLLs zu verhindern (Binary Planting), wurde intern in appcore.cpp in der Funktion _AfxLoadLangDLL geprüft, ob die DLLs in aus einem Activation Context geladen werden oder nicht. Sollte ein Activation Context vorhanden sein, dann kann man gefahrlos die Sprach DLLs (MFCDEUxxx.DLL etc.) ohne Pfadnamen laden. Ist kein Activation Context vorhanden wird der Pfad der Anwendung verwendet und LoadLibrary mit vollem Pfadnamen durchgeführt.

Der Code der dazu verwendet wird sieht so aus (Leerzeilen entfernt):

...
TCHAR *pszFilename = ::PathFindFileName(szLangDLL);
ACTCTX_SECTION_KEYED_DATA data;
if (FindActCtxSectionString(
    FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX,
    NULL,
    ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION,
    pszFilename,
    &data) )
{
    // Load using the dll name only...
    hInstance = ::LoadLibraryEx(pszFilename, NULL, 0);
}
else
{
    // Load using the full path...
    hInstance = ::LoadLibraryEx(szLangDLL, NULL, 0);
}
...

Eigentlich sieht der Code prima aus. Und er verträgt sich auch mit der Doku von FindActCtxSectionString dort wird der letzte Parameter als __out definiert.

BOOL FindActCtxSectionString(
  __in   DWORD dwFlags,
  __in   const GUID *lpExtensionGuid,
  __in   ULONG ulSectionId,
  __in   LPCTSTR lpStringToFind,
  __out  PACTCTX_SECTION_KEYED_DATA ReturnedData
);

Aber die Doku zu ACTCTX_SECTION_KEYED_DATA sagt was anderes:

Callers should initialize the ACTCTX_SECTION_KEYED_DATA structure as such:
“ACTCTX_SECTION_KEYED_DATA askd = { sizeof(askd) };”
which initializes all members to zero/null except the size field which is set correctly.

(BTW: Auch ein krasser Doku-Bug in meinen Augen)

Und jetzt sieht man was dem Code fehlt: data.cbSize wird nicht gesetzt :!:
Daraus ergeben sich nun drei Varianten, da data.cbSize nun zufälligen (nicht initialisierten) Inhalt hat:

  1. data.cbSize ist größer als  sizeof(ACTCTX_SECTION_KEYED_DATA):
    In diesem Fall wird korrekt ermittelt ob ein Activation Context vorhanden ist. Das Programm läuft normal. Mit Activation Context ist kein voller Pfadname nötig. Die MFC90xxx.DLL wird evtl. aus dem WinSxS (Side by Side) geladen, oder in einem der Suchpfade gefunden.
  2. data.cbSize ist kleiner als  sizeof(ACTCTX_SECTION_KEYED_DATA):
    In diesem Fall liefert FindActCtxSectionString einen Fehler und nun wird es spannend. Die DLL wird nun versucht mit dem vollen Pfadnamen zu laden um Binary Planting zu verhindern. Das Problem ist aber dass bei korrekter Installation im WinSxS, dass im Applikationsverzeichnis keine dieser Daten liegen. Die DLL wird nicht gefunden.
    Sollten die private applikationsnahe Assemblies in einem Unterverzeichnis installiert sein, werden diese auch nicht gefunden.
  3. Für die Zukunft.
    Ein zukünftiges OS vergrößert ACTCTX_SECTION_KEYED_DATA und data.cbSize hat zufälligen Inhalt und ist größer als sizeof(…):
    Ein Buffer-Overrun!

Ich empfehle nicht ohne Grund seit VS-2005 private Assemblies zu verwenden, und die MFC Dateien in das Anwendungsverzeichnis zu kopieren. Dazu habe ich auf Code-Projekt einen entsprechenden Artikel geschrieben und ein Hotfix für VS-2008 existiert auch :!:
Create projects easily with private MFC, ATL and CRT assemblies
Hotfix für UseMSPrivateAssemblies.h und VC-2008

Was ist zu tun?

Deinstallation aller hier erwähnten Sicherheitspatches mit den entsprechenden Arikelnummern:
Runtime-2005: KB2467175, Runtime-2008: KB2467174
VS-2007 SP1: KB2465367, VS-2008 SP1: KB2465361).

Weitere Anmerkungen:

Die betroffenen C/C++ Runtimes des Visual Studio haben die folgenden Versionsnummern
- VC-2005 8.0.50727.5592 (KB2467175)
- VC-2008 9.0.30729.5570 (KB2467174)

Mein Kommentar dazu:
Das Leben in der DLL-Hölle war fast angenehmer als das hier. Ohne Worte :(

Herzlichen Dank auch an meinem Mit-MVP Mike Ryan, der mit mir zusammen auf diese gesamte Problematik gestoßen ist :!:

Was meine ich mit “application local”?
Einige Entwickler installieren die MFC runtme Dateien im Applikationsverzeichnis. In diesem Fall werden diese DLLs nicht verwendet wenn eine neuere Version der DLLs im WinSxS Verzeichnis liegen. Das ist für mich keine applikationsnahe Instalation! Diese Manifeste im Programmverzeichnis haben immer noch einen publicKey Eintrag. Aber durch die Existenz der lokalen Dateien wird dieses hier beschriebene Problem umgangen, weil die lokalen Dateien eine Art Fallback bilden.
Meine Artikel beschriben wie man eine Anwendung wirklich applikationslokal macht und damit unabhängig von solchen “kaputten” Security Patches. Dazu muss der publicKey Token aus den Manifesten entfernt werden. (Lesen Sie meinen Artikel aufCodeproject).
(Danke an Co-MVP David Ching der mich um Kläurung gebeten hat)

C++MFCProgrammierenVS 2008Martin Richter - Mi 13 Apr 2011 22:48

Wer VS-2005 SP1 oder VS-2008 SP1 installiert hatte und bei dem auch die entsprechenden Patches von gestrigen Tag (12.04.2011) durchlaufen wurden, der hat nun auch veränderte statische Libraries.

Sollte man nun also EXEs oder DLLs mit den neuen Libararies statisch linken, dann sind diese genausowenig lauffähig unter Windows 2000. wie auch die EXEs und DLLs die gegen die MFC 8.0 bzw. MFC 9.0 DLLs gelinkt werden :!:

Das Ganze ist hier aufgelistet:
für VS-2005 SP1 http://support.microsoft.com/kb/2465367
für VS-2008 SP1 http://support.microsoft.com/kb/2465361

Die LIBs sind aufgeführt und auch diese verwenden auch die Funktion FindActCtxSectionStringA, die natürlich nicht unter Windows 2000 vorhanden ist.

Siehe auch:
http://blog.m-ri.de/index.php/2011/04/13/bug-schwarzer-patchday-fur-windows-2000-mfc-8-0-vc-2005-und-mfc-9-0-vc-2008-dlls-sind-nicht-mehr-lauffahig-nach-installation-von-kb2467175-bzw-kb2467175/

PS: Ich kann nur raten die entsprechenden Patches zu deinstallieren sofern man noch für Windows 2000 entwickelt und warten bis neue Securitypatches vorhanden sind.

Nachtrag:
Die betroffenen C/C++ Runtimes des Visual Studio haben die folgenden Versionsnummern
- VC-2005 8.0.50727.5592 (KB2467175)
- VC-2008 9.0.30729.5570 (KB2467174)

MFCProgrammierenVS 2008Martin Richter - Mi 13 Apr 2011 17:35

Ein wirklich übler Patchday von Microsoft für ale Windows 2000 Nutzer.
In den Sicherheitsfixes für Visual Studio 2005 und 2008 wurden auch die Runtime Module der MFC 8.0 und MFC 9.0 “gefixed”.
Siehe http://support.microsoft.com/kb/2467175 für VS-2005
Siehe http://support.microsoft.com/kb/2467174 für VS-2008

Es wurde ein Fix eingebaut, den wir schon in VS-2010 gesehen haben, der vermeiden soll, dass Satelite DLLs nur noch aus dem Verzeichnis geladen werden, in dem auch die MFC DLLs liegen
(gegen ‘Binary Planting’, d.h. unterschieben gefakter DLLs aus anderen Verzeichnissen).

Leider wurde dabei die Funktion FindActCtxSectionStringA mit eingebaut und verwendet. Eine Funktion die aber erst ab Windows XP vorhanden ist. Sofern man also eine Anwendung unter Windows 2000 hat, die die MFC80.DLL, MFC80U.DLL, MFC90.DLL oder MFC90U.DLL aus dem WINDOWS\SYSTEM32 Verzeichnis verwendet, dann hat man seit dem dem Patchday von gestern ein massives Problem. Die DLLs können nicht mehr geladen werden. :shock:

Einziger Rat ist hier, die neuen Dateien löschen und die ungepatchten Versionen installieren und das am besten direkt in den Programmverzeichnissen :!:
Alte Runtimes für VS-2005 SP1 http://www.microsoft.com/downloads/en/details.aspx?FamilyID=200b2fd9-ae1a-4a14-984d-389c36f85647
Alte Runtimes für VS-2008 SP1 http://www.microsoft.com/downloads/en/details.aspx?FamilyID=a5c84275-3b97-4ab7-a40d-3802b2af5fc2
Alle Versionen mit den VCRedist_x86.exe Versionen und dem Datum vom 13.04.2010 sind für Windows 2000 tabu.
Das heißt auch, dass man diese DLLs nicht in seinem eigenen Setup für Windows 2000 ausrollen sollte.

Nicht betroffen sind Applikationen, die diese DLLs applikationsnah, d.h. im Programmverzeichnis installieren.
Eine Methode, die ich seit Jahren empfehle!

Nur noch einmal zur Klarstellung: Der Sicherheitspatch ist nur problematisch für Windows 2000 Rechner :!:

PS: Was bin ich froh, dass wir Windows 2000 schon lange nicht mehr unterstützen :mrgreen:

PPS: Bei solch einem Fehler frage ich mich ob hier irgend jemand überhaupt etwas getestet hat, außer das die Installation durchgeführt wird.

Nachtrag:
Die betroffenen C/C++ Runtimes des Visual Studio haben die folgenden Versionsnummern
- VC-2005 8.0.50727.5592 (KB2467175)
- VC-2008 9.0.30729.5570 (KB2467174)

Workaround:
Workaround für Patchday Bug vom 12.04.2011: Wenn unter Windows 2000 der Einsprungpunkt FindActCtxSectionStringA nicht gefunden wird

C++ProgrammierenVS 2010Martin Richter - Sa 09 Apr 2011 20:53

Ich habe einige Manifest Dateien in meinen Projekten. Viele steuern COM Module und machen diese registration-free. Das ist einfach und effektiv. Dazu habe ich einfach eine entsprechende Manifestdatei angelegt mit den entsprechenden Einträgen und diese in das Projekt eingefügt. Soweit alles gut.

Jetzt stellte sich aber heraus, dass VS-2010 (auch SP1), eine Änderung der Manifest Datei nicht bemerkt und die EXE/DLL weder neu linkt, noch das Manifest-Tool anwirft, um das geänderte Manifest in das Executable einzutragen.

Dabei spielt es keinerlei Rolle, ob die Datei nur einfach in das Projekt eingefügt wurde, oder ob die Datei zusätzlich in den Projekteinstellungen für das Manifest Tool bei Additional Manifest Files eingetragen wird.

Das ist ein lästiger Fehler, der in VS-2005 und VS-2008 nicht vorhanden war.
Er hat mich mindestens 4 Stunden Arbeit gekostet, weil ich bestimmte Abhängigkeiten testen und Fehler beheben wollte aber die neuen Dateien in dem Projekt immer wieder SxS Fehler lieferten. Es dauerte eine ganze Weile bis ich merkte, dass meine Änderungen an den Manifest Dateien überhaupt nicht in meine Executables übernommen wurden und ich immer wieder nur alte Manifeste in den EXEs und DLLs hatte.
Und dann das Ganze an einem Montagmorgen und nach der Zeitumstellung… ;)

Auf Connect habe ich einen entsprechenden Bug veröffentlich und mir wurde ein Fix empfohlen, der bei mir das Problem behebt.
Changes to a Manifest file in a C++ project does not trigger a rebuild of the EXE or DLL

In der Datei %Programfiles%\msbuild\microsoft.cpp\v4.0\Microsoft.cppcommon.targets wird die folgenden Kommentarzeile gesucht:

<!-- If RC did produce an output, then force link to embed that manifest.
     This enforcement is required for projects residing on FAT32 drives. -->

Darunter wird der folgende Textblock eingefügt:

<PropertyGroup>
    <LinkSkippedExecution Condition="@(RCSourcesCompiled)!=''">
        false
    </LinkSkippedExecution>
</PropertyGroup>

Danch wird eine Änderung im Manifest korrekt erkannt und der Linker für die geänderte Ressouce angeworfen.

Nächste Seite »