Android 下雪动画

这本是一个愉快的季节,但是,呵呵,胡扯! 因为这篇文章的发表时间是2015年的圣诞节,所以我们需要给Style Android用制造出一些节日气氛。感谢读者们,因为有的读者可能没有在庆祝圣诞,有些读者可能还是6月份。
那么问题来了,我们应该做些什么来让这个节日像是真正的节日呢? 最简单的方法:带上圣诞帽,拍个照。

tree

React-Native 知乎日报首页

主要练习从服务器获取数据,然后展示到界面,最外层用ScrollView包裹起来顶部是一个ViewPager,api使用的知乎日报的api,在这里只使用了一个主题日报列表查看的api,获取到json数据之后,显示到ListView中.最终效果如下:

zhihu

React-Native 实现Android中的ViewPager

ViewPager在Android中的使用率也算是非常高的,React-Native也提供了类似Android的ViewPager组件,ViewPagerAndroid , 使用 ViewPagerAndroid可以轻松实现Android中的ViewPager.
ViewPager

React-Native 网络请求

大部分的app都需要进行网络连接,通过网络获取服务器的数据,然后更新app的界面展示内容,React-Native自然拥有网络的相关操作.
官方文档

React-Native 组件练习-购物app侧滑菜单

参照官方例子练习组件的使用,练习View Image Text 还有Android平台的DrawerLayoutAndroid
效果图:
React-Native

React-Native 中的 width,height的 100%

在React-Native 中没有百分比这样的宽高,但是有替代的,具体见 issue
flex:1 相当于 height:100%
alignSelf:'stretch' 相当于 width:100%

React-Native中的布局

React-Native 使用 FlexBox布局来放置元素

Flex是Flexible Box的缩写,意为”弹性布局”,用来为盒状模型提供最大的灵活性。
任何一个容器都可以指定为Flex布局。

Flexbox

1
2
3
4
5
6
7
8
9
10
11
alignItems enum('flex-start', 'flex-end', 'center', 'stretch')
alignSelf enum('auto', 'flex-start', 'flex-end', 'center', 'stretch')
flex number
flexDirection enum('row', 'column')
flexWrap enum('wrap', 'nowrap')
justifyContent enum('flex-start', 'flex-end', 'center', 'space-between', 'space-around')

Atom安装Nuclide

Nuclide 是Facebook 基于Atom开发的IDE,本地环境Ubuntu 64位, 通过Atom的setting界面安装 nuclide 总出错, 出错后打开atom巨卡,然后通过手动安装成功. 包括安装 flow同样也是手动安装成功.

运行React-Native例子UIExplorer

运行 FaceBook 给出的例子UIExplorer, 由于网络问题折腾了半天, 在公司网络死活不行,到家一小会儿搞定. 原因是我使用 npm install 的时候 直接 ctrl+c 断掉了, 需要把目录下的node_modules文件夹删除再重新安装.

Ubuntu进行 React-Native 的开发

尝试在 Ubuntu 14.04 上面进行 react-native 的开发, 安装官方文档进行配置,记录配置流程

安装 node.js

官网下载源码安装即可, 安装后查看版本

1
$ node -v

安装 watchman

文档

安装依赖

1
$ sudo apt-get install autoconf automake python-dev

重构 Plaid app - 响应式的MVP模式(一)

Nick Butcher 在 github 上开源了Plaid。这个app不仅很酷,还拥有极致的 UI / UX。如此炫酷的app的代码对开发者来说copy下来阅读一下可谓是最佳实践。于是我也copy下来,决定深入Plaid的代码。和往常一样,你会发现里面的一些代码可以用其他的方式实现。因此不只是谈论代码,我决定花一些时间来重构Plaid的部分源代码。我将写3篇文章来分享我关于如何重构Plaid的,这篇文章是第一部分。

前言: 开始我以为自己能重构整个app. 然而,事实是我太天真了,我低估了这个应用的复杂度和新特性的数量。在我花了几个小时阅读 Nick Butcher 的代码之后, 我发现有些功能仅仅通过使用应用程序很难发现。一句话: 我意识到我没有时间把所有的想法付诸行动。因此我的“重构”集中于应用程序的“主页”和“搜索屏幕”。本来想看看有没有更多模块可以重构,但是我没有太多空闲时间来做了。重构代码可以在github上找到

Rx中的线程切换

