Ayer me aburrí un poco después del trabajo, así que iba a ponerme a ver una serie, pero siempre me olvido de cuál fue el último capítulo que vi. Hay muchas Apps para eso, pero la que más me gusta es TrackSeriesTV, hecha por unos amigos antiguos compañeros de Microsoft. ¿Qué dónde está el problema? Bueno, que yo estaba con mi portátil en MacOS X y la App es únicamente para Windows Store. La solución estaba clara… tenía que portar la aplicación, esa misma noche, sin documentación y sin acceso al código fuente. ¿Te animas?
Obteniendo acceso a los binarios
Las Apps de la Store se instalan en %ProgramFiles%\WindowsApps aunque por defecto no tenemos acceso a ella. Simplemente tenemos que darnos permiso. Podemos hacerlo en las propiedades del directorio, Security -> Advanced y poniéndonos como Owner.
Una vez hecho esto, podemos identificar el directorio de TrackSeries. Pero si entramos, veremos que las cosas han cambiado un poco desde mi charla en 2013 para el CodeMotion. Por aquel entonces las Apps mostraban los XAML en claro, el código JS aparecía tal cuál, podías modificar lo que quisieras, etc. Ahora es un poco más difícil. Para empezar, los ficheros XAML ya no se encuentran en claro, sino en formato xbf. Además, el equipo de TrackSeries no está con el hype de JS (porque sí, es un hype), así que han usado un lenguaje de verdad y en vez de ficheros JS listos para leer, tenemos exes y dlls. Nada que no podamos solucionar, pero nos va a dar un poco más de trabajo. Como dijo Jack el destripador, vayamos por partes.
Descifrando los XBF
Como la entrada va de lo que va, voy a seguir el método de Juán Palomo… así que nos vamos a hacer nuestro “decompilador” de XBF. La verdad es que vamos a tomar un mega-atajo y usaremos la dll ReducerEngine que distribuye Microsoft con DotNet Native. Antiguamente había que instalarlo por separado, pero con VS 2015 viene de serie (bueno, se instala cuando se activa la capacidad de desarrollo de aplicaciones universales) así que lo más probable es que ya lo tengáis :) Generalmente el path es: %PROGRAMFILES(X86)%\MSBuild\Microsoft\.NetNative\x64\ilc\tools
Una vez tenemos la librería, nos toca localizar qué partes necesitamos de ella. Para ver la arquitectura de un ensamblado, Visual Studio nos ofrece Object Browser. Esa es la herramienta que deberíamos usar… a menos que tengamos alguna otra. En ese caso, usad siempre la otra. El Object Browser no merece otra cosa más que la muerte. En este caso voy a usar dotPeek de JetBrains por dos razones: funciona y es gratis.
¿Estás decepcionado porque no vamos a hacer nuestro propio decompilador .NET? No me he metido en el fregado porque ya lo hice hace cosa de 4 años, cuando me desperté y dije «voy a hacer un decompilador». Puedes echarle un ojo al código, pero con todos los cambios que ha habido en .NET desde entonces, lo sorprendente sería que decompilase algo más que un hola mundo jeje.
Si navegáis un poco por el ensamblado veréis varias cosas interesantes, pero para lo que nos ocupa ahora mismo, el método ReadXBFFile suena delicioso :D ¿Os imagináis lo que vamos a hacer verdad?
El “problema” es que la clase no es visible, así que nos toca usar un poco de reflectividad. Los pasos serían los siguientes:
- Crear un proyecto de consola
- Referenciar la librería ReducerEngine
- Obtener el Assembly a partir de un tipo público
- Instanciar la clase que queremos (Xbf2Xaml)
- Invocar el método «ReadXBFFile»
- Guardar el resultado del método en alguna parte
A continuación os dejo el código que yo he usado. Básicamente le pasas un directorio como primer argumento y lo escanea en busca de archivos XBF. Para cada uno de ellos invoca al método ReadXBFFile y guarda el resultado en el mismo directorio pero con extensión XAML.
static void Main(string[] args)
{
Console.WriteLine("Xinm v0.1 (Xinm Is Not Magic) - XAML Decompiler");
if (args.Length == 1 && Directory.Exists(args[0]))
{
Array.ForEach(Directory.GetFiles(args[0], "*.xbf"), file =>; File.WriteAllText(Path.GetFileNameWithoutExtension(file) + ".xaml", ReadFile(file)));
Console.WriteLine("All done!");
}
else
{
Console.Error.WriteLine("Usage:");
Console.Error.WriteLine("\t xinm.exe directoryWithXbfs");
}
}
public static string ReadFile(string filePath)
{
Console.WriteLine("Decompiling {0}...", Path.GetFileNameWithoutExtension(filePath));
// Let's get a public type of the assembly
var analisysEngine = typeof(AnalysisEngine);
// Now we can ask for any type inside the assembly (yes, even non-public ones)
var type = analisysEngine.Assembly.GetType("Xbf2Xaml.XBFReader");
// We've the type, so let's get the method we're interested in
var method = type.GetMethod("ReadXBFFile");
// Once we've the type, we need to build the object
var ctor = type.GetConstructor(new Type[] { typeof(string), analisysEngine });
// Second parameter is null because we don't need a fully constructed object just to call our method... so I don't bother
var reader = ctor.Invoke(new[] { filePath, null });
// Everything is ready... let's call our method!
return method.Invoke(reader, new object[] { }).ToString();
}
Ahora ya podemos usar Xinm (Xinm Is Not Magic) para decompilar los XAML. A pesar de lo sencillo de la herramienta, funciona como es debido y obtenemos los XAML en claro :)
Amén de que el equipo ha tenido bastante sentido del humor (echad un ojo a ErrorPage.xaml), no parece que haya nada en los XAML que necesitemos. Esto es bueno, significa que TrackSeries tiene una buena arquitectura y no están metiendo en XAML lógica de negocio. En otras palabras, hemos perdido el tiempo decompilándolos :P
Si algún día tenéis una tarde aburrida, podéis ir decompilando los XAML de las Apps que tenéis en el ordenador… es sorprendente lo que algunos desarrolladores pueden llegar a poner en ellos xD
A por las DLL
El equipo de TrackSeries ha sido lo suficientemente considerado como para separar su modelo en una DLL (TrackSeries.Web.Api.Models.DLL) y la lógica de negocio en otra (TrackSeries.Logic.DLL). El tercer archivo interesante es TrackSeries.exe, pero está íntimamente ligado a la plataforma, así que con las DLLs tenemos suficiente.
Lo normal aquí es usar un decompilador (yo he vuelto a usar dotPeek)
Parece ser que los programadores de TrackSeries se saltaron el curso de «Defensa contra las artes obscuras» y el código está sin obfuscar. De hecho, la arquitectura es buena y la nomenclatura bastante apropiada… y eso que estamos tratando con codigo decompilado. He tenido que mantener código «humano» mucho peor. Claro que no sé si eso dice mucho a favor de TrackSeries o en contra de algunos humanos… en fin, sigamos.
Llegados a este punto tenemos varias opciones. Podemos hacer ingeniería inversa (que no nos va a costar porque el código generado es muy claro) y crearnos nuestra propia librería, podemos generar un proyecto de VS con el código directamente desde dotPeek o podemos usar ambas DLLs en el proyecto que hagamos.
Recreando TrackSeries.Web.Api.Models.DLL
Esta DLL contiene las clases para almacenar la información que recibiremos y enviaremos a la API. Lo más cómodo es usar dotPeek para generar un proyecto de Visual Studio con el código decompilado. Podemos hacerlo sacando el menú contextual sobre el nombre del ensamblado y seleccionando «Export to Project…»
Si os fijáis, veréis que la librería es Portable usando el perfil 151. Que sea portable mola, que sea 151 no tanto. Mono, en el momento de escribir estas líneas, aun no soporta ese perfil… así que tendremos que retocar un poco las cosas.
Ahora nos conviene abrirlo con Xamarin Studio para asegurarnos de que el código sea compatible con Mono, ya que la idea es hacerlo correr en MacOS X. Si lo intentáis compilar tal cual, obtendréis un par de errores:
Los problemas que nos vamos a encontrar vienen de BindableBase y de distintas clases NoseQueOb. ¿Solución? Nos cargamos BindableBase y todas las NoseQueOb que veamos, no las vamos a necesitar.
Tras eliminar esos ficheros tenemos una compilación satisfactoria, pero aun sigue usando Framework 4.6, así que nos toca cambiarlo a 4.5:
Recreando TrackSeries.Logic.DLL
En este caso no voy a generar el proyecto desde dotPeek, sino que crearé el proyecto a mano únicamente con un puñado de clases. Principalmente porque la idea no es hacer una implementación completa, sino solamente una prueba de concepto y segundo porque la implementación de TrackSeries está pensada para ser consumida por una aplicación XAML y no va a ser así.
Vamos a crearnos un proyecto «Class Library (Portable)» en VS haciendo target a Framework 4.5:
Como prueba de concepto la única funcionalidad que quiero es:
- Autenticación básica (usuario y contraseña)
- Información del usuario
- Obtener lista de próximos capítulos
Si observáis la decompilación que hace dotPeek, podemos aprovechar mucho código. Básicamente necesitamos implementar lo suficiente para que los métodos «AccountInfo», «AccountLogin», «AccountLogout» y «GetFollowEpisodesComingUp» de la clase TrackSeriesApiService puedan funcionar.
El primer paso es implementar (léase, copiar directamente la decompilación de dotPeek) BaseProvider, que es de quién extiende TrackSeriesApiService. Sin embargo, esta depende del paquete NuGet «Microsoft.AspNet.WebApi.Client», así que nos toca instalarlo.
Depende del Framework 4.5, que es precisamente la versión más reciente soportada por Mono, así que todo bien :)
Esto debería solucionar todos los problemas de dependencias externas. Tan solo quería solucionar algunos fallos en BaseProvider. Por algún motivo, dotPeek ha decidido que es mucho más divertido usar HttpClient sin instanciarlo antes… pero al margen de eso el código es usable. Así pues nos quedaríamos con:
using System;
using System.Net.Http;
using System.Threading.Tasks;
using TrackSeries.Exceptions;
namespace TrackSeries.Logic.Services
{
public class BaseProvider
{
protected string _baseUrl;
protected HttpClient GetClient()
{
return GetClient(_baseUrl);
}
protected virtual HttpClient GetClient(string baseUrl)
{
HttpClient httpClient = new HttpClient();
Uri uri = new Uri(baseUrl);
httpClient.BaseAddress = uri;
return httpClient;
}
protected async Task Get(string url)
{
T obj;
using (HttpClient client = GetClient())
{
try
{
var response = await client.GetAsync(url);
if (!response.IsSuccessStatusCode)
{
var trackSeriesApiError = await HttpContentExtensions.ReadAsAsync(response.Content);
throw new TrackSeriesApiException(trackSeriesApiError != null ? trackSeriesApiError.Message : "", response.StatusCode);
}
obj = await HttpContentExtensions.ReadAsAsync(response.Content);
}
catch (Exception)
{
throw new TrackSeriesApiException("", false);
}
}
return obj;
}
protected async Task Post(string url, Tsource content)
{
Ttarget target;
using (HttpClient client = GetClient())
{
try
{
var response = await HttpClientExtensions.PostAsJsonAsync(client, url, content);
if (!response.IsSuccessStatusCode)
{
var trackSeriesApiError = await HttpContentExtensions.ReadAsAsync(response.Content);
throw new TrackSeriesApiException(trackSeriesApiError != null ? trackSeriesApiError.Message : "", response.StatusCode);
}
target = await HttpContentExtensions.ReadAsAsync(response.Content);
}
catch (Exception)
{
throw new TrackSeriesApiException("", false);
}
}
return target;
}
}
}
Con esto ya podemos «implementar» TrackSeriesApiService, que quedaría más o menos así:
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using TrackSeries.Web.Api.Models;
using TrackSeries.Web.Api.Models.Series;
namespace TrackSeries.Logic.Services
{
public class TrackSeriesApiService : BaseProvider
{
public string Token { get; set; }
public TrackSeriesApiService()
{
_baseUrl = "https://api.trackseries.tv/v1/";
}
public async Task AccountInfo()
{
return await Get("Account/Info");
}
public async Task AccountLogin(string username, string password)
{
TokenViewModel tokenViewModel = await Post("Account/Login", new LoginModel()
{
grant_type = "password",
UserName = username,
Password = password
});
Token = tokenViewModel.access_token;
return tokenViewModel;
}
public async Task> GetFollowEpisodesComingUp()
{
return await Get>("Follow/Episodes/ComingUp");
}
protected override HttpClient GetClient(string baseUrl)
{
var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(baseUrl);
if (Token != "")
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", Token);
httpClient.DefaultRequestHeaders.Add("apikey", "Windows8");
httpClient.DefaultRequestHeaders.Add("User-Agent", "Carballude-Client");
return httpClient;
}
}
}
La parte más interesante es el método GetClient de TrackSeriesApiService, pues es donde aparece el «apikey» (super-originales con el valor Windows8) que usa el cliente para autenticarse con la API. Ya que estaba, añadí un User-Agent distinto para que puedan diferenciar las peticiones que hace mi cliente.
Hasta ahora hemos obtenido el código XAML (aunque no nos haya servido para nada), hemos decompilado y recompilado para una versión inferior del framework la libraría TrackSeries.Web.Api.Models y por último hemos recreado algunas partes de la librería TrackSeries.Logic a modo prueba de concepto. ¿Qué nos queda? Pues hacer una aplicación que haga uso de ambas librerías.
Haciendo nuestra primera llamada a TrackSeries
Para esto no voy a comerme mucho la cabeza. Nos basta con crearnos un proyecto de consola con Xamarin Studio y agregar las dos DLLs que hemos creado en Windows al proyecto. Como ambas librerías hacen target a Framework 4.5, no tendremos problemas para correrlas en MacOS X.
El código del programa podría ser algo estilo:
using System;
using System.Linq;
using TrackSeries.Services;
using TrackSeries.Web.Api.Models;
namespace ConsoleApplication1
{
class Program
{
public Program()
{
Console.WriteLine ("Would it be possible to use TrackSeries on Mac?");
Console.WriteLine ("Let's see...");
var trackSeriesApi = new TrackSeriesApiService();
var tokenViewModel = trackSeriesApi.AccountLogin("carballude", "FAKE_PASSWORD").Result;
Console.WriteLine ("Login user: " + tokenViewModel.UserName + " of type " + tokenViewModel.token_type);
Console.WriteLine ("Loading upcoming TVShows...");
var episodes = trackSeriesApi.GetFollowEpisodesComingUp().Result;
episodes.Take(10).ToList().ForEach(x => Console.WriteLine(x.SerieName + " - " + x.Title));
}
static void Main(string[] args)
{
new Program();
}
}
}
Y podemos comprobar que efectivamente funciona:
Conclusión
Creo que todos teníamos claro que, a pesar de las apariencias, Windows no ofrece ningún tipo de protección para acceder a los binarios de las Apps de la Windows Store. Lo que quizá muchos no tienen tan claro, es la sencillez con la que las aplicaciones pueden ser decompiladas, diseccionadas e incluso regenerar su código fuente con un par de clicks usando únicamente herramientas gratuitas.
Esto también sirve como prueba de que si nos preocupamos mínimamente por separar la lógica de negocio de la capa de presentación de nuestras Apps, la migración a otras plataformas se convierte en algo tremendamente sencillo.
La próxima vez que hagáis una aplicación para la Windows Store recordad no dejar en el código cosas que no queráis que otros vean porque… seguramente alguien las verá.
Happy hacking!
Consciente da notícia, que resultou amplamente divulgada por sabido Jornal local, SECUNDUS, famoso mentor universitário, sentindo-se ofendido em sua reputação, pela inequívoca intenção com que fora declinado seu nome, ingressou,
no prazo lítico, com um queixa versus Diretor daquele Jornal, baseado nos
artigos 21 e 37, I e também seu § 3° da Lei n°
5.250/67, tendo em vista que PRIMUS, comunicado por missiva, negou houvesse autorizado a publicação da entrevista.
, entregue a Satanás para a ruína da carne, a
termo de que espírito seja salvo no dia do Jesus, reunidos vós e meu deus, com
ser capaz de Jesus, nosso A saída dos resíduos de relva é lateral e vem equipado com botão de segurança que impede a partida casual
do motor. , senhoria, supra.Exemplo : Chichiue , traduçã literal:
Stunning! You do not come by information similar to this quickly and
that I am gracious! Maintain it up guys!
I am no longer sure the place you’re getting your information,
but great topic. I must spend a while finding out much more or understanding more.
Thanks for fantastic information I used to be searching for this information for my mission.
As vagas serão a fim de 208 cursos em mas de 38
cidades do ES, essas vagas serão liberadas por meio do PRONATEC – Programa Nacional de Aproximação ao Ensino
Técnico e também Ofício.
Good information. Lucky me I found your website by accident (stumbleupon).
I have book-marked it for later!
Quality information! I must share this with my followers on Twitter.
Your style is really unique in comparison to other folks I’ve read stuff from.
Thanks for posting when you’ve got the opportunity, Guess I’ll just book mark this web site.
Just useful information is provided by this web
site and that I am checking to it-this instant! Thank
you people!
I see something really special in this website.
What i do not realize is in reality how you’re not actually a lot more well-liked than you
may be right now. You’re so intelligent. You realize thus considerably on the subject of this topic, produced me for my
part imagine it from so many various angles. Its like women and men aren’t involved unless
it is something to accomplish with Lady gaga!
Your individual stuffs nice. Always deal with it up!
Good post and straight to the point. I am not sure if this is in fact the best place to ask but do you
folks have any ideea where to get some professional writers?
Thx :)
Com exercícios de fixação a cada capítulo, curso do
Learn Cafe é completamente gratuito e também tem duração de 4h.
Quem quer conceder continuidade ao tirocínio conta com os módulos intermediário e avançado disponíveis no website,
também gratuitos.
Having read this I believed it was extremely informative.
I appreciate you taking the time and effort to put this short article together.
I once again find myself personally spending a lot of time
both reading and leaving comments. But so what, it was still worthwhile!
What i do not understood is in fact how you’re
no longer actually much more well-liked than you might be now.
You are very intelligent. You understand thus significantly in the case of
this matter, produced me personally imagine it from so many numerous angles.
Its like men and women are not interested until it’s one thing to accomplish
with Girl gaga! Your individual stuffs excellent.
At all times maintain it up!
Have you ever considered creating an e-book or guest authoring on other sites?
I have a blog based on the same information you discuss and would really like
to have you share some stories/information. I know my readers would appreciate your work.
If you are even remotely interested, feel free to shoot me an e mail.
I quite like looking through a post that can make people think.
Also, many thanks for allowing for me to comment!
Every weekend i used to pay a quick visit this web site, because i want enjoyment,
for the reason that this this site conations in fact good funny data
too.
http://Stylestudios.com/paul
Your style is really unique compared to other
folks I have read stuff from. Thanks for posting when you’ve got the opportunity, Guess I’ll just
bookk mark this blog.
Here iis my page :: juntar varios creditos num so [Michel]
What’s up, this weekend is good in support of me, for the reason that this
point in time i am reading this enormous educational piece of writing here at my residence.
Awesome information.
Hi there very cool website!! Man .. Excellent .. Amazing ..
I’ll bookmark your website and take the feeds additionally?
I am satisfied to find a lot of helpful info here in the submit, we need work out
extra strategies on this regard, thank you for sharing. . .
. . .
Good information. Lucky me I ran across your blog by accident (stumbleupon).
I’ve saved it for later!
Breathtaking! You do not come by information like this easily and I am not so ungrateful!
Keep it-up folks!
Troca de link nada mais é mas é só colocação
de outra pessoa blog link em sua página no apropriado lugar e outra pessoa também irão fazer mesmo para
você.
Yes! Finally something about wangralutako.
You actually make it appear really easy together with your
presentation however I find this matter to be
actually one thing that I believe I’d by no means understand.
It seems too complicated and very huge for me. I am taking a look forward on your subsequent submit, I will attempt to get the cling of it!
It’s a shame you don’t have a donate button! I’d without a doubt donate to this superb blog!
I suppose for now i’ll settle for book-marking and adding your RSS feed to my Google account.
I look forward to fresh updates and will talk about this
website with my Facebook group. Talk soon!
It’s really very complicated in this active life to listen news on TV,
thus I just use web for that purpose, and take the most up-to-date news.
Тогда их путевки также превращаются в горящие туры.
Great blog here! Additionally your web site quite a bit up fast!
What host are you the usage of? Can I am getting your associate hyperlink to your host?
I wish my site loaded up as fast as yours lol
If some one needs expert view regarding blogging
after that i advise him/her to pay a quick visit
this weblog, Keep up the nice job.
Hi friends, its great post concerning cultureand entirely explained, keep it up all the time.
Quality content is the crucial to be a focus for the users to visit the web
page, that’s what this site is providing.
Great post. I’m going through some of these issues as well..
What’s up, everything is going fine here and ofcourse every one is sharing information, that’s in fact excellent, keep
up writing.
This is very interesting, You’re a very skilled blogger.
I have joined your feed and sit up for in quest of
more of your magnificent post. Also, I’ve shared your web site in my social
networks
Awesome! Its really awesome piece of writing, I have got much clear idea
about from this piece of writing.
Having read this I believed it was rather informative. I appreciate you spending some time and
effort to put this informative article together. I once again find myself personally spending a significant amount
of time both reading and commenting. But so what, it was still
worthwhile!
I will fix wordpress issues, error, problem, bugs
Link: …
I will fix things:
Fix WordPress errors, layout issues, any CSS errors, plugin errors.
PHP errors: the call too undefined functions, fatal error etc., responsiveness issues.
speed up WP site.
Alsoo visi my weblog: Helen
This is really attention-grabbing, You’re an overly skilled
blogger. I have joined your feed and look ahead to looking for more of your magnificent post.
Also, I’ve shared your website in my social networks
I don’t even know how I ended up here, but I believed
this submit was great. I don’t know who you’re but definitely you’re going to a well-known blogger
in the event you aren’t already. Cheers!
Nice blog here! Also your web site a lot up fast! What web host are you the
usage of? Can I am getting your associate link on your host?
I desire my web site loaded up as fast as yours lol
It’s awesome designed for me to have a web page, which
is beneficial in favor of my knowledge. thanks admin
I am no longer sure where you are getting your info, but great topic.
I needs to spend a while studying more or working out more.
Thank you for excellent info I used to be looking for this information for my mission.
Wonderful goods from you, man. I’ve take into account your
stuff previous to and you’re simply too wonderful. I actually like
what you have acquired right here, certainly like what you’re stating and the way in which during
which you assert it. You are making it enjoyable and you continue to care for to stay it
sensible. I can not wait to learn far more from you. This is really a terrific site.
I am really glad to glance at this website posts which carries tons of
valuable data, thanks for providing these information.
It’s going to be ending of mine day, except before ending I am reading this great post to improve my experience.
Thanks for the marvelous posting! I genuinely enjoyed reading
it, you happen to be a great author.I will be sure to bookmark your blog
and will come back sometime soon. I want to encourage you
to definitely continue your great writing, have a nice day!
Good respond in return of this question with solid arguments and explaining the whole thing on the topic of that.