Justin then hacked some Windows 8 games and rang alarm bells once again. Hope Microsoft won't ignore the security issues this time. As a follow-up on Justin's work I am going to do some testing to reverse engineering a WinJS app. The goal is to run and debug the app using Visual Studio 2010 locally. I am doing this only for study purpose by no means any other.
The test environment I have is a Windows 8 64-bit virtual machine with Visual Studio 2012 and BingMap SDK installed. The app to be reversely engineered is called USAToday, one of my favorite apps also one of the top free apps under News category in Windows store. After installed the app you can see the physical HTML, JavaScript and CSS files under C:\Program Files\WindowsApps\USATODAY.USATODAY_1.2.0.0_neutral__wy7mw3214mat8 folder (google it if you can't see or open this folder). Following are steps I did to import USAToday files into VS2012.
Step 1. Open up VS2012 and create a JavaScript Windows Store project named "USATodayMock" using Navigation App template. The VS2012 solution looks like:
Step 2. Copy highlighted files and folders in C:\Program Files\WindowsApps\USATODAY.USATODAY_1.2.0.0_neutral__wy7mw3214mat8 as below to the VS2012 project:
After files copied to the VS2010 solution:
Step 3. Add project references: Windows Library for JavaScript 1.0, BingMap for JavaScript, and two dlls from USAToday source files:
Now the VS2012 solution can be compiled. When you run it in debug mode in VS2012 it will prompt you a warning, just click yes to override the existing app:
Then you will get an exception thrown by getResourceString method inside Microsoft.PlayerFramework.Js/js/PlayerFramework.js:
It shows missing resource string for "Microsoft.PlayerFramework.Js/resources/TimeFormatterTemplate". Apparently the original USAToday solution includes Microsoft Player Framework which requires some resource strings. To make it simple I just reverse engineer the compiled PRI resource file (resources.pri) come with Windows 8 app package, and directly add those resource strings to the solution. See next step for detail.
Step 4. Open Developer Command Prompt, go to app installation folder C:\Program Files\WindowsApps\USATODAY.USATODAY_1.2.0.0_neutral__wy7mw3214mat8, run "makepri dump" to build the resources.pri.xml file:
The exported resources.pri.xml looks something like:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <PriInfo> <ResourceMap name="USATODAY.USATODAY" version="1.0" primary="true"> <Qualifiers></Qualifiers> <ResourceMapSubtree name="Bing.Maps.Javascript"></ResourceMapSubtree> <ResourceMapSubtree name="Files"> <ResourceMapSubtree name="Microsoft.PlayerFramework.Js"> <ResourceMapSubtree name="resources"> <NamedResource name="TimeElapsedTitle" uri="ms-resource://USATODAY.USATODAY/Microsoft.PlayerFramework.Js/resources/TimeElapsedTitle"> <Candidate qualifiers="Language-EN-US" isDefault="true" type="String"> <Value>{hour.integer}:{minute.integer(2)}:{second.integer(2)}</Value> </Candidate> </NamedResource> </ResourceMapSubtree> </ResourceMapSubtree> </ResourceMap> </PriInfo>Copying those resource strings is tedious. I created a simple console app that convert those player framework resource strings in resources.pri.xml to WinJS recognizable JSON format:
using System; using System.Collections.Generic; using System.Linq; using System.IO; using System.Xml.Linq; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Dictionary<string, string> resources = new Dictionary<string, string>(); XDocument xdoc = XDocument.Load("c:\\temp\\resources.pri.xml"); var frameworkResources = xdoc.Descendants("ResourceMapSubtree") .Where(e => (string)e.Attribute("name") == "Microsoft.PlayerFramework.Js") .Elements("ResourceMapSubtree") .Where(e => (string)e.Attribute("name") == "resources") .Elements(); foreach (var item in frameworkResources) { var key = item.Attribute("name").Value; var value = item.Descendants("Value").First().Value; resources.Add(key, value); } using (TextWriter tw = new StreamWriter("C:\\temp\\resources.resjson")) { tw.WriteLine("{"); int resourceCount = resources.Count, counter = 1; foreach (var item in resources) { if (counter++ < resourceCount) tw.WriteLine(string.Format("\"{0}\" : \"{1}\",", item.Key, item.Value)); else tw.WriteLine(string.Format("\"{0}\" : \"{1}\"", item.Key, item.Value)); } tw.WriteLine("}"); } } } }Above console app will create a resources.resjson file in C:\temp folder. Copy it to USATodayMock solution.
Step 5. Modify getResourceString method in Microsoft.PlayerFramework.Js/js/PlayerFramework.js, remove the prefix of "Microsoft.PlayerFramework.Js/resources/" for the resource keys since they are included in the project directly:
function getResourceString(id) { /// <summary>Returns the resource string with the specified ID.</summary> /// <param name="id" type="String">The resource identifier.</param> /// <returns type="String">The resource string.</returns> if (id.indexOf("resources/") > 0) id = id.substring(id.lastIndexOf("/") + 1); var string = WinJS.Resources.getString(id); if (string.empty) { throw invalidResourceId; } return string.value; }Click F5 and bang the USATodayMock app can be run and debug now!
Step 6. This step is to do a little more of test by modifying the default page. USAToday Win8 app shows weather in the right top corner:
Let's just change weather text to red and show some dummy text when you click it. Search following text inside default.html page under the root of the USATodayMock solution:
<div class="banner"> <div class="snapped-back"></div> <div class="app-logo"></div> <div class="weather"> <div class="location"></div> <div class="icon-wrapper hidden"><img class="icon" /></div> <div class="temp"></div> </div> </div>Then replace it with following HTML:
<div class="weather"> <div class="location" style="color:red;" onclick="showAlert();"></div> <div class="icon-wrapper hidden"><img class="icon" /></div> <div class="temp"></div> </div> <div id="divAlert" style="display:none; position:fixed; margin-top: 200px; margin-left: 150px;"> <h1>App has been modified!</h1> </div> <script type="text/javascript"> function showAlert() { event.cancelBubble = true; WinJS.Utilities.query("#divAlert").setStyle("display", "block"); WinJS.Utilities.query("#app-body").setStyle("display", "none"); } </script>Run the application again you will see the red location text:
When you click that red text (New York) the page will show the custom content instead of going to the location setting page:
So we see how easy it's to import an installed Windows 8 application into Visual Studio and run it directly. In my next post I will discuss some methods to secure the code and make it harder to be reversely engineered.