初学者在使用RxJava的过程中,经常搞不清Observable的事件序列和每次操作应该怎样切换线程,切换哪个线程
首先需要搞懂在RxJava.subscribeOn()observeOn() 之间的区别:

  • .subscribeOn() 用来指定Observable应该操作的调度器(Scheduler)
  • .observeOn() 指定 Observable在一个指定的调度器(Scheduler)上给观察者发送通知
  • 默认情况下, 事件序列操作的线程与调用.subscribe()的线程一致

没理解?

OS X中Java环境变量配置

类似Linux下Java环境的配置,流程就是 下载 -> 解压 -> 配置环境变量文件

下载 jdk

到oracle官网下最新 jdk 下载链接, 注意下载的是jdk(Java Development Kit ),不是jre(Java Runtime Environment)

配置Java环境变量

安装过jdk之后, 查看jdk的安装路径, 一般在 /Library/Java/JavaVirtualMachines目录下面
然后编辑环境变量, 打开文件 /etc/profile

1
sudo vim /etc/profile

Ubuntu 安装 OS X 10.11 虚拟机

看到论坛里有人在windows平台上面利用 VirtualBox 成功安装了 OS X 10.11,(我的是Ubuntu 14.04)于是尝试一下虚拟机装mac,既然 VirtualBox 有linux版, 于是尝试在Ubuntu下装mac os虚拟机.

ReactiveX

An API for asynchronous programming with observable streams


利器 -- 抓包工具总结

