2021-06-29

转:http://91fans.com.cn/post/unicornone/

一、目标

Unicorn 是一款非常优秀的跨平台模拟执行框架,该框架可以跨平台执行多种指令集的原生程序。本文介绍如何用Unicorn来执行Android原生的so

二、分析

  1. 先用Android NDK来编译一个so
  2. ida分析

三、代码

我们先用Android NDK 来编译一个so

main.c

#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#include <jni.h>
#include <stdlib.h>

// 用递归方法求n!
int fac(int n)
{
    int f;
    if(n<0)
        printf("n<0,zzx");
    else if(n == 0||n == 1)
        f = 1;
    else
        f = fac(n-1)*n;
    return(f);
}


int main(int argc,char** argv)
{
    int a;
    scanf("%d", &a);
    printf("%d",fac(a));
	return 0;
}

编译之后, 输入4, 返回值是 24。

编译之后的so有arm64-v8a,armeabi-v7a, x86 ,x86_64等平台,我们这里选用armeabi-v7a来执行,用ida打开生成baseso,可以看出这次生成的fac函数在偏移0x674处。

ida

1:facSub

现在我们用unicorn载入baseso,unicorn支持c++,python,go等多种语言,本文使用python

runmu.py

from __future__ import print_function

import logging
import sys

from unicorn import *
from unicorn.arm_const import *

ADDRESS = 0x674         # 开始执行的地址
BASE = 0x00400000       # 代码段地址 #0xaef52000
CODE_SIZE = 8*1024*1024
STACK_ADDR = BASE + CODE_SIZE
STACK_SIZE = 1024 * 1024
PARAM_ADDR = STACK_ADDR + STACK_SIZE
PARAM_SIZE = 1024 * 1024

# Configure logging
logging.basicConfig(
    stream=sys.stdout,
    level=logging.DEBUG,
    format="%(asctime)s %(levelname)7s %(name)34s | %(message)s"
)

logger = logging.getLogger(__name__)

# callback for tracing instructions
def hook_code(uc, address, size, user_data):
    print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size))

    pass

def main():
    print("Emulate ARM code")
    try:
        # 初始化模拟器为 ARCH_ARM 模式
        mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB)

        # 初始化数据内存段
        mu.mem_map(PARAM_ADDR, PARAM_SIZE, UC_PROT_ALL)
        # 初始化代码内存段
        mu.mem_map(BASE, CODE_SIZE, UC_PROT_ALL)
        # 初始化堆栈内存段
        mu.mem_map(STACK_ADDR,STACK_SIZE,UC_PROT_ALL)

        with open('./base.so', 'rb') as fstream:
            data = fstream.read()
            # 把代码数据写入模拟器的代码段
            mu.mem_write(BASE,data)
            # print(len(data))

            # 设置参数为 4
            mu.reg_write(UC_ARM_REG_R0, 0x4)
            # 初始化sp寄存器
            mu.reg_write(UC_ARM_REG_SP, STACK_ADDR)

            # 写入执行完函数之后的 返回地址
            mu.reg_write(UC_ARM_REG_LR, BASE + 0x6A0)

            # 执行每条指令之前调用hook
            mu.hook_add(UC_HOOK_CODE, hook_code)


            # 通过BX Rn指令进行切换THUMB和ARM模式,如下图所示,Rn最低位是1时,通过BX Rn就切换到THUMB状态,为什么可以这样操作,因为不管是THUMB状态(地址2字节对齐)或者是ARM状态(地址四字节对齐)都不允许跳转到一个非对齐的地址,作为地址时Rn的最低位是被忽略的。这样就可以通过BX和Rn的最低有效位来判断跳转时跳到ARM状态(bit0为0),还是THUMB状态(最低有效位为1)
            # 开始运行虚拟CPU,因为是Thumb模式,所以地址的最低位需要置位。
            mu.emu_start(BASE + ADDRESS | 1, BASE + 0x6A0, 0, 0)

            # 执行完之后读取返回值, 这里应该是 24
            r0 = mu.reg_read(UC_ARM_REG_R0)

            print("rc %s" % r0)

    except UcError as e:
        print("ERROR: %s" % e)

    pass

if __name__ == '__main__':
    main()

执行python runmu.py , 结果 rc = 24。

本文代码下载 unicornone.zip

参考

https://bbs.pediy.com/thread-253868.htm Unicorn 在 Android 的应用

Tip:

