<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[風の音]]></title><description><![CDATA[（伪）技术博客]]></description><link>https://hjc.im/</link><generator>Ghost 0.11</generator><lastBuildDate>Tue, 26 Jun 2018 09:17:52 GMT</lastBuildDate><atom:link href="https://hjc.im/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[三种方式轻松绕过Windows App Certification Kit (WACK)的API检测(UWP, C++)]]></title><description><![CDATA[<p>微软在UWP中提供了一组丰富的API能够满足99%的应用的需求<del>（棒读）</del>，然而在那剩下1%的情况下找不到桌面API的替代品是很棘手的，正如我<a href="http://hjc.im/uwp-named-pipe/">上一篇文章</a>里提到的CreateFile，就不属于微软在UWP中允许使用的API。 <br>
实际上WACK的API检测是个没什么用的东西，因为它是通过读取PE导入表，以及 .NET程序的P/Invoke签名的方法来判断一个App是否使用了不允许使用的API。因此只要调用LoadLibrary就能轻松绕过。 <br>
问题在于LoadLibrary(Ex)本身不被允许调用，微软表示替代品是LoadPackagedLibrary，而这个API在调用时会检测路径是否在appx内，如果不在就直接报错。（<del>太愚蠢了</del>）因此首先我们要设法获得LoadLibraryEx的地址，在此之前先获取kernel32.dll或kernelbase.dll的地址。</p>

<p>方法1：使用VirtualQuery获取</p>

<pre><code>MEMORY_BASIC_INFORMATION info = {};  
if (VirtualQuery(VirtualQuery, &amp;info, sizeof(info)))  
{
    auto kernelAddr = (HMODULE)info.AllocationBase;
    auto loadlibraryPtr = GetProcAddress(kernelAddr, "LoadLibraryExW");
    // load</code></pre>]]></description><link>https://hjc.im/3-ways-to-bypass-wack/</link><guid isPermaLink="false">777ff16e-418c-4d98-8af9-d119cef7305f</guid><dc:creator><![CDATA[David Huang]]></dc:creator><pubDate>Wed, 02 Mar 2016 05:49:30 GMT</pubDate><content:encoded><![CDATA[<p>微软在UWP中提供了一组丰富的API能够满足99%的应用的需求<del>（棒读）</del>，然而在那剩下1%的情况下找不到桌面API的替代品是很棘手的，正如我<a href="http://hjc.im/uwp-named-pipe/">上一篇文章</a>里提到的CreateFile，就不属于微软在UWP中允许使用的API。 <br>
实际上WACK的API检测是个没什么用的东西，因为它是通过读取PE导入表，以及 .NET程序的P/Invoke签名的方法来判断一个App是否使用了不允许使用的API。因此只要调用LoadLibrary就能轻松绕过。 <br>
问题在于LoadLibrary(Ex)本身不被允许调用，微软表示替代品是LoadPackagedLibrary，而这个API在调用时会检测路径是否在appx内，如果不在就直接报错。（<del>太愚蠢了</del>）因此首先我们要设法获得LoadLibraryEx的地址，在此之前先获取kernel32.dll或kernelbase.dll的地址。</p>

<p>方法1：使用VirtualQuery获取</p>

<pre><code>MEMORY_BASIC_INFORMATION info = {};  
if (VirtualQuery(VirtualQuery, &amp;info, sizeof(info)))  
{
    auto kernelAddr = (HMODULE)info.AllocationBase;
    auto loadlibraryPtr = GetProcAddress(kernelAddr, "LoadLibraryExW");
    // load your library here ...
}
</code></pre>

<p>方法2：使用Thread Environment Block获取  </p>

<p>首先定义结构</p>

<pre><code>typedef struct _PEB_LDR_DATA  
{
    ULONG Length;
    UCHAR Initialized;
    PVOID SsHandle;
    LIST_ENTRY InLoadOrderModuleList;
    LIST_ENTRY InMemoryOrderModuleList;
    LIST_ENTRY InInitializationOrderModuleList;
    PVOID EntryInProgress;
} PEB_LDR_DATA, *PPEB_LDR_DATA;

typedef struct _PEB  
{
    BYTE Reserved1[2];
    BYTE BeingDebugged;
    BYTE Reserved2[1];
    PVOID Reserved3[2];
    PPEB_LDR_DATA Ldr;
    PVOID ProcessParameters;
    PVOID Reserved4[3];
    PVOID AtlThunkSListPtr;
    PVOID Reserved5;
    ULONG Reserved6;
    PVOID Reserved7;
    ULONG Reserved8;
    ULONG AtlThunkSListPtr32;
    PVOID Reserved9[45];
    BYTE Reserved10[96];
    PVOID PostProcessInitRoutine;
    BYTE Reserved11[128];
    PVOID Reserved12[1];
    ULONG SessionId;
} PEB, *PPEB;

typedef struct _TEB  
{
    PVOID Reserved1[12];
    PPEB ProcessEnvironmentBlock;
    PVOID Reserved2[399];
    BYTE Reserved3[1952];
    PVOID TlsSlots[64];
    BYTE Reserved4[8];
    PVOID Reserved5[26];
    PVOID ReservedForOle;  // Windows 2000 only
    PVOID Reserved6[4];
    PVOID TlsExpansionSlots;
} TEB, *PTEB;
</code></pre>

<p>然后计算地址</p>

<pre><code>auto teb = NtCurrentTeb();  
auto peb = teb-&gt;ProcessEnvironmentBlock;  
auto pebldr = peb-&gt;Ldr;  
auto ioml = *(DWORD*)pebldr-&gt;InInitializationOrderModuleList.Flink;  
auto kernelAddr = (HMODULE)*(DWORD*)(ioml + 8);  
auto loadlibraryPtr = GetProcAddress(kernelAddr, "LoadLibraryExW");  
// load your library here ...
</code></pre>

<p>方法3：直接搜索（方法来自<a href="http://forum.xda-developers.com/showthread.php?t=1944675">XDA</a>）  </p>

<pre><code>HMODULE SearchKernelAddress()  
{
    char *Tmp = (char*)GetTickCount64;
    Tmp = (char*)((~0xFFF)&amp;(DWORD_PTR)Tmp);

    while (Tmp)
    {
        __try
        {
            if (Tmp[0] == 'M' &amp;&amp; Tmp[1] == 'Z')
                break;
        }
        __except (EXCEPTION_EXECUTE_HANDLER)
        {
        }
        Tmp -= 0x1000;
    }

    if (Tmp == 0)
        return nullptr;
    else 
        return (HMODULE)Tmp;
}
</code></pre>

<p>上述方法不仅适用于UWP，还适用于Windows Phone 8/8.1应用，以及Windows 8/8.1的应用。 <br>
个人最推荐的方式是VirtualQuery，它使用的全是公开的API和结构，而使用TEB获取的方式里使用了私有结构，因此将来的系统更新可能会导致兼容性出现问题。 <br>
项目示例与源码： <a href="https://github.com/hjc4869/WACKBypass">https://github.com/hjc4869/WACKBypass</a></p>]]></content:encoded></item><item><title><![CDATA[UWP使用命名管道与桌面程序通信 (C#)]]></title><description><![CDATA[<p>关于UWP的历史，其起源是Microsoft在Windows 8中引入的Metro apps。（后来又被称作Modern apps, Windows apps, Universal Windows Apps等）无论是从目的还是从效果上看，这一类应用模型都与iOS/Android比较相似，是为了更有利于移动平台生态的发展设计的。 <br>
然而UWP目前面向的最大的用户群体是Windows桌面用户，只用UWP实现一个程序就会出现很多feature无法实现的问题，因此这种情况下，让用户安装并运行一个普通权限的后台进程，使用UWP做UI与之通信就成为了一种选择，毕竟UWP的C#/XAML性能比WPF好得多，分发/支付上也比桌面程序方便不少，虽说原则上微软并不允许商店应用与桌面应用互相通信。  </p>

<p>除了命名管道(named pipe)，进程间通信的方式还有很多，比如Socket，还有微软给Runtime Broker用的COM RPC(rpcrt4)等。对于前者，如果是AppContainer默认的防火墙配置，与本机其它程序不允许通过127.0.0.1通信，而调节这个防火墙配置需要管理员权限<del>（不出450买个EV证书还想骗用户确认UAC？）</del>。而对于COM RPC，由于其API比较复杂，并且rpc.h里大部分的API是desktop only，因此调用起来也不方便。</p>]]></description><link>https://hjc.im/uwp-named-pipe/</link><guid isPermaLink="false">8ac75324-9a4d-4485-9768-f584db1fffd2</guid><category><![CDATA[Windows 10]]></category><category><![CDATA[UWP]]></category><dc:creator><![CDATA[David Huang]]></dc:creator><pubDate>Tue, 16 Feb 2016 11:30:44 GMT</pubDate><content:encoded><![CDATA[<p>关于UWP的历史，其起源是Microsoft在Windows 8中引入的Metro apps。（后来又被称作Modern apps, Windows apps, Universal Windows Apps等）无论是从目的还是从效果上看，这一类应用模型都与iOS/Android比较相似，是为了更有利于移动平台生态的发展设计的。 <br>
然而UWP目前面向的最大的用户群体是Windows桌面用户，只用UWP实现一个程序就会出现很多feature无法实现的问题，因此这种情况下，让用户安装并运行一个普通权限的后台进程，使用UWP做UI与之通信就成为了一种选择，毕竟UWP的C#/XAML性能比WPF好得多，分发/支付上也比桌面程序方便不少，虽说原则上微软并不允许商店应用与桌面应用互相通信。  </p>

<p>除了命名管道(named pipe)，进程间通信的方式还有很多，比如Socket，还有微软给Runtime Broker用的COM RPC(rpcrt4)等。对于前者，如果是AppContainer默认的防火墙配置，与本机其它程序不允许通过127.0.0.1通信，而调节这个防火墙配置需要管理员权限<del>（不出450买个EV证书还想骗用户确认UAC？）</del>。而对于COM RPC，由于其API比较复杂，并且rpc.h里大部分的API是desktop only，因此调用起来也不方便。（在Store apps里有办法使用desktop only API，但是如果数目多了会比较麻烦）</p>

<p>相比之下，命名管道只需要在app中获取一个desktop only的API（即CreateFile）就能使用了，并且用户运行桌面程序时不需要管理员权限，大概是最简单的方式。  </p>

<p>在AppContainer中不能创建命名管道，只能连接到有权限访问的命名管道。因此我们使用桌面程序创建。  </p>

<p>桌面程序：</p>

