一:准备工作 APPcmd.exe 位于 C:\Windows\System32\inetsrv 目录 使用 Cd c:\Windows\System32\inetsrv 切换到该目录 二:命令操作简介 IIS 命令行管理工具基本格式: APPCMD (命令) (对象类型) <标识符> </参数1:值1 …> 支持的对象类型: SITE 虚拟站点的管理 APP 管理应用程序 VDIR 管理虚拟目录 APPPOOL 管理应用程序池 CONFIG 管理常规配置节 WP 管理工作进程 REQUEST 管理 HTTP 请求 MODULE 管理服务器模块 BACKUP 管理服务器配置备份 TRACE 使用失败请求跟踪日志 ———–这个就不解释很详细了,懂的人都知道这个IIS的层次。 三:对Site的命令详解 1.命令介绍 虚拟站点的管理 APPCMD <命令> SITE [标识符] [-参数1:值1 …] 如: appcmd list site (列出当前存在的站点) 支持的命令: list 列出虚拟站点 set 配置虚拟站点 add 添加新虚拟站点 delete 删除虚拟站点 start 启动虚拟站点 stop 停止虚拟站点 2.详解 列出虚拟站点 APPCMD list SITE [标识符] [-参数1:值1 …] 列出计算机上的虚拟站点。此命令可用于使用特定站点的标识符或 url 查找该站点,或者 基于指定的站点属性匹配零个或多个站点。 示例: appcmd list sites 列出计算机上的所有站点。 appcmd list site "Default Web Site" 查找站点“Default Web Site”。 appcmd list site http://localhost/app1 查找与指定的 url 关联的站点。 appcmd list site /serverAutoStart:false 查找“serverAutoStart”配置属性设置为“false”的所有站点。 添加新虚拟站点 APPCMD add SITE [标识符] [-参数1:值1 …] 使用指定的设置创建新的虚拟站点。至少,必须提供站点名称和 ID。 支持的参数: /name (必需) 站点名称 /id 站点 ID /bindings “http://domain:port,…”好记格式或“protocol/bindingInformation,…”原始格式的绑定列表 /physicalPath 如果指定,将导致为该站点创建包含指向指定路径的根虚拟目录的根应用程序。如果忽 略,将创建一个没有根应用程序的站点,并且该站点在创建根应用程序之前将无法启动。 示例: appcmd add site /name:"My New Site" /id:2 /bindings:http://www.domain.com:80 使用指定的好记绑定创建一个新站点“我的新站点”,以便在端口 80 上侦听对 www.d omain.com 的 http 请求。 appcmd add site /name:"My New Site" /id:2 /bindings:http://*:81 使用指定的好记绑定创建一个新站点“我的新站点”,以便在端口 81 上侦听对任何域 名的 http 请求。 appcmd add site /name:"My New Site" /id:2 /bindings:http/:81: 使用指定的 ID 和绑定创建新站点“我的新站点”。创建后,新站点中不包含任何应用 程序。 appcmd add site /name:"My New Site" /id:2 /bindings:http/:81: /physicalPath:"c :\inetpub\mynewsite" 使用指定的 ID 和绑定创建新站点“我的新站点”。系统将自动创建根虚拟目录指向指 定物理路径的根应用程序。 appcmd add site /name:"My New Site" /bindings:http/*:81: 使用绑定创建新站点“我的新站点”,并自动生成站点 ID。创建后,该新站点不包含 任何应用程序。 配置虚拟站点 APPCMD set SITE [标识符] [-参数1:值1 …] 允许设置指定虚拟站点的属性。必须提供准确的站点标识符,并且必须将该标识符解析为现 有站点。 支持的参数: identifier (必需) 要修改的站点的站点名称或 url /site.name 要修改的站点的站点名称或 url (与标识符相同) /bindings “http://domain:port,…”好记格式或“protocol/bindingInformation,…”原始 格式的绑定列表 示例: appcmd set site "Default Web Site" /serverAutoStart:false /id:100 设置“Default Web Site”站点的“serverAutoStart”和“id”属性。 这里对 set 操作附加一些高级操作(重要) 当时用 appcmd set site "Default Web Site" 当时用后其他高级参数 解释后续补上………… -name 名称 -id ID标识 -serverAutoStart 是否自动启动 -bindings.[protocol=’string’,bindingInformation=’string’].p -bindings.[protocol=’string’,bindingInformation=’string’].b -limits.maxBandwidth -limits.maxConnections -limits.connectionTimeout -logFile.logExtFileFlags -logFile.customLogPluginClsid -logFile.logFormat -logFile.directory -logFile.period -logFile.truncateSize -logFile.localTimeRollover -logFile.enabled -traceFailedRequestsLogging.enabled -traceFailedRequestsLogging.directory -traceFailedRequestsLogging.maxLogFiles -traceFailedRequestsLogging.maxLogFileSizeKB -traceFailedRequestsLogging.customActionsEnabled -applicationDefaults.path -applicationDefaults.applicationPool -applicationDefaults.enabledProtocols -virtualDirectoryDefaults.path -virtualDirectoryDefaults.physicalPath 网站的物理路径 -virtualDirectoryDefaults.userName 物理路径凭据的账户 -virtualDirectoryDefaults.password 物理路径凭据的密码 -virtualDirectoryDefaults.logonMethod -virtualDirectoryDefaults.allowSubDirConfig -[path=’string’].path -[path=’string’].applicationPool -[path=’string’].enabledProtocols -[path=’string’].virtualDirectoryDefaults.path -[path=’string’].virtualDirectoryDefaults.physicalPath -[path=’string’].virtualDirectoryDefaults.userName -[path=’string’].virtualDirectoryDefaults.password -[path=’string’].virtualDirectoryDefaults.logonMethod -[path=’string’].virtualDirectoryDefaults.allowSubDirConfig -[path=’string’].[path=’string’].path -[path=’string’].[path=’string’].physicalPath -[path=’string’].[path=’string’].userName -[path=’string’].[path=’string’].password -[path=’string’].[path=’string’].logonMethod -[path=’string’].[path=’string’].allowSubDirConfig 删除虚拟站点 APPCMD delete SITE [标识符] [-参数1:值1 …] 描叙: 从计算机删除指定的虚拟站点。必须提供准确的站点标识符,并且必须将该标识符解析为现有站点。 支持的参数: identifier (必需)要删除的站点的站点名称或 url /site.name 要删除的站点的站点名称或 url (与标识符相同) 示例: appcmd delete site "Default Web Site" 删除站点“Default Web Site”。 启动虚拟站点 APPCMD start SITE [标识符] [-参数1:值1 …] 启动指定的虚拟站点并启用该站点,以开始侦听其所有绑定端点上的新请求。必须提供准确的站点标识符,并且必须将该标识符解析为现有站点。 支持的参数: identifier (必需)要启动的站点的站点名称或 url /site.name 要启动的站点的站点名称或 url (与标识符相同) 示例: appcmd start site "Default Web Site" 启动站点“Default Web Site”。 停止虚拟站点 APPCMD stop SITE [标识符] [-参数1:值1 …] 停止指定的虚拟站点,阻止在其绑定端点上接收新请求。必须提供准确的站点标识符,并且 必须将该标识符解析为现有站点。 支持的参数: identifier (必需)要停止的站点的站点名称或 url /site.name 要停止的站点的站点名称或 url (与标识符相同) 示例: appcmd stop site "Default Web Site" 停止站点“Default Web Site”。 转自:http://blog.chinaunix.net/uid-20787846-id-3126923.html
作者: zhongdaiqi
-
Hook javascript (转)
一、概述 javascript函数劫持,也就是老外提到的javascript hijacking技术。最早还是和剑心同学讨论问题时偶然看到的一段代码,大概这样写的: window.alert = function(s) {}; 觉得这种用法很巧妙新颖,和API Hook异曲同工,索性称之为javascript function hook,也就是函数劫持。通过替换js函数的实现来达到劫持这个函数调用的目的,一个完整的hook alert函数例子如下: <!–1.htm–> <script type="text/javascript"> <!– var _alert = alert; window.alert = function(s) { if (confirm("是否要弹框框,内容是/"" + s + "/"?")) { _alert(s); } } //–> </script> <html> <body> <input type="button" onclick="javascript: alert(‘Hello World!’)" value="test" /> </body> </html> 搞过API Hook的同学们看到这个代码一定会心的一笑,先保存原函数实现,然后替换为我们自己的函数实现,添加我们自己的处理逻辑后最终再调用原来的函数实现,这样这个alert函数就被我们劫持了。原理非常简单,下面举些典型的应用来看看我们能利用它来做些什么。 二、应用举例
- 实现一个简易的javascript debugger,这里说是debugger比较标题党,其实只是有点类似于debugger的功能,主要利用js函数劫持来实现函数的break point,来看看个简单的例子: <script type="text/javascript"> <!– var _eval = eval; eval = function(s) { if (confirm("eval被调用/n/n调用函数/n" + eval.caller + "/n/n调用参数/n" + s)) { _eval(s); } } //–> </script> <html> <head> </head> <body> <script type="text/javascript"> <!– function test() { var a = "alert(1)"; eval(a); } function t() { test(); } t(); //–> </script> </body> </html> 通过js函数劫持中断函数执行,并显示参数和函数调用者代码,来看一个完整例子的效果: >help debug commands: bp <function name> – set a breakpoint on a function, e.g. "bp window.alert". bl – list all breakpoints. bc <breakpoint number> – remove a breakpoint by specified number, e.g. "bc 0". help – help information. >bp window.alert
- breakpoint on function "window.alert" added successfully. >bl
- 1 breakpoints: 0 – window.alert >bc 0
- breakpoint on function "window.alert" deleted successfully. 这里演示设置断点,察看断点和删除断点,完整代码在本文附录[1]给出。
- 设置陷阱实时捕捉跨站测试者,搞跨站的人总习惯用alert来确认是否存在跨站,如果你要监控是否有人在测试你的网站xss的话,可以在你要监控的页面里hook alert函数,记录alert调用情况: <script type="text/javascript"> <!– function log(s) { var img = new Image(); img.style.width = img.style.height = 0; img.src = "http://yousite.com/log.php?caller=" + encodeURIComponent(s); } var _alert = alert; window.alert = function(s) { log(alert.caller); _alert(s); } //–> </script> 当然,你这个函数要加到页面的最开始,而且还要比较隐蔽一些,赫赫,你甚至可以使alert不弹框或者弹个警告框,让测试者抓狂一把。
- 实现DOM XSS自动化扫描,目前一般的XSS自动化扫描的方法是从http返回结果中搜索特征来确定是否存在漏洞,但是这种方法不适用于扫描DOM XSS,因为DOM XSS是由客户端脚本造成的,比如前段时间剑心发现的google的跨站(见附录[2])原理如下: document.write(document.location.hash); 这样的跨站无法反映在http response里,所以传统扫描方法没法扫描出来。但是如果你从上个例子里受到启发的话,一定会想到设置陷阱的办法,DOM XSS最终导致alert被执行,所以我们hook alert函数设置陷阱,如果XSS成功则会去调用alert函数,触发我们的陷阱记录结果,这样就可以实现DOM XSS的自动化扫描,陷阱代码类似于上面。
- 灵活的使用js劫持辅助你的页面代码分析工作,比如分析网页木马时,经常会有通过eval或者document.write来进行加密的情况,于是我们编写段hook eval和document.write的小工具,辅助解密: <script type="text/javascript"> <!– var _eval = eval; eval = window.execScript = window.document.write = window.document.writeln = function(s) { document.getElementById("output").value = s; } //–> </script> <html> <body> input: <textarea id="input" cols="80" rows="10"></textarea> <input type="button" onclick="javascript: _eval(document.getElementById(‘input’).value);" value="decode" /> <br /> output: <textarea id="output" cols="80" rows="10"></textarea> </body> </html> 在input框里输入加密的代码: eval(unescape("%61%6C%65%72%74%28%31%29%3B")); 在output框里输出解码后的代码: alert(1); 当然你还能想到更多的灵活应用:) 三、反劫持 谈到劫持也就必然要谈谈反劫持的话题,假设你要写一个健壮的xss playload,就需要考虑反劫持,有两个问题要解决: 如何判断是否被劫持了? 如果发现被劫持了,如何反劫持?
- 判断某个函数是否被劫持,这个好办,写个小程序对比一下一个函数被hook前后,有什么差别: <textarea id="tb1" cols="80" rows="8"></textarea> <script type="text/javascript"> <!– document.getElementById("tb1").value = eval + "/n"; var _eval = eval; eval = function(s) { alert(s); _eval(s); } document.getElementById("tb1").value += eval; //–> </script> 结果: function eval() { [native code] } function(s) { alert(s); _eval(s); } 我们发现那些内置函数是[native code],而自定义的则是具体的函数定义,用这个特征就可以简单的检测函数是否被劫持: function checkHook(proc) { if (proc.toString().indexOf("[native code]") > 0) { return false; } else { return true; } }
- 如何反劫持,第一个想法就是恢复被劫持的函数,如果劫持的人把原函数保存在某个变量里那还好办,直接调用原函数就可以了,但是劫持者自己也没保存副本怎么办,只能自己创建个新的环境,然后用新环境里的干净的函数来恢复我们这里被hook了的,怎么创建新环境?整个新的iframe好了,里面就是个全新的环境。ok,动手吧: function unHook(proc) { var f = document.createElement("iframe"); f.style.border = "0"; f.style.width = "0"; f.style.height = "0"; document.body.appendChild(f); var d = f.contentWindow.document; d.write("<script type=/"text/javascript/">window.parent.escape = escape;<//script>"); d.close(); } 综合1、2节,整个测试代码如下: <!–antihook.htm–> <script type="text/javascript"> <!– escape = function(s) { return s; } //–> </script> <html> <body> <input type="button" onclick="javascript: test();" value="test" /> <script type="text/javascript"> <!– function test() { alert(escape("s y")); if (checkHook(escape)) { unHook(escape); } alert(escape("s y")); } function checkHook(proc) { if (proc.toString().indexOf("[native code]") > 0) { return false; } else { return true; } } function unHook(proc) { var f = document.createElement("iframe"); f.style.border = "0"; f.style.width = "0"; f.style.height = "0"; document.body.appendChild(f); var d = f.contentWindow.document; d.write("<script type=/"text/javascript/">window.parent.escape = escape;<//script>"); d.close(); } //–> </script> </body> </html>
- 不是上面两个问题都解决了么,为什么要有第3节?因为那不是个最好的解决办法,既然我们可以创建全新的iframe,何不把代码直接放到全新iframe里执行呢,这样做的话绿色环保,既不用考虑当前context里的hook问题,也不用改动当前context,不会影响本身的程序执行。给出两个比较通用点的函数: function createIframe(w) { var d = w.document; var newIframe = d.createElement("iframe"); newIframe.style.width = 0; newIframe.style.height = 0; d.body.appendChild(newIframe); newIframe.contentWindow.document.write("<html><body></body></html>"); return newIframe; } function injectScriptIntoIframe(f, proc) { var d = f.contentWindow.document; var s = "<script>/n(" + proc.toString() + ")();/n</script>"; d.write(s); } 把你的payload封装进一个函数,然后调用这两个方法来在iframe里执行: function payload() { // your code goes here } var f = createIframe(top); injectScriptIntoIframe(f, payload); 四、最后 由于国内很少有见文章提及这个东西,所以才草成这篇,希望能够抛砖引玉。由于本人水平有限,难免有错误或者疏漏之处请谅解,没有说清楚的地方,欢迎和我交流。 还有就是一些不得不感谢的人,感谢剑心一直以来毫无保留的交流,感谢黑锅多次鼓励我把自己的心得体会写成文字,感谢幻影所有的朋友们、B.C.T的朋友们以及群里那帮经常一起扯淡的朋友们。 广告一下,没法幻影blog的朋友,可以添加hosts来突破: 72.14.219.190 pstgroup.blogspot.com 五、附录 [1] 简易的javascript inline debugger代码 <!–test.htm–> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <html xmlns="http://www.w3.org/1999/xhtml”> <head><title>Javascript Inline Debugger</title></head> <body> <script language="javascript" type="text/javascript" src="js_inline_debugger.js"></script> <input type="button" value="hahaha" onclick="javascript: alert(this.value);" style="margin-left: 300px;" /> </body> </html> / FileName: js_inline_debugger.js Author: luoluo Contact: luoluonet_at_yahoo.cn Date: 2007-6-27 Version: 0.1 Usage: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <html xmlns="http://www.w3.org/1999/xhtml”> <head> </head> <body> <script language="javascript" type="text/javascript" src="js_inline_debugger.js"></script> </body> </html> Instruction: It is a simple javascript inline debugger. You must add xhtml1-transitional dtd to your html document if you wanna to use the script. / //————————————————————————–// // 公用的函数 //————————————————————————–// // 判断是否是IE function isIE() { return document.all && window.external; } // 去除字符串两边的空格 String.prototype.trim = function() { var re = /(^/s)|(/s)$/g; return this.replace(re, ""); } // 删除数组中某个元素 Array.prototype.remove = function(i) { var o = this[i]; for (var j = i; j < this.length – 1; j ++) { this[j] = this[j + 1]; } — this.length; return o; } // 判断一个数组中是否存在相同的元素 Array.prototype.search = function(o) { for (var i = 0; i < this.length; i ++) { if (this[i] == o) { return i; } } return -1; } // html编码 function htmlEncode(s) { s = s.replace(/&/g, "&"); s = s.replace(/</g, "<"); s = s.replace(/>/g, ">"); s = s.replace(//"/g, """); s = s.replace(//’/g, """); return s; } // js编码 function jsEncode(s) { s = s.replace(////g, "////"); s = s.replace(//n/g, "//n"); s = s.replace(//"/g, "///""); s = s.replace(//’/g, "///’"); return s; } //————————————————————–// // 命令行窗口工具 //————————————————————–// function Console(parentNode, processInputProc) { // 窗口 var panel = document.createElement("div"); panel.style.width = "250px"; panel.style.height = "250px"; panel.style.borderColor = "#666666"; panel.style.borderWidth = "1px"; panel.style.backgroundColor = "#ffffff"; panel.style.borderStyle = "solid"; panel.style.position = "absolute"; panel.style.zIndex = 100; parentNode.appendChild(panel); // 标题栏 var title = document.createElement("div"); title.style.width = "250px"; title.style.height = "15px"; title.style.backgroundColor = "#dddddd"; title.style.fontSize = "12px"; title.style.fontFamily = "verdana,tahoma"; panel.appendChild(title); // 标题栏拖动窗口功能 var isDragging = false; var startPos = new Position(panel.offsetLeft, panel.offsetTop); var startMousePos = new Position(0, 0); title.onmouseover = function(evt) { this.style.cursor = "pointer"; } title.onmousedown = function(evt) { if (isDragging == true) { return; } var event = evt || window.event; startMousePos.x = event.clientX; startMousePos.y = event.clientY; isDragging = true; } title.onmousemove = function(evt) { if (isDragging == false) { return; } activateWindow(); var event = evt || window.event; panel.style.left = (event.clientX – startMousePos.x) + startPos.x + "px"; panel.style.top = (event.clientY – startMousePos.y) + startPos.y + "px"; } title.onmouseup = function(evt) { if (isDragging == false) { return; } var event = evt || window.event; startPos.x = (event.clientX – startMousePos.x) + startPos.x; panel.style.left = startPos.x + "px"; startPos.y = (event.clientY – startMousePos.y) + startPos.y; panel.style.top = startPos.y + "px"; isDragging = false; } // 关闭窗口功能 var closeButton = document.createElement("div"); closeButton.style.width = "13px"; closeButton.style.height = "13px"; closeButton.style.backgroundColor = "#ffffff"; closeButton.style.styleFloat = closeButton.style.cssFloat = "left"; closeButton.style.fontSize = "12px"; closeButton.style.borderWidth = "1px"; closeButton.style.borderColor = "#999999"; closeButton.style.borderStyle = "solid"; closeButton.style.paddingButton = "2px"; closeButton.innerHTML = "<div style=/"margin-top: -2px;margin-left: 3px;/">x</div>"; title.appendChild(closeButton); var isVisible = true; // 窗口关闭 closeButton.onclick = function(evt) { isVisible = false; panel.style.display = "none"; } // 标题栏文字 var titleLabel = document.createElement("div"); titleLabel.style.height = "14px"; titleLabel.style.width = "200px"; titleLabel.style.fontSize = "11px"; titleLabel.style.color = "#ffffff"; titleLabel.style.styleFloat = titleLabel.style.cssFloat = "left"; titleLabel.style.fontFamily = "verdana,tahoma"; titleLabel.innerHTML = "Javascript Inline Debugger"; //titleLabel.style.marginTop = isIE() ? -14 : "-14px"; titleLabel.style.marginLeft = isIE() ? 18 : "18px"; title.appendChild(titleLabel); // 中间的显示部分 var showPanel = document.createElement("div"); showPanel.style.width = "250px"; showPanel.style.height = isIE() ? "221px" : "219px"; showPanel.style.fontSize = "11px"; showPanel.style.padding = "0"; showPanel.style.margin = "0"; showPanel.style.overflow = "auto"; showPanel.style.marginTop = isIE() ? -1 : "0"; panel.appendChild(showPanel); // 命令输入框 var cmdArea = document.createElement("div"); panel.appendChild(cmdArea); var cmdTextbox = document.createElement("input"); cmdTextbox.type = "text"; cmdTextbox.style.width = "250px"; cmdTextbox.style.height = "12px"; cmdTextbox.style.fontSize = "12px"; cmdTextbox.style.padding = "0"; cmdTextbox.style.margin = "0"; cmdTextbox.style.marginTop = isIE() ? -2 : "0"; cmdTextbox.style.borderWidth = "0"; cmdTextbox.style.borderTopWidth = "1px"; cmdTextbox.style.paddingTop = "2px"; cmdArea.appendChild(cmdTextbox); // 窗口激活或者不激活 var isActive = false; // 激活窗口 var activateWindow = function() { if (! isVisible) { return; } if (isActive) { return; } cmdTextbox.focus(); title.style.backgroundColor = "#015DF4"; isActive = true; } panel.onclick = activateWindow; // 不激活窗口 var deactivateWindow = function() { if (! isVisible) { return; } if (! isActive) { return; } title.style.backgroundColor = "#cccccc"; isActive = false; } cmdTextbox.onfocus = activateWindow; cmdTextbox.onblur = deactivateWindow; // 输出函数 var dbgPrint = function(s) { showPanel.innerHTML += htmlEncode(s).replace(//n|(/r/n)/g, "<br />"); if (parseInt(showPanel.scrollHeight) > parseInt(showPanel.style.height)) { showPanel.scrollTop += parseInt(showPanel.scrollHeight) – parseInt(showPanel.style.height); } } // 调用输入处理回调函数 cmdTextbox.onkeydown = function(evt) { var event = evt || window.event; if (event.keyCode == 0x0d) { processInputProc(this.value, dbgPrint); this.value = ""; } } } // 坐标类 function Position(x, y) { this.x = x; this.y = y; } //————————————————————————–// // 内联调试器类 //————————————————————————–// function InlineDebugger() { var bpList = new Array(); var id_eval; // 设置断点 var bp = function(funcName) { // 检查eval是否被hook if ((new String(eval)).indexOf("[native code]") < 0) { return "error: eval function was hooked by other codes in the front./n"; } // 保存未被hooked的eval ideval = eval; var re = /^[a-zA-Z0-9/.]+$/i; if (! re.test(funcName)) { return "error: bad argument of command bp /"" + funcName + "/"./n"; } try { ideval("if (typeof(" + funcName + ") == /"object/" || typeof(" + funcName + ") == /"function/") {var obj = " + funcName + ";}"); } catch (e) { return "error: " + e + ", invalid function name /"" + funcName + "/"./n"; } if (obj == undefined) { return "error: the argument of command bp /"" + funcName + "/" is not a function object./n"; } if ((new String(obj)).indexOf("function") < 0) { return "error: the argument of command bp /"" + funcName + "/" is a property, a function is required./n"; } if (bpList.search(funcName) >= 0) { return "error: there is a breakpoint on function /"" + funcName + "/"/n"; } try { var sc = "window." + funcName.replace(//./g, "") + "_bak = " + funcName + ";/n" + funcName + " = " + "function() {/n" + " var args = /"/";/n" + " for (var i = 0; i < arguments.length; i ++) {/n" + " args += arguments[i] + (i == (arguments.length – 1) ? /"/" : /",/");/n" + " }/n" + " if (confirm(/"function ///"" + funcName + "///" was called, execute it?//n//narguments://n/" + args + /"//n//ncaller://n/" + " + funcName + ".caller)) {/n" + " ideval(/"" + funcName.replace(//./g, "") + "_bak(///"/" + jsEncode(args) + /"///")/");/n" + " }/n" + "};" + "/n"; ideval(sc); bpList.push(funcName); return " breakpoint on function /"" + funcName + "/" added successfully./n"; } catch (e) { return "unkown error: " + e + "./n"; } } // 枚举所有的断点 var bl = function() { if (bpList.length == 0) { return " no breakpoint./n"; } var bps = "* " + bpList.length + " breakpoints: /n"; for (var i = 0; i < bpList.length; i ++) { bps += i + " – " + bpList[i] + "/n"; } return bps; } // 清除某个断点 var bc = function(n) { try { n = parseInt(n); } catch (e) { return "error: bc command requires a numeric argument./n"; } if (bpList.length == 0) { return "error: no breakpoint./n"; } var funcName = bpList.remove(n); try { eval(funcName + " = window." + funcName.replace(//./g, "") + "_bak;"); return " breakpoint on function /"" + funcName + "/" deleted successfully./n"; } catch (e) { return "error: " + e + "./n"; } } // 帮助 var help = function() { var s = "debug commands:/n/n" + "bp <function name> – set a breakpoint on a function, e.g. /"bp window.alert/"./n" + "bl – list all breakpoints./n" + "bc <breakpoint number> – remove a breakpoint by specified number, e.g. /"bc 0/"./n" + "help – help information./n" "/n"; return s; } // 处理命令 this.exeCmd = function(cmd) { cmd = cmd.trim(); var cmdParts = cmd.split(//s+/g); var cmdName; var cmdArg; if (cmdParts.length == 1) { cmdName = cmd; } else { cmdName = cmdParts[0]; cmdArg = cmdParts[1]; } switch (cmdName) { case "bp": if (cmdArg == undefined) { return "error: bp command requires an argument./n"; } else { return bp(cmdArg); } break; case "bl": return bl(); break; case "bc": if (cmdArg == undefined) { return "error: bc command requires an argument /"number of breakpoint/"./n"; } else { return bc(cmdArg); } break; case "help": return help(); break; default: return "error: command /"" + cmdName + "/" not found, you can get information by /"help/" command./n"; break; } } } //—————————————————————————–// // 主过程 //—————————————————————————–// /try { debugger; } catch (e) {}*/ var id = new InlineDebugger(); var console = new Console(document.body, function(s, printProc){printProc(id.exeCmd(s));});
-
net破解一(反编译,反混淆-剥壳) (转)
大家好,前段时间做数据分析,需要解析对方数据,而数据文件是对方公司内部的生成方式,完全不知道它是怎么生成的. 不过还好能拿到客户端(正好是C#开发)所以第一件事就是用Reflector编译,但是没有想象的那么简单,看看反编译结果
代码已经混淆了,方法体内部处理也看不见 ,怎么办喃?
现在就来说说反混淆(剥壳)那些事
目前比较常用的混淆(加壳)有Dotfuscator,MaxToCode,Xenocode,ThemIDA.
反混淆的工具有很多Dedot ,DePhe,XeCoString等,但是这些只能剥对应算法,今天重点推荐一个工具 De4Dot
De4Dot支持多种反混淆.Dotfuscator,MaxToCode这两种是网友测试的,Xenocode是我测试的,
官方介绍支持:
- Agile.NET (aka CliSecure)
- Babel.NET
- CodeFort
- CodeVeil
- CodeWall
- CryptoObfuscator
- DeepSea Obfuscator
- Dotfuscator
- .NET Reactor
- Eazfuscator.NET
- Goliath.NET
- ILProtector
- MaxtoCode
- MPRESS
- Rummage
- Skater.NET
- SmartAssembly
- Spices.Net
- Xenocode
源码:
可以看出de4dot确实能剥很多算法的壳,很强大!
现在来试试
它在我指定文件同目录下生成了 X-cleaned.(exe/dll) 文件,我们在用Reflector来解析一下
成功了,是不是应该欢呼一下. 如果有更强大的工具,求通知!
大家发现没,现在这个客户端是需要登录的,而我没有账户密码,而我想进去怎么办?
本篇只是过渡用,主要是下一篇,如何修改对方dll,跳过登录过程,所谓的破解
如果有兴趣的朋友想了解核心算法,可以去官网下来看https://bitbucket.org/0xd4d/de4dot/overview 开源的
发现问题 请留言,如果喜欢请推荐一下,
转载请注明出处! 谢谢
-
[书摘]微软.NET程序的加密与解密 第二部分(转)
- 第 9章一辅助保护手段
- 用反编译软件载入一个程序后,根本 无法查看其结构,而直接弹出异常窗口;或者用ildasm顺利反编译后,怎么找也无法用ilasm 再次编译。 原因正是加密软件采#A的一些特 殊的保护手段。 利用的是系统的BUG 或是元数据的缺陷,
- 9.1用户字符串编码
- 在.NET中,用户字符串是被保存在#US流中的特殊元娄数据,因此 反编译软件可以通过枚举#US流的数据得到所有的字符串数据,由于用户字符串包含着大量敏感信息,可让调试者轻易定位至关键代弋码,因此它 是保护软件的加密重点之一 .
- 最常见的用户字符串保护方式,根据强度的不同,将其分为两类:一般编码和使用强名称的编码。
- 9.2给程序集添加错误元数据
- 让反编译软件失效。
- 9.2.1、」#GUID堆大小错误
- 被这种方式保护的程序在Reflector 无法查看其任何结构和源代码,而仅能显示出错信息.
- ,#GUID堆的大小只可能是16的倍数,
- 一般有两种方法,使用ilasm再编译或使用CFF直接修 用CFF修改时只需选中MetaData Streams节 点,将#GUID项的大小改为16即可.
- 9.2.2 .TypeDef的Extends项错误
- TypeDef表中的Extends项值指明了该类型的父类型。大; Extends项指明了.NET程序中类型的继承关系,对于一般类型来说是不能随意修改 ,但每个程序都包含一项特殊的类型"<Module>",它的Extends项错误看来比##GUID大小错误要严重些,Reflecto:程序发 生异常,:弹出了报错窗口,异常的信息为:索引超出了数组界限.这种改变Extends项的方式仅仅适用于<<Module>类型,可以利 用.NET SDK提供的peverify工具了: 将<<1Viodule>类型的Extends项改回; 0就可以了,
- 9.2.3利用PE结构
- 9.2.4加多个Module
- 9.3打包
- 当一个程序集含有多个模块时,可以通过打包的方式整合为一个单独的可执行文件, 这种整合的过程称为打包。一般采取运行时在内存或硬盘上动态释放文件的方法。 一 http://research.microsoft.com/~mbanett/ILMerge.aspx,其作 其作用就是:将多个个.NET程序集合并 为一个.NET程序集。勺打包原理相当于将DLL中的代码在原程 序中进行了集成
- 另一种常见的打包方式是将需要的文件(主文件、库文件等)集成在资源中,一这里的 “资源”所指范围较广,可以是一段数据,也可以是托管资源,这些资a也可以被加密或者压缩。
- 9.4特殊的..NET属性
- DebuggerHiddenAttribute起的作用。从字面上看,该属 性是面向调试器隐藏代码。除- 除了DebuggerHidden外,.NET中还有两个与专调试器相关的属 :
- DebuggerNonUserCodeAUribute 此属性禁止在调试器窗口中显示这些附属类型甩和成员,并自 动逐句通过而不会进入并单步执行设计器提供的代码。
- DebuggerStepThroughAttribute 公共语言运行库不向该属性附加任何语义。方法中不停止,
- 防止ildasm反编译的SuppresslldasmAttribute属性。当在源代码中删除并 重新反编译后,便可以直接使用ildasm反编译- 用十六进制编辑器将ildasm 中的" SuppressIldasmAttribute”字符串改掉.
- 9.5;利用系统特性 Windows的文件名 只支持少部分字符,且不支持乱码、某些符号、空字 符串作为文件名。 在原文件中将资源改名为Windows文件系统允许的名称,然 后使用ildasm反编译即可。再次编译回可执行文件时,尽量将名称再修改回去,以保证源 代码中对资源的调用正常。
- 第10章壳保护
- 完全基于.NET的壳,加密的层次也从最初的纯.NET“降低”为Win32、直至 挂钩.NET框架内核。
- 10.1什么是程序集整体保护
- 将.NET的壳产品按保护机理分类,可大体分为两种:程序集整体保护(Whole Assembly Protection)和基于每个方法的保护(Per-Method Protection).
- Win32壳的加密对象是asm指令,相应的,.NET壳的加密对象则是元数据和IL代码。
- 程序集整体保护是指,无论壳采用了什么 加密机制,但最终运行状态时,原始程序集的数据(主要是元数据和IL代码)在某个时刻 完整出现在了内存中。 ’该类壳的几大特点:
- 1)加密机制多样。有的壳是纯.NET实现的,有的可能用Win32代码进行加密,还有的可能对.NET内 核DLL进行包装或挂钩。只有理解了不同壳的实现机理,逆向时才能更有针对性。
- 2)最终运行时,内存中会出现完整的元数据和B.。这就意味着,对付此类壳的终级 手段就)v- dump.不会修复PE文件,也就无法继续进行分析。
- 3)只在某个时刻可以dump。元数据和IL再次加密。吧握时机,“该出手时就出手
- 不掌握壳的原理是不行的。
- 10.2纯.NET实现的压缩壳
- 纯.NET实现的压缩壳有两层含义:一是壳本身是.NET程序;二是程序被加壳后仍 是.NET的。这类壳一般加密功能较弱 .NETZ:全名为“.NET Executables Compressor",开源的.NET压缩引擎,目前仍在更 推荐使用。
- 使用工具自动进仃dump的内容:
- 运行WinDBG,载入被加壳程序,运行如下命令,
- (1) .load sos(载入.NET调试扩展。) (2) sxe ld:mscorwks(在mscorwks载入时设置断点。) (3) 9(继续执行,程序将在mscorwks载入时中断。) (4) Id mscorwks(载入mscorwks的符号。) (5) Id mscoree(载入mscoree的符号。) (6) by mscorwks!Assembly::GetEntryPoint(在Assembly::GetEntryPoint方法中断,注 意此处的EntryPoint是被加壳程序的入口方法。) 7)g(继续执行)
- !bpmd AoRE-Keygenmel.NET.exe netz.NETzStarter. GetAssembly,回车后继续执行程序,取得MethodDesc后,便可以使用!u命令查看方法对应的JIT汇编代码:
- 匕采用Wi11HeX进行dIdump。运行wjnHeX, 打开keygenme进程的全部内存(Entire Memory), Alt+G弹出偏移选择窗口(如E WinHex中内存块的选择由“Beginning of block”和“End of block”定义( 内存块定义完毕后,单击“Edit-Copy Block-Into New File",将该段数据保存在文件 .
- 10.3;基于Win32的壳
- 传统Win32壳增加一了对.NET的支持,典型的如Themida, NsPack等耳熟能详的猛壳,
- 使用专用dump工具,比如.NET下比较好用的Task Explorer,它和CFF配套打包 在Explorer Suite中。运行Task Explorer,选择进程今模块,单击鼠标右键选择“Dump PE ", 保存在硬盘j上就可以了. 使用现成的工具,如.NET Assembly ReBuilder. 在PE文件结构上修改. 10.4挂钩内核的壳 一个.NET下很重要的壳:.NET Reactor它是专门 面向.NET程序的加壳工具, .NET Generic Unpacker(简称NET Unpacker)是.NET下专用的脱壳工具,可垃 可以搜索进 程中的托管程序集并dump为文件。
- 分析另一种非常流行的.NET整体保护的壳CodeVeil, } :NET内核被hook了! 挂钩compileMethod方法的后果是 每当.NET内核需要对一个方法进行JIT时,都会 调用挂钩后的方法,CodeVeil便得到了预先处理的机会。在方法体处设置硬件写入断点,便可得到再次加密的核心代码。(注意,这里不能设置内存断点,因为内存断点改变了数据,会造成解密出的结果出错。 10.5什么是基于每个方法的保护 NET的一大特点是即时编译:只在需要执行某个方法时才将其编译为本地代码。壳保护可以以单个方法为保护单位,在需要JIT时才将该方法的代码解密, 10.5.1常见的挂钩形式 基于每个方法保护的壳的实现,从方式上说就是Win32下的hook,关键是hook的位 一 最简单的原理是:hook的位置越少,对内核改变的地方越少,则壳的兼容性越高。要掌握 为什么壳要hook这些位置,首先要掌握.NET内核JIT的过程。
- WinDbg调试来演示JIT的一些工作原理 主使用!!name2ee分析主窗口类Form 1的内部信息: 目前最值得关注的是 MethodTable,这是每一个类型对应的方法表。使用!dumpmt -and命令可以进一步显示该方 法表中的全部细节: 一个.NET程序的方法在JIT前的 执行就是一段预先准备的本地代码,在这段代码中调用mscorwks!PreStubWorker,最终再调用JIT引擎进行编译。 ,NET规定:当某个方法被JIT后就无须再次编译,而是直接调用己经编译生成的林地代码, 对PreStubWorke:的反汇编中可以发现对DoPreStub的调用, 关键之处就是MethodDesc: : GetILHeader,该函数的功能是返巨 MethodDesc对应方法的RVA地址。RVA指向了方法头。因此,该调用是基于每个方法保护的壳常用的hook位置之一。- 在DoPreStub方法中,含发现对MakeJitWorke:的调用: !UnsafeJitFunction是另一个常被hook的函数,本身不是线程安全的, ,MakeJitWorker会解决.NET内核在进行JIT时的同步,已最终它调用 ,compileMethod这个函数。因此,壳只需要在.NET内核最终调用compileMethod前将IL代码解密并传递给JIT引 擎即可。 。二得到正确脚数据,挂钩是在EE层的,因此可以称之为 EE层的保护。 10.5.2。更进一步的保护
- EE层的挂钩解决了程序集整体保护叮中最大的弱点 在任何时候都不在内存中完整出现解密过的程I IJ}集。但 是传入JIT的总是解密过的IL,因此,只要守住JIT的入p,便可以得到程序的源ILL。更具体一点,这个 JIT的入口就是compileMethod函数。N此,如果找到二种遍历程序集所有有.方法的途径,通 过简单地挂钩compileMethod函数井截联共传人J甲的几.if便可以将原程序集恢复,让 Iv保护失效. ‘壳的解密代码”被融合进JIT层中,这乖 壳会实现 部分JIT的代码,这部分代码改变了JIT引擎的工作模式,一是由于JIT引擎完全(或部分)由为用户代码实现,脱壳者若想继续通过挂钩来得 到原IL,则至少要将该部分引擎代码的反汇编读懂。t :二是壳可以改变JIT引擎的运行流程, 10.5.3实现方式 基于每个方法保护的壳是如何实现的呢,或者更确切地说,运行库是如何加载并挂 钩..NET内核的呢?第一种方式较常见,壳在程序集中添加加载程序集的代码(下面} ,并 给程序集的每个个类型添加静态构造函数.cctor,在其中加对loader代码的调用,之后loader 代码转入运行库中并调用核心解密代码。这种方式的特点之一是静态构造函数和loader代 码无法加密,因为在调用这两处代码时,运行库还没有加载。第二种运行库的加载方式则是利用Window,系统的PE加载时读入,比如’Win32下已 经非常成熟的修改导入表加载DLL的方法。这种方法的好处是程序集中所有的方法都可以 加密,但无法像前一二种方法那样在运行时选择加载的运行库版本‘ 10.5.4·一般分析方法 目前对于这种保护的常用的脱壳方法是“反射一调用一挂钩一重建”。“反射”是指利用.NET 的反射机制遍历程序集中的所有方法,并通过Invoke机制对其进行“调用,,此时会触发 JIT引擎工作从而使壳的运行库进行解密,在适当的位置“挂钩”,便可获取解密后的方法 体和元数据,最后用合适的方法“重建”程序集。这里的“挂钩”还可以引申为建立自己 的虚拟机环境竟,让运行库在自己的掌控下运行。Re-Max正是利用这一原理编写的脱壳机. 第11 章其他保护万式 11.1许可证保护 许可证以授权文件(K或者注册表数据的形式存在,文件中存有经 过加密的用户授权信息,Win23下较成熟的保护系统是FLEXlm. 11.1.1一许可证机制*01介 p .NET自身提供的许可证 机制便是入门的最好材料。 许可证编译器Lc.exe,LC是.NET提供的命令行工具,即许可证编译器(License Compiler), 11.1.2 .NET许可证机制的扩展 11.1.3·一般分析方法 色可证保护的基本要素总结一下。一个典型的.NET许可证保 护应至少包含下面的三个组成部分: (1)许可证(License)提供需要验证的数据,供授权机制使用。 (2)许可证提供者((LicenseProvider)::授权机制的核心,负责取得许可证及执行验证 数据的任务。 (3)许可证管理者(Licensemanager ):负责按照元数据中的、LicenseProviderAttribute 属性初始化LicenseProvider,被保护控件调用它的Validate或者IsValid方法进进行验证。 ;商业级授权保护系统的三个共同特点。 共同特点之一,许可证中包含大量信息。 共同特点之二,采用大强度的非对称算法。,许可证中的大量信息以及许可证的结构本 身都是非常敏感的信息,必需要采用大强度的算法进行加密与解密。 RSA被选择的频率较高。 共同特点之三,结合其他保护方式。 (1)多处验证。一是验证许可证信息的位置较多,二 是验证时不是简单地以true或false判别,而是返回许可证本身(通常是个类),而后再利 用其中的信息控制程序的流程,卜因此简单的patch验证方法的返回值为true或null只会造 sv异常,必须更全面地考虑许可证的内容; (2)部分授权系统利用了本地代码的Whole Assembly保护以及部分Per-Method保护 分析前需要dun刀P或者脱壳; (3)部分授权系统使用了虚拟机,为了保证运行效率,仅在关键的算法处(或者用户 算定的方法处)将MS1L转换为虚拟机代码,被保护后的程序是无法直接分析的。 破解整个授权系统,最完美的方法无疑是分析出许可证结构及加密解密算法,但算法是授权系统的核心,因此能轻易逆向的情 况并不多。通常是采取次完美的方法,既分析出许可证结构,替换程序中的公钥,以对应 的私钥进行加密,这样做是以最小的补丁得到的最好结果。 11.2.1 , .NET提供的算法空间 System. Security. Cryptography是.NET提供的加密解密算法相关命名空间,大1 CryptoServiceProvide:结尾的类表示该类的实现是对底层Win32的相关API进行了包装, i以Managed结尾的算法类则完全是基于托管代码的。 ,.NET还提供了众多辅助算法实现的类,比如随机数的生成、基于XML 的加密 对称密钥的算法有两个最大的缺陷:一是无法解 决密钥的发布问题,一旦密钥公布,则无安全性可言;二是无法解决信任问题,即无法对 被加密的信息进行验证,因为无法得知密钥的真实性。非对称算法最大的优势是利用了密钥对,其中不公开的私钥用于解密,而 「发布的公钥用于加密。,最基 本的原则是:私钥永远在自己手里,不要公开,发布的程序中只能带有公钥数教据。在此原 则基础上,有两种基本使用方法。一种是公钥加密,私钥解密。比如判断这些信息的有效性.另一种是 私钥加密,公钥解密。这种情况要注意,公钥的解密只用于验证,而不是得到原始信息。 10.3虚拟机保护 虚拟机在Win32下已经发展得非常成熟,保护强度大,破解难度高,「将.NET下的虚拟机分为两大类:整体级的和代码级的。 整体级的虚拟机保护是指程序集本身(特别是MSIL指令)’没有改变,’添护软件只是 虚拟了文件 在PostBuild和Studi。两个软件中实现的虚拟机(女 这种虚拟机保护的最 大应用是:方便了程序的发布,可以将多个文件打包为一个可执行文件,甚至可以将.NET框架打包使得.NET程序运行在没有安装.NET框架的机器上。 当程序运行后所有的原始程 序集均会在内存中释放,利用内存 dump工具可以很轻松地得到这些原始程序集,NET Unpacker就是一个不错的选择。; 代码级的虚拟机保护是指以虚拟彩 的伪代码替换部分或全部MSIL指令,使得逆向者在内存中得到的代码不是有效的MSIL, 或者根本就不是MSIL,这样就无法采用传统的逆向工具直接反编译,而必须花大量的时间 来解读伪代码 替换部分MSIL指令的虚拟机可以通过修改JIT引擎实现,迷 这样的.NET执行文件虽然在PE和元数据结构上都没有改变,但大部 分反编译软件都无法正常反编译。 要想实现替换全部(至少是大部分)MSIL指令为伪指令,则需要解释执行的虚拟机。 这种方式最大的缺点是运行效率会明显降低,L 出于运行效能的考虑,通常将关键方法替换 为伪指令. 先来看 中,用d.1T命令查看目录,便可以看见一些以·Nativelmages·开头的目录,,里面保存晶便 是程序集的本地版本,通常以.ni.dll或:.ni.exe结尾。(在explorer中无法直接妾看到目录结构, 可以修改注册表以屏蔽此扩展,方法为 HKLM\SOFTWARE\1VIicrosoft\Fusion中新建DisableCacheViewer键,令其值为1,)本机映像中保存的是汇编代码,区别于托管程序集中保存 的是托管IL,这也是其名称的由来。 .NET的命令粤具ng势(全称CLR Native Image Generator) #t了林机映像的管 理,包括安装、显示、卸载和更新等功能。i 可对任意一个托管程序集采用ngen install 命令将其安装至全局缓存中,这样当再次运行该程序集时,.NET发现缓存中有相对应的本 机映像则运行之。ngen生成的本机映像是无法直接运行的,只能由.NET调用。 利用编译为本机代码的保护方式的实现过程就清楚了:利用.NET的本机映像机制, 将MSIL编译为汇编代码,同时将托管和本机两个版本的映像中的原始元数据和MSIL都 删除,以达到保护效果。 MegaX编写了一个脱机使用.NET程序的工具DotNetBox, 不安装.NET框架便运行.NET程序,其编写也多少使用了上面的原理。 11.5动态方法委托调用 委托(Delegate)和动态方法(DynamicMethod),1.0版.NET中就出现了委托,而2.0版以后则出现了动态方法,i It用动态方法保护关键方法的另类加密, 在Win32程序设计时,经常用到回调函数,在Windows系系统中,窗口过 程、钩子过程、异步过程的实现都需要使用回调函数。但在非托管代码中,回调函数的地 址就是一个内存地址,该地址不会携带任何额外的信息,例如函数期望的参数左个数、参数基类型、函数的返回值类型以及函数的调用约定。一言以蔽之,非托管代码中的回调函数是 非类型安全的。.NET框架则使用一种称做委托的技术来提供回调函数机制,,更重要的是, 委托是类型安全的。; NET除了 支持将方法静态地编译到程序集文件中外,刃还支持动态方法:即在程序运行时,使用 DynamicMethod类生成和执行方法,且不必同时生成动态程序集和动态类型卫来包含该方法。 动态方法是生成和执行少量代码的最有效方式。 第12章非托管API 。非托管 API是.NET提供的一组非托管编程接口,利用这些API,可以实现一些无月法利用托管代码 实现的功能。如果将C#, VB.NET, MSIL等托管语言看做最顶层,将Winiin32/64汇编看做 为最底层,则可以将非托管API看做中间层,它利用本地代码实现操作托乍管程序的功能。 利用非托管API可以做许多很底层的事,比如编写调试器、元数据查看器等。 12.1非托管API缘述 非托管API可分为10大类,这些API存在着交互引用的现象,之间的联系是很密权切的。 在1.1版.NET框架SDK的安装目录中,有一个子目录名为Tool Developers Guide,其 中的docs文件夹中包含了数个Word文档,其中有六篇文档介绍了四种非托管API:元数 据、分析、调试和宿主,可见这四类在所有非托管API中的重要性,因习 自2.0版开始,非托管API的文件不再单独随SDK发布,而是整合在了MSDN .1.1版中的帮助文档仍极具参考价值。 非托管API之所以被称为“非托管”是因为这些API的实现代码是本地代码,其调用 机制是COM组件。实际使用中,既可以在本地代码中调用这些API,也可以在托管代码中 通过互操作机制调用。 12.2宿主API Hosting 概括宿主API的功能:通过宿主API,非托管程序可以加载CLR,从而在进 程中运行托管誉程序,并控制CLR和托管程序的行为。 利用宿主API可以管理:程序集的加载、策略、宿主 保护、内存、垃圾回收、调试、公共语言运行库事件、任务、线程池、同步和I/O完成, 涉及CLR运行各个方面的行为。 Visual Studio的命令行 :cl *.cpp mscoree.lib 程序流程: (1)定义ICLRRuntimeHost接口; (2)调用CorBindToRuntimeEx将CLR加载到内存中,根据输入的参数,取得需要的 ICLRRuntimeHost接口的实例; (3)调用Start方法将CLR初始化至进程中; (4)通过ExecuteInDefaultAppDomain调用托管程序集中某个类型的某个方法。 !ICLRRuntimeHost接口,这种以字母“I"开头的写法来源于 COM编程.NET框架实际也源于COM. VISDN中关于宿主接口的列表,接前缀分类,大致划分为三类:ICLRxxxx, IHostxxxx 和其他。,其中形如ICLRxxxxManager的接口实例需要通过GetCLRManager方法取得,而 IHostxxxxManager则需要通过SetHostControl指定IHostControl接口实例后通过 GetHostMan得到相应的接口实例后,便可以使用接口提供的功能。比如ICLRValidator提供了 Validate方法,该方法能对指定程序集的元数据和IL进行验证,这· 经常用到的peverify.exe正是利用该接口编写的;取得了ICLRDebugManager接口, 便可以调用IsDebuggerAttached方法检测当前进程是否被调试。 12.3合成API 合成API允许运行库宿主访问应用程序资源的属性,以 便定位这些应用程序资源的正确版本。或者更简单地概括,合成API是用于定位程序集以 及相关资资源的。 IAssemblyCache是用来控制全局程序集缓存的接口,通过它可以将指定的程序集安装 到缓存中,或是将指定的程序集从缓存中删除,也可以获取缓存中程序集的相关信息。 合成API作者之一一的博客: http://blogs.msdn.com/junfeng. 取得lAssemblyCache接口实例的方法为CreateAssemblyCache,:该方法由 fusion.dll导出。熟悉Win32的读者都知道,获取某个DLL导出方法可以调用GetProcAddress,之前需要用LoadLibrary载入该库,不过这里行不通,因为fusion.dll属 于.NET的动态链接库,不在System32目录下,因此正确的方法是通过mscoree的 LoadLibraryShim载入fusion.dll后再调用GetProcAddress. ,NET提供合成API有什么用处呢?两个同名的DLL同时并存于系统中,程序可以根据自己的需要进行选择调用,而 在Win32下这是不可能实现的,因为DLL中的唯一区分就是名称。通过合成巧妙以解决了长久以来一直存在的“DLL陷阱”问题, 12.4强名称API 在MSDN中查看3.0版之前的框架帮助文档,则找不到专门的强名称API 因为过去强名称相关的API是整合在全局静态函数中介绍的,直到3.0版之后的MSDN才 将其单独分为一类。强名称API是..NET提供的专门用于操作(包括签署、移除等)强名称 的编程接口,强名称替换工具RE-Sign也正是利用强名称API来进行的。 强名称API的定义位于头文件StrongName.h中,包含了各API的功能及输入输出参数 的定义。非托管API还可以对强名称进行签署和删除,并且在托 管程序中也可以使用强名称API,当然是通过P/Invoke机制。 12.5元数据API 元数据API 的功能是读写程f序集中的元数据,可操作的对象几乎囊括了元数据的各个方面,比如逻辑结构上的程序集、类型、成员方法等,物理结构上的方法RVA, #US流、#Blob流、元数据表等。元数据API是分析与调试API的基础,凡是针对元数据的调用都需要通过它来实现,这就免去了用户完全重新编程来实现元数据读取的麻烦。 一反射的底层实现原理和元数据API是 一样的。 12.6分析API 更过分析API,可以得到以下过程的相关信息并控制创:Application的开始/结束,Assembly的载入/卸载,Methods的开始/缎束,Modul。的载 入/卸载,Class的载入/卸载,与COM接口的互操作,托管/非托管代码的即时编扁译等。 换句话说,基本上.NET的所有核心操作都可以通过分析API来得到信息并进行控制。 分析API最初被设计用来优化.NET程序。比如最常见的内存优化和防资源泄露,一般 说来是依靠.NET自身的垃圾收集(GC)机制进行处理的,但有时GC的自动处理效果并 不尽如人意,这时就需要找出影响效率的关键点,利用代码手动释放内存。通过分析API 监测.NET程序的内存使用状况和各对象存在的时间,并将存在时间过长的对象采用Close 或Dispose方法手动释放。 分析器有 开 源的,界面简洁,功能简单实用,关键是有源代码可以学习,比如微软CLR Profilerl.l和 2.0版、NProf等。 利用分析API 还可以得到如方法的相互调用关系、方法的IL代码、方法的输入参数及返回值等关键信息。 分析器的运行原 里基于COM服务,因此本身需要以非托管C++编写,以DLL的形式编译。一个最简单的 分析器源代码大致包含三个文件:一个实现DLL框架的文件、一个实现分析器核心代码的 文件和一个分析器的头文件, 由于分析API可以监视.NET的许多行为,故收集全部监测信息既没有必要也影响速 因此要设定感兴趣的事件: }GetEventMask方法是需要重点修改的地方之一,该方法返回一个掩码,用于设需要监测的事件。 分析器是作为COM服务加载的. ·如何运用分析器动态的给程序打内存补丁。 可参考文献《Modifying IL at Runtime)),文章 中详细演示了分析API与元数据API 结合。一般的Anti-Profiling有两个层次:一是高层的方法,通过在 程序开始运行时修改环境变量,将COR_ENABLEee PROFILING由1改为。,这种anti自然 是非常容易被anti-anti的了;另一种方法相对低层,通过给运行中的.NET内核DLL打补 丁使得分析器失效. 《Debuggers Under.NET》这篇文章。 第13章 MONO SSCLI与.Net注下内核调试 ‘基于Linux的开源项目MONO,和可跨平台的精简.NET框架SSCLI(又 被称作ROTOR ,,MONO的部分着 重介绍Cecil这个子项目在逆向工程中. 13.1 MONO简介 它包含了一个C#语言的编译器,一个符合ECMA标准 的CLR运行时和一组类库。 该类库既包含微软.NET框架的部分类库,也包含MONO独有的一些类库及第三方类库如Gtk#. oCecil是MONO的一个子库,用于对符合ECMA CIL 标准的程序和库进行操作,简单地说,就是可以用Cecil读取托管程序集,浏览其中所有的 类型,修改这些类型,并保存至程序集中。运 月Cecil编写.NET逆向工具,程序员便无须 和烦琐的PE与元数据结构打交道,极大地提高开发效率。并且Cecil项目还有一揽子开发 计划,比如实现编译器和反编译器,实现代码优化工具等。目 比如WiCKY Hu编写的Simple Assembly Explorer. Reflector的插件 Reflexil, CLI Secure的脱壳机,、等等。而且最重要的是:Cecil是开源的! 通过将各种操作分类合并,Cecil共划分为以下七个命名空间。 (1) Mono.Cecil:最基础的命名空间,定义了许多表示..NET基本概念的类 (2) Mono.Cecil.Binary:从名字就可以看出来,该名称空间主要定义了二进制操作相 关的代码和概念。i 这其中包括基本PE结构及其操作,同时还将一些PE中的概 念扩展为类,提供了资源和文件(Image)读写的相关类. (3) Mono.Cecil.Cil:主要用于方法、参数、变量、IL指令及操作数等的读取、修改 (4) Mono. Cecil.Metadata:用于对元数据进行操作,支持绝大部分元数据流。 (5) Mono.Cecil.Signatures:用于操作各类Signature (6 )Mono.Cecil.Text:仅包含UnicodeEncoding类,用于Unicode编码的相关操作,而 了.NET 内核I正是采用该种编码。 (7 Mono.Xml:执行Xml的相关操作。 Cecil编译后的可执行文件(DLL)可以用于编写各类.NET下PE、元数据、MSIL指 令的相关工具。通常编写此类工具有三种方法。第一完全从头开始。一个结构一个结构 Y9分析,所可有的签名0解码都需要掌握,所有的特殊情况都需要考虑,但没有人愿意这样做. 第二,运用.NET类库提供的Reflection功能。这似乎是个不错的方法,但可惜反射命名当 间中没有提供全部的功能,比如 比如反射就不支持操作方法的异常处理表。第三,运用Cecil类库。该类库不仅loo%兼容微软.NET可执行文件,而且对各种元素进行了抽象,便于用 户在高级层次上实现其他功能(比如将指令抽象为Instruction类,方便各种修改MSIL尔 操作),更重要的是,Cecil是完全静态的,而反射是需要载入程序集的,在某些些情况下, 完全静态的修改兼容性更强。 Cecil源代码更是学习元数据结构不可多得的“葵花宝典”,下 利用Mono.Cecil的源码来学习PE与元数据结构,同时,Cecil也是海内外 众多.NET逆向高手钟爱的类库,值得好好学习和利用。目前,利用Cecil编写且使用月较广 泛的两个工具是Simple Assembly Explorer和Reflexil,读者“ 尝试一下在Linux平台下安装 MONO Develop开发.NET程序的感觉 . 13.2 SSCIL简介 。由于提交了ECMA标准,因此微软多多少少也开放了二些源码, 这便是SSCLI (Shared Source CLI ),又被称作ROTOR. 。SSCLI是简化版的.NET但两者的核心代码有许多相似之处,,。是学习.NET内核不可多得的好材料。 从微软的网站下载SSCLI 2.0版的压缩包并解压,会得到1200()多个文件,其中核心 代码位于clr\src目录里。有 有两种方式可以方便地浏览源代码,一是使用Source Insight建立 新项目,二是利用网上的VS项目文件,在Visual Studio中浏览。 通过SSCLI,不仅可以方便地阅读.NET内核函数的C++代码,还可以深入了解一些框 架内部执行过程 NET内核动作的各个方面都可以通过源代码清楚地 知道其原理,只要SSCLI包含该机制。 NET内核概念与SSCLI源代码文件的对应表 13.3 .NET框架内核调试 想通过给内核打补丁来使Profiler失效,关键是要找到合适的位置来patch.因此第一的任务就是分析.NET是如何从内核转而执行Profiler中的用户代码的,这个过程利用 WinDbg可以很轻松地得到。 使用命令便可以在加载Profiler.dll时中断。 NET内核正在取Cor Enable Profiling环境变量的设置,而这不正是对应分析器启动时的环境变量吗?因此,最简单的方法就是在这里进行补丁,让.NET内核 误以为没有设置该变量,自然也就不会再向下执行初始化分析器的代码了。 第14章Win64平台上的.NET 14.2 C++编程的改变 在64位编程时,一般会遇到三种变量类型。第一类是传统的C++数据类型,如int,lang(这两者在64位系统中的长度为32),微软不建议使用这一类型。 第二类是固定精度的数据类型,这些类型的名称以32或64结尾用以标示精度,使用 }J时,不管处理器的精度大小如何,它们的精度都是固定的(女 第三类数据类型是指针精度的数据类型,又称“多态”数据类型,因为它们的精度随处理器的精度大小而改变,这些类型常以-PTR结尾。当需要编写同时面向32位与64位的程序时,采用这种数据类型会避免很多移植上的麻烦. 另一个问题是printf与sprintf.在Win32中通过%X或%08X来显示指针的值是很常见的用法,而在Win64下应采用%P。对于与大小相 关的类型,printf和sprintf还具有I前缀,可以使用%Iu来打印UINT PTR变量. 14.2.2 . NET编程的改变 Win32的托管程序通过P/Invoke机制调用了本地API,或者使用了本地COM服务。一旦涉及到本地代码、内存指针、内存对齐、浮点数运算这四种情 况时,要特别注意测试程序的兼容性,这种程序便很有可能无法在Win64系统下运行。 14.3 64位PE结构 PE格式反映了系统的底层机制,系统的改变也会或多或少地体现在PE格式的改变中。 Win64系统中对Win32的PE文件格式进行了扩展,扩展后的格式称为PE32+。从大体结 构看,PE32+与PE32几乎是一样的,这是出于Windows可执行问题兼容性的考虑, 第一个主要的改变在可选头结构(Optinal Header)中,64位系统的SDK头文件WinNT.h中包含了针对64位的头结构IMAGE OPTIONAL_HEADER64,另外还有一些结构的变化(主要是长度被增加至64位), 导要记住一个原则:win64系统下,虚拟地址(VA)是被扩展至64位长 的,而相对虚拟地址(RVA)仍旧保持32位长。 旦64位的托管程序有一个最大的特点:不再有本 地代码入口点。可以用cFF来验证,cFF是目前较好的同时支持32和64位平台PE 文件及元数据结构的查看工具,同时用其载入一个程序的32和64位版本,对比可发现32 位的程序Import Directory的RVA和Size都不为。,且CFF左方多出一项Import Directory 节点,‘而64位程序则没有. 14.4 64位.NET程序调试
-
Book abstraction
The key to successfully learning C# is to start coding as soon as possible.Don’t wait until you are an “expert” in theory; start writing software immediately. As a believer in iterative development, I hope this book enables even a novice programmer to begin writing basic C# code by the end of Chapter 2.
当你读到这段话的时候,是不是觉得还不错ing.其实不仅对于c#来说这样子,对C++,php,OC,C…..都是这么回事啊!!让我们提取一下模板 =>
The key to successfully learning {Somting} is to start coding as soon as possible. Don’t wait until you are an “expert” in theory; start writing software immediately. As a believer in iterative development, I hope this book enables even a novice programmer to begin writing basic {Someting} code by the end of Chapter {StartChapter}.
=> 接下来生成另一本书:
Generate a new book.
book.generate([“Somting”:”C++”,”StartChapter”:”3”]); 好了,用一本C#的经典书藉生成了一堆其他语言的新书。