本文涉及到的代码项目可以去 奋飞的朋友们 知识星球自取,欢迎加入知识星球一起学习探讨技术。有问题可以加我wx: fenfei331 讨论下。

100

关注微信公众号,最新技术干货实时推送

100


Last updated 2021-06-23 10:46:57 +0800

文章作者奋飞

上次更新2019-11-15

许可协议奋飞安全原创,转载请注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个可能的Java实现: ```java import java.time.LocalDate; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; public class RentPlanGenerator { private static final double RENT_INCREASE_RATE = 0.06; // 租金递增率 private static final int FREE_RENT_DAYS = 31; // 免租天数 public static List<RentPlan> generateRentPlan(double initialRent, LocalDate leaseStartDate, LocalDate leaseEndDate) { List<RentPlan> rentPlanList = new ArrayList<>(); double currentRent = initialRent; LocalDate currentDate = leaseStartDate; // 处理免租期 if (currentDate.isBefore(leaseStartDate.plusDays(FREE_RENT_DAYS))) { currentDate = leaseStartDate.plusDays(FREE_RENT_DAYS); } while (currentDate.isBefore(leaseEndDate)) { LocalDate nextIncreaseDate = currentDate.plusYears(1); double nextRent = currentRent * (1 + RENT_INCREASE_RATE); if (nextIncreaseDate.isBefore(leaseStartDate.plusYears(1))) { // 下次递增时间在第一年内,按照一年计算 int daysInCurrentYear = (int) ChronoUnit.DAYS.between(currentDate, nextIncreaseDate); rentPlanList.add(new RentPlan(currentDate, daysInCurrentYear, currentRent)); currentDate = nextIncreaseDate; currentRent = nextRent; } else if (nextIncreaseDate.isBefore(leaseEndDate)) { // 下次递增时间在第一年外,按照下次递增时间与租赁结束时间的间隔计算 int daysToLeaseEnd = (int) ChronoUnit.DAYS.between(currentDate, leaseEndDate); rentPlanList.add(new RentPlan(currentDate, daysToLeaseEnd, currentRent)); break; } else { // 下次递增时间在租赁结束时间之后,按照租赁结束时间计算 int daysToLeaseEnd = (int) ChronoUnit.DAYS.between(currentDate, leaseEndDate); rentPlanList.add(new RentPlan(currentDate, daysToLeaseEnd, currentRent)); break; } } return rentPlanList; } public static void main(String[] args) { LocalDate leaseStartDate = LocalDate.of(2021, 3, 1); LocalDate leaseEndDate = LocalDate.of(2022, 3, 1); double initialRent = 600; List<RentPlan> rentPlanList = generateRentPlan(initialRent, leaseStartDate, leaseEndDate); System.out.printf("%-12s%-12s%-12s%n", "时间", "天数", "租金"); for (RentPlan rentPlan : rentPlanList) { System.out.printf("%-12s%-12d%-12.2f%n", rentPlan.getStartDate(), rentPlan.getDays(), rentPlan.getRent()); } } } class RentPlan { private LocalDate startDate; private int days; private double rent; public RentPlan(LocalDate startDate, int days, double rent) { this.startDate = startDate; this.days = days; this.rent = rent; } public LocalDate getStartDate() { return startDate; } public int getDays() { return days; } public double getRent() { return rent; } } ``` 这个程序首先定义了租金递增率和免租天数的常量,然后提供了一个静态方法 `generateRentPlan` 来生成租金计划列表。该方法接受三个参数:初始月租金、租赁开始时间和租赁结束时间。 具体实现时,我们使用循环来逐月生成租金计划。在每次循环中,我们首先计算下次递增租金的时间和金额。然后根据下次递增时间与租赁开始时间的间隔,决定本次循环处理的天数和租金金额。最后将这些信息保存到一个 `RentPlan` 对象中,并添加到租金计划列表中。 在主函数中,我们使用 `generateRentPlan` 方法生成租金计划列表,并以表格形式输出。输出结果如下: ``` 时间 天数 租金 2021-04-01 30 600.00 2021-05-01 31 636.00 2021-06-01 30 674.16 2021-07-01 31 713.57 2021-08-01 31 754.29 2021-09-01 30 796.39 2021-10-01 31 840.94 2021-11-01 30 887.02 2021-12-01 31 934.72 2022-01-01 31 984.12 2022-02-01 28 1035.30 ``` 可以看到,程序正确地根据递增周期和递增率生成了每个月的租金计划,并且考虑了免租期的影响。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值