<pre><code>using (var pipe = new NamedPipeServerStream("mypipe", PipeDirection.InOut, -1, PipeTransmissionMode.Byte, PipeOptions.None, 0, 0, null, HandleInheritability.None, PipeAccessRights.ChangePermissions))  
{
    PipeSecurity ps = pipe.GetAccessControl();
    PipeAccessRule clientRule = new PipeAccessRule(
        new SecurityIdentifier("S-1-15-2-1"), // All application packages
        PipeAccessRights.ReadWrite,
        AccessControlType.Allow);
    PipeAccessRule ownerRule = new PipeAccessRule(
        WindowsIdentity.GetCurrent().Owner,
        PipeAccessRights.FullControl,
        AccessControlType.Allow);
    ps.AddAccessRule(clientRule);
    ps.AddAccessRule(ownerRule);
    pipe.SetAccessControl(ps);
    pipe.WaitForConnection();
    using (var sr = new StreamReader(pipe, Encoding.UTF8))
    {
        while (true)
        {
            string message = sr.ReadLine();
            //在此处处理App写入命名管道的内容
            pipe.WaitForPipeDrain();
        }
    }
}
</code></pre>

<p>其中"mypipe"是命名管道的名称，SID <code>S-1-15-2-1</code> 是All application packages的SID，如果只想让一个AppContainer访问，可以通过获取进程信息来获取它的SID。  </p>

<p>App程序：  </p>

<pre><code>var handle = CreateFileW(@"\\.\pipe\mypipe", FileAccess.ReadWrite, FileShare.None, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);  
if (handle.IsInvalid)  
{
    var err = Marshal.GetLastWin32Error();
    //handle error
}
sw = new StreamWriter(new FileStream(handle, FileAccess.Write), Encoding.UTF8);  
sw.WriteLine("Hello from AppContainer");  
sw.Flush();  
</code></pre>

<p>其中"mypipe"是命名管道的名称。 <br>
由于UWP的.NET标准库并没有<code>System.IO.Pipes</code>，必须手动调用<code>CreateFile</code>。UWP允许使用的<code>CreateFile2</code>是不能打开命名管道的，即使有权限也会返回<code>ERROR_NOT_SUPPORTED_IN_APPCONTAINER</code>。<code>CreateFileW</code>并不能直接使用<code>DllImport("kernel32")</code>，否则无法通过WACK。  </p>]]></content:encoded></item><item><title><![CDATA[Chakra实战：UWP与js交互(C#)]]></title><description><![CDATA[<p>几个月前在翻MSDN时发现Microsoft已经允许在Windows Store Apps（即UWP）里使用Chakra的API了。这意味着大家终于可以光明正大地在app中调用Javascript。//另外UWP允许JIT了所以你自己移植个V8上去其实也行 <br>
在8.x时代，Chakra是被标记为Desktop only的API，想要在Store apps里使用js，要么整个App使用HTML/js编写，要么使用WebView调用。前者显然不符合主要使用C#/XAML编写UI的前提，后者<del>麻烦的要死，</del>不好用。</p>

<p>UWP写起来真舒服<del>（棒读）</del></p>

<p>使用Chakra之前需要较为深入地了解Chakra API，COM和JavaScript。<del>其实不了解直接照抄代码拿着用也没什么不好，就是出了错之后不好排除。</del></p>

<h2 id="cchakraapi">使用C#调用Chakra API</h2>

<p>UWP是可以直接使用chakra.dll大部分函数的，除去<code>JsStartProfiling</code>  <code>JsStopProfiling</code> <code>JsEnumerateHeap</code> 和 <code>JsIsEnumeratingHeap</code> 四个。 <br>
然而Microsoft并没有在SDK里提供C#/WinRT API，所以需要用P/Invoke进行基本的封装。这里以<a href="https://github.com/Microsoft/Chakra-Samples/blob/master/Chakra%20Samples/JSRT%20Universal%20Windows%20Apps%20Hosting%20Samples/C%23/JsRT_UWP_Sample/Hosting/Native.cs">Microsoft官方示例</a>为准。 <br>
将上述的Native.cs以及该目录下所有文件都加入项目。</p>]]></description><link>https://hjc.im/uwp-chakra-js-1/</link><guid isPermaLink="false">831ad4af-1c5c-4aac-b016-d1231ab0d106</guid><category><![CDATA[Windows]]></category><category><![CDATA[Windows 10]]></category><category><![CDATA[Javascript]]></category><category><![CDATA[Chakra]]></category><category><![CDATA[UWP]]></category><dc:creator><![CDATA[David Huang]]></dc:creator><pubDate>Fri, 05 Feb 2016 12:40:20 GMT</pubDate><content:encoded><![CDATA[<p>几个月前在翻MSDN时发现Microsoft已经允许在Windows Store Apps（即UWP）里使用Chakra的API了。这意味着大家终于可以光明正大地在app中调用Javascript。//另外UWP允许JIT了所以你自己移植个V8上去其实也行 <br>
在8.x时代，Chakra是被标记为Desktop only的API，想要在Store apps里使用js，要么整个App使用HTML/js编写，要么使用WebView调用。前者显然不符合主要使用C#/XAML编写UI的前提，后者<del>麻烦的要死，</del>不好用。</p>

<p>UWP写起来真舒服<del>（棒读）</del></p>

<p>使用Chakra之前需要较为深入地了解Chakra API，COM和JavaScript。<del>其实不了解直接照抄代码拿着用也没什么不好，就是出了错之后不好排除。</del></p>

<h2 id="cchakraapi">使用C#调用Chakra API</h2>

<p>UWP是可以直接使用chakra.dll大部分函数的，除去<code>JsStartProfiling</code>  <code>JsStopProfiling</code> <code>JsEnumerateHeap</code> 和 <code>JsIsEnumeratingHeap</code> 四个。 <br>
然而Microsoft并没有在SDK里提供C#/WinRT API，所以需要用P/Invoke进行基本的封装。这里以<a href="https://github.com/Microsoft/Chakra-Samples/blob/master/Chakra%20Samples/JSRT%20Universal%20Windows%20Apps%20Hosting%20Samples/C%23/JsRT_UWP_Sample/Hosting/Native.cs">Microsoft官方示例</a>为准。 <br>
将上述的Native.cs以及该目录下所有文件都加入项目。</p>

<h2 id="cjavascript">使用C#调用Javascript</h2>

<p>主要步骤： <br>
1.使用<code>JsCreateRuntime</code>创建一个Javascript运行时(runtime)  </p>

<pre><code>JavaScriptRuntime runtime;  
Native.ThrowIfError(Native.JsCreateRuntime(JavaScriptRuntimeAttributes.None, null, out runtime));  
</code></pre>

<p>2.使用<code>JsCreateContext</code>在这个运行时内创建一个上下文(context)  </p>

<pre><code>JavaScriptContext context;  
Native.ThrowIfError(Native.JsCreateContext(runtime, out context));  
</code></pre>

<p>3.使用<code>JsSetCurrentContext</code>将该上下文设置到当前线程  </p>

<pre><code>Native.ThrowIfError(Native.JsSetCurrentContext(context));  
</code></pre>

<p>4.（可选）使用<code>JsStartDebugging</code>开启调试  </p>

<pre><code>Native.ThrowIfError(Native.JsStartDebugging());  
</code></pre>

<p>5.使用<code>JsRunScript</code>运行Javascript  </p>

<pre><code>JavaScriptValue result;  
JavaScriptSourceContext currentSourceContext = JavaScriptSourceContext.FromIntPtr(IntPtr.Zero);  
if (Native.JsRunScript(script, currentSourceContext, ""/*如果需要调试，需要在此处指定源码绝对路径*/, out result) != JavaScriptErrorCode.NoError)  
{
    JavaScriptException exception;
    Native.ThrowIfError(Native.JsGetAndClearException(out exception));
    //在此处理异常
}
JavaScriptValue stringResult;  
UIntPtr stringLength;  
Native.ThrowIfError(Native.JsConvertValueToString(result, out stringResult));  
Native.ThrowIfError(Native.JsStringToPointer(stringResult, out returnValue, out stringLength));  
var ret = Marshal.PtrToStringUni(returnValue);//处理返回值  
</code></pre>

<p>6.其它用途（如<code>JsCallFunction</code>等） <br>
需要注意的是，如果当前使用的上下文已经被设定到一个线程（第三步），那么该上下文仅能用于这个线程。当这个线程不再需要这个上下文时，需要将其设置为NULL（第七步）。 <br>
7.将当前线程的jsrt上下文设置为NULL  </p>

<pre><code>Native.ThrowIfError(Native.JsSetCurrentContext(new JavaScriptContext()));  
</code></pre>

<p>8.（可选）在其它线程使用这个jsrt上下文（从第三步开始重复） <br>
9.使用完成后销毁这个runtime  </p>

<pre><code>Native.ThrowIfError(Native.JsDisposeRuntime(runtime));  
</code></pre>

<h2 id="">类型转换</h2>

<p>JavaScript的类型与C#是不同的，而在Chakra API中使用JsValueRef（即C#中封装的JavaScriptValue）来表示一个值。 <br>
常用的类型主要有Undefined, Null, Number, String, Boolean, Object, Function, Array等。C#与Javascript交互时，需要将JavaScriptValue与 .NET 的类型互相转换。 <br>
JavaScriptValue本身封装了集中简单类型的转换，例如Number与System.Double：<code>JavaScriptValue.FromDouble()</code> 与 <code>JavaScriptValue.ToDouble()</code> <br>
判断JavaScriptValue的类型使用<code>JsGetValueType</code>函数  </p>

<pre><code>JavaScriptValueType type;  
Native.ThrowIfError(Native.JsGetValueType(val, out type));  
switch (type)  
{
    //对特定类型进行处理
}
</code></pre>

<p>WinRT类型的转换 <br>
任何<strong>WinRT类型</strong>（写C#时可粗略理解为放在winmd里的类型）均继承自IInspectable，可以直接将其对象使用<code>JsInspectableToObject</code>转换成JavaScriptValue使用。 <br>
同样如果<strong>确定一个JavaScriptValue代表的对象继承自IInspectable</strong>，也可以使用<code>JsObjectToInspectable</code>将其转换为System.Object。</p>

<p>数组类型的转换 <br>
JavaScript的数组并没有实现IInspectable接口，因此它不能直接使用COM交互，需要手动读取其值并且进行转换。 <br>
举例：转换为.NET的List</p>

<pre><code>List&lt;T&gt; JsArrayToList&lt;T&gt;(JavaScriptValue arrayval)
{
    var _retList = new List&lt;T&gt;();

    JavaScriptValueType type;
    Native.ThrowIfError(Native.JsGetValueType(arrayval, out type));
    if (type != JavaScriptValueType.Array)
        return null;

    JavaScriptValue lengthvalue;
    Native.ThrowIfError(Native.JsGetProperty(
            arrayval,
            JavaScriptPropertyId.FromString("length"),
            out lengthvalue));

    int length;
    Native.ThrowIfError(Native.JsNumberToInt(lengthvalue, out length));

    for (int i = 0; i &lt; length; i++)
    {
        JavaScriptValue elem;
        Native.ThrowIfError(Native.JsGetIndexedProperty(
            arrayval,
            JavaScriptValue.FromInt32(i),
            out elem));

        JavaScriptValueType elemtype;
        Native.ThrowIfError(Native.JsGetValueType(elem, out elemtype));

        if (elemtype == JavaScriptValueType.Object)
        {
            object insp;
            var err = Native.JsObjectToInspectable(elem, out insp);
            if (err == JavaScriptErrorCode.NoError &amp;&amp;
                    insp.GetType() == typeof(T))
                _retList.Add((T)insp);
        }

    }
    return _retList;
}
</code></pre>

<h2 id="">函数调用</h2>

<p>首先调用<code>JsGetGlobalObject</code>获取当前上下文的全局对象，使用<code>JsGetProperty</code>获得函数的对象，再使用<code>JsCallFunction</code>调用函数。函数的参数需要全部转换成JavaScriptValue，同时将返回值从JavaScriptValue转换成所需要的类型。</p>

<pre><code>JavaScriptValue CallFunction(string functionName, params JavaScriptValue[] parameters)
{
    JavaScriptValue _globalObject;
    Native.ThrowIfError(Native.JsGetGlobalObject(out _globalObject));
    var functionId = JavaScriptPropertyId.FromString(functionName);
    var function = _globalObject.GetProperty(functionId);
    return function.CallFunction(parameters);
}
</code></pre>

<h2 id="javascriptwinrt">使用Javascript调用WinRT</h2>

<p>在UWP中使用Javascript而不是Python等别的脚本语言做扩展，原因之一就是JavaScript调用Windows Runtime Component（WinRT组件）非常方便。无论是对于系统API还是自己创建的WinRT组件，都可以用<code>JsProjectWinRTNamespace</code>轻松地映射到Javascript中。映射后的使用方式，与直接使用HTML/js编写UWP时调用WinRT API相同。需要注意的是，有WebHostHiddenAttribute的WinRT类仍然无法被js使用。  </p>

<p>如果想将WinRT对象映射为js的全局对象，也可以先使用<code>JsInspectableToObject</code>将其转换为JavaScriptValue，使用<code>JsGetGlobalObject</code>获得全局对象，<code>JsSetProperty</code>将WinRT类型的对象设置为全局对象的属性。</p>

<h2 id="">调试</h2>

<p>集成VS调试方便是Chakra的另一大优点。将VS的C#项目内的调试器类型设置为"Script"（如图），并在代码中调用<code>JsStartDebugging</code>，并且指定js源代码文件位置（见上文），在VS中打开相应js文件，附加调试器，即可开始调试脚本。 <br>
<img src="https://hjc.im/content/images/2016/02/snip_20160205203654.png" alt="" title="">  </p>

<p><del>2016年的第一篇文章，如此长篇大论，药丸</del></p>]]></content:encoded></item><item><title><![CDATA[绕过Windows 10默认UAC配置]]></title><description><![CDATA[<p>项目地址： <a href="https://github.com/hjc4869/UacBypass">https://github.com/hjc4869/UacBypass</a> <br>
自从Vista开始，这类绕过UAC的方法层出不穷，于是一周前做了一个win10上能用的<del>，所以说UAC不开最高级根本没有什么卵用，防君子不防小人</del>。</p>

<h4 id="">适用条件</h4>

<ul>
<li>账号拥有管理员权限，开启UAC，并且使用普通权限运行本程序  </li>
<li>账号的UAC使用默认配置（如图）
<img src="https://hjc.im/content/images/2015/11/uac-default.PNG" alt="" title="">  </li>
<li>系统版本Windows 10.10240 x86或x64</li>
</ul>

<h4 id="">使用方法</h4>

<ul>
<li>下载（或自己使用VS编译）对应架构(x86或x64不能通用)的<a href="https://github.com/hjc4869/UacBypass/releases">Release</a>，将UacBypass.dll UacBypassTest.exe与ntwdblib.dll放在同一目录下</li>
<li>执行UacBypassTest.exe，如果能直接弹出带管理员权限的cmd窗口，则为成功。</li>
</ul>

<h4 id="bug">不良影响和bug</h4>

<ul>
<li>虚假的ntwdblib.dll会被复制到C:\Windows\System32，此dll被加载时，会<a href="https://github.com/hjc4869/UacBypass/blob/master/ntwdblib/dllmain.cpp#L15">使用CreateProcess创建cmd窗口</a>，并<a href="https://github.com/hjc4869/UacBypass/blob/master/ntwdblib/dllmain.cpp#L16">直接退出进程</a>。<del>使用到此dll的程序都会有奇怪的表现。</del> <br>
解决方法：实验完成后直接删除此dll</li></ul>]]></description><link>https://hjc.im/bypass-win10-uac/</link><guid isPermaLink="false">0d695d61-ecf5-4b7b-8361-e0d4e6723443</guid><category><![CDATA[Win32]]></category><category><![CDATA[安全]]></category><category><![CDATA[Windows 10]]></category><dc:creator><![CDATA[David Huang]]></dc:creator><pubDate>Sat, 07 Nov 2015 15:29:44 GMT</pubDate><content:encoded><![CDATA[<p>项目地址： <a href="https://github.com/hjc4869/UacBypass">https://github.com/hjc4869/UacBypass</a> <br>
自从Vista开始，这类绕过UAC的方法层出不穷，于是一周前做了一个win10上能用的<del>，所以说UAC不开最高级根本没有什么卵用，防君子不防小人</del>。</p>