[github 地址] (https://github.com/hanks-zyh/capturedata)

抓包工具整理

抓包工具 – Charles

基于Java 跨平台: Linux , Mac OS X, Windows
官网

Charles is an HTTP proxy / HTTP monitor / Reverse Proxy that enables a developer to view all of the HTTP and SSL / HTTPS traffic between their machine and the Internet. This includes requests, responses and the HTTP headers (which contain the cookies and caching information).


以下为Linux 平台

安装

下载 charles-proxy-3.11.2.tar.gz

1
2
3
4
5
# 解压
tar zxvf charles-proxy-3.11.2.tar.gz
# 启动 charles
./charles/bin/charles

手机抓包

前提:使手机和电脑在一个局域网内,不一定非要是一个ip段,只要是同一个路由下就可以了,比如电脑连接的有线网ip为192.168.16.12,然后手机链接的wifi ip为192.168.1.103,但是这个有线网和无线网的最终都是来自于一个外部ip,这样的话也是可以的。

在安卓手机的 设置 -> wlan -> 长按连接的wifi -> 修改网络 -> 高级选项 -> 代理选手动 , 填入电脑的 ip 和 charles 监听的 端口(默认为 8888)

查看电脑端口 : ifconfig
查看/修改 charles 监听端口: charles的 Proxy -> Proxy Setting

设置好之后Charles弹出确认框,点击Allow按钮即可

图片

图片

抓取 https

图片

安装证书

charles的 Help -> SSL Proxy -> Install charles Root

设置 ssl enable

在需要抓取的 https 链接上 右键 enable ssl proxy
图片

参考 charles使用教程指南

抓包工具 – mitmproxy

基于python 跨平台: linux, windows, OSX (Mountain Lion and later)
官网

Mitmproxy是一个基于python的中间人代理的框架。做过渗透测试的肯定很熟悉工具burpsuite或Fiddler,这些工具能够拦截并修改http或https的数据包,对于分析数据包交互的应用来说是非常有用的。但是这些工具都是整套给我们做好了。比如如果想自己定制一套这样的工具,添加一些自己需要的功能的话,那么我想,mitmproxy将是一个比较好的选择,因为它提供了一个可供用户调用的库libmproxy(注意该库目前只支持linux系统)。

图片

图片

图片


以下基于Linux台(Ubuntu14.04)

下载

1
$ sudo pip install mitmproxy

如果下载速度慢可以下载tar包

启动

1
$ mitmproxy -b 192.168.1.29 -p 9999

输入 查看帮助, q返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
A accept all intercepted flows
a accept this intercepted flow
b save request/response body
C clear flow list or eventlog
d delete flow
D duplicate flow
e toggle eventlog
F toggle follow flow list
l set limit filter pattern
L load saved flows
m toggle flow mark
n create a new request
P copy flow to clipboard
r replay request
U unmark all marked flows
V revert changes to request
w save flows
W stream flows to file
X kill and delete flow, even if it's mid-intercept
tab tab between eventlog and flow list
enter view flow
| run script on this flow

手机设置好代理,界面上就可以看到请求了
图片

mitmproxy 查看http请求响应

C (大写) 清除抓包结果

j k 选择请求, 回车查看详细信息

图片

tab 切换 RequestResponse

m Display Mode 美化信息
图片
上图输入 m,在输入 s,便可以以json形式展示

e 编辑
Edit request (cookies,query,path,url,header,form,raw body,method)?
Edit response (cookies,code,message,header,raw body)?

mitmproxy拦截 (Intercept)

相当于在客户端和服务器做中间人,可以修改客户端请求,修改服务器返回

输入 i(代表Intercept filter)即可,此时界面便会让你输入想要拦截的条件:

mitmproxy的条件拦截在默认情况下是过滤抓包的URL的。也就是说当你直接输入要拦截的条件(比如输入“weibo”),那么接下来要出现抓包会将匹配的抓包整体变黄:

mitmproxy条件过滤效果
mitmproxy条件拦截效果
这些橘黄色的数据包都代表被拦截了,还未发送给服务器,这个时候你就可以对这些数据包进行修改,我们选择一个数据包enter进入:

mitmproxy 拦截 选择数据包
与之前的类似,输入“e”,进行request编辑模式,然后输入“h”代表要编辑request的头部:

mitmproxy 编辑拦截包的头部
输入enter便可对高亮的User-Agent的值进行修改,上图的weibo版本之前是5.0的,被我改成了6.0 。我们还可以对header进行添加属性,输入“a”即可,然后使用tab分别键入key和value。这里我添加了“test-test”键值对:

mitmproxy 拦截header添加键值对
至此,我对拦截的request header已经修改完毕,现在要做的就是我要认可接受这个修改,然后发给服务器。所以我们输入“a”(代表“accept”)即可,等到服务器响应后,注意,mitmproxy便又了拦截服务器发过来的response(注意那个“Response intercepted”):

mitmproxy 拦截response
现在如果你想修改这个response也可以,方式同上面修改request一样。这个时候我再输入“a”,代表我接受了这个response,然后这个response便可发给客户端了:

mitmproxy 拦截response之后accept

更多类型的mitmproxy拦截

同时mitmproxy还支持不同类型的条件过滤,之前在拦截字符串前面加上特定的参数比如我要拦截所有的POST request怎么办?输入:~m POST 即可(m代表method):

mitmproxy 拦截特定的request 方法

拦截所有的request: ~q

拦截特定的header: ~h

拦截特定的domain: ~d

拦截特定的响应代码(404之类的): ~c

mitmproxy官方文档。


抓包工具 – Fiddler

基于C# windows, Linux看这里 Mono Fiddler


下载

直接下载,安装即可

手机抓包


抓包工具 – wireshark

跨平台:Windows,OS X ,Linux

官网

解析 dex 文件结构 - DexHeader

原文: https://kiya-z.github.io/2015/11/17/parse-dex-file-part-dex-header/


解析 dex 文件结构 - DexHeader

简介

dex 文件是 dalvik 虚拟机的可执行文件。


dex 文件结构

该结构位于系统源码 dalvik\libdex\DexFile.h 中,描述的是 dex 文件被映射到内存中的结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/*
 * Structure representing a DEX file.
 *
 * Code should regard DexFile as opaque, using the API calls provided here
 * to access specific structures.
 */
struct DexFile {
    /* directly-mapped "opt" header */
    const DexOptHeader* pOptHeader;

    /* pointers to directly-mapped structs and arrays in base DEX */
    const DexHeader*    pHeader;
    const DexStringId*  pStringIds;
    const DexTypeId*    pTypeIds;
    const DexFieldId*   pFieldIds;
    const DexMethodId*  pMethodIds;
    const DexProtoId*   pProtoIds;
    const DexClassDef*  pClassDefs;
    const DexLink*      pLinkData;

    /*
     * These are mapped out of the "auxillary" section, and may not be
     * included in the file.
     */
    const DexClassLookup* pClassLookup;
    const void*         pRegisterMapPool;       // RegisterMapClassPool

    /* points to start of DEX file data */
    const u1*           baseAddr;

    /* track memory overhead for auxillary structures */
    int                 overhead;

    /* additional app-specific data structures associated with the DEX */
    //void*               auxData;
};

基本的文件结构只需关注:

1
2
3
4
5
6
7
8
9
10
11
struct DexFile{
    DexHeader    Header;
    DexStringId  StringIds[stringIdsSize];
    DexTypeId    TypeIds[typeIdsSize];
    DexFieldId   FieldIds[fieldIdsSize];
    DexMethodId  MethodIds[methodIdsSize];
    DexProtoId   ProtoIds[protoIdsSize];
    DexClassDef  ClassDefs[classDefsSize];
    DexData      Data[];
    DexLink      LinkData;
};

大体结构图

dex-file-general-structure


DexHeader 结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/*
 * 160-bit SHA-1 digest.
 */
enum { kSHA1DigestLen = 20,
       kSHA1DigestOutputLen = kSHA1DigestLen*2 +1 };

/*
 * Direct-mapped "header_item" struct.
 */
struct DexHeader {
    u1  magic[8];           /* 版本标识 */
    u4  checksum;           /* adler32 检验 */
    u1  signature[kSHA1DigestLen]; /* SHA-1 哈希值 */
    u4  fileSize;           /* 整个文件大小 */
    u4  headerSize;         /* DexHeader 大小 */
    u4  endianTag;          /* 字节序标记 */
    u4  linkSize;           /* 链接段大小 */
    u4  linkOff;            /* 链接段偏移 */
    u4  mapOff;             /* DexMapList 的文件偏移 */
    u4  stringIdsSize;      /* DexStringId 的个数 */
    u4  stringIdsOff;       /* DexStringId 的文件偏移 */
    u4  typeIdsSize;        /* DexTypeId 的个数 */
    u4  typeIdsOff;         /* DexTypeId 的文件偏移 */
    u4  protoIdsSize;       /* DexProtoId 的个数 */
    u4  protoIdsOff;        /* DexProtoId 的文件偏移 */
    u4  fieldIdsSize;       /* DexFieldId 的个数 */
    u4  fieldIdsOff;        /* DexFieldId 的文件偏移 */
    u4  methodIdsSize;      /* DexMethodId 的个数 */
    u4  methodIdsOff;       /* DexMethodId 的文件偏移 */
    u4  classDefsSize;      /* DexClassDef 的个数 */
    u4  classDefsOff;       /* DexClassDef 的文件偏移 */
    u4  dataSize;           /* 数据段的大小 */
    u4  dataOff;            /* 数据段的文件偏移 */
};

其中:

magic : dex文件标识,值必须为常量 DEX_FILE_MAGIC

1
2
ubyte[8] DEX_FILE_MAGIC = { 0x64 0x65 0x78 0x0a 0x30 0x33 0x35 0x00 }
                        = "dex\n035\0"

checkSum : 对除 magic 和 checkSum 外的剩余文件计算 adler32 校验值,目的是检测文件是否损坏;

signature:对除 magic、checkSum 和 signature 外的剩余文件计算 SHA-1 校验值,用来确定文件的唯一性

fileSize:以字节为单位,整个文件(包括头部)的大小;

headerSize:头部大小,0x70 字节,已经考虑到兼容性;

endianTag:两种字节序取值;

1
2
uint ENDIAN_CONSTANT = 0x12345678;
uint REVERSE_ENDIAN_CONSTANT = 0x78563412;

linkSize:链接段的大小,如果没有使用静态链接,值为0;

linkOff:链接段的文件偏移,指向链接数据段内,如果 linkSize 为 0,则为 0;

mapOff:map item 的文件偏移,指向数据段内,数据结构为 mapList,如果没有 map,值为 0;

stringIdsSize:字符串 id 的个数;

stringIdsOff:字符串 id 清单的文件偏移,指向 stringIds 的起始地址,如果 stringIdsSize 为 0,值为0;

typeIdsSize:类型标识符的个数;

typeIdsOff:类型标识符清单的文件偏移,指向 typeIds 的起始地址,如果 typeIdsSize 为 0,值为 0 (这就很奇怪了哟);

protoIdsSize:原型标识符的个数;

protoIdsOff:原型标识符清单的文件偏移,指向 protoIds 的起始地址,如果 protoIdsSize 为 0,值为 0 (这就很奇怪了哟);

fieldIdsSize:字段标识符的个数;

fieldIdsOff:字段标识符清单的文件偏移,指向 fieldIds 的起始地址,如果 fieldIdsSize 为 0,值为 0;

methodIdsSize:方法标识符的个数;

methodIdsOff:方法标识符清单的文件偏移,指向 methodIds 的起始地址,如果 methodIdsSize 为 0,值为 0;

classDefsSize:类的个数;

classDefsOff:类清单的文件偏移,指向 classDefs 的起始地址,如果 classDefsSize 为 0,值为 0 (这就很奇怪了哟);

dataSize:数据段的大小,以字节为单位,并且是 sizeof(uint) 的偶数倍;

dataOff:数据段的文件偏移。


手工查找

1
2
3
4
5
6
7
00000000  64 65 78 0a 30 33 35 00  3b ba fe c3 83 7e aa be  |dex.035.;....~..|
00000010  09 97 71 1e 17 96 9f e9  0c bd 01 60 b4 2a 1a c9  |..q........`.*..|
00000020  c4 10 00 00 70 00 00 00  78 56 34 12 00 00 00 00  |....p...xV4.....|
00000030  00 00 00 00 00 10 00 00  5c 00 00 00 70 00 00 00  |........\...p...|
00000040  19 00 00 00 e0 01 00 00  12 00 00 00 44 02 00 00  |............D...|
00000050  01 00 00 00 1c 03 00 00  2b 00 00 00 24 03 00 00  |........+...$...|
00000060  02 00 00 00 7c 04 00 00  08 0c 00 00 bc 04 00 00  |....|...........|
binary field
64 65 78 0a 30 33 35 00 magic
3b ba fe c3 checksum
83 7e aa be 09 97 71 1e 17 96 9f e9 0c bd 01 60 b4 2a 1a c9 signature
c4 10 00 00 fileSize
70 00 00 00 headerSize
78 56 34 12 endianTag
00 00 00 00 linkSize
00 00 00 00 linkOff
00 10 00 00 mapOff
5c 00 00 00 stringIdsSize
70 00 00 00 stringIdsOff
19 00 00 00 typeIdsSize
e0 01 00 00 typeIdsOff
12 00 00 00 protoIdsSize
44 02 00 00 protoIdsOff
01 00 00 00 fieldIdsSize
1c 03 00 00 fieldIdsOff
2b 00 00 00 methodIdsSize
24 03 00 00 methodIdsOff
02 00 00 00 classDefsSize
7c 04 00 00 classDefsOff
08 0c 00 00 dataSize
bc 04 00 00 dataOff

写程序解析 DexHeader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import struct

class DexStruct(object):
    DexHeader = {
          "magic": 0,
          "checkSum": 0,
          'signature': 0,
          'fileSize': 0,
          "headerSize": 0,
          "endianTag": 0,
          "linkSize": 0,
          "linkOff": 0,
          "mapOff": 0,
          "stringIdsSize": 0,
          "stringIdsOff": 0,
          "typeIdsSize": 0,
          "typeIdsOff": 0,
          "protoIdsSize": 0,
          "protoIdsOff": 0,
          "fieldIdsSize": 0,
          "fieldIdsOff": 0,
          "methodIdsSize": 0,
          "methodIdsOff": 0,
          "classDefsSize": 0,
          "classDefsOff": 0,
          "dataSize": 0,
          "dataOff": 0,   }


def parseHeader(header_data):

        header_list = [header_data[i:i+4] for i in range(32,112,4)]
        header_list.insert(0,header_data[12:32])
        header_list.insert(0,header_data[8:12])
        header_list.insert(0,header_data[:8])

        DexStruct.DexHeader['magic'] = struct.unpack('8s',header_list[0])[0]
        if DexStruct.DexHeader['magic'] != "dex\n035\0":
            print 'invalid dex file.'
            exit(-1)
        DexStruct.DexHeader['checkSum'] = struct.unpack('I',header_list[1])[0]
        DexStruct.DexHeader['signature'] = struct.unpack('20s',header_list[2])[0]
        DexStruct.DexHeader['fileSize'] = struct.unpack('I',header_list[3])[0]
        DexStruct.DexHeader['headerSize'] = struct.unpack('I',header_list[4])[0]
        DexStruct.DexHeader['endianTag'] = struct.unpack('I',header_list[5])[0]
        DexStruct.DexHeader['linkSize'] = struct.unpack('I',header_list[6])[0]
        DexStruct.DexHeader['linkOff'] = struct.unpack('I',header_list[7])[0]
        DexStruct.DexHeader['mapOff'] = struct.unpack('I',header_list[8])[0]
        DexStruct.DexHeader['stringIdsSize'] = struct.unpack('I',header_list[9])[0]
        DexStruct.DexHeader['stringIdsOff'] = struct.unpack('I',header_list[10])[0]
        DexStruct.DexHeader['typeIdsSize'] = struct.unpack('I',header_list[11])[0]
        DexStruct.DexHeader['typeIdsOff'] = struct.unpack('I',header_list[12])[0]
        DexStruct.DexHeader['protoIdsSize'] = struct.unpack('I',header_list[13])[0]
        DexStruct.DexHeader['protoIdsOff'] = struct.unpack('I',header_list[14])[0]
        DexStruct.DexHeader['fieldIdsSize'] = struct.unpack('I',header_list[15])[0]
        DexStruct.DexHeader['fieldIdsOff'] = struct.unpack('I',header_list[16])[0]
        DexStruct.DexHeader['methodIdsSize'] = struct.unpack('I',header_list[17])[0]
        DexStruct.DexHeader['methodIdsOff'] = struct.unpack('I',header_list[18])[0]
        DexStruct.DexHeader['classDefsSize'] = struct.unpack('I',header_list[19])[0]
        DexStruct.DexHeader['classDefsOff'] = struct.unpack('I',header_list[20])[0]
        DexStruct.DexHeader['dataSize'] = struct.unpack('I',header_list[21])[0]
        DexStruct.DexHeader['dataOff'] = struct.unpack('I',header_list[22])[0]


if __name__ == '__main__':

    with open("classes.dex", 'rb') as f:
        parseHeader(f.read(0x70))
        for x in DexStruct.DexHeader:
            print x, hex(DexStruct.DexHeader[x])

Reference

《Android 软件安全与逆向分析》

Android安全–Dex文件格式详解


如何运行 .smali 程序

原文: https://kiya-z.github.io/2015/11/16/how-to-run-file-ended-with-smali/




如何运行 .smali 程序

准备 smali 程序

从 《Android 软件安全与逆向》书中抠出一段,起名为 FirstSmali.smali :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
.class public LFirstSmali;  # 定义类名
.super Ljava/lang/Object;   # 定义父类
.method public static main([Ljava/lang/String;)V     # 声明静态方法
    .registers 4     # 寄存器
    #.parameter      # 参数

    .prologue      # 代码起始
    nop
    nop
    nop
    nop
    #数据定义
    const/16 v0,0x8
    const/4 v1,0x5
    const/4 v2,0x3
    #数据操作
    move v1,v2
    #数组操作
    new-array v0,v0,[I
    array-length v1,v0
    #实例操作
    new-instance v1,Ljava/lang/StringBuilder;
    #方法调用
    invoke-direct {v1},Ljava/lang/StringBuilder;-><init>()V
    #跳转
    if-nez v0, :cond_0
    goto :goto_0
    :cond_0
    #数据转换
    int-to-float v2,v2
    #运算
    add-float v2,v2,v2
    #比较
    cmpl-float v0,v2,v2
    #字段操作
    sget-object v0,Ljava/lang/System;->out:Ljava/io/PrintStream;
    const-string v1,"Hello Smali"
    #调用
    invoke-virtual {v0,v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
    #返回
    :goto_0
    return-void
    return-void     # 返回空

.end method


编译 smali 程序

将 .smali 文件编译为 .dex 文件 :
java -jar smali.jar -o FirstSmali.dex FirstSmali.smali


执行 smali 程序

打开 adb 环境,连上手机,在命令行下
执行 adb devices 查看手机是否连接成功;
执行 adb push FirstSmali.dex /sdcard/ 将 dex 文件推到手机上;
执行如下命令就 OK 了。

1
adb shell dalvikvm -cp /sdcard/FirstSmali.dex FirstSmali

备注

-cp 是 classpath 的意思,dalvikvm 命令第一个参数指定类路径,第二个指定类名。

另外,如果想要 push 到如 data/local 之类的目录下是没有权限的,可以先 push 到 sdcard,进入 adb shell 执行 su 获得 root 权限之后,就可以复制到 data 目录了。

本文程序只有一个 dex 文件,多个的话需要打包为 zip ,将此 zip 文件作为 dalvikvm 的第一个参数便可。

原文: https://kiya-z.github.io/2015/11/16/how-to-run-file-ended-with-smali/