# -*- coding: utf-8 -*- import base64 import binascii import re import pycurl from Crypto.Cipher import AES from module.plugins.Crypter import Crypter from module.utils import html_unescape class LinkCryptWs(Crypter): __name__ = "LinkCryptWs" __type__ = "crypter" __version__ = "0.07" __pattern__ = r'http://(?:www\.)?linkcrypt\.ws/(dir|container)/(?P<ID>\w+)' __description__ = """LinkCrypt.ws decrypter plugin""" __license__ = "GPLv3" __authors__ = [("kagenoshin", "kagenoshin[AT]gmx[DOT]ch"), ("glukgluk", None), ("Gummibaer", None)] CRYPTED_KEY = "crypted" JK_KEY = "jk" def setup(self): self.captcha = False self.links = [] self.sources = ['cnl', 'web', 'dlc', 'rsdf', 'ccf'] def prepare(self): # Init self.fileid = re.match(self.__pattern__, self.pyfile.url).group('ID') self.req.cj.setCookie("linkcrypt.ws", "language", "en") # Request package self.req.http.c.setopt(pycurl.USERAGENT, "Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko") #: better chance to not get those key-captchas self.html = self.load(self.pyfile.url) def decrypt(self, pyfile): if not self.js: self.fail(_("Missing JS Engine")) self.prepare() if not self.isOnline(): self.offline() if self.isKeyCaptchaProtected(): self.retry(4, 30, _("Can't handle Key-Captcha")) if self.isCaptchaProtected(): self.captcha = True self.unlockCaptchaProtection() self.handleCaptchaErrors() # Check for protection if self.isPasswordProtected(): self.unlockPasswordProtection() self.handleErrors() # get unrar password self.getunrarpw() # Get package name and folder package_name, folder_name = self.getPackageInfo() #get the container definitions from script section self.get_container_html() # Extract package links for type in self.sources: links = self.handleLinkSource(type) if links: self.links.extend(links) break if self.links: self.packages = [(package_name, self.links, folder_name)] def isOnline(self): if "<title>Linkcrypt.ws // Error 404</title>" in self.html: self.logDebug("folder doesen't exist anymore") return False else: return True def isPasswordProtected(self): if "Authorizing" in self.html: self.logDebug("Links are password protected") return True else: return False def isCaptchaProtected(self): if 'id="captcha">' in self.html: self.logDebug("Links are captcha protected") return True else: return False def isKeyCaptchaProtected(self): if re.search(r'Key[ -]', self.html, re.I): return True else: return False def unlockPasswordProtection(self): password = self.getPassword() if password: self.logDebug("Submitting password [%s] for protected links" % password) self.html = self.load(self.pyfile.url, post={"password": password, 'x': "0", 'y': "0"}) else: self.fail(_("Folder is password protected")) def unlockCaptchaProtection(self): captcha_url = re.search(r'<form.*?id\s*?=\s*?"captcha"[^>]*?>.*?<\s*?input.*?src="([^"]*?)"', self.html, re.I | re.S).group(1) captcha_code = self.decryptCaptcha(captcha_url, forceUser=True, imgtype="gif", result_type='positional') self.html = self.load(self.pyfile.url, post={"x": captcha_code[0], "y": captcha_code[1]}) def getPackageInfo(self): name = self.pyfile.package().name folder = self.pyfile.package().folder self.logDebug("Defaulting to pyfile name [%s] and folder [%s] for package" % (name, folder)) return name, folder def getunrarpw(self): sitein = self.html indexi = sitein.find("|source|") + 8 indexe = sitein.find("|",indexi) unrarpw = sitein[indexi:indexe] if not (unrarpw == "Password" or "Dateipasswort") : self.logDebug("File password set to: [%s]"% unrarpw) self.pyfile.package().password = unrarpw def handleErrors(self): if self.isPasswordProtected(): self.fail(_("Incorrect password")) def handleCaptchaErrors(self): if self.captcha: if "Your choice was wrong!" in self.html: self.invalidCaptcha() self.retry() else: self.correctCaptcha() def handleLinkSource(self, type): if type == 'cnl': return self.handleCNL2() elif type == 'web': return self.handleWebLinks() elif type in ('rsdf', 'ccf', 'dlc'): return self.handleContainer(type) else: self.fail(_("Unknown source type: %s") % type) #@TODO: Replace with self.error in 0.4.10 def handleWebLinks(self): self.logDebug("Search for Web links ") package_links = [] pattern = r'<form action="http://linkcrypt.ws/out.html"[^>]*?>.*?<input[^>]*?value="([^"]*?)"[^>]*?name="file"' ids = re.findall(pattern, self.html, re.I | re.S) self.logDebug("Decrypting %d Web links" % len(ids)) for idx, weblink_id in enumerate(ids): try: self.logDebug("Decrypting Web link %d, %s" % (idx + 1, weblink_id)) res = self.load("http://linkcrypt.ws/out.html", post = {'file':weblink_id}) indexs = res.find("window.location =") + 19 indexe = res.find('"', indexs) link2 = res[indexs:indexe] self.logDebug(link2) link2 = html_unescape(link2) package_links.append(link2) except Exception, detail: self.logDebug("Error decrypting Web link %s, %s" % (weblink_id, detail)) return package_links def get_container_html(self): self.container_html = [] script = re.search(r'<div.*?id="ad_cont".*?<script.*?javascrip[^>]*?>(.*?)</script', self.html, re.I | re.S) if script: container_html_text = script.group(1) container_html_text.strip() self.container_html = container_html_text.splitlines() def handle_javascript(self, line): return self.js.eval(line.replace('{}))',"{}).replace('document.open();document.write','').replace(';document.close();',''))")) def handleContainer(self, type): package_links = [] type = type.lower() self.logDebug('Search for %s Container links' % type.upper()) if not type.isalnum(): # check to prevent broken re-pattern (cnl2,rsdf,ccf,dlc,web are all alpha-numeric) self.fail(_("Unknown container type: %s") % type) #@TODO: Replace with self.error in 0.4.10 for line in self.container_html: if type in line: jseval = self.handle_javascript(line) clink = re.search(r'href=["\']([^"\']*?)["\']',jseval,re.I) if not clink: continue self.logDebug("clink avaible") package_name, folder_name = self.getPackageInfo() self.logDebug("Added package with name %s.%s and container link %s" %( package_name, type, clink.group(1))) self.core.api.uploadContainer( "%s.%s" %(package_name, type), self.load(clink.group(1))) return "Found it" return package_links def handleCNL2(self): self.logDebug("Search for CNL links") package_links = [] cnl_line = None for line in self.container_html: if "cnl" in line: cnl_line = line break if cnl_line: self.logDebug("cnl_line gefunden") try: cnl_section = self.handle_javascript(cnl_line) (vcrypted, vjk) = self._getCipherParams(cnl_section) for (crypted, jk) in zip(vcrypted, vjk): package_links.extend(self._getLinks(crypted, jk)) except: self.logError(_("Unable to decrypt CNL links (JS Error) try to get over links")) return self.handleWebLinks() return package_links def _getCipherParams(self, cnl_section): # Get jk jk_re = r'<INPUT.*?NAME="%s".*?VALUE="(.*?)"' % LinkCryptWs.JK_KEY vjk = re.findall(jk_re, cnl_section) # Get crypted crypted_re = r'<INPUT.*?NAME="%s".*?VALUE="(.*?)"' % LinkCryptWs.CRYPTED_KEY vcrypted = re.findall(crypted_re, cnl_section) # Log and return self.logDebug("Detected %d crypted blocks" % len(vcrypted)) return vcrypted, vjk def _getLinks(self, crypted, jk): # Get key jreturn = self.js.eval("%s f()" % jk) key = binascii.unhexlify(jreturn) self.logDebug("JsEngine returns value [%s]" % jreturn) # Decode crypted crypted = base64.standard_b64decode(crypted) # Decrypt Key = key IV = key obj = AES.new(Key, AES.MODE_CBC, IV) text = obj.decrypt(crypted) # Extract links text = text.replace("\x00", "").replace("\r", "") links = text.split("\n") links = filter(lambda x: x != "", links) # Log and return self.logDebug("Package has %d links" % len(links)) return links