<h4 id="">适用条件</h4>

<ul>
<li>账号拥有管理员权限，开启UAC，并且使用普通权限运行本程序  </li>
<li>账号的UAC使用默认配置（如图）
<img src="https://hjc.im/content/images/2015/11/uac-default.PNG" alt="" title="">  </li>
<li>系统版本Windows 10.10240 x86或x64</li>
</ul>

<h4 id="">使用方法</h4>

<ul>
<li>下载（或自己使用VS编译）对应架构(x86或x64不能通用)的<a href="https://github.com/hjc4869/UacBypass/releases">Release</a>，将UacBypass.dll UacBypassTest.exe与ntwdblib.dll放在同一目录下</li>
<li>执行UacBypassTest.exe，如果能直接弹出带管理员权限的cmd窗口，则为成功。</li>
</ul>

<h4 id="bug">不良影响和bug</h4>

<ul>
<li>虚假的ntwdblib.dll会被复制到C:\Windows\System32，此dll被加载时，会<a href="https://github.com/hjc4869/UacBypass/blob/master/ntwdblib/dllmain.cpp#L15">使用CreateProcess创建cmd窗口</a>，并<a href="https://github.com/hjc4869/UacBypass/blob/master/ntwdblib/dllmain.cpp#L16">直接退出进程</a>。<del>使用到此dll的程序都会有奇怪的表现。</del> <br>
解决方法：实验完成后直接删除此dll</li>
<li>UacBypass.dll被explorer进程加载后不会被正确卸载，导致UacBypass.dll无法删除，并且demo只能成功运行一次。 <br>
解决方法：重新启动explorer。</li>
</ul>

<p><strong>WARNING</strong> <br>
这只是个非常粗糙的demo，请不要在生产环境下做实验。</p>]]></content:encoded></item><item><title><![CDATA[使用MLang API检测窄字符串编码]]></title><description><![CDATA[<p>做播放器时一般人多多少少都会遇到点字符乱码的问题，这些乱码基本都是出在mp3等老旧的格式上。这大概都是因为这些音频文件使用不遵循编码标准的软件制作而成。解决方法似乎也不怎么好（至少我的乱码mp3没有办法解决），不过MLang API作为Windows系统自带的API（Universal App可用）是非常方便好用的窄字符串编码检测方式，特定情况下非常好用。  </p>

<p>使用方法非常简单：  </p>

<pre><code>#include &lt;MLang.h&gt;

HRESULT DetectCodepage(char* text, int* codePage) {
    IMultiLanguage2* iml;
    HRESULT hr = CoCreateInstance(CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER, IID_IMultiLanguage2, (LPVOID*)&amp;iml);
    if (SUCCEEDED(hr)) {
        DetectEncodingInfo info;
        int size = strlen(text) + 1, cnt</code></pre>]]></description><link>https://hjc.im/shi-yong-mlang-apijian-ce-zhai-zi-fu-chuan-bian-ma/</link><guid isPermaLink="false">7d1b8599-6150-446e-bf1f-93166e880f54</guid><category><![CDATA[Windows Runtime]]></category><category><![CDATA[WinRT]]></category><category><![CDATA[Windows]]></category><category><![CDATA[编码]]></category><dc:creator><![CDATA[David Huang]]></dc:creator><pubDate>Sat, 18 Jul 2015 05:23:23 GMT</pubDate><content:encoded><![CDATA[<p>做播放器时一般人多多少少都会遇到点字符乱码的问题，这些乱码基本都是出在mp3等老旧的格式上。这大概都是因为这些音频文件使用不遵循编码标准的软件制作而成。解决方法似乎也不怎么好（至少我的乱码mp3没有办法解决），不过MLang API作为Windows系统自带的API（Universal App可用）是非常方便好用的窄字符串编码检测方式，特定情况下非常好用。  </p>

<p>使用方法非常简单：  </p>

<pre><code>#include &lt;MLang.h&gt;

HRESULT DetectCodepage(char* text, int* codePage) {
    IMultiLanguage2* iml;
    HRESULT hr = CoCreateInstance(CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER, IID_IMultiLanguage2, (LPVOID*)&amp;iml);
    if (SUCCEEDED(hr)) {
        DetectEncodingInfo info;
        int size = strlen(text) + 1, cnt = 1;
        hr = iml-&gt;DetectInputCodepage(0, 0, text, &amp;size, &amp;info, &amp;cnt);
        if (SUCCEEDED(hr)) {
            *codePage = info.nCodePage;
            iml-&gt;Release();
            return S_OK;
        }
    }
    return hr;
}
</code></pre>

<p>获得Code Page之后就可以用MultiByteToWideChar之类的API将窄字符转换为宽字符了。 <br>
其他相关内容可以参考MSDN： <a href="https://msdn.microsoft.com/en-us/library/aa741220(v=vs.85).aspx">https://msdn.microsoft.com/en-us/library/aa741220(v=vs.85).aspx</a></p>]]></content:encoded></item><item><title><![CDATA[WinRT开发：使用API将异步读写流转换为同步]]></title><description><![CDATA[<p>几个月之前我写了一篇文章讲怎么把C++/CX的异步操作同步执行（<a href="https://hjc.im/winrt-ckai-fa-zhi-yi-bu-zhuan-tong-bu/">https://hjc.im/winrt-ckai-fa-zhi-yi-bu-zhuan-tong-bu/</a> ），当初做这个其实最主要就是为了给ffmpeg读写文件。</p>

<p>不过如果只是为了读写文件，这样是个比较笨的办法，更好的办法是使用API函数<code>CreateStreamOverRandomAccessStream</code>直接将<code>IRandomAccessStream</code>转换成COM的<code>IStream</code>。  </p>

<pre><code>IStream* fileStreamData;
HRESULT hr = CreateStreamOverRandomAccessStream(
    reinterpret_cast&lt;IUnknown*&gt;(stream), IID_PPV_ARGS(&amp;fileStreamData));
if (!SUCCEEDED(hr)) {
...处理异常
}
</code></pre>

<p>得到一个<code>IStream</code>对象之后就可以使用它来读写流了。  </p>

<p>例如读取：  </p>

<pre><code>uint8_t* buf = (uint8_t*)malloc(sizeof(uint8_t)*1024);
int</code></pre>]]></description><link>https://hjc.im/winrtkai-fa-shi-yong-apijiang-yi-bu-du-xie-liu-zhuan-huan-wei-tong-bu/</link><guid isPermaLink="false">a26e21b2-f325-4ef6-b91a-b0caa3195339</guid><category><![CDATA[Windows Runtime]]></category><category><![CDATA[WinRT]]></category><category><![CDATA[Windows Phone]]></category><category><![CDATA[Windows]]></category><category><![CDATA[COM]]></category><category><![CDATA[Windows 10]]></category><dc:creator><![CDATA[David Huang]]></dc:creator><pubDate>Fri, 17 Jul 2015 05:55:25 GMT</pubDate><content:encoded><![CDATA[<p>几个月之前我写了一篇文章讲怎么把C++/CX的异步操作同步执行（<a href="https://hjc.im/winrt-ckai-fa-zhi-yi-bu-zhuan-tong-bu/">https://hjc.im/winrt-ckai-fa-zhi-yi-bu-zhuan-tong-bu/</a> ），当初做这个其实最主要就是为了给ffmpeg读写文件。</p>

