質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.33%

  • C#

    7710questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • Windows 10

    1024questions

    Windows 10は、マイクロソフト社がリリースしたOSです。Modern UIを標準画面にした8.1から、10では再びデスクトップ主体に戻され、UIも変更されています。PCやスマホ、タブレットなど様々なデバイスに幅広く対応していることが特徴です。

  • .NET Framework

    489questions

    .NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

CredEnumerate より CREDENTIAL#Attributes を エラーなく取得したい。

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 437

juner

score 11

 前提・実現したいこと

C# でCredEnumerate で取得した値をすべて表示したく思い、プログラムサンプルを書いています。
すると CREDENTIAL#Attributes の構造体への割り当て時に次の様なエラーが発生します。

 発生している問題・エラーメッセージ

マネージ デバッグ アシスタント 'FatalExecutionEngineError' : 'ランタイムの重大なエラーが発生しました。エラーのアドレスは 0x7355853e、スレッド 0x3238 です。エラー コードは 0xc0000005 です。これは CLR のバグであるか、またはユーザー コードのアンセーフまたは確認不可能な部分にバグがある可能性があります。このバグの一般的な原因には、スタックが壊れる可能性のある COM-interop または PInvoke のユーザー マーシャリング エラーが含まれています。'

発生個所は次のソースコードのMain内の

.Select(x => Marshal.PtrToStructure<CREDENTIAL_ATTRIBUTE>(x)))


の箇所となります。

 該当のソースコード

using System;
using System.Linq;
using System.Runtime.InteropServices;

namespace CredReadExample
{
    class Program
    {
        static void Main(string[] args)
        {
            IntPtr Credentials = IntPtr.Zero;
            try
            {
                var i = 0;
                if(CredEnumerate(null, 0, out var count,out Credentials))
                    foreach(var c in Enumerable.Range(0, count)
                     .Select(n => Marshal.ReadIntPtr(Credentials, n * Marshal.SizeOf(typeof(IntPtr))))
                     .Select(ptr => Marshal.PtrToStructure<CREDENTIAL>(ptr)))
                    {
                        Console.WriteLine($"{++i}");
                        Console.WriteLine($"{nameof(c.Flags)}:{c.Flags}");
                        Console.WriteLine($"{nameof(c.Type)}:{c.Type}");
                        Console.WriteLine($"{nameof(c.TargetName)}:{c.TargetName}");
                        Console.WriteLine($"{nameof(c.Comment)}:{c.Comment}");
                        Console.WriteLine($"{nameof(c.LastWritten)}:{DateTime.FromFileTime((long)(c.LastWritten.dwHighDateTime << 32) | (uint)c.LastWritten.dwLowDateTime)}");
                        Console.WriteLine($"{nameof(c.CredentialBlobSize)}:{c.CredentialBlobSize}");
                        var _CredentialBlob = new byte[c.CredentialBlobSize];
                        if (c.CredentialBlobSize > 0)
                            Marshal.Copy(c.CredentialBlob, _CredentialBlob, 0, (int)c.CredentialBlobSize);
                        Console.WriteLine($"{nameof(c.CredentialBlob)}:[{string.Join(" ", _CredentialBlob.Select(b => $"{b:X2}"))}]");
                        Console.WriteLine($"{nameof(c.Persist)}:{c.Persist}");
                        Console.WriteLine($"{nameof(c.AttributeCount)}:{c.AttributeCount}");
                        var ai = 0;
                        foreach(var attribute in Enumerable.Range(0, (int)c.AttributeCount)
                                        .Select(x => Marshal.ReadIntPtr(c.Attributes, x * Marshal.SizeOf<CREDENTIAL_ATTRIBUTE>()))
                                        .Select(x => Marshal.PtrToStructure<CREDENTIAL_ATTRIBUTE>(x)))
                        {
                            Console.WriteLine($"{nameof(c.Attributes)} - {++ai}");
                            Console.WriteLine($"{nameof(attribute.Keyword)}:{attribute.Keyword}");
                            Console.WriteLine($"{nameof(attribute.Flags)}:{attribute.Flags}");
                            Console.WriteLine($"{nameof(attribute.ValueSize)}:{attribute.ValueSize}");
                            var _AttributeValue = new byte[attribute.ValueSize];
                            if (attribute.ValueSize > 0)
                                Marshal.Copy(attribute.Value, _AttributeValue, 0, (int)attribute.ValueSize);
                            Console.WriteLine($"{nameof(attribute.Value)}:[{string.Join(" ", _AttributeValue.Select(v => $"{v:X2}"))}]");
                        }
                        Console.WriteLine($"{nameof(c.TargetAlias)}:{c.TargetAlias}");
                        Console.WriteLine($"{nameof(c.UserName)}:{c.UserName}");
                    }
            }
            finally
            {
                if (Credentials != IntPtr.Zero)
                    CredFree(Credentials);
            }
            Console.ReadLine();
        }
        [DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)]
        private static extern bool CredEnumerate(string filter, CRED_FLAGS flag, out int count, out IntPtr Credentials);
        [DllImport("advapi32.dll", SetLastError = true)]
        static extern bool CredFree([In] IntPtr buffer);
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        internal struct CREDENTIAL
        {
            public CRED_FLAGS Flags;
            public CRED_TYPE Type;
            public string TargetName;
            public string Comment;
            public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
            public uint CredentialBlobSize;
            public IntPtr CredentialBlob;
            public CRED_PERSIST Persist;
            public uint AttributeCount;
            public IntPtr Attributes;
            public string TargetAlias;
            public string UserName;
        }
        public enum CRED_TYPE : uint
        {
            GENERIC = 1,
            DOMAIN_PASSWORD = 2,
            DOMAIN_CERTIFICATE = 3,
            DOMAIN_VISIBLE_PASSWORD = 4,
            GENERIC_CERTIFICATE = 5,
            DOMAIN_EXTENDED = 6,
            MAXIMUM = 7,
            MAXIMUM_EX = (MAXIMUM + 1000),
        }

