1421 lines
57 KiB
HTML
1421 lines
57 KiB
HTML
<!DOCTYPE html>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<html class="theme-next gemini use-motion" lang="zh-Hans">
|
||
<head><meta name="generator" content="Hexo 3.8.0">
|
||
<meta charset="UTF-8">
|
||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||
<meta name="theme-color" content="#222">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<meta http-equiv="Cache-Control" content="no-transform">
|
||
<meta http-equiv="Cache-Control" content="no-siteapp">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css">
|
||
|
||
<link href="/css/main.css?v=5.1.4" rel="stylesheet" type="text/css">
|
||
|
||
|
||
<link rel="apple-touch-icon" sizes="180x180" href="/images/hackerrank.png?v=5.1.4">
|
||
|
||
|
||
<link rel="icon" type="image/png" sizes="32x32" href="/images/hackerrank.png?v=5.1.4">
|
||
|
||
|
||
<link rel="icon" type="image/png" sizes="16x16" href="/images/hackerrank.png?v=5.1.4">
|
||
|
||
|
||
<link rel="mask-icon" href="/images/logo.svg?v=5.1.4" color="#222">
|
||
|
||
|
||
|
||
|
||
|
||
<meta name="keywords" content="Hexo, NexT">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<meta name="description" content="壳是最早出现的一种专用加密软件技术。一些软件会采取加壳保护的方式。壳附加在原始程序上,通过Windows加载器载入内存后,先于原始程序执行,以得到控制权,在执行的过程中对原始程序进行解密还原,然后把控制权还给原始程序,执行原来的代码。加上外壳后,原始程序在磁盘文件中一般是以加密后的形式存在的,只在执行时在内存中还原。这样可以有效防止破解者对程序文件进行非法修改,也可以防止程序被静态反编译。 壳的加">
|
||
<meta property="og:type" content="article">
|
||
<meta property="og:title" content="加壳与脱壳">
|
||
<meta property="og:url" content="https://cool-y.github.io/2019/05/14/pack-and-unpack/index.html">
|
||
<meta property="og:site_name" content="混元霹雳手">
|
||
<meta property="og:description" content="壳是最早出现的一种专用加密软件技术。一些软件会采取加壳保护的方式。壳附加在原始程序上,通过Windows加载器载入内存后,先于原始程序执行,以得到控制权,在执行的过程中对原始程序进行解密还原,然后把控制权还给原始程序,执行原来的代码。加上外壳后,原始程序在磁盘文件中一般是以加密后的形式存在的,只在执行时在内存中还原。这样可以有效防止破解者对程序文件进行非法修改,也可以防止程序被静态反编译。 壳的加">
|
||
<meta property="og:locale" content="zh-Hans">
|
||
<meta property="og:image" content="https://res.cloudinary.com/dozyfkbg3/image/upload/v1557817831/%E5%8A%A0%E5%A3%B3/1.png">
|
||
<meta property="og:image" content="https://res.cloudinary.com/dozyfkbg3/image/upload/v1557817867/%E5%8A%A0%E5%A3%B3/2.png">
|
||
<meta property="og:image" content="https://res.cloudinary.com/dozyfkbg3/image/upload/v1557819108/%E5%8A%A0%E5%A3%B3/3.png">
|
||
<meta property="og:image" content="https://res.cloudinary.com/dozyfkbg3/image/upload/v1557819290/%E5%8A%A0%E5%A3%B3/4.png">
|
||
<meta property="og:image" content="https://res.cloudinary.com/dozyfkbg3/image/upload/v1557820111/%E5%8A%A0%E5%A3%B3/5.png">
|
||
<meta property="og:image" content="https://res.cloudinary.com/dozyfkbg3/image/upload/v1557821441/%E5%8A%A0%E5%A3%B3/6.png">
|
||
<meta property="og:image" content="https://res.cloudinary.com/dozyfkbg3/image/upload/v1557836854/%E5%8A%A0%E5%A3%B3/7.png">
|
||
<meta property="og:image" content="https://res.cloudinary.com/dozyfkbg3/image/upload/v1557837144/%E5%8A%A0%E5%A3%B3/8.png">
|
||
<meta property="og:image" content="https://res.cloudinary.com/dozyfkbg3/image/upload/v1557837250/%E5%8A%A0%E5%A3%B3/9.png">
|
||
<meta property="og:image" content="https://res.cloudinary.com/dozyfkbg3/image/upload/v1557837519/%E5%8A%A0%E5%A3%B3/10.png">
|
||
<meta property="og:image" content="https://res.cloudinary.com/dozyfkbg3/image/upload/v1557837859/%E5%8A%A0%E5%A3%B3/11.png">
|
||
<meta property="og:updated_time" content="2019-07-01T12:04:55.244Z">
|
||
<meta name="twitter:card" content="summary">
|
||
<meta name="twitter:title" content="加壳与脱壳">
|
||
<meta name="twitter:description" content="壳是最早出现的一种专用加密软件技术。一些软件会采取加壳保护的方式。壳附加在原始程序上,通过Windows加载器载入内存后,先于原始程序执行,以得到控制权,在执行的过程中对原始程序进行解密还原,然后把控制权还给原始程序,执行原来的代码。加上外壳后,原始程序在磁盘文件中一般是以加密后的形式存在的,只在执行时在内存中还原。这样可以有效防止破解者对程序文件进行非法修改,也可以防止程序被静态反编译。 壳的加">
|
||
<meta name="twitter:image" content="https://res.cloudinary.com/dozyfkbg3/image/upload/v1557817831/%E5%8A%A0%E5%A3%B3/1.png">
|
||
|
||
|
||
|
||
<script type="text/javascript" id="hexo.configurations">
|
||
var NexT = window.NexT || {};
|
||
var CONFIG = {
|
||
root: '/',
|
||
scheme: 'Gemini',
|
||
version: '5.1.4',
|
||
sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
|
||
fancybox: true,
|
||
tabs: true,
|
||
motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
|
||
duoshuo: {
|
||
userId: '0',
|
||
author: '博主'
|
||
},
|
||
algolia: {
|
||
applicationID: '',
|
||
apiKey: '',
|
||
indexName: '',
|
||
hits: {"per_page":10},
|
||
labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
|
||
|
||
<link rel="canonical" href="https://cool-y.github.io/2019/05/14/pack-and-unpack/">
|
||
|
||
|
||
|
||
|
||
|
||
<title>加壳与脱壳 | 混元霹雳手</title>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</head>
|
||
|
||
<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="container sidebar-position-left page-post-detail">
|
||
<div class="headband"></div>
|
||
|
||
<header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
|
||
<div class="header-inner"><div class="site-brand-wrapper">
|
||
<div class="site-meta ">
|
||
|
||
|
||
<div class="custom-logo-site-title">
|
||
<a href="/" class="brand" rel="start">
|
||
<span class="logo-line-before"><i></i></span>
|
||
<span class="site-title">混元霹雳手</span>
|
||
<span class="logo-line-after"><i></i></span>
|
||
</a>
|
||
</div>
|
||
|
||
<p class="site-subtitle"></p>
|
||
|
||
</div>
|
||
|
||
<div class="site-nav-toggle">
|
||
<button>
|
||
<span class="btn-bar"></span>
|
||
<span class="btn-bar"></span>
|
||
<span class="btn-bar"></span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<nav class="site-nav">
|
||
|
||
|
||
|
||
<ul id="menu" class="menu">
|
||
|
||
|
||
<li class="menu-item menu-item-home">
|
||
<a href="/" rel="section">
|
||
|
||
<i class="menu-item-icon fa fa-fw fa-home"></i> <br>
|
||
|
||
首页
|
||
</a>
|
||
</li>
|
||
|
||
|
||
<li class="menu-item menu-item-about">
|
||
<a href="/about/" rel="section">
|
||
|
||
<i class="menu-item-icon fa fa-fw fa-user"></i> <br>
|
||
|
||
关于
|
||
</a>
|
||
</li>
|
||
|
||
|
||
<li class="menu-item menu-item-tags">
|
||
<a href="/tags/" rel="section">
|
||
|
||
<i class="menu-item-icon fa fa-fw fa-tags"></i> <br>
|
||
|
||
标签
|
||
</a>
|
||
</li>
|
||
|
||
|
||
<li class="menu-item menu-item-categories">
|
||
<a href="/categories/" rel="section">
|
||
|
||
<i class="menu-item-icon fa fa-fw fa-th"></i> <br>
|
||
|
||
分类
|
||
</a>
|
||
</li>
|
||
|
||
|
||
<li class="menu-item menu-item-archives">
|
||
<a href="/archives/" rel="section">
|
||
|
||
<i class="menu-item-icon fa fa-fw fa-archive"></i> <br>
|
||
|
||
归档
|
||
</a>
|
||
</li>
|
||
|
||
|
||
<li class="menu-item menu-item-bookmarks">
|
||
<a href="/bookmarks/" rel="section">
|
||
|
||
<i class="menu-item-icon fa fa-fw fa-map"></i> <br>
|
||
|
||
书签
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
<li class="menu-item menu-item-search">
|
||
|
||
<a href="javascript:;" class="popup-trigger">
|
||
|
||
|
||
<i class="menu-item-icon fa fa-search fa-fw"></i> <br>
|
||
|
||
搜索
|
||
</a>
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
|
||
|
||
<div class="site-search">
|
||
|
||
<div class="popup search-popup local-search-popup">
|
||
<div class="local-search-header clearfix">
|
||
<span class="search-icon">
|
||
<i class="fa fa-search"></i>
|
||
</span>
|
||
<span class="popup-btn-close">
|
||
<i class="fa fa-times-circle"></i>
|
||
</span>
|
||
<div class="local-search-input-wrapper">
|
||
<input autocomplete="off" placeholder="搜索..." spellcheck="false" type="text" id="local-search-input">
|
||
</div>
|
||
</div>
|
||
<div id="local-search-result"></div>
|
||
</div>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</nav>
|
||
|
||
|
||
|
||
</div>
|
||
</header>
|
||
|
||
<main id="main" class="main">
|
||
<div class="main-inner">
|
||
<div class="content-wrap">
|
||
<div id="content" class="content">
|
||
|
||
|
||
<div id="posts" class="posts-expand">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
|
||
|
||
|
||
|
||
<div class="post-block">
|
||
<link itemprop="mainEntityOfPage" href="https://cool-y.github.io/2019/05/14/pack-and-unpack/">
|
||
|
||
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
|
||
<meta itemprop="name" content="Cool-Y">
|
||
<meta itemprop="description" content>
|
||
<meta itemprop="image" content="/images/avatar.png">
|
||
</span>
|
||
|
||
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
|
||
<meta itemprop="name" content="混元霹雳手">
|
||
</span>
|
||
|
||
|
||
<header class="post-header">
|
||
|
||
|
||
|
||
<h1 class="post-title" itemprop="name headline">加壳与脱壳</h1>
|
||
|
||
|
||
<div class="post-meta">
|
||
<span class="post-time">
|
||
|
||
<span class="post-meta-item-icon">
|
||
<i class="fa fa-calendar-o"></i>
|
||
</span>
|
||
|
||
<span class="post-meta-item-text">发表于</span>
|
||
|
||
<time title="创建于" itemprop="dateCreated datePublished" datetime="2019-05-14T11:20:59+08:00">
|
||
2019-05-14
|
||
</time>
|
||
|
||
|
||
|
||
|
||
|
||
</span>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<span id="/2019/05/14/pack-and-unpack/" class="leancloud_visitors" data-flag-title="加壳与脱壳">
|
||
<span class="post-meta-divider">|</span>
|
||
<span class="post-meta-item-icon">
|
||
<i class="fa fa-eye"></i>
|
||
</span>
|
||
|
||
<span class="post-meta-item-text">阅读次数:</span>
|
||
|
||
<span class="leancloud-visitors-count"></span>
|
||
</span>
|
||
|
||
|
||
|
||
|
||
|
||
<div class="post-wordcount">
|
||
|
||
|
||
<span class="post-meta-item-icon">
|
||
<i class="fa fa-file-word-o"></i>
|
||
</span>
|
||
|
||
<span title="字数统计">
|
||
4.3k 字
|
||
</span>
|
||
|
||
|
||
|
||
<span class="post-meta-divider">|</span>
|
||
|
||
|
||
|
||
<span class="post-meta-item-icon">
|
||
<i class="fa fa-clock-o"></i>
|
||
</span>
|
||
|
||
<span title="阅读时长">
|
||
15 分钟
|
||
</span>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
|
||
</div>
|
||
</header>
|
||
|
||
|
||
|
||
|
||
|
||
<div class="post-body" itemprop="articleBody">
|
||
|
||
|
||
|
||
|
||
|
||
<p>壳是最早出现的一种专用加密软件技术。一些软件会采取加壳保护的方式。<br>壳附加在原始程序上,通过Windows加载器载入内存后,先于原始程序执行,以得到控制权,在执行的过程中对原始程序进行解密还原,然后把控制权还给原始程序,执行原来的代码。<br>加上外壳后,原始程序在磁盘文件中一般是以加密后的形式存在的,只在执行时在内存中还原。这样可以有效防止破解者对程序文件进行非法修改,也可以防止程序被静态反编译。</p>
|
||
<h1 id="壳的加载过程"><a href="#壳的加载过程" class="headerlink" title="壳的加载过程"></a>壳的加载过程</h1><p>壳和病毒在某些地方类似,都需要获得比原程序更早的控制权。壳修改了原程序执行文件的组织结构,从而获得控制权,但不会影响原程序正常运行。</p>
|
||
<ol>
|
||
<li>保存入口参数<br>加壳程序在初始化时会保存各寄存器的值,待外壳执行完毕,再恢复各寄存器的内容,最后跳回原程序执行。通常用pushad/popad等指令保存和恢复现场环境。</li>
|
||
<li>获取壳本身需要使用的API地址<br>一般情况下,外壳的输入表中只有GetProcAddress、GetModuleHandle和LoadLibrary这三个API函数,甚至只有Kernel32.dll及GetProcAddress。如果需要其他API函数,可以通过LoadlibraryA或LoadlibraryExA将DLL文件映像映射到调用进程的地址空间中,函数返回的HINSTANCE值用于标识文件映像所映射的虚拟内存地址。</li>
|
||
<li>解密原程序各个区块的数据<br>在程序执行时,外壳将解密区块数据,使程序正常运行。</li>
|
||
<li>IAT的初始化<br>IAT的填写本来由PE装载器实现,但由于在加壳时构造了一个自建输入表,并让PE文件头数据目录表中的输入表指针指向自建输入表,PE装载器转而填写自建输入表,程序的原始输入表被外壳变形后存储,IAT的填写会由外壳程序实现。外壳从头到尾扫描变形后的输入表结构,重新获得引入函数地址,填写在IAT中。</li>
|
||
<li>重定位项的处理<br>针对加壳的DLL</li>
|
||
<li>Hook API<br>壳在修改原程序文件的输入表后自己模仿操作系统的流程,向输入表填充相关数据。在填充过程中,外壳可以填充Hook API代码的地址,从而获得程序控制权。</li>
|
||
<li>跳转到OEP</li>
|
||
</ol>
|
||
<h1 id="通用脱壳方法"><a href="#通用脱壳方法" class="headerlink" title="通用脱壳方法"></a>通用脱壳方法</h1><p>通常脱壳的基本步骤如下:<br>1:寻找OEP<br>2:转储(PS:传说中的dump)<br>3:修复IAT(修复导入表)<br>4:检查目标程序是否存在AntiDump等阻止程序被转储的保护措施,并尝试修复这些问题。<br>以上是脱壳的经典步骤,可能具体到不同的壳的话会有细微的差别。</p>
|
||
<h2 id="寻找OEP"><a href="#寻找OEP" class="headerlink" title="寻找OEP"></a>寻找OEP</h2><ol>
|
||
<li>搜索JMP或者CALL指令的机器码(即一步直达法,只适用于少数壳,包括UPX,ASPACK壳)<br>对于一些简单的壳可以用这种方式来定位OEP,但是对于像AsProtect这类强壳(PS:AsProtect在04年算是强壳了,嘿嘿)就不适用了,我们可以直接搜索长跳转JMP(0E9)或者CALL(0E8)这类长转移的机器码,一般情况下(理想情况)壳在解密完原程序各个区段以后,需要一个长JMP或者CALL跳转到原程序代码段中的OEP处开始执行原程序代码。按CTRL+B组合键搜索一下JMP的机器码E9(CTRL+L查看下一个),看看有没有这样一个JMP跳转到原程序的代码段。</li>
|
||
<li>使用OllyDbg自带的功能定位OEP(SFX法)<br>演示这种方法目标程序我们还是选择CRACKME UPX.EXE,用OD加载该程序,然后选择菜单项Options-Debugging options-SFX。该选项只有当OllyDbg发现壳的入口点位于代码段之外的时候才会起作用,壳的入口点位于代码段中的情况还是比较少见的。</li>
|
||
<li>使用Patch过的OD来定位OEP(即内存映像法)<br>正常的内存访问断点读取,写入,执行的时候都会断下来,该Patch过的OD内存访问断点仅当执行的时候才会断下来,我们可以利用这一点来定位OEP。<br>UPX壳的解密例程会解密原程序的各个区段并将各个区段原始字节写回到原处,我们最好不要在解密区段的过程中断下来,说不定要断成千上万次才能到达OEP,这里有了这个Patch过的OD就方便多了,其内存访问断点仅当执行的时候才会断下来,当其在执行第一个区段中的代码时,基本上就可以断定是OEP了。</li>
|
||
<li>堆栈平衡法(即ESP定律法)<br>这种方法适用于一些古老的壳。这些壳首先会使用PUSHAD指令保存寄存器环境,在解密各个区段完毕,跳往OEP之前,会使用POPAD指令恢复寄存器环境。<br>有的情况下保存寄存器环境可能不是第一条指令,但也在附近了,还有些情况下,有些壳不使用PUSHAD,而是逐一PUSH各个寄存器(例如:PUSH EAX,PUSH EBX等等),总而言之,在解密完区段,跳往OEP之前会恢复寄存器环境。<br>按F7键执行PUSHAD:可以看到各个寄存器的初始值被压入到堆栈中了,这里我们可以对这些初始值设置内存或者硬件访问断点,当解密例程读取这些初始值的时候就会断下来,断下来处基本上就在OEP附近了。<br>这里我们可以通过在ESP寄存器值上面单击鼠标右键选择-Follow in dump在数据窗口中定位到这些寄存器的初始值。对这些初始值的第一个字节或者前4个字节设置硬件访问断点。当壳的解密例程读取该值的时候断了下来,停在popad的下一行,紧接着下面就是跳往OEP处,说明这个方法起作用了。</li>
|
||
<li>VB应用程序定位OEP法(Native 或者 P-CODE)<br>定位VB程序的OEP比较容易,因为VB应用程序都有一个特点-开始都是一个PUSH指令,紧接着一个CALL指令调用一个VB API函数。我们可以使用Patch过的OD,首先定位到VB的动态库,接着给该动态库的代码段设置内存访问断点,<br>当壳的解密例程解密完原程序各个区段,接着就会断在VB DLL的第一条指令处,接着我们可以在堆栈中定位到返回地址,就可以来到OEP的下一条指令处。这里我们也可以使用前面介绍的方法-跟逐一给各个区段设置内存访问断点(使用Patch过的OD),但是很多壳会检测这种方法,所以大家可能根据需要不同的情况来尝试这不同的方法。这种方法很容易理解,我就不举例子了,以后大家如果遇到了VB程序可以试试这种方法。</li>
|
||
<li>最后一次异常法<br>如果我们在脱壳的过程中发现目标程序产生大量异常的话,就可以使用最后一次异常法,将EXCEPTIONS菜单项中的忽略各个异常的选项都勾选上,运行起来。这里我们可以看到产生了好几处异常,但是都不是位于第一个区段,说明这些异常不是在原程序运行期间发生的,是在壳的解密例程执行期间产生的异常。重新启动OD,将EXCEPTIONS菜单项中忽略的异常选项的对勾都去掉,仅保留Ignore memory access violations in KERNEL32这个选项的对勾。按SHIFT + F9忽略异常继续运行,我们直到最后一次异常。<br>接着我们可以 <strong><em>对代码段设置内存访问断点</em></strong> ,可能有人会问,为什么不在一开始设置内存访问断点呢?原因是很多壳会检测程序在开始时是否自身被设置内存访问断点,如果执行到了最后一次异常处的话,很可能已经绕过了壳的检测时机!</li>
|
||
<li>用壳最常用的API函数来定位OEP<br>将忽略的异常选项都勾选上,我们来定位一下壳最常用的API函数,比如GetProcAddress,LoadLibrary。ExitThread有些壳会用。<br>使用bp GetProcAddress命令给该API函数设置一个断点。我们只需要知道壳在哪些地方调用GetProcAddress,所以我们在断下来的这一行上面单击鼠标右键选择-Breakpoint-Conditional log,来设置条件记录。将Pause program这一项勾选上Never,记录的表达式设置为[ESP],也就是记录返回地址,这样我们就能知道哪些地方调用GetProcAddress。接着在日志窗口中单击鼠标右键选择-Clear Log(清空日志)。运行起来,我们可以看到程序的主窗口弹了出来,打开日志窗口,看看最后一次GetProcAddress(排除掉第一个区段中调用的位置)是在哪里被调用的。<br>我们可以在 <strong><em>对代码段设置内存访问断点</em></strong> 之前尝试一下这种方法,这样就可以绕过很多壳对内存断点的检测,但是有一些壳也会对API函数断点进行检测,所以说我们需要各种方式都尝试一下,找到最合适的。</li>
|
||
<li>利用应用程序调用的第一个API函数来定位OEP</li>
|
||
</ol>
|
||
<h2 id="IAT表修复"><a href="#IAT表修复" class="headerlink" title="IAT表修复"></a>IAT表修复</h2><p>为了确保操作系统将正确的API函数地址填充到IAT中,应该满足一下几点要求:<br>1:可执行文件各IAT项所在的文件偏移处必须是一个指针,指向一个字符串。<br>2:该字符串为API函数的名称。<br>如果这两项满足,就可以确保程序在启动时,操作系统会将正确的API函数地址填充到IAT中。<br>假如,我们当前位于被加壳程序的OEP处,我们接下来可以将程序dump出来,但是在dump之前我们必须修复IAT,为什么要修复IAT呢?难道壳将IAT破坏了吗?对,的确是这样,壳压根不需要原程序的IAT,因为被加壳程序首先会执行解密例程,读取IAT中所需要的API的名称指针,然后定位到API函数地址,将其填入到IAT中,这个时候,IAT中已经被填充了正确的API函数地址,对应的API函数名称的字符串已经不需要了,可以清除掉。<br>大部分的壳会将API函数名称对应的字符串以密文的形式保存到某个地址处,让Cracker们不能那么容易找到它们。</p>
|
||
<h1 id="压缩壳"><a href="#压缩壳" class="headerlink" title="压缩壳"></a>压缩壳</h1><p>压缩壳的特点就是减小软件的体积,加密保护不是重点。目前,兼容性和稳定性较好的压缩壳有UPX、ASPack、PECompact等。</p>
|
||
<h2 id="UPX"><a href="#UPX" class="headerlink" title="UPX"></a><a href="https://upx.github.io/" target="_blank" rel="noopener">UPX</a></h2><p>UPX-the Ultimate Packer for eXecutables是以命令行方式操作的可执行文件压缩程序。<br>UPX早期的压缩引擎是有UPX自己实现的,其3.x版本也支持LZMA第三方压缩引擎。UPX除了对目标程序进行压缩,也可以解压缩。它不包含任何反调试或保护策略。另外,UPX保护工具UPXPR、UPX-sCRAMBLER等可修改UPX加壳标志,使其自解压功能失效。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Usage: upx [-123456789dlthVL] [-qvfk] [-o file] file</span><br></pre></td></tr></table></figure></p>
|
||
<h3 id="识别UPX加壳"><a href="#识别UPX加壳" class="headerlink" title="识别UPX加壳"></a>识别UPX加壳</h3><p>被加壳程序:点击按钮之后弹框</p>
|
||
<ol>
|
||
<li><p>导入函数很少<br>未加壳程序的导入函数:<br><img src="https://res.cloudinary.com/dozyfkbg3/image/upload/v1557817831/%E5%8A%A0%E5%A3%B3/1.png" alt><br>加壳后的导入函数:<br><img src="https://res.cloudinary.com/dozyfkbg3/image/upload/v1557817867/%E5%8A%A0%E5%A3%B3/2.png" alt></p>
|
||
</li>
|
||
<li><p>使用IDA识别代码段<br>只有少量的代码被识别</p>
|
||
</li>
|
||
<li><p>使用OllyDbg打开程序,警告被加壳<br><img src="https://res.cloudinary.com/dozyfkbg3/image/upload/v1557819108/%E5%8A%A0%E5%A3%B3/3.png" alt></p>
|
||
</li>
|
||
<li><p>程序的节名包含加壳器的标识<br>加壳后的程序节名为UPX0、UPX1、rsrc</p>
|
||
</li>
|
||
<li><p>程序拥有不正常的节大小。例如.text节的原始数据大小为0,但虚拟大小非0<br><img src="https://res.cloudinary.com/dozyfkbg3/image/upload/v1557819290/%E5%8A%A0%E5%A3%B3/4.png" alt></p>
|
||
</li>
|
||
<li><p>使用加壳探测工具如PEiD<br><img src="https://res.cloudinary.com/dozyfkbg3/image/upload/v1557820111/%E5%8A%A0%E5%A3%B3/5.png" alt></p>
|
||
</li>
|
||
<li><p>熵值计算<br>压缩或加密数据更接近于随机数据,熵值更高。如使用PEiD计算熵值<br><img src="https://res.cloudinary.com/dozyfkbg3/image/upload/v1557821441/%E5%8A%A0%E5%A3%B3/6.png" alt><br>PEiD计算熵值的方法:<br>1.重新组织需要计算的数据<br>i.以下数据不列入计算熵的范围:导出表数据、导入表数据、资源数据、重定向数据。<br>ii. 尾部全0的数据不列入计算熵的范围。<br>iii. PE头不列入计算熵的范围。<br>2.分别计算每一部分数据的熵E和该部分数据大小S。<br>3.以下列公式得到整个PE文件的熵 Entropy = ∑Ei * Si / ∑Si (i = 1,2…n)。</p>
|
||
</li>
|
||
</ol>
|
||
<h3 id="UPX手动脱壳"><a href="#UPX手动脱壳" class="headerlink" title="UPX手动脱壳"></a>UPX手动脱壳</h3><p>根据 <strong><em>栈平衡原理</em></strong> 寻找OEP<br>在编写加壳软件时,必须保证外壳初始化的现场环境(各寄存器值)与原程序的现场环境相同。因此,加壳程序在初始化时保存各寄存器的值,待外壳执行完毕后恢复寄存器的内容,最后跳转到原程序执行。通常用pushad(push eax/ecx/edx/ebx/esp/ebp/esi/edi)、popad来保存和恢复现场环境。<br>首先用Ollydbg加载已加壳的程序,起始代码如下:<br><img src="https://res.cloudinary.com/dozyfkbg3/image/upload/v1557836854/%E5%8A%A0%E5%A3%B3/7.png" alt><br>此时现场环境(寄存器值)如下:<br><img src="https://res.cloudinary.com/dozyfkbg3/image/upload/v1557837144/%E5%8A%A0%E5%A3%B3/8.png" alt><br>在执行pushad指令后,寄存器的值被压入栈中,如下所示:<br><img src="https://res.cloudinary.com/dozyfkbg3/image/upload/v1557837250/%E5%8A%A0%E5%A3%B3/9.png" alt><br>此时esp指向12FFA4h,对这个地址设置硬件访问断点,然后运行程序,在调用popad恢复现场环境时会访问12FFA4h,造成中断,此时离OEP已经不远了:<br><img src="https://res.cloudinary.com/dozyfkbg3/image/upload/v1557837519/%E5%8A%A0%E5%A3%B3/10.png" alt><br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">005B5155 .- E9 9506F4FF jmp carckUPX.004F57EF</span><br></pre></td></tr></table></figure></p>
|
||
<p>即为跳转到OEP的指令,设置断点,跟进到004F57EF,此时我们就来到了OEP。<br>dump和修复IAT表的工具很多。</p>
|
||
<ol>
|
||
<li>使用Ollydump进行程序脱壳和IAT表修复。<br><img src="https://res.cloudinary.com/dozyfkbg3/image/upload/v1557837859/%E5%8A%A0%E5%A3%B3/11.png" alt><br>使用PEiD检查,果然壳已经脱掉!</li>
|
||
<li>使用PETools dump和Import Reconstruct修复IAT<br>使用PETools出来的程序不能运行,提示win32无法识别,这是因为IAT表没有重建。<br>使用Import Reconstruct需要知道IAT表的起始位置。<br>我们知道API函数的调用通常是通过间接跳转或者间接CALL来实现的。<br>即JMP [XXXXXXX] or CALL [XXXXXX],这样是直接调用IAT中保存的API函数地址。<br>首先,定位到获取IAT中函数地址的跳转表,这里就是该程序将要调用到的一些API函数,我们可以看到这些跳转指令的都是以机器码FF 25开头的,有些教程里面说直接搜索二进制FF 25就可以快速的定位该跳表。<br>看到整个IAT后,我们直接下拉到IAT的尾部,我们知道属于同一个动态库的API函数地址都是连续存放的,不同的动态库函数地址列表是用零隔开的。<br>Import REConstructor重建IAT需要三项指标:<br>1)IAT的起始地址,这里是403184,减去映像基址400000就得到了3184(RVA:相对虚拟地址)。<br>2)IAT的大小<br>IAT的大小 = 40328C - 403184 = 108(十六进制)<br>3)OEP = 401000(虚拟地址)- 映像基址400000 = 1000(OEP的RVA)。</li>
|
||
</ol>
|
||
<h2 id="ASPack"><a href="#ASPack" class="headerlink" title="ASPack"></a><a href="http://www.aspack.com/" target="_blank" rel="noopener">ASPack</a></h2><p>ASPack是一款Win32可执行文件压缩软件,可压缩Win32可执行文件EXE、DLL、OCX,具有很高的兼容性和稳定性。</p>
|
||
<h1 id="加密壳"><a href="#加密壳" class="headerlink" title="加密壳"></a>加密壳</h1><p>加密壳种类较多,一些壳只保护程序,另一些壳提供额外的功能如注册、使用次数、时间限制。越有名的加密壳,其破解可能性越大。</p>
|
||
<h2 id="ASProtect"><a href="#ASProtect" class="headerlink" title="ASProtect"></a>ASProtect</h2><p>这个壳在pack界当选老大是毫无异议的,当然这里的老大不仅指它的加密强度,而是在于它开创了壳的新时代,SEH,BPM断点的清除都出自这里,更为有名的当属RSA的使用,使得Demo版无法被crack成完整版本,code_dips也源于这里。IAT的处理即使到到现在看来也是很强的。他的特长在于各种加密算法的运用,这也是各种壳要学习的地方。<br>它可以压缩、加密、反跟踪代码、CRC校验和花指令等保护措施。<br>使用Blowfish、Twofish、TEA等加密算法,以RSA1024为注册密钥生成器,通过API钩子与加壳程序通信。<br>ASProtect为软件开发人员提供了SDK,从而实现了加密程序的内外结合。<br>ASProtect 1.x系列,低版本用Stripper工具可自动脱壳。ASProtect的SKE系列主要在protect OEP和SDK上采用了虚拟机技术。</p>
|
||
<h3 id="加密后的特征"><a href="#加密后的特征" class="headerlink" title="加密后的特征"></a>加密后的特征</h3><ol>
|
||
<li>导入函数很少</li>
|
||
<li>程序的节名不再是典型的.text</li>
|
||
<li>使用IDA打开,几乎没有可识别的代码</li>
|
||
<li>使用OllyDbg打开程序,被警告该程序已加密,查找参考文本字符串都是乱码</li>
|
||
<li>使用PEiD检查出是ASProtect加壳</li>
|
||
</ol>
|
||
|
||
|
||
</div>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div>
|
||
<div style="padding: 10px 0; margin: 20px auto; width: 90%; text-align: center;">
|
||
<div>您的支持将鼓励我继续创作!</div>
|
||
<button id="rewardButton" disable="enable" onclick="var qr = document.getElementById('QR'); if (qr.style.display === 'none') {qr.style.display='block';} else {qr.style.display='none'}">
|
||
<span>打赏</span>
|
||
</button>
|
||
<div id="QR" style="display: none;">
|
||
|
||
|
||
<div id="wechat" style="display: inline-block">
|
||
<img id="wechat_qr" src="/images/Wechatpay.png" alt="Cool-Y 微信支付">
|
||
<p>微信支付</p>
|
||
</div>
|
||
|
||
|
||
|
||
<div id="alipay" style="display: inline-block">
|
||
<img id="alipay_qr" src="/images/Alipay.png" alt="Cool-Y 支付宝">
|
||
<p>支付宝</p>
|
||
</div>
|
||
|
||
|
||
|
||
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
|
||
<footer class="post-footer">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="post-nav">
|
||
<div class="post-nav-next post-nav-item">
|
||
|
||
<a href="/2019/05/13/PE-file/" rel="next" title="PE文件格式学习">
|
||
<i class="fa fa-chevron-left"></i> PE文件格式学习
|
||
</a>
|
||
|
||
</div>
|
||
|
||
<span class="post-nav-divider"></span>
|
||
|
||
<div class="post-nav-prev post-nav-item">
|
||
|
||
<a href="/2019/07/01/AFL-first-learn/" rel="prev" title="模糊测试与AFL">
|
||
模糊测试与AFL <i class="fa fa-chevron-right"></i>
|
||
</a>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
|
||
|
||
|
||
</footer>
|
||
</div>
|
||
|
||
|
||
|
||
</article>
|
||
|
||
|
||
|
||
<div class="post-spread">
|
||
|
||
</div>
|
||
</div>
|
||
|
||
|
||
</div>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="comments" id="comments">
|
||
|
||
<div id="gitment-container"></div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
|
||
|
||
<div class="sidebar-toggle">
|
||
<div class="sidebar-toggle-line-wrap">
|
||
<span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
|
||
<span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
|
||
<span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
|
||
</div>
|
||
</div>
|
||
|
||
<aside id="sidebar" class="sidebar">
|
||
|
||
<div class="sidebar-inner">
|
||
|
||
|
||
|
||
|
||
<ul class="sidebar-nav motion-element">
|
||
<li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
|
||
文章目录
|
||
</li>
|
||
<li class="sidebar-nav-overview" data-target="site-overview-wrap">
|
||
站点概览
|
||
</li>
|
||
</ul>
|
||
|
||
|
||
<section class="site-overview-wrap sidebar-panel">
|
||
<div class="site-overview">
|
||
<div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
|
||
|
||
<img class="site-author-image" itemprop="image" src="/images/avatar.png" alt="Cool-Y">
|
||
|
||
<p class="site-author-name" itemprop="name">Cool-Y</p>
|
||
<p class="site-description motion-element" itemprop="description">没人比我更懂中医#MAGA</p>
|
||
</div>
|
||
|
||
<nav class="site-state motion-element">
|
||
|
||
|
||
<div class="site-state-item site-state-posts">
|
||
|
||
<a href="/archives/">
|
||
|
||
<span class="site-state-item-count">22</span>
|
||
<span class="site-state-item-name">日志</span>
|
||
</a>
|
||
</div>
|
||
|
||
|
||
|
||
|
||
|
||
<div class="site-state-item site-state-categories">
|
||
<a href="/categories/index.html">
|
||
<span class="site-state-item-count">8</span>
|
||
<span class="site-state-item-name">分类</span>
|
||
</a>
|
||
</div>
|
||
|
||
|
||
|
||
|
||
|
||
<div class="site-state-item site-state-tags">
|
||
<a href="/tags/index.html">
|
||
<span class="site-state-item-count">44</span>
|
||
<span class="site-state-item-name">标签</span>
|
||
</a>
|
||
</div>
|
||
|
||
|
||
</nav>
|
||
|
||
|
||
|
||
|
||
<div class="links-of-author motion-element">
|
||
|
||
<span class="links-of-author-item">
|
||
<a href="https://github.com/Cool-Y" target="_blank" title="GitHub">
|
||
|
||
<i class="fa fa-fw fa-github"></i>GitHub</a>
|
||
</span>
|
||
|
||
<span class="links-of-author-item">
|
||
<a href="mailto:cool.yim@whu.edu.cn" target="_blank" title="E-Mail">
|
||
|
||
<i class="fa fa-fw fa-envelope"></i>E-Mail</a>
|
||
</span>
|
||
|
||
<span class="links-of-author-item">
|
||
<a href="https://www.instagram.com/yan__han/" target="_blank" title="Instagram">
|
||
|
||
<i class="fa fa-fw fa-instagram"></i>Instagram</a>
|
||
</span>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
</section>
|
||
|
||
|
||
<!--noindex-->
|
||
<section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
|
||
<div class="post-toc">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#壳的加载过程"><span class="nav-text">壳的加载过程</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#通用脱壳方法"><span class="nav-text">通用脱壳方法</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#寻找OEP"><span class="nav-text">寻找OEP</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#IAT表修复"><span class="nav-text">IAT表修复</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#压缩壳"><span class="nav-text">压缩壳</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#UPX"><span class="nav-text">UPX</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#识别UPX加壳"><span class="nav-text">识别UPX加壳</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#UPX手动脱壳"><span class="nav-text">UPX手动脱壳</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#ASPack"><span class="nav-text">ASPack</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#加密壳"><span class="nav-text">加密壳</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#ASProtect"><span class="nav-text">ASProtect</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#加密后的特征"><span class="nav-text">加密后的特征</span></a></li></ol></li></ol></li></ol></div>
|
||
|
||
|
||
</div>
|
||
</section>
|
||
<!--/noindex-->
|
||
|
||
|
||
|
||
|
||
</div>
|
||
</aside>
|
||
|
||
|
||
|
||
</div>
|
||
</main>
|
||
|
||
<footer id="footer" class="footer">
|
||
<div class="footer-inner">
|
||
<div class="copyright">© <span itemprop="copyrightYear">2019</span>
|
||
<span class="with-love">
|
||
<i class="fa fa-user"></i>
|
||
</span>
|
||
<span class="author" itemprop="copyrightHolder">Cool-Y</span>
|
||
|
||
|
||
<span class="post-meta-divider">|</span>
|
||
<span class="post-meta-item-icon">
|
||
<i class="fa fa-area-chart"></i>
|
||
</span>
|
||
|
||
<span title="Site words total count">67.7k</span>
|
||
|
||
</div>
|
||
|
||
|
||
<div class="powered-by">由 <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a> 强力驱动</div>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="busuanzi-count">
|
||
<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
|
||
|
||
|
||
<span class="site-uv">
|
||
<i class="fa fa-user"></i>
|
||
<span class="busuanzi-value" id="busuanzi_value_site_uv"></span>
|
||
|
||
</span>
|
||
|
||
|
||
|
||
<span class="site-pv">
|
||
<i class="fa fa-eye"></i>
|
||
<span class="busuanzi-value" id="busuanzi_value_site_pv"></span>
|
||
|
||
</span>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</div>
|
||
</footer>
|
||
|
||
|
||
<div class="back-to-top">
|
||
<i class="fa fa-arrow-up"></i>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
|
||
</div>
|
||
|
||
|
||
|
||
<script type="text/javascript">
|
||
if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
|
||
window.Promise = null;
|
||
}
|
||
</script>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
|
||
|
||
|
||
|
||
|
||
<script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
|
||
|
||
|
||
|
||
|
||
<script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
|
||
|
||
|
||
|
||
|
||
<script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
|
||
|
||
|
||
|
||
|
||
<script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
|
||
|
||
|
||
|
||
|
||
<script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<script type="text/javascript" src="/js/src/utils.js?v=5.1.4"></script>
|
||
|
||
<script type="text/javascript" src="/js/src/motion.js?v=5.1.4"></script>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<script type="text/javascript" src="/js/src/affix.js?v=5.1.4"></script>
|
||
|
||
<script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.1.4"></script>
|
||
|
||
|
||
|
||
|
||
<script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.4"></script>
|
||
<script type="text/javascript" src="/js/src/post-details.js?v=5.1.4"></script>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.4"></script>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<!-- LOCAL: You can save these files to your site and update links -->
|
||
|
||
|
||
<link rel="stylesheet" href="https://jjeejj.github.io/css/gitment.css">
|
||
<script src="https://jjeejj.github.io/js/gitment.js"></script>
|
||
|
||
<!-- END LOCAL -->
|
||
|
||
|
||
|
||
|
||
<script type="text/javascript">
|
||
function renderGitment(){
|
||
var gitment = new Gitment({
|
||
id: window.location.pathname,
|
||
owner: 'Cool-Y',
|
||
repo: 'gitment-comments',
|
||
|
||
oauth: {
|
||
|
||
|
||
client_secret: '1c5db4da72df5e6fc318d12afe5f4406f7c54343',
|
||
|
||
client_id: '180955a2c3ae3d966d9a'
|
||
}});
|
||
gitment.render('gitment-container');
|
||
}
|
||
|
||
|
||
renderGitment();
|
||
|
||
</script>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<script type="text/javascript">
|
||
// Popup Window;
|
||
var isfetched = false;
|
||
var isXml = true;
|
||
// Search DB path;
|
||
var search_path = "search.xml";
|
||
if (search_path.length === 0) {
|
||
search_path = "search.xml";
|
||
} else if (/json$/i.test(search_path)) {
|
||
isXml = false;
|
||
}
|
||
var path = "/" + search_path;
|
||
// monitor main search box;
|
||
|
||
var onPopupClose = function (e) {
|
||
$('.popup').hide();
|
||
$('#local-search-input').val('');
|
||
$('.search-result-list').remove();
|
||
$('#no-result').remove();
|
||
$(".local-search-pop-overlay").remove();
|
||
$('body').css('overflow', '');
|
||
}
|
||
|
||
function proceedsearch() {
|
||
$("body")
|
||
.append('<div class="search-popup-overlay local-search-pop-overlay"></div>')
|
||
.css('overflow', 'hidden');
|
||
$('.search-popup-overlay').click(onPopupClose);
|
||
$('.popup').toggle();
|
||
var $localSearchInput = $('#local-search-input');
|
||
$localSearchInput.attr("autocapitalize", "none");
|
||
$localSearchInput.attr("autocorrect", "off");
|
||
$localSearchInput.focus();
|
||
}
|
||
|
||
// search function;
|
||
var searchFunc = function(path, search_id, content_id) {
|
||
'use strict';
|
||
|
||
// start loading animation
|
||
$("body")
|
||
.append('<div class="search-popup-overlay local-search-pop-overlay">' +
|
||
'<div id="search-loading-icon">' +
|
||
'<i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>' +
|
||
'</div>' +
|
||
'</div>')
|
||
.css('overflow', 'hidden');
|
||
$("#search-loading-icon").css('margin', '20% auto 0 auto').css('text-align', 'center');
|
||
|
||
$.ajax({
|
||
url: path,
|
||
dataType: isXml ? "xml" : "json",
|
||
async: true,
|
||
success: function(res) {
|
||
// get the contents from search data
|
||
isfetched = true;
|
||
$('.popup').detach().appendTo('.header-inner');
|
||
var datas = isXml ? $("entry", res).map(function() {
|
||
return {
|
||
title: $("title", this).text(),
|
||
content: $("content",this).text(),
|
||
url: $("url" , this).text()
|
||
};
|
||
}).get() : res;
|
||
var input = document.getElementById(search_id);
|
||
var resultContent = document.getElementById(content_id);
|
||
var inputEventFunction = function() {
|
||
var searchText = input.value.trim().toLowerCase();
|
||
var keywords = searchText.split(/[\s\-]+/);
|
||
if (keywords.length > 1) {
|
||
keywords.push(searchText);
|
||
}
|
||
var resultItems = [];
|
||
if (searchText.length > 0) {
|
||
// perform local searching
|
||
datas.forEach(function(data) {
|
||
var isMatch = false;
|
||
var hitCount = 0;
|
||
var searchTextCount = 0;
|
||
var title = data.title.trim();
|
||
var titleInLowerCase = title.toLowerCase();
|
||
var content = data.content.trim().replace(/<[^>]+>/g,"");
|
||
var contentInLowerCase = content.toLowerCase();
|
||
var articleUrl = decodeURIComponent(data.url);
|
||
var indexOfTitle = [];
|
||
var indexOfContent = [];
|
||
// only match articles with not empty titles
|
||
if(title != '') {
|
||
keywords.forEach(function(keyword) {
|
||
function getIndexByWord(word, text, caseSensitive) {
|
||
var wordLen = word.length;
|
||
if (wordLen === 0) {
|
||
return [];
|
||
}
|
||
var startPosition = 0, position = [], index = [];
|
||
if (!caseSensitive) {
|
||
text = text.toLowerCase();
|
||
word = word.toLowerCase();
|
||
}
|
||
while ((position = text.indexOf(word, startPosition)) > -1) {
|
||
index.push({position: position, word: word});
|
||
startPosition = position + wordLen;
|
||
}
|
||
return index;
|
||
}
|
||
|
||
indexOfTitle = indexOfTitle.concat(getIndexByWord(keyword, titleInLowerCase, false));
|
||
indexOfContent = indexOfContent.concat(getIndexByWord(keyword, contentInLowerCase, false));
|
||
});
|
||
if (indexOfTitle.length > 0 || indexOfContent.length > 0) {
|
||
isMatch = true;
|
||
hitCount = indexOfTitle.length + indexOfContent.length;
|
||
}
|
||
}
|
||
|
||
// show search results
|
||
|
||
if (isMatch) {
|
||
// sort index by position of keyword
|
||
|
||
[indexOfTitle, indexOfContent].forEach(function (index) {
|
||
index.sort(function (itemLeft, itemRight) {
|
||
if (itemRight.position !== itemLeft.position) {
|
||
return itemRight.position - itemLeft.position;
|
||
} else {
|
||
return itemLeft.word.length - itemRight.word.length;
|
||
}
|
||
});
|
||
});
|
||
|
||
// merge hits into slices
|
||
|
||
function mergeIntoSlice(text, start, end, index) {
|
||
var item = index[index.length - 1];
|
||
var position = item.position;
|
||
var word = item.word;
|
||
var hits = [];
|
||
var searchTextCountInSlice = 0;
|
||
while (position + word.length <= end && index.length != 0) {
|
||
if (word === searchText) {
|
||
searchTextCountInSlice++;
|
||
}
|
||
hits.push({position: position, length: word.length});
|
||
var wordEnd = position + word.length;
|
||
|
||
// move to next position of hit
|
||
|
||
index.pop();
|
||
while (index.length != 0) {
|
||
item = index[index.length - 1];
|
||
position = item.position;
|
||
word = item.word;
|
||
if (wordEnd > position) {
|
||
index.pop();
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
searchTextCount += searchTextCountInSlice;
|
||
return {
|
||
hits: hits,
|
||
start: start,
|
||
end: end,
|
||
searchTextCount: searchTextCountInSlice
|
||
};
|
||
}
|
||
|
||
var slicesOfTitle = [];
|
||
if (indexOfTitle.length != 0) {
|
||
slicesOfTitle.push(mergeIntoSlice(title, 0, title.length, indexOfTitle));
|
||
}
|
||
|
||
var slicesOfContent = [];
|
||
while (indexOfContent.length != 0) {
|
||
var item = indexOfContent[indexOfContent.length - 1];
|
||
var position = item.position;
|
||
var word = item.word;
|
||
// cut out 100 characters
|
||
var start = position - 20;
|
||
var end = position + 80;
|
||
if(start < 0){
|
||
start = 0;
|
||
}
|
||
if (end < position + word.length) {
|
||
end = position + word.length;
|
||
}
|
||
if(end > content.length){
|
||
end = content.length;
|
||
}
|
||
slicesOfContent.push(mergeIntoSlice(content, start, end, indexOfContent));
|
||
}
|
||
|
||
// sort slices in content by search text's count and hits' count
|
||
|
||
slicesOfContent.sort(function (sliceLeft, sliceRight) {
|
||
if (sliceLeft.searchTextCount !== sliceRight.searchTextCount) {
|
||
return sliceRight.searchTextCount - sliceLeft.searchTextCount;
|
||
} else if (sliceLeft.hits.length !== sliceRight.hits.length) {
|
||
return sliceRight.hits.length - sliceLeft.hits.length;
|
||
} else {
|
||
return sliceLeft.start - sliceRight.start;
|
||
}
|
||
});
|
||
|
||
// select top N slices in content
|
||
|
||
var upperBound = parseInt('1');
|
||
if (upperBound >= 0) {
|
||
slicesOfContent = slicesOfContent.slice(0, upperBound);
|
||
}
|
||
|
||
// highlight title and content
|
||
|
||
function highlightKeyword(text, slice) {
|
||
var result = '';
|
||
var prevEnd = slice.start;
|
||
slice.hits.forEach(function (hit) {
|
||
result += text.substring(prevEnd, hit.position);
|
||
var end = hit.position + hit.length;
|
||
result += '<b class="search-keyword">' + text.substring(hit.position, end) + '</b>';
|
||
prevEnd = end;
|
||
});
|
||
result += text.substring(prevEnd, slice.end);
|
||
return result;
|
||
}
|
||
|
||
var resultItem = '';
|
||
|
||
if (slicesOfTitle.length != 0) {
|
||
resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + highlightKeyword(title, slicesOfTitle[0]) + "</a>";
|
||
} else {
|
||
resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + title + "</a>";
|
||
}
|
||
|
||
slicesOfContent.forEach(function (slice) {
|
||
resultItem += "<a href='" + articleUrl + "'>" +
|
||
"<p class=\"search-result\">" + highlightKeyword(content, slice) +
|
||
"...</p>" + "</a>";
|
||
});
|
||
|
||
resultItem += "</li>";
|
||
resultItems.push({
|
||
item: resultItem,
|
||
searchTextCount: searchTextCount,
|
||
hitCount: hitCount,
|
||
id: resultItems.length
|
||
});
|
||
}
|
||
})
|
||
};
|
||
if (keywords.length === 1 && keywords[0] === "") {
|
||
resultContent.innerHTML = '<div id="no-result"><i class="fa fa-search fa-5x" /></div>'
|
||
} else if (resultItems.length === 0) {
|
||
resultContent.innerHTML = '<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>'
|
||
} else {
|
||
resultItems.sort(function (resultLeft, resultRight) {
|
||
if (resultLeft.searchTextCount !== resultRight.searchTextCount) {
|
||
return resultRight.searchTextCount - resultLeft.searchTextCount;
|
||
} else if (resultLeft.hitCount !== resultRight.hitCount) {
|
||
return resultRight.hitCount - resultLeft.hitCount;
|
||
} else {
|
||
return resultRight.id - resultLeft.id;
|
||
}
|
||
});
|
||
var searchResultList = '<ul class=\"search-result-list\">';
|
||
resultItems.forEach(function (result) {
|
||
searchResultList += result.item;
|
||
})
|
||
searchResultList += "</ul>";
|
||
resultContent.innerHTML = searchResultList;
|
||
}
|
||
}
|
||
|
||
if ('auto' === 'auto') {
|
||
input.addEventListener('input', inputEventFunction);
|
||
} else {
|
||
$('.search-icon').click(inputEventFunction);
|
||
input.addEventListener('keypress', function (event) {
|
||
if (event.keyCode === 13) {
|
||
inputEventFunction();
|
||
}
|
||
});
|
||
}
|
||
|
||
// remove loading animation
|
||
$(".local-search-pop-overlay").remove();
|
||
$('body').css('overflow', '');
|
||
|
||
proceedsearch();
|
||
}
|
||
});
|
||
}
|
||
|
||
// handle and trigger popup window;
|
||
$('.popup-trigger').click(function(e) {
|
||
e.stopPropagation();
|
||
if (isfetched === false) {
|
||
searchFunc(path, 'local-search-input', 'local-search-result');
|
||
} else {
|
||
proceedsearch();
|
||
};
|
||
});
|
||
|
||
$('.popup-btn-close').click(onPopupClose);
|
||
$('.popup').click(function(e){
|
||
e.stopPropagation();
|
||
});
|
||
$(document).on('keyup', function (event) {
|
||
var shouldDismissSearchPopup = event.which === 27 &&
|
||
$('.search-popup').is(':visible');
|
||
if (shouldDismissSearchPopup) {
|
||
onPopupClose();
|
||
}
|
||
});
|
||
</script>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<script src="https://cdn1.lncld.net/static/js/av-core-mini-0.6.4.js"></script>
|
||
<script>AV.initialize("EWwoJgHNdlj6iBjiFlMcabUO-gzGzoHsz", "x8FxDrYG79C8YFrTww9ljo8K");</script>
|
||
<script>
|
||
function showTime(Counter) {
|
||
var query = new AV.Query(Counter);
|
||
var entries = [];
|
||
var $visitors = $(".leancloud_visitors");
|
||
|
||
$visitors.each(function () {
|
||
entries.push( $(this).attr("id").trim() );
|
||
});
|
||
|
||
query.containedIn('url', entries);
|
||
query.find()
|
||
.done(function (results) {
|
||
var COUNT_CONTAINER_REF = '.leancloud-visitors-count';
|
||
|
||
if (results.length === 0) {
|
||
$visitors.find(COUNT_CONTAINER_REF).text(0);
|
||
return;
|
||
}
|
||
|
||
for (var i = 0; i < results.length; i++) {
|
||
var item = results[i];
|
||
var url = item.get('url');
|
||
var time = item.get('time');
|
||
var element = document.getElementById(url);
|
||
|
||
$(element).find(COUNT_CONTAINER_REF).text(time);
|
||
}
|
||
for(var i = 0; i < entries.length; i++) {
|
||
var url = entries[i];
|
||
var element = document.getElementById(url);
|
||
var countSpan = $(element).find(COUNT_CONTAINER_REF);
|
||
if( countSpan.text() == '') {
|
||
countSpan.text(0);
|
||
}
|
||
}
|
||
})
|
||
.fail(function (object, error) {
|
||
console.log("Error: " + error.code + " " + error.message);
|
||
});
|
||
}
|
||
|
||
function addCount(Counter) {
|
||
var $visitors = $(".leancloud_visitors");
|
||
var url = $visitors.attr('id').trim();
|
||
var title = $visitors.attr('data-flag-title').trim();
|
||
var query = new AV.Query(Counter);
|
||
|
||
query.equalTo("url", url);
|
||
query.find({
|
||
success: function(results) {
|
||
if (results.length > 0) {
|
||
var counter = results[0];
|
||
counter.fetchWhenSave(true);
|
||
counter.increment("time");
|
||
counter.save(null, {
|
||
success: function(counter) {
|
||
var $element = $(document.getElementById(url));
|
||
$element.find('.leancloud-visitors-count').text(counter.get('time'));
|
||
},
|
||
error: function(counter, error) {
|
||
console.log('Failed to save Visitor num, with error message: ' + error.message);
|
||
}
|
||
});
|
||
} else {
|
||
var newcounter = new Counter();
|
||
/* Set ACL */
|
||
var acl = new AV.ACL();
|
||
acl.setPublicReadAccess(true);
|
||
acl.setPublicWriteAccess(true);
|
||
newcounter.setACL(acl);
|
||
/* End Set ACL */
|
||
newcounter.set("title", title);
|
||
newcounter.set("url", url);
|
||
newcounter.set("time", 1);
|
||
newcounter.save(null, {
|
||
success: function(newcounter) {
|
||
var $element = $(document.getElementById(url));
|
||
$element.find('.leancloud-visitors-count').text(newcounter.get('time'));
|
||
},
|
||
error: function(newcounter, error) {
|
||
console.log('Failed to create');
|
||
}
|
||
});
|
||
}
|
||
},
|
||
error: function(error) {
|
||
console.log('Error:' + error.code + " " + error.message);
|
||
}
|
||
});
|
||
}
|
||
|
||
$(function() {
|
||
var Counter = AV.Object.extend("Counter");
|
||
if ($('.leancloud_visitors').length == 1) {
|
||
addCount(Counter);
|
||
} else if ($('.post-title-link').length > 1) {
|
||
showTime(Counter);
|
||
}
|
||
});
|
||
</script>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<script>
|
||
(function(){
|
||
var bp = document.createElement('script');
|
||
var curProtocol = window.location.protocol.split(':')[0];
|
||
if (curProtocol === 'https') {
|
||
bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
|
||
}
|
||
else {
|
||
bp.src = 'http://push.zhanzhang.baidu.com/push.js';
|
||
}
|
||
var s = document.getElementsByTagName("script")[0];
|
||
s.parentNode.insertBefore(bp, s);
|
||
})();
|
||
</script>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</body>
|
||
</html>
|