<p>不过如果只是为了读写文件，这样是个比较笨的办法，更好的办法是使用API函数<code>CreateStreamOverRandomAccessStream</code>直接将<code>IRandomAccessStream</code>转换成COM的<code>IStream</code>。  </p>

<pre><code>IStream* fileStreamData;
HRESULT hr = CreateStreamOverRandomAccessStream(
    reinterpret_cast&lt;IUnknown*&gt;(stream), IID_PPV_ARGS(&amp;fileStreamData));
if (!SUCCEEDED(hr)) {
...处理异常
}
</code></pre>

<p>得到一个<code>IStream</code>对象之后就可以使用它来读写流了。  </p>

<p>例如读取：  </p>

<pre><code>uint8_t* buf = (uint8_t*)malloc(sizeof(uint8_t)*1024);
int bufSize = 1024;
int bytesRead = 0;
HRESULT hr = pStream-&gt;Read(buf, bufSize, &amp;bytesRead);
if (FAILED(hr))
{
    ...
}
if (!bytesRead)
{
    ...
}
...
</code></pre>

<p>注意<code>IStream</code>用完要调用<code>Release()</code>，或者用<code>ComPtr&lt;IStream&gt;</code>来控制，否则会导致内存和外部资源泄漏。</p>]]></content:encoded></item><item><title><![CDATA[FreeBSD的crontab的PATH问题]]></title><description><![CDATA[<p>一段时间前我<del>作死</del>将个人博客迁移到了FreeBSD虚拟机中，具体的和原来在Linux中基本相同，包括自动备份——使用crontab定时执行一个备份脚本，每天凌晨调用tar打包文件，mysqldump备份数据库，curl上传文件并删除这些临时文件。 <br>
跑了大半个月，还是相当稳定的，唯一的问题就是自动备份似乎不工作。 <br>
<img src="https://hjc.im/content/images/2015/07/Capture.PNG" alt="" title=""> <br>
<del>我没想到这么简单的6行命令也会出问题，真的</del> <br>
后来才发现<code>/etc/crontab</code>中有一行<code>PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin</code>，然而</p>

<pre><code>root@jc-blog:~ # which curl
/usr/local/bin/curl
root@jc-blog:~ # which mysqldump
/usr/local/bin/mysqldump
</code></pre>

<p>…… <br>
改成<code>PATH=/etc:/bin:/sbin:/usr/bin:</code></p>]]></description><link>https://hjc.im/freebsdde-crontabde-pathwen-ti/</link><guid isPermaLink="false">7634f9ae-5479-4928-8069-72a2b1ef406f</guid><category><![CDATA[Ghost]]></category><category><![CDATA[FreeBSD]]></category><dc:creator><![CDATA[David Huang]]></dc:creator><pubDate>Wed, 15 Jul 2015 16:41:36 GMT</pubDate><content:encoded><![CDATA[<p>一段时间前我<del>作死</del>将个人博客迁移到了FreeBSD虚拟机中，具体的和原来在Linux中基本相同，包括自动备份——使用crontab定时执行一个备份脚本，每天凌晨调用tar打包文件，mysqldump备份数据库，curl上传文件并删除这些临时文件。 <br>
跑了大半个月，还是相当稳定的，唯一的问题就是自动备份似乎不工作。 <br>
<img src="https://hjc.im/content/images/2015/07/Capture.PNG" alt="" title=""> <br>
<del>我没想到这么简单的6行命令也会出问题，真的</del> <br>
后来才发现<code>/etc/crontab</code>中有一行<code>PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin</code>，然而</p>

<pre><code>root@jc-blog:~ # which curl
/usr/local/bin/curl
root@jc-blog:~ # which mysqldump
/usr/local/bin/mysqldump
</code></pre>

<p>…… <br>
改成<code>PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin</code>就好了……</p>]]></content:encoded></item><item><title><![CDATA[CS:GO服务器安装metamod和sourcemod实现管理菜单(Linux)]]></title><description><![CDATA[<p>关于如何在Linux上架设CS:GO服务器，可以参考 <a href="https://hjc.im/linux-vps-csgo-server/">我之前的文章</a> <br>
CS:GO 自己架服务器玩了这么久了，管理时总得打开命令行rcon还是很麻烦的。不过好在可以装服务器插件，用菜单来做一些基本的操作。 <br>
首先，进入服务器程序的安装目录（/home/[用户名]/serverfiles/csgo/），分别wget下载<a href="http://www.sourcemm.net/snapshots">metamod</a>和<a href="http://www.sourcemod.net/snapshots.php">sourcemod</a>的最新版，并依次解压。 <br>
解压后编辑addons目录内的metamod.vdf：</p>

<pre><code>"Plugin"
{
        "file"  "addons/metamod/bin/server"
}
</code></pre>

<p>接着，配置sourcemod，将自己的steam id设置为管理员 <br>
编辑<code>addons/sourcemod/configs/admins.cfg</code>，在文件尾部新增以下内容  </p>

<pre><code>Admins
{
        "admin1"
        {
                "auth"                  "steam"
                "identity"              "[你的steam identity]"
                "flags"                 "z"</code></pre>]]></description><link>https://hjc.im/csgo-metamod-sourcemod/</link><guid isPermaLink="false">0617e105-8e07-4680-afce-9bcbfe27d28c</guid><category><![CDATA[CS:GO]]></category><category><![CDATA[Linux]]></category><dc:creator><![CDATA[David Huang]]></dc:creator><pubDate>Wed, 27 May 2015 15:54:17 GMT</pubDate><content:encoded><![CDATA[<p>关于如何在Linux上架设CS:GO服务器，可以参考 <a href="https://hjc.im/linux-vps-csgo-server/">我之前的文章</a> <br>
CS:GO 自己架服务器玩了这么久了，管理时总得打开命令行rcon还是很麻烦的。不过好在可以装服务器插件，用菜单来做一些基本的操作。 <br>
首先，进入服务器程序的安装目录（/home/[用户名]/serverfiles/csgo/），分别wget下载<a href="http://www.sourcemm.net/snapshots">metamod</a>和<a href="http://www.sourcemod.net/snapshots.php">sourcemod</a>的最新版，并依次解压。 <br>
解压后编辑addons目录内的metamod.vdf：</p>

<pre><code>"Plugin"
{
        "file"  "addons/metamod/bin/server"
}
</code></pre>

<p>接着，配置sourcemod，将自己的steam id设置为管理员 <br>
编辑<code>addons/sourcemod/configs/admins.cfg</code>，在文件尾部新增以下内容  </p>

<pre><code>Admins
{
        "admin1"
        {
                "auth"                  "steam"
                "identity"              "[你的steam identity]"
                "flags"                 "z"
        }
}
</code></pre>

<p>其中需要替换“[你的steam identity]”，类似STEAM_0:1:xxxxxxxx ，可以在 <a href="http://steamidfinder.com/">http://steamidfinder.com/</a> 输入自己的资料页面获得。  </p>

<p>配置完毕后返回主界面，启动服务器，<del>如果从游戏里进入服务器时没有出错</del>，就可以按Y键输入"!admin"使用管理菜单了！  </p>

<p>注意：如果服务器报segmentation fault，请检查metamod.vdf，配置文件和metamod/sourcemod版本。</p>]]></content:encoded></item><item><title><![CDATA[如何让UAC窗口正常地出现在Metro app上方]]></title><description><![CDATA[<p>首先澄清一下，这篇文章并没有讲如何用Metro App弹UAC，而讲的是如果用户在一个全屏的Metro app里，那么如何让桌面Program弹出的UAC正常地被用户看到。当然，在Windows 10预览版已经发布的时候谈这个话题可能已经有点晚了，因为Windows 10当前版本根本不存在这个问题。 <br>
熟悉Windows 8.1的用户肯定知道，当用户打开了一个全屏的Metro App时，大部分桌面程序如果弹出UAC，是不会直接出现在屏幕上的，而是会出现在任务栏上闪动（如下图）。 <br>
<img src="https://hjc.im/content/images/2015/02/Capture-1.PNG" alt="" title=""> <br>
其实这个比较影响用户体验，因为用户在全屏App中根本看不到任务栏，而容易错过这样的提示。（错过了不一定是坏事，因为弹UAC窗口真的是打扰用户的行为） <br>
查找了一下相关资料，巨硬从某个Windows版本之后，只有当创建UAC进程的父进程的窗口句柄在最前端时才会直接显示UAC安全桌面(Secure desktop)，其它的情况下将会在任务栏上显示闪动的盾牌图标。 <br>
于是这个问题就比较好解决了，以C#的Process为例，创建进程时可以设置ErrorDialogParentHandle属性指定父窗口句柄。于是这样就将目标转化为了取得当前正在运行的Metro App的hWnd。</p>

<p>我们主要需要FindWindow或FindWindowEx函数。</p>

<pre><code>[DllImport("User32.dll", EntryPoint = "FindWindow")]
private static extern IntPtr FindWindow(string lpClassName,</code></pre>]]></description><link>https://hjc.im/ru-he-rang-uacchuang-kou-zheng-chang-di-chu-xian-zai-metro-appshang-fang/</link><guid isPermaLink="false">0927d444-6c5e-46a9-aaf0-682a94f36714</guid><category><![CDATA[Windows Runtime]]></category><category><![CDATA[Windows]]></category><category><![CDATA[Win32]]></category><category><![CDATA[C#]]></category><dc:creator><![CDATA[David Huang]]></dc:creator><pubDate>Sun, 15 Feb 2015 01:32:47 GMT</pubDate><content:encoded><![CDATA[<p>首先澄清一下，这篇文章并没有讲如何用Metro App弹UAC，而讲的是如果用户在一个全屏的Metro app里，那么如何让桌面Program弹出的UAC正常地被用户看到。当然，在Windows 10预览版已经发布的时候谈这个话题可能已经有点晚了，因为Windows 10当前版本根本不存在这个问题。 <br>
熟悉Windows 8.1的用户肯定知道，当用户打开了一个全屏的Metro App时，大部分桌面程序如果弹出UAC，是不会直接出现在屏幕上的，而是会出现在任务栏上闪动（如下图）。 <br>
<img src="https://hjc.im/content/images/2015/02/Capture-1.PNG" alt="" title=""> <br>
其实这个比较影响用户体验，因为用户在全屏App中根本看不到任务栏，而容易错过这样的提示。（错过了不一定是坏事，因为弹UAC窗口真的是打扰用户的行为） <br>
查找了一下相关资料，巨硬从某个Windows版本之后，只有当创建UAC进程的父进程的窗口句柄在最前端时才会直接显示UAC安全桌面(Secure desktop)，其它的情况下将会在任务栏上显示闪动的盾牌图标。 <br>
于是这个问题就比较好解决了，以C#的Process为例，创建进程时可以设置ErrorDialogParentHandle属性指定父窗口句柄。于是这样就将目标转化为了取得当前正在运行的Metro App的hWnd。</p>

<p>我们主要需要FindWindow或FindWindowEx函数。</p>

<pre><code>[DllImport("User32.dll", EntryPoint = "FindWindow")]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("User32.dll", EntryPoint = "FindWindowEx")]
private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpClassName, string lpWindowName);
</code></pre>