        public enum CRED_PERSIST : uint
        {
            SESSION = 1,
            LOCAL_MACHINE = 2,
            ENTERPRISE = 3,
        }

        public enum CRED_FLAGS : uint
        {
            PROMPT_NOW = 0x2,
            USERNAME_TARGET = 0x4
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public struct CREDENTIAL_ATTRIBUTE
        {
            public string Keyword;
            public uint Flags;
            public uint ValueSize;
            public IntPtr Value;
        }
    }
}

 試したこと

エラーが発生する箇所の.Select を次のコードにしたところ、 Keyword値は正常に取得することができたが、他の値は不正な値となった。※勿論、 Value 及び ValueSize を設定しない様にして実行すれば最後まで実行できるが、 Flags の値は不正であろう値となる。

.Select(x =>
{
    var pos = 0;
    var Keyword = Marshal.PtrToStringUni(x);
    pos += Marshal.SizeOf<IntPtr>();
    var Flags = (uint)Marshal.ReadInt32(x, pos);
    pos += sizeof(uint);
    var ValueSize = (uint)Marshal.ReadInt32(x, pos);
    pos += sizeof(uint);
    var Value = Marshal.SizeOf<IntPtr>() == 4 ? new IntPtr(x.ToInt32() * pos)
        : Marshal.SizeOf<IntPtr>() == 8 ? new IntPtr(x.ToInt64() * pos)
        : new IntPtr(x.ToInt32() * pos);
    pos += Marshal.SizeOf<IntPtr>();
    return new CREDENTIAL_ATTRIBUTE
    {
        Keyword = Keyword,
        Flags = Flags,
        ValueSize = ValueSize,
        Value = Value,
    };
})

 補足情報(FW/ツールのバージョンなど)

VisualStudio 2017 15.6.6
.NET Frameworks 4.6.1
32bit/64bit
Windows10 1709

実際のソリューションファイル(上記ソース位置)
https://github.com/juner/testproject20180416
最新のソリューション(試したこと適用時の最終)
https://github.com/juner/testproject20180416

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • Zuishin

    2018/04/16 13:56

    文法が間違っているのでコンパイルできません。該当のエラーが出るソースを書いてください。

    キャンセル

  • Zuishin

    2018/04/16 13:58

    失礼しました。こちらのミスでした。

    キャンセル

回答 1

check解決した方法

0

原因は エラーが発生していた .Select の箇所ではなく、その手前の位置の取得部分である、

.Select(x => Marshal.ReadIntPtr(c.Attributes, x * Marshal.SizeOf<CREDENTIAL_ATTRIBUTE>()))


が原因。ポインタの先を読みに行くのではなく、 その場所自体に値があるので正しくは

.Select(x => IntPtr.Add(c.Attributes,x * Marshal.SizeOf<CREDENTIAL_ATTRIBUTE>()))


の様にしなくてはならない。(CREDENTIAL_ATTRIBUTE の取り方を CREDENTIAL の取り方とごっちゃにしていた可能性)

参考:当該修正方法を適用した時点のソリューション
https://github.com/juner/testproject20180416

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.33%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • C#

    7710questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • Windows 10

    1024questions

    Windows 10は、マイクロソフト社がリリースしたOSです。Modern UIを標準画面にした8.1から、10では再びデスクトップ主体に戻され、UIも変更されています。PCやスマホ、タブレットなど様々なデバイスに幅広く対応していることが特徴です。

  • .NET Framework

    489questions

    .NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。