ipa-medit是什么?

跑起来

先写个简单的 tap10000 程序,代码如下:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int count = 10000;
        char none[30] = {0};
        NSLog(@"[tap1000]pid -> %d", getpid());
        NSLog(@"-> %d", count);

        while (count>0) {
            gets(none);
            count--;
            NSLog(@"-> %d", count);
        }
    }
    return 0;
}

测试一下功能,完美!

Untitled


先从 find 和 patch 两个功能入手,核心功能在 ipa-medit/cmd/cmd.go 文件下:

package cmd

import (
...
)

type Found struct {
	addrs     []int
	converter func(string) ([]byte, error)
	dataType  string
}

func Find(pid string, targetVal string, dataType string) ([]Found, error) {
	...
}
...

func Patch(pid string, targetVal string, targetAddrs []Found) error {
	...
}
...

源码分析

Find

函数使用 vmmap --wide ${pid} 查看进程的虚拟内存区域,再调用 GetWritableAddrRanges(vmmapResult) 函数遍历可写区域获取地址范围,以"==== Writable regions for process" 开始的行即为可写的区域;

vmmap 输出示例:

Process:         tap10000 [19769]
Path:            /Users/USER/Downloads/tap10000
Load Address:    0x100350000
Identifier:      tap10000
Version:         ???
Code Type:       ARM64
Platform:        macOS
Parent Process:  zsh [11605]

...

Physical footprint:         1297K
Physical footprint (peak):  1297K
----

Virtual Memory Map of process 19769 (tap10000)
Output report format:  2.4  -- 64-bit process
VM page size:  16384 bytes

==== Non-writable regions for process 19769
REGION TYPE                    START - END         [ VSIZE  RSDNT  DIRTY   SWAP] PRT/MAX SHRMOD PURGE    REGION DETAIL
__TEXT                      100350000-100354000    [   16K    16K     0K     0K] r-x/r-x SM=COW          /Users/USER/Downloads/tap10000
...

==== Writable regions for process 19769
REGION TYPE                    START - END         [ VSIZE  RSDNT  DIRTY   SWAP] PRT/MAX SHRMOD PURGE    REGION DETAIL
Kernel Alloc Once           100460000-100468000    [   32K    16K    16K     0K] rw-/rwx SM=PRV
...
__DATA                      100640000-100644000    [   16K    16K    16K     0K] rw-/rw- SM=COW          /usr/lib/dyld
...

解析拿到可写内存区域地址范围后存入 addrRanges [][2]int,接下来就是读取内存并查找值为 targetVal 的地址:

splitSize = 0x5000000

func FindDataInAddrRanges(pid int, targetBytes []byte, addrRanges [][2]int) ([]int, error) {
	foundAddrs := []int{}
	searchLength := len(targetBytes)
	for _, s := range addrRanges {
		beginAddr := s[0]
		endAddr := s[1]
		memSize := endAddr - beginAddr
		for i := 0; i < (memSize/splitSize)+1; i++ {
			// target memory is too big to read all of it, so split it and then search in memory
			splitIndex := (i + 1) * splitSize
			splittedBeginAddr := beginAddr + i*splitSize
			splittedEndAddr := endAddr
			if splitIndex < memSize {
				splittedEndAddr = beginAddr + splitIndex
			}
			b := bufferPool.Get().([]byte)[:(splittedEndAddr - splittedBeginAddr)]
			task := GetTaskForPid(pid)
			if err := ReadMemory(task, b, splittedBeginAddr, splittedEndAddr); err == nil {
				findDataInSplittedMemory(&b, targetBytes, searchLength, splittedBeginAddr, 0, &foundAddrs)
				bufferPool.Put(b)
				if len(foundAddrs) > 500000 {
					fmt.Println("Too many addresses with target data found...")
					return foundAddrs, TooManyErr{&Err{errors.New("Error: Too many addresses")}}
				}
			} else {
				fmt.Printf("0x%x: %s\\\\n", beginAddr, err)
			}
		}
	}
	return foundAddrs, nil
}

因为目标内存太大,无法读取所有内存,因此 FindDataInAddrRanges 函数请将其拆分,然后在内存中搜。

task := GetTaskForPid(pid) 获取目标进程任务端口,c代码如下:

Powered by Kali-Team