<p>以下为调用示例。假定<code>[文件名].exe</code>在manifest里已经写明权限highestAvailable，那么使用Process类启动它时就会弹出UAC窗口。而FindWindowEx(IntPtr.Zero, IntPtr.Zero, "Windows.UI.Core.CoreWindow", null)返回的是当前运行的Metro App的hWnd或NULL（IntPtr.Zero，如果当前没有Metro App在前台）</p>

<pre><code>var newproc = new System.Diagnostics.Process();
var info = new System.Diagnostics.ProcessStartInfo("[文件名].exe")
{
    UseShellExecute = true,
    ErrorDialog = true,
};
var window = FindWindowEx(IntPtr.Zero, IntPtr.Zero, "Windows.UI.Core.CoreWindow", null);
if (window != IntPtr.Zero)
    info.ErrorDialogParentHandle = window;
newproc.StartInfo = info;
newproc.Start();  
</code></pre>

<p>附赠一段直接用Metro App弹UAC的gif效果图（与本文关系不大，本文是实现过程中遇到的问题之一） <br>
<img src="https://hjc.im/content/images/2015/02/bdfd5668gw1ep996jnd1fg21is0z444k.gif" alt="" title=""> <br>
具体实现方法不难，不影响上架机器审核和WACK<del>，但是欠折腾Orz，而且</del>可能影响人工审核。。</p>]]></content:encoded></item><item><title><![CDATA[动手修正了一个Ghostium主题长期以来的小问题]]></title><description><![CDATA[<p>嗯，细心的人也许早就发现了我的Blog在百度搜索中有异常情况。 <br>
其实这个问题早在去年（2014年） 9-10月时我就知道了，当时刚刚加MSTC社团，在群里面跟同级的前端/php大神聊到博客的时候他提到过我的博客首页的问题。 <br>
在那之前我一直很奇怪，博客虽说被百度收录了，只收了首页，但却能搜到任何内容，都显示在首页里。 <br>
都是<code>&lt;section&gt;&lt;/section&gt;</code>的错。  </p>

<p>以Ghostium主题<a href="http://ghostium.oswaldoacauan.com/">官方示例页面</a>为例 <br>
<img src="https://hjc.im/content/images/2015/01/Capture.PNG" alt="" title="">  </p>

<p>可以看到所有的内容都在一个section内。而且不仅首页，其它页面也是一个个超大的section。 <br>
<del>这是要闹哪样</del> <br>
今天下午改模板文件，把这些section全换成div了，应该不久后百度收录异常的问题就能解决了吧。。</p>

<p>难道Google和Bing真的不在意这个吗
<img src="https://hjc.im/content/images/2015/01/doge_org.gif" alt="">
……</p>]]></description><link>https://hjc.im/dong-shou-xiu-zheng-liao-ge-ghostiumzhu-ti-chang-qi-yi-lai-de-xiao-wen-ti/</link><guid isPermaLink="false">2aa07afc-a659-4411-be97-ed9a94ae7878</guid><category><![CDATA[博客]]></category><category><![CDATA[网站]]></category><category><![CDATA[Ghost]]></category><category><![CDATA[HTML5]]></category><dc:creator><![CDATA[David Huang]]></dc:creator><pubDate>Sat, 17 Jan 2015 23:51:15 GMT</pubDate><media:content url="http://hjc.im/content/images/2015/01/Capture-1.PNG" medium="image"/><content:encoded><![CDATA[<img src="http://hjc.im/content/images/2015/01/Capture-1.PNG" alt="动手修正了一个Ghostium主题长期以来的小问题"><p>嗯，细心的人也许早就发现了我的Blog在百度搜索中有异常情况。 <br>
其实这个问题早在去年（2014年） 9-10月时我就知道了，当时刚刚加MSTC社团，在群里面跟同级的前端/php大神聊到博客的时候他提到过我的博客首页的问题。 <br>
在那之前我一直很奇怪，博客虽说被百度收录了，只收了首页，但却能搜到任何内容，都显示在首页里。 <br>
都是<code>&lt;section&gt;&lt;/section&gt;</code>的错。  </p>

<p>以Ghostium主题<a href="http://ghostium.oswaldoacauan.com/">官方示例页面</a>为例 <br>
<img src="https://hjc.im/content/images/2015/01/Capture.PNG" alt="动手修正了一个Ghostium主题长期以来的小问题" title="">  </p>

<p>可以看到所有的内容都在一个section内。而且不仅首页，其它页面也是一个个超大的section。 <br>
<del>这是要闹哪样</del> <br>
今天下午改模板文件，把这些section全换成div了，应该不久后百度收录异常的问题就能解决了吧。。</p>

<p>难道Google和Bing真的不在意这个吗
<img src="https://hjc.im/content/images/2015/01/doge_org.gif" alt="动手修正了一个Ghostium主题长期以来的小问题">
……</p>]]></content:encoded></item><item><title><![CDATA[WP8.0 Silverlight显示webp图片]]></title><description><![CDATA[<p>其实这是因为布卡漫画官方给WP8第三方客户端留了坑，才有的一个解决方案。 <br>
事情是这样的： <br>
某天UVE开发组的一个成员跟我说WP8布卡漫画里<del>出现了奇怪的东西导致</del>看不了，于是我向作者反馈了一下。后来作者告诉我是布卡漫画官方接口变化，并且引入了webp格式图片。 <br>
于是我就试着做了一个libwebp的Wrapper，可以给Silverlight使用。</p>

<hr>

<p>项目使用了谷歌官方的libwebp，编译到Windows Phone 8.0 Silverlight，并使用LoadPackagedLibrary加载dll，获取函数指针后调用。只支持解码到RGB,BGR,RGBA,BGRA四种。</p>

<p>这个wrapper仅为Silverlight编写，如果是Windows Runtime App，可以使用更加高效的P/Invoke直接调用。</p>

<p>下载：<a href="https://blog.ligstd.com/HJC.LibWebP.Demo.zip">https://blog.ligstd.com/HJC.LibWebP.Demo.zip</a> </p>

<p>附正常情况下的显示效果</p>

<p><img src="https://hjc.im/content/images/2014/12/wp_ss_20141117_0002.png" alt="" title="">  </p>]]></description><link>https://hjc.im/wp8-0-silverlightxian-shi-webptu-pian/</link><guid isPermaLink="false">5cb96fed-106d-4272-b282-5cd146054247</guid><category><![CDATA[Windows Phone 8]]></category><category><![CDATA[Windows Runtime]]></category><category><![CDATA[WinRT]]></category><category><![CDATA[Windows Phone]]></category><category><![CDATA[Windows]]></category><dc:creator><![CDATA[David Huang]]></dc:creator><pubDate>Mon, 01 Dec 2014 12:28:49 GMT</pubDate><content:encoded><![CDATA[<p>其实这是因为布卡漫画官方给WP8第三方客户端留了坑，才有的一个解决方案。 <br>
事情是这样的： <br>
某天UVE开发组的一个成员跟我说WP8布卡漫画里<del>出现了奇怪的东西导致</del>看不了，于是我向作者反馈了一下。后来作者告诉我是布卡漫画官方接口变化，并且引入了webp格式图片。 <br>
于是我就试着做了一个libwebp的Wrapper，可以给Silverlight使用。</p>

<hr>

<p>项目使用了谷歌官方的libwebp，编译到Windows Phone 8.0 Silverlight，并使用LoadPackagedLibrary加载dll，获取函数指针后调用。只支持解码到RGB,BGR,RGBA,BGRA四种。</p>

<p>这个wrapper仅为Silverlight编写，如果是Windows Runtime App，可以使用更加高效的P/Invoke直接调用。</p>

<p>下载：<a href="https://blog.ligstd.com/HJC.LibWebP.Demo.zip">https://blog.ligstd.com/HJC.LibWebP.Demo.zip</a> </p>

<p>附正常情况下的显示效果</p>

