有一个界面,里面有组员名字。点击组员名字,调到另一个组员信息界面。
1、android stdio开发工具
2、java语法。
过程
最后目录结构图:
创建work1项目,编辑activity_main.xml
添加背景图片在res->drawablebg.jpg,cxw.jpg,cyx.jpg,zlw.jpg
创建组员.xml(layout–>右键–>New–>layout resource file–>cxw)
创建组员activity,这里继续Activity类
类似创建其三个组员。
AndroidManifest中申明acitivy
MainActivity函数
最后附加string.xml
耐心
过程
sq === 1'
如果报错存在sql注入sq === 1' --
正常运行sq === 1' and 1=1 --
报错(可能过滤了=)sq === 1' and 1 like 1 --
正常运行
sq === 1' order by 22 --
正常运行sq === 1' order by 23 --
报错(说明有22个字段)
但是,有时候会不对,不懂。猜测是当前表,而不是dual表
爆破dual字段数量
可以手动一个一个试,但是字段多了就不容易了。
这里写了一个简单的爆破脚本
还没有学会,学会来在来更新吧!
]]>如果你看过前面一篇,那么你对xposed模块的有一定的了解。接着继续来了解更多。
|
|
创建一个demo类
actvity_main.xml
创建一个xModule.java
在main->创建一个assets目录,并在之下创建xposed_init(声明xposed入口)
将这个工程,编译,打包,安装到已经支持Xposed的手机中。
需要注意的是,要关闭instant run功能(File->Settings->instant run->去掉打钩)
|
|
dependencies配置,Android studio修改成上面那样
将xopsed打钩,重启xposed框架,在运行xposedLab1,会出现->“我已经被劫”
这是已经hook成功啦。
|
|
MainActivity
actvity_main.xml
创建一个项目,将下载的->
XposedBridgeApi-54.jar
添加到app->libs中,右键->Add As Libary。
在main下面的AndroidMainfest.xml中的application下添加(自己标识为一个Xposed模块)
在build.gradle的dependencies
创建一个xModule.java
在main->创建一个assets目录,并在之下创建xposed_init(声明xposed入口)
将这个工程,编译,打包,安装到已经支持Xposed的手机中。
需要注意的是,要关闭instant run功能(File->Settings->instant run->去掉打钩)
|
|
dependencies配置,Android studio修改成上面那样
将xopsed打钩,重启xposed框架,在运行xposedLab1,会出现->“我已经被劫”
这是已经hook成功啦。
类加载器(class loader)是 Java™中的一个很重要的概念。类加载器负责加载 Java 类的字节代码到 Java 虚拟机中。
Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成java.lang.Class类的一个实例。每个这样的实例用来表示一个 Java 类。通过此实例的 newInstance()方法就可以创建出该类的一个对象。
Dalvik虚拟机类加载机制
Dalvik的虚拟机不能用ClassCload直接加载.dex,Android从ClassLoader派生出了两个类:DexClassLoader和PathClassLoader;而这两个类就是我们加载dex文件的关键,这两者的区别是:
这里(参考)[https://segmentfault.com/a/1190000004062880]讲得很详细
|
|
这两者只是简单的对BaseDexClassLoader做了一下封装
平时进行动态加载开发的时候,使用DexClassLoader就够了。但我们也可以创建自己的类去继承ClassLoader,需要注意的是loadClass方法并不是final类型的,所以我们可以重载loadClass方法并改写类的加载逻辑。
动态加载进来的class如何使用,一般有2种办法
一种是使用反射调用,这种我不多做介绍。
一种是使用接口编程的方式来调用对应的方法,毕竟.dex文件也是我们自己维护的,所以可以把方法抽象成公共接口,把这些接口也复制到主项目里面去,就可以通过这些接口调用动态加载得到的实例的方法了。
源码包下面新建一个包名称是dynamic,然后在dynamic下新建一个interface接口Dynamic,里面有个接口方法,就叫DynamicXor(String st)吧,返回一个String,到时候我们可以通过Toast弹出来,Dynamic.java:
接着在dynamic目录下新建一个imp包,创建Impdynamic.java,并实现Dynamic接口:
点击Build -> make project,这时候会在build\intermediates\classes\debug目录下生成对应的classes文件。
好了我们要把DynamicImpl这个class转换成Dalvik可识别的dex文件,分两步:
打开app目录下的build.gradle文件最后,加上以下代码
打开AS的 terminal窗口:
执行成功,出现Build Successfully,会在生成app/build/libs/dynamic.jar
使用sdk提供dx.jar将导出的dynamic.jar转换成Dalvik可识别的dex格式,sdk已经将dx.jar放到build-tools\27.0.3\lib目录下,将dynamic.jar复制到dx.jar目录下,AS终端运行
在dx.jar目录下会生成一个dynamic_dex.jar文件(Davilk虚拟机可执行的dex文件,因为这条命令同时会打包dex文件,因此后缀是jar),接下来就是要在demo中使用这个dex文件。
新建一个项目,在app/src/main/新建一个assets目录,把刚刚生成的dynamic_dex.jar文件放到assets目录下
新建一个FileUtils.java,FileUtils类是从assets目录下copy文件到app/data/cache目录
FileUtils
main函数
activity_main.xml配置如下:
strings配置如下:
check1.java
AndroidManifest中申请权限,
准备
将ida目录下的(如果以前调试过就不用了)
开始
|
|
|
|
查看so文件是否加载进内存map中
如下,成功
不成功,可能需要查看加载位置,可能需要点击一下按钮触发。
(1)、ida->Go->Debugger->Attach->Remote ArmLinux/Android denugger
(2)、Hostname:127.0.0.1
(3)、接着出现一个窗口,右键选择Quick filter->包名[几个字母就行],(这里会出现pid和运行包名)->ok
(4)、ctrl+s->右键选择Quick filter->lib的名字->选择可执行的[X->x]
静态偏移: 0x988
动态基址: 0x7b8d3000
动态偏移: 0x988 + 0x7b8d3000 = 0x7b8d3988
g->0x7b8d3988
f2下断点
答:就是像上面那样做一遍,找到偏移后,运行程序,ida回闪退,那就有反调试了。可能结果如下:
接着开始调试具有反调试的so文件。
|
|
|
|
这时候手机会出现Waiting For Debugger,等待调试界面。
如:
1、载入apk的要调试so文件[反编译或者改后缀为zip]、下断点的JNI_Onload处开始下断点。
2、Debugger -> Switch-debuge -> Remote ArmLinux/Android denugger
3、Debugger -> Process options -> Hostname:127.0.0.1
4、Debugger -> Attach to process -> 右键选择Quick filter->包名[几个字母就行]。
方法一、
如:
方法二、
直接打开ddms,选择这个app程序,如果ddms不能显示程序,可以安装XLnstaller.apk
f9运行就好了
]]>利用JNI_Onload()函数
|
|
|
|
Application.mk
Android
在app目录下的build.gradle中添加
这时一个简单的输入结构:
MainActivity结构
运行就可以得到调用native方法的apk了。
1.源码改动少,只需要添加JNI_Onload函数
2.无需加解密so,就可以实现混淆so中的JNI函数
3.后续可以添加so加解密,使破解难度更大
参考
上一篇只是单纯的编写so文件,只要ida打开就可以直接看见方法名。这里对so进行简单的混淆,利用JNI_Onload()函数
|
|
在jni目录创建一个check.c
|
|
|
|
在app目录下的build.gradle中添加
这时一个简单的输入结构:
MainActivity结构
运行就可以得到调用native方法的apk了。
1.源码改动少,只需要添加JNI_Onload函数
2.无需加解密so,就可以实现混淆so中的JNI函数
3.后续可以添加so加解密,使破解难度更大
参考
我这里直接安装adnroid studio,环境就搭好了。
在MainActivity同一级创建一个check.java
工具栏Build->Make Project(ctrl+f9)。在main的下一层(java层)创建jni目录
android在终端
Terminal>>cd app/src/main/java
Terminal>>javah -jni com.example.ese.jni.check
(这时会产生一个com.example.ese.jni.check.h文件)
Terminal>>cd ..
Terminal>>cd jni
(将上面的com.example.ese.jni.check.h拖入jni目录中)
在jni目录下新建一个check.c文件
在jni目录下新建一个Android.mk文件和Application.mk文件
Android.mk
Application.mk
现在terminal在jni目录下
terminal>>ndk-build
这时候会生成libcheck.so文件在libs(jni同级目录)
在方法Check添加调用库check
在app目录下的build.gradle中添加
这时一个简单的输入结构:
MainActivity结构
运行就可以得到调用native方法的apk了。
apk生成路径:C:\Users\Administrator\AndroidStudioProjects\jni\app\build\outputs\apk\debug
File->New Project[Application name: Crackme1,Project Location : ..;Company domain: ese.example.com]->Phone and Tablet[API 19…]->Empty Activity->Finish[默认就好]
activit_main_xml编辑如下:
如图
填加需要的字符res->values->strings.xml
接着编写MainActivity类的代码,添加一个checkSN()方法如下:
onCreate方法
运行结果:
调试信息: atl+6可以查看log.d()的调试结果.
修改保存路径: Create new project -> Select Project Location
生成apk的位置: 存储路径下app/build/outputs/apk
Terminal窗口: 可以查看当前文件的位置
生成版本: Build->Edit Build Types->release
生成: Build->Build Apk
破解Android程序通常的方法是将apk文件利用ApkTool反编译或者Apkdb反编译,生成Smali格式的返汇编代码,然后阅读Smali文件的代码来理解程序运行的机制,找到程序的突破口进行修改。最后使用ApkTool重新编译生成apk文件并签名,最后进行测试是否破解成功。
在实际的分析过程中,还可以使用ida直接分析apk,或者dex2jar与jd-gui配合来进行java源码级的分析等。
这里就是返回结果为true则成功。接着我们使用apkdb[这是一键编译,方便点]反编译apk,并找到MainActvity.smali代码
调用checkSN方法,参数是传递两个string[p1,p2],返回值是Z[boolean]布尔型
这里将返回结果修改为1,
在进行打包和签名。安装,成功绕过。这里有反编译打包回来,签名的方法。
使用ddms动态调试,在程序算出sn的时候,我们将sn打印出来。
修改
这是添加log.d(“TEST”,v6),v6就是算出来序列号的值。然后在反编译回去,签名。运行结果:
输入,显示成功
直接解压。配置环境变量
然后在cmd下输入adb(下面就是成功):
这里以夜神模拟器设置
连接
连接成功
dex2jar可以直接获取apk的java源码,jd-gui可以直接获取jar的java源码
apk反编译
之后对smali进行修改,然后对修改好的进行打包。
这里一般apk都不能安装(如果这里能按装apk,就不用签名啦),接着进行签名。生成自己的签名文件,
各个参数解释如下:
-genkey 产生证书文件
-alias 产生别名
-keystore 指定密钥库的.keystore文件中
-keyalg 指定密钥的算法,这里指定为RSA(非对称密钥算法)
-validity 为证书有效天数,这里我们写的是20000天
这里生成android.keystore签名,进行apk签名
签名
在安装apk。安装成功
DDMS的作用它提供截屏,查看线程和堆的信息,logcat,进程,广播状态信息,模拟来电呼叫和短信,虚拟地理坐标等等。
会出现一个命令行界面和图形化界面
这是打开模拟器,然后命令行:
这就显示连接上模拟器,在smali中添加打印函数
是函数log.d(“TEST”,v6)。模拟器中运行apk,结果如图[这是一个test]:
下载jeb
]]>在虚拟机Dalvik中,方法中有两种方式定义方法中可以使用的寄存器数量。.registers 指令定义了方法中可以使用的寄存器的总数量。可以选择性的使用.locals ,该指令定义了方法中非参数寄存器的数量。寄存器的总数量应当包括方法参数所使用的寄存器的数量
move(move destination)。
数据定义指令用来定义程序中用到的常量、字符串、类等。
基础字节码为const
跳转指令用于从当前地址跳转到指定的偏移处。Dalvik指令集中有三种跳转指令:无条件跳转(goto),分支跳转(switch)与条件跳转(if)。
|
|
|
|
|
|
|
|
先看一个smali代码熟悉下
数组:
例1:
例2:
字段:
|
|
样本名: 1.exe
MD5: 612974dcb49adef982d9ad8d9cbdde36
SHA1: b817e361bd0cc1819d7f6a1189f0f5d56ed48721
下载(解压密码 x-encounter)
分析环境及工具:
win7 ida od
|
|
|
|
|
|
|
|
PEID查壳
ida加载找到主函数
首先呼叫SetProcessShutdownParameters,WinHttpOpen,WinHttpConnect,WinHttpQueryOption连接网络的函数
但是传入的参数都是0,可能是大量无关 API,可能是混淆代码,并没有什么用。接着
这里并且在 0040127D 处会进入一个循环,判断 var_8 的值等不等于 8B9A6F96h,里面就是呼叫一个GetWindowsDirectoryW。但是感觉没有什么用,而且循环n次。是无关的循环。
紧接着调用 GlobalMemoryStatus,获取内存信息,在 004013EF 处调用 LocalAlloc 分配了 8700h 的空间
接着偏移数据(加密的 shellcode )复制到刚刚分配的空间,之后进入右边的条件分支,对 shellcode 进行解密
解密函数如下
对加密的 shellcode 分组,通过一个循环,每 8 字节一组,对这 8 字节调用 sub_4015C0 进行解密,该函数的第三个参数为解密 key。
接着用od跟,shellcode还没有解密
解密后,
在解密完成后,dump下来shellcode保存为shellcode(等下会分析这个shellcode),再接着看看。
显示获取VirtualProtect的函数地址,使解密后的shellcode可读可写可执行。程序接着调用shellcode。
将shellcode的代码dump出来。IDA 载入 shellcode.txt,发现 shellcode 只有两个 Call。
进入第一个 call
该函数通过 fs 寄存器获取 Kernel32.dll 的基址
接着通过解析 PE 文件,获取 PE 头,接着获取数据目录第 0 项也就是导出表的地址,接着在导出表中获取 GetProcAddress 和 LoadLibrary 的地址,然后返回
(用OD)进入 shellcode
shellcode的第二个 call,如图,观察此处的 eax 所指向的地址的值
很明显这是一个经过加密处理的 PE 文件,那么在 MZ 字符之前的值是什么呢?
经过后续的分析,我们可以了解到,这一段内存实际上是以 “新 PE 的参数+加密的新 PE” 这样的结构存储的,结构的详细分析如下图
然后通过之前获取的 GetProcAddress 和 LoadLibrary 的地址,来动态获取其他函数的的地址
函数地址获取完后,调用 GetVersionExA,
判断 dwMajorVersion 等不等于 6
接着分配内存,并调用解密函数
该函数的两个参数是通过 “新 PE 的参数” 这个结构获得的,解密函数大概是通过分组的形式,逐步对 PE 进行解密的,解密的关键一个是索引值,一个是 key 都在 `”新 PE 的参数” 这个结构中,解密后的 PE 文件如下图
紧接着调用 VirtualProtect,目标地址是 400000,也就是老 PE 文件的加载基址,将其属性改为可读可写可执行,为后面的替换做准备。然后将 400000 处的值清零,开始复杂的 PE 操作。
首先通过 DOS 头的 e_lfanew 获取 PE 头的偏移,找到 PE 头之后将 PE 头偏移四个字节的数据(也就是 Machine)存到局部变量中,然后通过 PE 头偏移 14h 处也就是 SizeOfOptionalHeader 获得 OptionalHeader 的长度,之所以计算各个段的长度是为了获取最后一个节区的偏移,通过偏移把解密后的新 PE 的 DOS 头,PE 头和节表头复制到 400000 的内存空间中,之后从 “新 PE 的参数” 中获取新 PE 的 EP,修正 EP,最后解析节表头获取节表的 pointerToRawData,准备进行节表数据的复制。
右边分支为复制节区数据,由于新PE文件只有一个节区所以循环只有一次,这里不做过多的解释,重点在 IAT 的修复上,当右边分支执行完后,会执行左边的分支,首先会释放掉解密后的新 PE 文件,通过 PE 头的偏移获取 DataDirectory 数据目录表,在表中接着获取 DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_IMPORT] 的 RVA 也就是导入表的 RVA 和 Size,进行修复,而且还对 DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE] 资源目录项的 RVA 和 SIZE 也进行了修复,由于 DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_IMPORT] 的 RVA 指向 IMAGE_IMPORT_DESCRIPTOR 结构体,接着比较该结构体的 Name字段是不是等于零,如果不为零,如下图
调用 LoadLibrary 载入导入的 DLL,之后判断 IMAGE_IMPORT_DESCRIPTOR 结构体中第一个字段,也就是 OriginalFirstThunk 是否为零,如果不为零,接着获取 IMAGE_IMPORT_DESCRIPTOR 结构的第 5 个字段,也就是 FirstThunk,通过 FirstThunk找到 IMAGE_THUNK_DATA32 结构体的 AddressofData 字段,通过 AddressofData 找到获得 _IMAGE_IMPORT_BY_NAME 结构体中的 Name字段,也就是该 DLL 导出函数的名称,之后调用 GetProcAddress 对其进行修复。修复 IAT 的操作一共有两层循环,外层循环遍历所有的导入的 DLL,内层循环对 DLL 导出的函数进行修复,
IDA中如下
在 OD 中如下
修复完 IAT 之后,调用 atexit 注册终止函数,最终跳转到新 PE 的 OEP
折腾了半天终于见到了病毒的真身
我们通过 LoadPE 将此时病毒的内存,转存下来命名为 dump.exe,载入到 IDA 中,由于 dump.exe 可能用了一些对抗反汇编的技术,导致 IDA 无法判别哪些是数据哪些是代码,我们可以手动将其转换,但即使转换成功后也无法正常反编译,加大了我们的分析难度,
进入新的oep后
只能通过 OD(1.exe) 一点一点的单步进行分析。
我们进入函数入口处,步入第一个 call[],先分配了 5 个堆空间,接着获取病毒文件的完整地址和系统的临时目录
之后进行对相关字符串进行解密的操作
大概算法是解密了一个 256 位的 key (怀疑是黑客的 RSA 公钥),用对 key 进行操作然后与加密的字符串进行异或,两次解密后的字符串如下
接着调用 00409FE7 处的 call sub_409CB8 解密函数,将解密后的数据(一些 windows 上常用的软件名称)以.为分割符分为 43 个字符串的首地址存放在分配的内存地址中。
ida
解密函数call sub_409CB8
以同样的方式解密 401430 处的数据(系统环境之类的参数),之后通过系统环境变量获取 appdata 的目录,进行字符串拼接,
之后调用 CopyFile 将该病毒复制到 appdata 目录,这两次解密的目的是对某些特殊字符的文件夹进行绕过
在 C:\Documents and Settings\Administrator\Application Data 下可以找到该病毒的副本,接着在 0040A088 调用与注册表有关的函数,为了常驻系统。
继续获取系统目录 C:\Documents and Settings\All Users,并通过一个加密函数生成一个固定的 Hash 值,接着进行字符串拼接,最终调用 0040A11A 处的函数创建文件
进入 0040A11A 处的函数,创建文件,写入文件,文件内容是通过复杂的加密算法生成的用户 ID,
这里我们只要了解一下病毒的行为,具体加密流程在加密部分会详细介绍,创建的文件如下图
之后对勒索文档的内容进行解密,勒索文件的组合分为三部分
第一步先将解密后的数据前64E部分复制到分配好的缓冲区中
第二步将之前提到过的用户 ID的部分数据复制到第一部分的后面
第三步将剩下的勒索信息复制到第二部分后面,如图
在 0040A1FA 处调用 call sub_402354,该函数功能如下,
该函数用来关闭一些进程,如 word,excel,防止后面加密函数运行时,由于文件占用导致加密失败。
在最后连续调用了 3 个 call
第一个 call 寻找盘符,加密文件,这里不做过多分析,放到加密部分细讲
第二个 call 创建 bat,创建进程并运行,用于善后工作,从内存中 dump 出批处理文件,内容如下
vssadmin.exe 删除卷影副本,wevtutil.execl 命令删除日志信息
第三个 call 用于自删除
目前主流的勒索软件使用的加密算法为 RSA 和 AES。AES 属于对称密码体制,RSA 属于非对称密码体制,一般来讲 RSA 有两对,黑客有一对,用户生成一对,黑客的 RSA 私钥自己保留,将 RSA 公钥以硬编码(或加密)的方式存储在病毒中,用来加密用户的 RSA 私钥,用户的 RSA 公钥则用来加密 AES 密钥,AES 密钥用来加密文件。视病毒的情况而定,有些病毒每一个加密文件对应一个 AES 密钥,有些病毒所有加密文件只对应一个 AES 密钥。
之前已经提到过,该病毒主要有两次加密,第一次是写入 Hash 文件和勒索文档的用户 ID,第二次就是对文件进行加密
在之前的分析中我刻意回避了一个函数 00409F43 处的 call sub_408F24,该函数的功能如下
a1 也就是第一个参数是 256 位的字符串(应该是未被加密),个人推测应该是黑客的 RSA 公钥,如下图
该函数内出现了 SHA256_K,ROL4 和 ROR4 的字样,推测该函数的目的是对黑客的 RSA 公钥进行 SHA256 加密,加密后的内容
是不是很熟悉……在对加密的字符串进行解密的时候作为解密的 key入栈,在创建 Hash 文件的时候作为文件名。转到生成用户 ID的函数空间,进入 sub_40A534,首先分配了 1024 大小的内存空间,在 sub_40A3DD 函数中的 sub_404336 函数生成了用户 RSA 的密钥,之后将该密钥写入 Hash 文件中。
接来下调用两次 sub_4024E8 生成 128 位的明文(该明文应该是与用户 RSA 私钥有关系的,不然用黑客 RSA 公钥加密就没有意义了,不过我没有分析出来这个拼接的 128 位明文是什么),在 sub_40A3F4 处用黑客的 RSA 公钥对明文进行加密,返回给 V26 变量,最后调用 sub_40267E 将 V26 中的十六进制数据转换为 ASCII 数据,如图
回到原来的函数中,接着调用 SetFilePoint 定位到 Hash 文件的末尾,使用 WriteFile 将上图的值写入 Hash 文件末尾(之后合成勒索文档时,中间插的那一段数据也是上图所示的数据)
至此用户 ID分析完毕
来到 0040A20A 处的 call sub_409B9C 进入文件加密函数,注意调用该函数时的参数,第一个参数为之前生成的用户 RSA 的公钥
首先会调用 GetLogicalDrivers 函数判断宿主主机有几个盘,将返回值转换为二进制 10000000000000000000000100 从右向左数,一共有两个盘,C 盘和Z 盘,调用 GetDriveTypeA 获取相应磁盘的类型,接着进行判断如果属于硬盘驱动器、远程(网络驱动器)和移动驱动器中的一种,则对每一个符合要求的磁盘调用 CreateThread 创建一个加密线程,回调函数为 sub_40994F,回调参数为指向相应的盘符的指针,在回调函数处下断点,顺便在c盘根目录下创建 a.txt,内容为 123,以便分析加密方式
接着对各种字符串的操作,调用 FindFirstFileW,先寻找 C 盘根目录的文件,找到之后会进行一系列的判断
比较后缀名是不是 ..doc 判断是否被加密
比较文件名是不是 Read__ME.html(生成的勒索文档),比较文件名是不是 Hash 文件
比较文件名是不是比较是不是复制到 C:\Documents and Settings\Administrator\Application Data 目录的病毒副本
如果全部判断失败则进入 00409B01 处的 call sub_409081 文件加密函数,这里要留心该文件加密函数的三个参数
第一个参数为要加密的文件名称,也就是 a.txt
第二个参数是用户 RSA 的公钥
第三个参数是用户 ID的第二部分
函数会调用 UuidCreate 创建一个全世界独一无二的 uuid 值,uuid 的值如下
后调用 CreateFile 打开要加密的文件,获取文件大小,计算 uuid 的长度,调用 sub_407009 分配了两块内存空间,之后将要加密的文件大小与文件名称进行运算
之后将运算后的结果与 uuid 进行加密运算(应该是 SHA256 加密),而且加密了 2000h 次
经过运算之后的值为下图,从 D1BB98 到 D1BBB8,我们可以设这个值为 A
紧接着调用 004092EF 处的 call sub_402780 对 D1BB70 的值进行扩充,该函数有一个参数为 A,通过 A 的值生成 AES 加密的密钥,后面的分析也验证了这一点,扩充的数据中包含一个 256 位的 AES 密钥,从 D1BC10 到 D1BD20,设这个值为 B。
接着调用 ReadFile 将文件内容读取到缓冲区中,并在 00409489 处 call sub_408F7F 对文件内容进行加密,其中该函数的参数包含 B
这里可以推测一下 00409489 处的函数应该为 AES 加密,而该函数的第四个参数也就是 B (位于扩展数据中)作为 AES 的 key 进行加密。加密后的文件数据如下(该勒索病毒对每一个文件创建一个 AES 的密钥)
接着将加密后的文件数据复制到 D1BBB8 处,然后调用三次 WriteFile,分别将数据复制到文件中,使用用户 RSA 公钥将 uuid 进行加密,并写入文件,最后将用户 ID的第二部分写入文件中,加密后的 a.txt
通过上面的分析,我们可以了解到该病毒对每个文件的 uuid 进行运算,通过运算结果生成 AES 密钥,并使用用户 RSA 公钥对 uuid 进行加密放在了文件中,从用户的角度考虑要解密文件数据首先要获得 uuid 的值,由于 uuid 被用户 RSA 公钥进行加密了,所以我们要获得用户 RSA 的私钥,而用户 RSA 的私钥又被黑客 RSA 的公钥进行加密后写入用户 ID 中,所以我们要获得黑客 RSA 私钥,但是黑客的 RSA 私钥只有黑客一个人拥有,所以用户只能向黑客支付赎金并提供被加密的文件和在机器上生成的 Hash 文件才能解密
1、加密技术掌握的不熟练,外加上该病毒是自己实现 RSA 和 AES 的加密的,没有调用微软提供的 CSP 容器,所以对分析造成很大的困难。
2、分析病毒要有耐心,切记分析加密部分的时候要一口气分析完,由于表哥只给了一个样本所以我们并不知道该病毒是如何传播的。
3、写文不易,且看且珍惜。
参考:
https://mp.weixin.qq.com/s/TznmkeU1zZU_wJSw0Jx6GQ
使用win32 api写出的程序有以下优势。
1、生成的可执行体积小
2、执行效率高
3、更适合用于编写直接对系统进行底层操作的程序,其所生成的代码质量也更加高效简洁。
所有在win32平台上的Winsock编程都要经历的步骤:
定义变量->获取Winsocket版本->加载winsocket库->初始化->创建套接字->设置套接字选项->关闭套接字->卸载winsocket库->释放所有资源
传统的网络通信模式是C/S模式,即客户端与服务器端。服务端先启动监听,客户端连接。
接下来将详细阐述Windows Sockets编程中服务端的Socket编程。服务端编程主要涉及初始化阶段调用WSAStartup函数、建立Socket、绑定和监听端口,设置接受函数,异步处理函数WSAAsyncSelect、Socket连接断开。
在使用Scoket之前必须调用WSAStartup函数。此函数是应用程序中用来初始化Windows Socket dll。只有此函数调用成功后,应用程序才可以调用dll中的其他函数,否则后面的函数都调用失败。int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData)
1、第一个参数wVersionRequested:是Windows Socket的版本号
2、第二个参数lpWSAData: 是指向WSADATA的指针,用来存储系统传回的关于Winsocket的结构。
3、返回值:执行成功返回0
调用方式:WSAStartup(MAKEWORD(2,2),(LPWSADATA)&WSAData)
初始化Winsocket的动态链接库后,需要在服务器端建立一个用来监听Socket句柄。SOCKET socket(int af,int type,int protocol)
1、第一个参数af:指应用程序使用的通讯协议的协议族,对于TCP/IP—>FF_INET
2、第二个参数type: 指定要创建的套接字类型。
3、第二个参数protocol: 指定要使用的通讯协议。
3、返回值:执行成功返回Socket对象,否则返回ICVALID_SOCKET.
创建了一个Socket以后,一个服务器程序必须调用bind函数来为其绑定一个IP地址和特定的端口号。这样客户端才指定连接谁int bind(SOCKET s,const struct sockaddr FAR *name,int namelen)
1、第一个参数s:指定待绑定的Socket描述符
2、第二个参数name: 指定一个Sockaddr。
3、第二个参数namelen: 指定name结构体的大小。
这里介绍一下第二个参数name:struct sockaddr(u_short sa_family; char sa_data[14]);
对于TCP/IP协议族的套接字进行绑定时,通常使用另一个结构体
调用的例子
可以通过getsockname()函数来获知其被设定的值。
服务器必须建立一个监听的队列来接受客户端发起的请求。int listen(SOCKET s,int backlog)
第二个参数backlog:为一次连接的最大数目。
accept函数SOCKET accept(SOCKET s,struct sockaddr FAR *addr,int FAR *addrlen)
WSAAsyncSelect函数int WSAAsyncSelect()b
|
|
大多数 Windows 程式在 WinMain 中进入讯息回圈之前的初始化期间都要呼 叫函式 UpdateWindow。通过呼叫 GetUpdateRect,可以在任何时候取得这些座标。 装置内容(简称为「DC」)实际上是 GDI 内部保存的资料结构。装置内容 与特定的显示设备(如视讯显示器或印表机)相关。程式必须在处理单个讯息处理期间取得和 释放代号。除了呼叫 CreateDC(函式,在本章暂不讲述)建立的装置内容之外, 程式不能在两个讯息之间保存其他装置内容代号。
在处理 WM_PAINT 讯息时,使用这种方法。它涉及 BeginPaint 和 EndPaint两个函式,这两个函式需要视窗代号(作为参数传给视窗讯息处理程式)和 PAINTSTRUCT结构的变数(在WINUSER.H表头档案中定义)的地址为参数
绘图资讯结构,Windows 为每个视窗保存一个「绘图资讯结构」,这就是 PAINTSTRUCT,定义如下:
在程式呼叫 BeginPaint 时,Windows 会适当填入该结构的各个栏位值。使 用者程式只使用前三个栏位,其他栏位由 Windows 内部使用.Windows 程式使用白色画刷
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
要得到视窗显示区域的装置内容代号,可以呼叫 GetDC 来取得代号,在使 用完後呼叫 ReleaseDC
与 BeginPaint 和EndPaint一样,GetDC 和ReleaseDC 函式必须成对地使用。 如果在处理某讯息时呼叫 GetDC,则必须在退出视窗讯息处理程式之前呼叫 ReleaseDC。不要在一个讯息中呼叫 GetDC 却在另一个讯息呼叫 ReleaseDC。
TextOut 是用於显示文字的最常用的 GDI 函式。语法是:TextOut (hdc, x, y, psText, iLength) ;
系统字体
呼叫 GetTextMetrics 取得字体大小。GetTextMetrics 传回装置内容中 目前选取的字体资讯,因此它需要装置内容代号。Windows 将文字大小的不同值 复制到在 WINGDI.H 中定义的 TEXTMETRIC 型态的结构中。TEXTMETRIC 结构有 20 个栏位,我们只使用前七个:
要使用 GetTextMetrics 函式,需要先定义一个结构变数(通常称为 tm):
TEXTMETRIC tm ;
在需要确定文字大小时,先取得装置内容代号,再呼叫 GetTextMetrics:
最重要的值是 tmHeight,它是 tmAscent 和 tmDescent 的和。这两个值表示 了基准线上下字元的最大纵向高度
假设要编写一个 Windows 程式,在显示区域显示几行文字,这需要先取得 字元宽度和高度。您可以在视窗讯息处理程式内定义两个变数来保存平均字元 宽度(cxChar)和总的字元高度(cyChar)static int cxChar, cyChar ;
下面是取得系统字体的字元宽度和高度的 WM_CREATE 程式码:
您会发现常常需要显示格式化的数字跟简单的字串。您不能使惯用的工具(可爱的 printf 函式)来完成这项工作,但是可以使用 sprintf 和 Windows 版的 sprintf——wsprintf。这些函式与 printf 相似,只是把格式化字串放到字串中。然後,可以用 TextOut 将字串输出到显示器上。 非常方便的是,从 sprintf 和 wsprintf 传回的值就是字串的长度。您可以将这 个值传递给 TextOut 作为 iLength 参数。下面的程式码显示了 wsprintf 与 TextOut 的典型组合:
也可以这样TextOut (hdc, x, y, szBuffer, wsprintf (szBuffer, TEXT ("The sum of %i and %i is %i"), iA, iB, iA + iB)) ;
|
|
卷动列是图形使用者介面中最好的功能之一,它很容易使用,而且提供了 很好的视觉回馈效果。您可以使用卷动列显示任何东西–无论是文字、图形、 表格、资料库记录、图像或是网页,只要它所需的空间超出了视窗的显示区域 所能提供的空间,就可以使用卷动列。 卷动列既有垂直方向的(供上下移动),也有水平方向的(供左右移动)。
在应用程式中包含水平或者垂直的卷动列,程式写作者只需要在 CreateWindow 的第三个参数中包括视窗样式(WS)识别字 WS_VSCROLL(垂直卷 动)和/或 WS_HSCROLL(水平卷动)即可。
每个卷动列均有一个相关的「范围」(这是一对整数,分别代表最小值和最大值)和「位置」(它是卷动方块在此范围内的位置)。当卷动方块在卷动 列的顶部(或左部)时,卷动方块的位置是范围的最小值;在卷动列的底部(或 右部)时,卷动方块的位置是范围的最大值。 在内定情况下,卷动列的范围是从 0(顶部或左部)至 100(底部或右部), 但将范围改变为更方便於程式的数值也是很容易的:SetScrollRange (hwnd, iBar, iMin, iMax, bRedraw) ;
参数 iBar 为 SB_VERT 或者 SB_HORZ,iMin 和 iMax 分别是范围的最小值和 最大值。如果想要 Windows 根据新范围重画卷动列,则设置 bRedraw 为 TRUE(如 果在呼叫 SetScrollRange 後,呼叫了影响卷动列位置的其他函式,则应该将 bRedraw 设定为 FALSE 以避免过多的重画)。
您可以使用 SetScrollPos 在卷动列范围内设置新的卷动方块位置:SetScrollPos (hwnd, iBar, iPos, bRedraw) ;
以下是程式写作者应该完成的工作
|
|
来源:《Windows程序设计》(第五版)
]]>以前编写的都是dos程序,Windows程序却更贴近windows.
新建项目->win32->windows应用程序(空项目)
参数
1、#include <windows.h>
每个用C编写Windows程序的开头都应该包含,主要含入档案。
2、int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
程序的入口点,向main一样,只是Windows叫做WinMain。第一个参数代表执行实体代号,函数定义第三参数个是LPSTR(是16位windows下)。
显示小视窗,实际上是一个对话框。
参数1:通常是视窗代号
参数2:讯息方块主体中显示的字符串
参数3:标题
参数4:按钮数量(如下图)
Win32程序中标准字元定义char c = 'A';
占一个字节,需要一个位的元组存储char *p
这里p占4个字节char a[] = "Hello!"
占七个字节(最后加一个0结束),需要七个位的元组存储
Windwos程序标准元定义typedef unsigned short wchar_t;
这里wchar_t与unsigned short int相同,都是16位元宽,也就是2字节wchar_t c = 'A';
这时候c=0x0041,虽然’A’是一个字节,但是前面是2字节,编译器会自动扩充,就是Unicode字母A,占2个字节,存储顺序0x41,0x00wchar_t *p = L'Hello!';
这里有个L(代表[long])。字符串需要14个位元(14个字节),指数p需要4个字节wchar_t a[] = L'Hello';
这里字符串需要14个位元
char pc=”Hello!”;
len=strlen(pc);
这时len==6
wchar_t pw=”Hello!”;
len=strlen(pw);
这时len==1 ????
因为strlen()应该接受char类型的指标,但是却接受unsigned short类型,我们来看看为什么是1
0x0048 0x0065 0x006c 0x006c 0x006d 0x0021
48 00 64 00 6c 00 6c 00 6f 00 21 00
当strlen()遇见0,则表示字符结束。那么怎么办。这时候就出现了wcslen(宽字节长度)。
strlen()函数定义size_t __cdecl strlen(const char *);
wcslen()函数定义size_t __cdecl wcslen(const wchar_t *);
如果这时len=wcslen(pw)
这时len==6
扩展
strlen wcslen
printf wprintf
char TCHAR
如果定义了_UNICODE识别字,那么一个称作_T的巨集就定义如下
#define __T(x) L##x
_TEXT(“Hello!”)==L”Hello!”
如果没有定义_UNICODE
_TEXT(“Hello!”)==8字节
MessageBox的两个变形
MessageBoxA
MessageBoxW
上述而MessageBox也可以
Windows定义的一组字符串函数式
printfint printf (const char * szFormat, ...) ;
sprintf()int sprintf (char * szBuffer, const char * szFormat, ...) ;
创建一个自己的视窗
呼叫CreateWindow,先要注册一个视窗类别。
上面函数至少呼叫了 18 个 Windows 函式。下面以它们在 HELLOWIN 中出现 的次序列出这些函式以及各自的简明描述:
来源:《Windows程序设计》(第五版)
]]>windows下的动态链接库,
创建一个dll,添加一个
dll.h
dll.cpp
用dependency walker查看dll
隐式链接就是在程序开始执行时就将DLL文件加载到应用程序当中。实现隐式链接很容易,只要将导入函数关键字_declspec(dllimport)函数名等写到应用程序相应的头文件中就可以了。
由于dll文件不能单独调试,所以需要创建一个调试dll工程的project,创建一个test项目
MFC调用,dll文件拷贝到工程的根目录下
目标程序
dll程序
注入程序
结果
网络行为的基本属性包含IP地址、TCP、与UDP端口、域名,以及流量内容等。因此,网络和安全设备可以利用它们,来提供网络应对措施,根据IP地址和端口,防火墙和路由器可以限制对网络的访问。配置DNS服务器,可以将一个已知的恶意域名重定向到内部主机,这被称为sinkhole技术。配置代理服务器,可以检测或者阻止访问某些域名。
入侵检测系统(IDS)、入侵防御系统(IPS),以及电子邮寄和web代理等其他安全应用。
恶意代码分析的第一步不应该是在实验环境中运行恶意代码,也不应该是解刨恶意代码分析它的反汇编代码。与之相反的是,你应该首先查看已经获得的关于恶意代码的所有数据。恶意代码分析师经常是得到一个没有任何上下文的恶意代码样本(或是可疑的可执行文件),但是在大多数情况下,你可以获取额外的更多数据。开始恶意代码网络行为分析的最好方法是挖掘恶意代码已经生成的日志、报警以及网络包。
来自真实网络的信息比来自实验环境的信息,有着明显的优势。
1、真实环境捕获的信息将提供恶意应用程序真实 行为的最佳视图,而恶意代码可以被编程,以加入探测实验环境的能力。
2、活跃恶意代码的真实场景信息 能够提供独特的视角,来加快分析过程。真实流量提供了恶意代码在两个端点(客户端和服务器端)之间的通讯信息,然而,在实验环境里,分析者通常只能获取其中一端的信息。而分析恶意代码如何接受内容,比起分析恶意代码如何产生内容,往往会更具有挑战性。因此,双向的样本流量可以帮助分析师加快恶意代码解析
3、此外,当你被动地查看信息时,不存在 泄露给攻击者分析活动的风险。
我们在实验环境中运行这个文件,需要时刻注意它的网络行为。
1、请求域名
2、ip地址
3、http的GET请求
当调查恶意代码过程中采取某些行动时,会告诉恶意代码编写者你已经识别出了恶意代码,甚至可能会向攻击者透露你的个人信息。对于攻击者而言,有许多潜在的攻击方法,可以确定出调查活动
1、发送带有到某个特定链接的针对性网络钓鱼邮件(也被称为鱼叉式网络钓鱼),观察试图从期望地理区域之外IP地址发来链接的访问。
2、设计一种利用方式,在博客评论中创建一个经过编码的链接,从而有效地创建一个私人的但可以公开访问的感染审计记录。
3、在恶意代码中嵌入一个未使用域名,观察对这个域名的解析。
4、如果攻击者意识到自己正在被调查,他们可能会改变策略,从而消失得无影无踪。
最安全的选择是完全不使用互联网去调查攻击,但是这是不切实际的。
一种间接策略是使用某些旨在提供匿名服务机制,如 Tor
另一种策略是使用专门的机器来研究,通常是虚拟机。也可以用一些方法来隐藏专用机器的具体位置,如下:
1、使用匿名的蜂链接
2、通过安全shell(SSH)或者一个虚拟机私有网络(VPN),将你的连接通过远程的基础设施来进行隧道传输。
3、使用运行在线云服务中的一个临时远程机器。
DomainTools:http://www.domaintools.com
RobTex:http://www.robtex.com
BFX DNS logger: http://www.bfk.de/bfk_dnslogger_en.html
网络基本属性,ip和地址
基于内容特征往往更有价值并且作用更加持久,因为它们使用更多基础特征来识别恶意代码。
ida为主,其它为辅
评估网络通信的第一步是找到用于执行通信的系统调用。Windows套接字(winsock)中的 部分API是最常用的底层函数。
特征生成中最有价值的元素是恶意代码中的硬编码。
1、随机数据
2、来源于标准网络库的数据
3、来源于恶意代码的硬编码数据
4、关于主机及其配置信息的数据
5、从其他来源接受数据
恶意代码运行时会生成一下临时数据,这可能会很有价值。确定编码方式。
1、专注属与两端之间的协议元素
2、专注于已知的任何协议元素作为秘钥的部分
3、确定流量中不太明显的协议元素
拿到数据包使用wireshark打开后看到Protocol 为USB协议。USB协议的数据部分在Leftover Capture Data域之中,kali下可以用tshark命令可以将 leftover capture data单独提取出来 命令如下:tshark -r usb1.pcapng -T fields -e usb.capdata > usbdata.txt
运行命令并查看usbdata.txt 发现数据包长度为八个字节,这里查到USB流量分为键盘流量和鼠标流量
键盘数据包的数据长度为8个字节,击键信息集中在第3个字节,每次key stroke都会产生一个keyboard event usb packet。
鼠标数据包的数据长度为4个字节,第一个字节代表按键,当取0x00时,代表没有按键、为0x01时,代表按左键,为0x02时,代表当前按键为右键。第二个字节可以看成是一个signed byte类型,其最高位为符号位,当这个值为正时,代表鼠标水平右移多少像素,为负时,代表水平左移多少像素。第三个字节与第二字节类似,代表垂直上下移动的偏移。
这里数据包长度是八个字节显然为键盘数据包
这里有usb的的映射表http://www.usb.org/developers/hidpage/Hut1_12v2.pdf
usb keyboard的映射表 根据这个映射表有写脚本解码得出的数据包
|
|
|
|
|
|
1.过滤IP,如源IP或者目标 x.x.x.x
ip.src eq x.x.x.x or ip.dst eq x.x.x.x
2.过滤端口
tcp.port eq 80 or udp.port eq 80
3.过滤协议
tcp/udp/arp/icmp/http/ftp/dns/ip……
4.过滤MAC
eth.dst ==A0:00:00:04:C5:84
5.包长度过滤
6.http模式过滤
http.request.method== “GET”
7.ftp
ftp-data
1.在statistics下选择protocol hierarchy,可以查看当前数据包中包含哪些协议
2.将所有选项都展开,通常我们关注HTTP协议的内容,其他明文的TCP协议或UDP协议内容也有可能需要关注。
3.在我们关注的协议上右键点击apply as filter ->selected,可以过滤出需要的协议类型。
在关注的http数据包或tcp数据包中选择流汇聚,可以将HTTP流或TCP流汇聚或还原成数据,在弹出的框中可以看到数据内容。
1、HTML中直接包含重要信息
2、上传或下载文件内容,通常包含文件名、hash值等关键信息,常用POST请求上传。
3、一句话木马,POST请求,内容包含eval,内容使用base64加密
4、TCP流:通常是命令行操作。
使用wireshark可以自动提取通过http传输的文件内容,方法如下:
file->exportobjects->http
在打开的对象列表中找到有价值的文件,如压缩文件、文本文件、音频文件、图片等,点击saveas进行保存,或者saveall保存所有对象再进入文件夹进行分析。
有时候我们打开一个文件,里面只有wireless LAN 协议,很有可能是WPA或WEP加密后的无线数据包。
使用aircrack-ng应用程序可以进行解密。
1、首先是命令行模式的教程,将目标加密包复制到aircrack-ng文件夹下
(1)用aircrack-ng检查cap包:
aircrack-ng.exeshipin.cap
(2)使用弱口令字典破解wpa加密
aircrack-ng.exe shipin.cap -w wordlist.txt
这里wordlist.txt是弱口令字典,包含了常见的路由器密码,可以网上下载到,也可以自动生成。
(3)用密码解密cap
这里用到airdecap-ng解密cap报文,使用到了刚才的essid和破解的密码
airdecap-ng.exe shipin.cap -e 0719 -p 88888888
于是在目录下生成一个shipin-dec.cap,使用wireshark打开。
参考: http://www.freebuf.com/column/153197.html
拿到流量包,先放到kali用strings,也就是几秒钟的事情strings for1.pcapng | grep "{*}"
结果:
Pwnium{408158c115a82175de37e8b3299d1f93}
wireshark的过滤规则
直接查找Ctrl+f;字节流->字符串->查找
统计->对话->tcp
显示和保存数据[原始数据]->另存为->hex
验证:保存后,可以将保存的数据16进制显示,和源数据显示是否一样。
rar,zip等
##
]]>链接:https://pan.baidu.com/s/1u7IHEIt4045aLwKhh_soLw 密码:2oz4
一看加密函数不多,感觉可以快速做出来。然后,就做了很久很久。
直接找到加密函数
用od找到初始化生成的数组,并且dump出来
|
|
后来发现是国密4加密算法,想想自己对算法了解太少了,应该多多学习,不过也真正的逆了一次国密算法。今天又尝试下载python3.6(感觉还是习惯了2.7),不过3.6肯定有很大的有点,多多适应吧。
]]>