<p><img src="https://hjc.im/content/images/2014/12/wp_ss_20141117_0002.png" alt="" title="">  </p>]]></content:encoded></item><item><title><![CDATA[使用Strongswan搭建IPSec/IKEv2 VPN]]></title><description><![CDATA[<p>本来Strongswan搭建IKEv2 VPN有一篇很好的教程（在nsshell.com上），但是貌似nsshell.com貌似挂了（反正我打不开），于是我就做个搬运，把教程拿过来。。正好原文中有一两处遗漏，我也好补上去。  </p>

<p><strong>重要说明：本文原本只是给自己留一个参考，但似乎已经有很多人参考了这篇文章。一个月来我已经人工帮助了将近10个人无偿排查疑难问题。而作为一个学生，并没有多余的精力做这些事情。因此以后请不要找我问如何搭建VPN，谢谢。</strong></p>

<p>条件： <br>
RAM大小合适的VPS或者服务器（96MB RAM就足够了，64MB RAM未测试） <br>
可以是OpenVZ，但注意看教程中标注的针对OpenVZ的特殊步骤。</p>

<p>1.准备工作 <br>
请在虚拟机或服务器上安装好Ubuntu操作系统，32位、64位均可，建议14.04 LTS，并执行以下命令：</p>

<pre><code>apt-get update
apt-get install libpam0g-dev libssl-dev make gcc
</code></pre>

<p>将PAM库和SSL库安装在系统中</p>

<p>2.下载最新的strongswan源代码并编译  </p>

<pre><code>wget http://download.</code></pre>]]></description><link>https://hjc.im/shi-yong-strongswanda-jian-ipsecikev2-vpn/</link><guid isPermaLink="false">5bfd553e-1c86-48f4-aa34-3533868531df</guid><category><![CDATA[VPS]]></category><category><![CDATA[OpenVZ]]></category><category><![CDATA[VPN]]></category><category><![CDATA[IPSec]]></category><category><![CDATA[IKEv2]]></category><category><![CDATA[iOS]]></category><category><![CDATA[Android]]></category><category><![CDATA[Windows Phone]]></category><category><![CDATA[Windows]]></category><dc:creator><![CDATA[David Huang]]></dc:creator><pubDate>Mon, 10 Nov 2014 21:34:00 GMT</pubDate><content:encoded><![CDATA[<p>本来Strongswan搭建IKEv2 VPN有一篇很好的教程（在nsshell.com上），但是貌似nsshell.com貌似挂了（反正我打不开），于是我就做个搬运，把教程拿过来。。正好原文中有一两处遗漏，我也好补上去。  </p>

<p><strong>重要说明：本文原本只是给自己留一个参考，但似乎已经有很多人参考了这篇文章。一个月来我已经人工帮助了将近10个人无偿排查疑难问题。而作为一个学生，并没有多余的精力做这些事情。因此以后请不要找我问如何搭建VPN，谢谢。</strong></p>

<p>条件： <br>
RAM大小合适的VPS或者服务器（96MB RAM就足够了，64MB RAM未测试） <br>
可以是OpenVZ，但注意看教程中标注的针对OpenVZ的特殊步骤。</p>

<p>1.准备工作 <br>
请在虚拟机或服务器上安装好Ubuntu操作系统，32位、64位均可，建议14.04 LTS，并执行以下命令：</p>

<pre><code>apt-get update
apt-get install libpam0g-dev libssl-dev make gcc
</code></pre>

<p>将PAM库和SSL库安装在系统中</p>

<p>2.下载最新的strongswan源代码并编译  </p>

<pre><code>wget http://download.strongswan.org/strongswan.tar.gz
tar xzf strongswan.tar.gz
cd strongswan-*
</code></pre>

<p>OpenVZ使用以下参数</p>

<pre><code>./configure  --enable-eap-identity --enable-eap-md5 \
--enable-eap-mschapv2 --enable-eap-tls --enable-eap-ttls --enable-eap-peap  \
--enable-eap-tnc --enable-eap-dynamic --enable-eap-radius --enable-xauth-eap  \
--enable-xauth-pam  --enable-dhcp  --enable-openssl  --enable-addrblock --enable-unity  \
--enable-certexpire --enable-radattr --enable-tools --enable-openssl --disable-gmp --enable-kernel-libipsec
</code></pre>

<p>其它服务器执行</p>

<pre><code>./configure  --enable-eap-identity --enable-eap-md5 \
--enable-eap-mschapv2 --enable-eap-tls --enable-eap-ttls --enable-eap-peap  \
--enable-eap-tnc --enable-eap-dynamic --enable-eap-radius --enable-xauth-eap  \
--enable-xauth-pam  --enable-dhcp  --enable-openssl  --enable-addrblock --enable-unity  \
--enable-certexpire --enable-radattr --enable-tools --enable-openssl --disable-gmp
</code></pre>

<p>等待这个过程结束后，执行以下命令：</p>

<pre><code>make; make install
</code></pre>

<p>耐心地等待编译，性能不同编译所需时间也有所不同。</p>

<p>完成后使用命令<code>ipsec version</code>检查是否出现版本号等信息，如下图 <br>
<img src="https://hjc.im/content/images/2014/11/Capture.PNG" alt="" title=""> <br>
若出现<code>ipsec: command not found</code>则代表没有成功编译安装。  </p>

<p>3.配置strongswan和证书</p>

<p>生成CA证书 <br>
生成私钥</p>

<pre><code>ipsec pki --gen --outform pem &gt; ca.pem  
</code></pre>

<p>利用私钥，签名CA证书</p>

<pre><code>ipsec pki --self --in ca.pem --dn "C=com, O=myvpn, CN=VPN CA" --ca --outform pem &gt;ca.cert.pem
</code></pre>

<p>服务器证书
生成私钥</p>

<pre><code>ipsec pki --gen --outform pem &gt; server.pem
</code></pre>

<p>用CA证书签发服务器证书</p>

<p>首先确认访问服务器的IP地址或域名，连接时<strong>不可使用其它地址，只能使用证书中的地址，请将下面一句命令中的123.123.123.123替换为自己服务器的IP地址或域名，连接时使用</strong>，一共需要替换<strong>两处</strong>。</p>

<pre><code>ipsec pki --pub --in server.pem | ipsec pki --issue --cacert ca.cert.pem \
--cakey ca.pem --dn "C=com, O=myvpn, CN=123.123.123.123" \
--san="123.123.123.123" --flag serverAuth --flag ikeIntermediate \
--outform pem &gt; server.cert.pem
</code></pre>

<p>客户端证书
生成私钥</p>

<pre><code>ipsec pki --gen --outform pem &gt; client.pem
</code></pre>

<p>利用CA签名客户端证书</p>

<pre><code>ipsec pki --pub --in client.pem | ipsec pki --issue --cacert ca.cert.pem --cakey ca.pem --dn "C=com, O=myvpn, CN=VPN Client" --outform pem &gt; client.cert.pem
</code></pre>

<p>生成pkcs12证书</p>

<pre><code>openssl pkcs12 -export -inkey client.pem -in client.cert.pem -name "client" -certfile ca.cert.pem -caname "VPN CA"  -out client.cert.p12
</code></pre>

<p>安装证书</p>

<pre><code>cp -r ca.cert.pem /usr/local/etc/ipsec.d/cacerts/
cp -r server.cert.pem /usr/local/etc/ipsec.d/certs/
cp -r server.pem /usr/local/etc/ipsec.d/private/
cp -r client.cert.pem /usr/local/etc/ipsec.d/certs/
cp -r client.pem  /usr/local/etc/ipsec.d/private/
</code></pre>

<p>证书安装完成，接下来配置strongswan，编辑<code>/usr/local/etc/ipsec.conf</code></p>

<pre><code>config setup
    uniqueids=never 

conn iOS_cert
    keyexchange=ikev1
    # strongswan version &gt;= 5.0.2, compatible with iOS 6.0,6.0.1
    fragmentation=yes
    left=%defaultroute
    leftauth=pubkey
    leftsubnet=0.0.0.0/0
    leftcert=server.cert.pem
    right=%any
    rightauth=pubkey
    rightauth2=xauth
    rightsourceip=10.31.2.0/24
    rightcert=client.cert.pem
    auto=add

conn android_xauth_psk
    keyexchange=ikev1
    left=%defaultroute
    leftauth=psk
    leftsubnet=0.0.0.0/0
    right=%any
    rightauth=psk
    rightauth2=xauth
    rightsourceip=10.31.2.0/24
    auto=add

conn networkmanager-strongswan
    keyexchange=ikev2
    left=%defaultroute
    leftauth=pubkey
    leftsubnet=0.0.0.0/0
    leftcert=server.cert.pem
    right=%any
    rightauth=pubkey
    rightsourceip=10.31.2.0/24
    rightcert=client.cert.pem
    auto=add

conn windows7
    keyexchange=ikev2
    ike=aes256-sha1-modp1024! 
    rekey=no
    left=%defaultroute
    leftauth=pubkey
    leftsubnet=0.0.0.0/0
    leftcert=server.cert.pem
    right=%any
    rightauth=eap-mschapv2
    rightsourceip=10.31.2.0/24
    rightsendcert=never
    eap_identity=%any
    auto=add
</code></pre>

<p>编辑<code>/usr/local/etc/strongswan.conf</code></p>

<pre><code> charon {
         load_modular = yes
         duplicheck.enable = no
         compress = yes
         plugins {
                 include strongswan.d/charon/*.conf
         }
         dns1 = 8.8.8.8
         dns2 = 8.8.4.4
         nbns1 = 8.8.8.8
         nbns2 = 8.8.4.4
 }
 include strongswan.d/*.conf
</code></pre>

<p>编辑<code>/usr/local/etc/ipsec.secrets</code>中的用户名、密码  </p>

<pre><code>: RSA server.pem
: PSK "mykey"
: XAUTH "mykey"
[用户名] %any : EAP "[密码]"
</code></pre>

<p>注意将PSK、XAUTH处的"mykey"编辑为<strong>唯一且私密的字符串</strong>，并且将[用户名]改为自己想要的登录名，[密码]改为自己想要的密码（[]符号去掉），可以添加多行，得到多个用户。</p>

<p>4.修改系统转发以及防火墙配置 <br>
首先编辑<code>/etc/sysctl.conf</code>，将<code>net.ipv4.ip_forward=1</code>一行前面的<code>#</code>号去掉，保存后执行<code>sysctl -p</code>。 <br>
接下来修改iptables。 <br>
OpenVZ执行：  </p>

<pre><code>iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -s 10.31.0.0/24  -j ACCEPT
iptables -A FORWARD -s 10.31.1.0/24  -j ACCEPT
iptables -A FORWARD -s 10.31.2.0/24  -j ACCEPT
iptables -A INPUT -i venet0 -p esp -j ACCEPT
iptables -A INPUT -i venet0 -p udp --dport 500 -j ACCEPT
iptables -A INPUT -i venet0 -p tcp --dport 500 -j ACCEPT
iptables -A INPUT -i venet0 -p udp --dport 4500 -j ACCEPT
iptables -A INPUT -i venet0 -p udp --dport 1701 -j ACCEPT
iptables -A INPUT -i venet0 -p tcp --dport 1723 -j ACCEPT
iptables -A FORWARD -j REJECT
iptables -t nat -A POSTROUTING -s 10.31.0.0/24 -o venet0 -j MASQUERADE
iptables -t nat -A POSTROUTING -s 10.31.1.0/24 -o venet0 -j MASQUERADE
iptables -t nat -A POSTROUTING -s 10.31.2.0/24 -o venet0 -j MASQUERADE
</code></pre>

<p>其它服务器执行</p>

<pre><code>iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -s 10.31.0.0/24  -j ACCEPT
iptables -A FORWARD -s 10.31.1.0/24  -j ACCEPT
iptables -A FORWARD -s 10.31.2.0/24  -j ACCEPT
iptables -A INPUT -i eth0 -p esp -j ACCEPT
iptables -A INPUT -i eth0 -p udp --dport 500 -j ACCEPT
iptables -A INPUT -i eth0 -p tcp --dport 500 -j ACCEPT
iptables -A INPUT -i eth0 -p udp --dport 4500 -j ACCEPT
iptables -A INPUT -i eth0 -p udp --dport 1701 -j ACCEPT
iptables -A INPUT -i eth0 -p tcp --dport 1723 -j ACCEPT
iptables -A FORWARD -j REJECT
iptables -t nat -A POSTROUTING -s 10.31.0.0/24 -o eth0 -j MASQUERADE
iptables -t nat -A POSTROUTING -s 10.31.1.0/24 -o eth0 -j MASQUERADE
iptables -t nat -A POSTROUTING -s 10.31.2.0/24 -o eth0 -j MASQUERADE
</code></pre>

<p>接下来（公共部分）保存iptables配置并配置开机自动载入  </p>

<pre><code>iptables-save &gt; /etc/iptables.rules
cat &gt; /etc/network/if-up.d/iptables&lt;&lt;EOF
#!/bin/sh
iptables-restore &lt; /etc/iptables.rules
EOF
chmod +x /etc/network/if-up.d/iptables
</code></pre>

<p>5.尝试连接 <br>
WP8.1手机安装ca.cert.pem，进入设置-VPN添加IKEv2连接，地址为<strong>证书中的地址或IP</strong>，通过用户名-密码连接。 <br>
Windows连接也是一样，但注意<strong>将证书导入本地计算机而不是当前用户的“受信任的证书颁发机构”</strong>。 <br>
iOS/Android/Mac OS X设备添加Cisco IPSec PSK验证方式，预共享密钥是<code>/usr/local/etc/ipsec.secrets</code>中PSK后的字符串（不含引号），用户名密码同上，<strong>可以通过任意域名或IP连接，不需要证书</strong>  </p>]]></content:encoded></item><item><title><![CDATA[WinRT C++开发之异步转同步]]></title><description><![CDATA[<p>微软设计API的人脑袋绝对有坑，好多API只给异步不给同步，搞得像所有开发都是在UI线程里进行一样。事实上开发非UI代码，尤其是使用C++，利用现成的库时，简直是痛不欲生！  </p>

<p>不多说，直接贴代码了。</p>

<p>AsyncHelper.h</p>

<pre><code>/*
*   AsyncHelper.h
*
*   Date: 1st July, 2014   Author: David Huang
*   (C) 2014 Light Studio. All Rights Reserved.
*/
#pragma once
#include &lt;ppltasks.h&gt;
#include &lt;Windows.h&gt;
#include &lt;synchapi.h&gt;
using namespace Platform;

template &lt;typename</code></pre>]]></description><link>https://hjc.im/winrt-ckai-fa-zhi-yi-bu-zhuan-tong-bu/</link><guid isPermaLink="false">6d762051-989f-4f4f-ab13-cf6dadecb80f</guid><category><![CDATA[Windows Phone 8]]></category><category><![CDATA[Windows Runtime]]></category><category><![CDATA[WinRT]]></category><category><![CDATA[Windows 8]]></category><category><![CDATA[C++]]></category><dc:creator><![CDATA[David Huang]]></dc:creator><pubDate>Wed, 05 Nov 2014 10:23:31 GMT</pubDate><content:encoded><![CDATA[<p>微软设计API的人脑袋绝对有坑，好多API只给异步不给同步，搞得像所有开发都是在UI线程里进行一样。事实上开发非UI代码，尤其是使用C++，利用现成的库时，简直是痛不欲生！  </p>

<p>不多说，直接贴代码了。</p>

<p>AsyncHelper.h</p>

<pre><code>/*
*   AsyncHelper.h
*
*   Date: 1st July, 2014   Author: David Huang
*   (C) 2014 Light Studio. All Rights Reserved.
*/
#pragma once
#include &lt;ppltasks.h&gt;
#include &lt;Windows.h&gt;
#include &lt;synchapi.h&gt;
using namespace Platform;

template &lt;typename TResult&gt;
inline TResult AWait(Windows::Foundation::IAsyncOperation&lt;TResult&gt;^ asyncOp)
{
    HANDLE handle = CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS);
    TResult result;
    Exception^ exceptionObj = nullptr;
    asyncOp-&gt;Completed = ref new Windows::Foundation::AsyncOperationCompletedHandler&lt;TResult&gt;([&amp;]
        (Windows::Foundation::IAsyncOperation&lt;TResult&gt;^ asyncInfo, Windows::Foundation::AsyncStatus asyncStatus)
{
        try
        {
            result = asyncInfo-&gt;GetResults();
        }
        catch (Exception^ e)
        {
            exceptionObj = e;
        }
        SetEvent(handle);
    });
    WaitForSingleObjectEx(handle, INFINITE, FALSE);
    if (exceptionObj != nullptr) throw exceptionObj;
    return result;
}

inline void AWait(Windows::Foundation::IAsyncAction^ asyncAc)
{
    HANDLE handle = CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS);
    Exception^ exceptionObj = nullptr;
    asyncAc-&gt;Completed = ref new Windows::Foundation::AsyncActionCompletedHandler([&amp;]
        (Windows::Foundation::IAsyncAction^ asyncInfo, Windows::Foundation::AsyncStatus asyncStatus)
    {
        try
        {
            asyncInfo-&gt;GetResults();
        }
        catch (Exception^ e)
        {
            exceptionObj = e;
        }
        SetEvent(handle);
    });
    WaitForSingleObjectEx(handle, INFINITE, FALSE);
    if (exceptionObj != nullptr) throw exceptionObj;
}
</code></pre>

<p>虽说有两个函数，但作用是一样的，原理也非常简单，一个针对没有返回值的Action，一个针对有返回值的Operation。原理是使用WaitForSingleObject高效地等待SetEvent(handle)的执行，并且将异步操作中出现的Exception原封不动地抛出。  </p>

<p>使用示例：（为了方便我直接贴一段我项目里的代码）</p>

<pre><code>auto stream = r-&gt;stream;
auto reader = ref new DataReader(stream);
auto bytes = AWait(reader-&gt;LoadAsync(buf_size));
auto nBytes = ref new Array&lt;unsigned char&gt;(bytes);
reader-&gt;ReadBytes(nBytes);
reader-&gt;DetachStream();
</code></pre>

<p>P.S. <br>
如果只是读取安装目录和data目录下的内容，不建议使用StorageFile，直接用C/C++/Win32的API读取即可，比StorageFile不知道方便多少…… <br>
如果不是特别需要，Windows Runtime/Windows Phone 8.1不建议使用C++开发……←这是真的！</p>]]></content:encoded></item><item><title><![CDATA[使用Linux VPS搭建一个CS:GO服务器]]></title><description><![CDATA[<p>声明： <br>
1.<strong>本文并非完全原创，脚本和部分内容来自<a href="http://danielgibbs.co.uk/lgsm/csgoserver/">此处</a></strong> <br>
2.由于要使用Steam下载csgo服务器，请确保你的VPS能够顺畅地连接Steam服务器（Linode只需要20秒不到，国内的下载很痛苦，等一晚上吧）  </p>

<p>VPS硬件要求： <br>
1.CPU：单核性能最好比较强，UnixBench单核500-600分就够了，最好是800-1000分，因为CSGO服务器程序只使用一个核心（也许是我不会开多核）。<del>当年我为何要吐槽世纪互联Azure？</del> <br>
实测Linode，Vultr，DigitalOcean在30个BOT以内流畅，美国Wable，新加坡OneAsiaHost可以开更多。 <br>
2.RAM：建议512MB，实际上人少的话256MB就够了。 <br>
3.硬盘：本体占用8GB，Steam占用几十M，建议10GB+，最好是SSD。</p>

<p>VPS网络要求： <br>
1.带宽：一个人大约会增加200K的双向带宽占用（包含语音的平均）。每个人连接的速度要保证，千万别指望用2M的网让几十个人加进来玩。用Linode的125M出口速度就比较好了，流量消耗不大。以今天的情况为参考，10-12个人玩大约占用2M出口带宽，0.5M入口带宽。 <br>
2.延迟：</p>]]></description><link>https://hjc.im/linux-vps-csgo-server/</link><guid isPermaLink="false">219399dc-7deb-4526-85b8-c34046486e3a</guid><category><![CDATA[VPS]]></category><category><![CDATA[CS:GO]]></category><category><![CDATA[Linux]]></category><category><![CDATA[Ubuntu]]></category><dc:creator><![CDATA[David Huang]]></dc:creator><pubDate>Wed, 01 Oct 2014 17:59:26 GMT</pubDate><content:encoded><![CDATA[<p>声明： <br>
1.<strong>本文并非完全原创，脚本和部分内容来自<a href="http://danielgibbs.co.uk/lgsm/csgoserver/">此处</a></strong> <br>
2.由于要使用Steam下载csgo服务器，请确保你的VPS能够顺畅地连接Steam服务器（Linode只需要20秒不到，国内的下载很痛苦，等一晚上吧）  </p>

<p>VPS硬件要求： <br>
1.CPU：单核性能最好比较强，UnixBench单核500-600分就够了，最好是800-1000分，因为CSGO服务器程序只使用一个核心（也许是我不会开多核）。<del>当年我为何要吐槽世纪互联Azure？</del> <br>
实测Linode，Vultr，DigitalOcean在30个BOT以内流畅，美国Wable，新加坡OneAsiaHost可以开更多。 <br>
2.RAM：建议512MB，实际上人少的话256MB就够了。 <br>
3.硬盘：本体占用8GB，Steam占用几十M，建议10GB+，最好是SSD。</p>

<p>VPS网络要求： <br>
1.带宽：一个人大约会增加200K的双向带宽占用（包含语音的平均）。每个人连接的速度要保证，千万别指望用2M的网让几十个人加进来玩。用Linode的125M出口速度就比较好了，流量消耗不大。以今天的情况为参考，10-12个人玩大约占用2M出口带宽，0.5M入口带宽。 <br>
2.延迟：30-100ms完美，100-200ms一般，200ms+就不建议用来搭建了。 <br>
3.<strong>经常掉包的VPS就别想了</strong>，卡成狗，并且经常瞬移。 <br>
4.能够顺畅连接Steam服务器（经常更新）  </p>

<p>个人对网络非常满意的VPS是OAH和Linode，Vultr其次。但是Linode晚上延迟容易变高，如果作为游戏服务器并不是特别推荐。</p>

<p>综上，大概每个月$7-$10就能建一个比较完美的小型CS:GO 服务器了（当然是针对中国大陆的，我会说针对美国只需要$0.99就可以了吗）  </p>

<h2 id="">正文  </h2>

<p>首先确定服务器系统版本，以下Linux发行版兼容： <br>
◦基于Debian的 (Ubuntu, Mint etc.). <br>
◦基于Redhat的 (CentOS, Fedora etc.). <br>
脚本使用bash和python编写。</p>

<h3 id="">准备</h3>

<h4 id="ubuntu">Ubuntu</h4>

<p>32位Ubuntu执行此命令</p>

<pre><code>apt-get install gdb mailutils postfix
</code></pre>

<p>64位Ubuntu执行此命令</p>

<pre><code>apt-get install gdb mailutils postfix lib32gcc1
</code></pre>

<h4 id="debian">Debian</h4>

<p>32位Debian执行此命令</p>

<pre><code>apt-get install gdb mailutils postfix tmux ca-certificates
</code></pre>

<p>64位Debian执行此命令</p>

<pre><code>dpkg --add-architecture i386
apt-get update
apt-get install gdb mailutils postfix tmux ca-certificates lib32gcc1
</code></pre>

<p>RHEL 6/CentOS 6</p>

<p>注意: 需要EPEL或与其相同的repository
<a href="http://fedoraproject.org/wiki/EPEL">http://fedoraproject.org/wiki/EPEL</a></p>

<p>32位RHEL 6或CentOS 6</p>

<pre><code>yum install gdb mailx wget nano tmux
</code></pre>

<p>64位RHEL 6或CentOS 6</p>

<pre><code>yum install gdb mailx wget nano tmux glibc.i686 libstdc++.i686
</code></pre>

<h3 id="">安装</h3>

<p>首先你需要有一个非root的账户，如果没有可以使用以下命令创建并进入用户：  </p>

<pre><code>adduser csgoserver【输入信息】
passwd csgoserver【输入密码】
su - csgoserver
</code></pre>

<p>确保自己的终端显示$而不是#时进行下载安装：</p>

<pre><code>wget http://danielgibbs.co.uk/dl/csgoserver
chmod +x csgoserver
./csgoserver install
</code></pre>

<p>按照其简单的英文提示操作即可。会要求你输入远程操作密码（rcon password），具体用途是在本地游戏中，按"`"键打开控制台，输入rcon_password 【你设置的远程密码】[回车]，然后使用rcon+命令即可远程控制。具体的控制方法和命令请参考csgo官方文档。<strong>rcon密码会明文保存</strong>  </p>

<h3 id="">运行</h3>

<p>首先进入用户（如果不是使用上述方法创建的用户可以跳过此步）  </p>

<pre><code>su - csgoserver
</code></pre>

<p>运行服务器：<code>./csgoserver start</code> <br>
需要注意：如果你的服务器有多个IP，你需要修改<code>csgoserver</code>文件，改为你想监听的IP（CS:GO 服务器只能监听一个IP地址）。 <br>
停止服务器：<code>./csgoserver stop</code> <br>
重新启动服务器：<code>./csgoserver restart</code>  </p>

<p>升级服务器：<code>./csgoserver update</code> <br>
升级并重启（个人推荐）：<code>./csgoserver update-restart</code>  </p>

<p>至此一个基本的CS:GO 服务器就搭建完成了，在CS:GO 本地进入搜索服务器页面，进入<strong>收藏</strong>标签，可以在“添加服务器”中输入IP地址或域名添加。  </p>

<h3 id="">高级用法</h3>

<p>请参考csgoserver脚本原站<a href="http://danielgibbs.co.uk/lgsm/csgoserver/">http://danielgibbs.co.uk/lgsm/csgoserver/</a> ，其中包括开机启动和自动升级，此处不再详细介绍。</p>]]></content:encoded></item><item><title><![CDATA[自己反代Google字体库，实现国内/外均高速访问]]></title><description><![CDATA[<p>最近感觉网站还是把Google字体库加上去比较好，非常不想就<del>某堵墙的错</del>Google服务访问不通畅的原因导致网站的英文字体全部变丑。突然觉得前段时间将google字体去掉完全是一种妥协，<del>非常的不符合我的性格</del>。 <br>
很多人推荐使用国内的镜像，但是国内的镜像普遍存在两个问题。 <br>
1.部分不支持HTTPS <br>
2.大部分在国外加载缓慢 <br>
将自己网页的打开速度寄托在别人服务器上，显然<del>这又是一件不符合我的性格的事情，所以</del>这个绝对不能被采用。 <br>
考虑到我的网站用的是Linode，在国内的访问速度少说也有100K/s吧，<del>哪怕是偏远地区</del>，于是最后我还是决定自己反代一个。  </p>

<p>虽然每次不想强调这个悲伤的事实，但是还是得说：<strong>本文所述内容不适合大陆VPS，并且只适合带宽充足，速度较快的国外VPS，如Linode Tokyo/Fremont</strong></p>

<hr>

<p>分割线上方为废话。下面开始正题。 <br>
完成这个<del>伟大的</del>目标，你需要： <br>
1.VPS，专用于此的独立IP。如果你不想让windows xp用户正常使用HTTPS，或者根本不用HTTPS，或者你有wildcard证书，那么独立的IP可以不需要。<del>我就是不想让XP用户用HTTPS访问我的网站，因为需要HTTPS的只有我一个。</del> <br>
2.nginx-extras。Apache2做反代没有尝试过，不在本文的讨论范围内。</p>]]></description><link>https://hjc.im/google-fonts-reverse-proxy/</link><guid isPermaLink="false">07337764-38e6-439e-adfe-57ff229c361a</guid><category><![CDATA[VPS]]></category><category><![CDATA[Linode]]></category><category><![CDATA[SSL]]></category><category><![CDATA[Google]]></category><category><![CDATA[Ubuntu]]></category><category><![CDATA[nginx]]></category><category><![CDATA[反向代理]]></category><dc:creator><![CDATA[David Huang]]></dc:creator><pubDate>Sat, 06 Sep 2014 11:14:36 GMT</pubDate><content:encoded><![CDATA[<p>最近感觉网站还是把Google字体库加上去比较好，非常不想就<del>某堵墙的错</del>Google服务访问不通畅的原因导致网站的英文字体全部变丑。突然觉得前段时间将google字体去掉完全是一种妥协，<del>非常的不符合我的性格</del>。 <br>
很多人推荐使用国内的镜像，但是国内的镜像普遍存在两个问题。 <br>
1.部分不支持HTTPS <br>
2.大部分在国外加载缓慢 <br>
将自己网页的打开速度寄托在别人服务器上，显然<del>这又是一件不符合我的性格的事情，所以</del>这个绝对不能被采用。 <br>
考虑到我的网站用的是Linode，在国内的访问速度少说也有100K/s吧，<del>哪怕是偏远地区</del>，于是最后我还是决定自己反代一个。  </p>

<p>虽然每次不想强调这个悲伤的事实，但是还是得说：<strong>本文所述内容不适合大陆VPS，并且只适合带宽充足，速度较快的国外VPS，如Linode Tokyo/Fremont</strong></p>

<hr>

<p>分割线上方为废话。下面开始正题。 <br>
完成这个<del>伟大的</del>目标，你需要： <br>
1.VPS，专用于此的独立IP。如果你不想让windows xp用户正常使用HTTPS，或者根本不用HTTPS，或者你有wildcard证书，那么独立的IP可以不需要。<del>我就是不想让XP用户用HTTPS访问我的网站，因为需要HTTPS的只有我一个。</del> <br>
2.nginx-extras。Apache2做反代没有尝试过，不在本文的讨论范围内。 <br>
3.域名以及SSL证书。如果不需要HTTPS访问可以不要SSL证书。  </p>

<p>STEP.1 安装nginx并建立nginx配置文件 <br>
首先你需要一个正常工作（包括处理SSL）的nginx <br>
<del>作为一个小白用户，</del>我曾经不懂得Ubuntu直接apt-get install nginx安装的nginx是不支持SSL的，nginx也不给我报个错，这让我烦恼了很久。 <br>
正确安装<del>姿势</del>（Ubuntu）：  </p>

<pre><code>add-apt-repository ppa:nginx/stable  
apt-get update
apt-get install nginx-extras
rm /usr/share/nginx/html/index.html #这一步视情况而定，不要随意执行。。
</code></pre>

<p>正常情况下nginx会在配置文件中<code>include /etc/nginx/sites-enabled/*</code>和<code>include /etc/nginx/conf.d/*.conf</code>，于是我们就在<code>/etc/nginx/conf.d/</code>中新建一个<code>google.conf</code>文件。 <br>
文件中需要改动的部分已经用中文注释标明。由于这部分内容较长，建议复制到文本编辑器中查看/编辑。</p>

<pre><code>upstream google {
    server fonts.googleapis.com:80;
}

upstream gstatic {
    server fonts.gstatic.com:80;
}

server {
    listen 80;
    listen [::]:80;

    server_name fonts.ligstd.com;#改为自己的字体库域名，fonts.xxxxxx.com
    valid_referers server_name *.ligstd.com ligstd.com *.hjc.im hjc.im; # 限制引用的域名。改成自己需要用到字体库的网站域名的即可。如果你想要做公益服务，可以将此行和下方的#if ($invalid_referer) {...}去掉。
    if ($invalid_referer) {
        return 404;
    }

    location /css {
        sub_filter 'fonts.gstatic.com' 'fonts.ligstd.com';#将fonts.ligstd.com改为自己的字体库域名。
        sub_filter_once off;
        sub_filter_types text/css;
        proxy_pass_header Server;
        proxy_set_header Host fonts.googleapis.com;
        proxy_set_header Accept-Encoding '';
        proxy_redirect off;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Scheme $scheme;
        proxy_pass http://google;
    }

    location / {
        proxy_pass_header Server;
        proxy_set_header Host fonts.gstatic.com;
        proxy_redirect off;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Scheme $scheme;
        proxy_pass http://gstatic;
    }
}
#下方为HTTPS设置，如果只需要HTTP访问，从这里开始往下的内容就不需要看了。
server {
    listen 106.186.18.133:443 ssl spdy; #将这一行改为"自己的IP地址:443"
    listen [2400:8900::f03c:91ff:fe73:bc8f]:443 ssl spdy;#将这一行改为"[自己的IPv6地址]:443"，没有IPv6可以不填。
    ssl on;
    ssl_certificate /root/fonts.ligstd.com/ssl.crt; #改为自己的SSL证书位置
    ssl_certificate_key /root/fonts.ligstd.com/ssl.key; #改为自己的SSL私钥位置
    ssl_session_timeout 5m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:RSA+3DES:!ADH:!AECDH:!MD5;
    #上面几行视情况而定，可以去掉ssl_ciphers和ssl_prefer_server_ciphers两行。
    server_name fonts.ligstd.com;#改为自己的字体库域名

    valid_referers server_name *.ligstd.com ligstd.com *.hjc.im hjc.im;#同样，改为自己需要使用字体库的网站域名。公益服务去掉这几行。
    if ($invalid_referer) {
        return 404;
    }

    location /css {
        sub_filter 'http://fonts.gstatic.com' 'https://fonts.ligstd.com'; #将fonts.ligstd.com改为字体库域名，但是https千万别改。
        sub_filter_once off;
        sub_filter_types text/css;
        proxy_pass_header Server;
        proxy_set_header Host fonts.googleapis.com;
        proxy_set_header Accept-Encoding '';
        proxy_redirect off;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Scheme $scheme;
        proxy_pass http://google;
    }

    location / {
        proxy_pass_header Server;
        proxy_set_header Host fonts.gstatic.com;
        proxy_redirect off;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Scheme $scheme;
        proxy_pass http://gstatic;
    }
}
</code></pre>

<p>建立这样一个配置文件后直接nginx -s reload，不出任何错误的话，你的字体库域名就可以直接用来替换fonts.googleapis.com了。</p>

<p>其实如果你不想专门用一个域名/IP地址，又想给XP用户访问HTTPS，或者你安装了apache，nginx不能监听80端口，完全可以将80端口换成其他端口，只是sub_filter需要在后面加上端口，并且替换fonts.googleapis.com的时候也需要加上。<del>当然，这样就显得没有那么优雅了。</del>  </p>]]></content:encoded></item></channel></rss>