├── .gitignore
├── .idea
├── encodings.xml
├── misc.xml
├── modules.xml
├── re_scripts.iml
└── vcs.xml
├── LICENSE
├── README.md
├── ida
├── Armariris_flow_flattening_bypass.py
├── Armariris_string_obfuscation_bypass.md
├── Armariris_string_obfuscation_bypass.py
├── Simulator.py
├── img
│ ├── after1.png
│ ├── after2.png
│ ├── after3.png
│ ├── after4.png
│ ├── before.png
│ ├── data_orig.png
│ └── xref.png
└── sample
│ ├── test.c
│ ├── test_linux_x86_64
│ ├── test_linux_x86_64_fla
│ └── test_macos_x86_64
└── jeb
└── FridaCodeGenerator.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### JetBrains template
3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
5 |
6 | # User-specific stuff
7 | .idea/**/workspace.xml
8 | .idea/**/tasks.xml
9 | .idea/**/dictionaries
10 | .idea/**/shelf
11 |
12 | # Sensitive or high-churn files
13 | .idea/**/dataSources/
14 | .idea/**/dataSources.ids
15 | .idea/**/dataSources.local.xml
16 | .idea/**/sqlDataSources.xml
17 | .idea/**/dynamic.xml
18 | .idea/**/uiDesigner.xml
19 | .idea/**/dbnavigator.xml
20 |
21 | # Gradle
22 | .idea/**/gradle.xml
23 | .idea/**/libraries
24 |
25 | # CMake
26 | cmake-build-debug/
27 | cmake-build-release/
28 |
29 | # Mongo Explorer plugin
30 | .idea/**/mongoSettings.xml
31 |
32 | # File-based project format
33 | *.iws
34 |
35 | # IntelliJ
36 | out/
37 |
38 | # mpeltonen/sbt-idea plugin
39 | .idea_modules/
40 |
41 | # JIRA plugin
42 | atlassian-ide-plugin.xml
43 |
44 | # Cursive Clojure plugin
45 | .idea/replstate.xml
46 |
47 | # Crashlytics plugin (for Android Studio and IntelliJ)
48 | com_crashlytics_export_strings.xml
49 | crashlytics.properties
50 | crashlytics-build.properties
51 | fabric.properties
52 |
53 | # Editor-based Rest Client
54 | .idea/httpRequests
55 | ### VisualStudioCode template
56 | .vscode/*
57 | !.vscode/settings.json
58 | !.vscode/tasks.json
59 | !.vscode/launch.json
60 | !.vscode/extensions.json
61 | ### SublimeText template
62 | # Cache files for Sublime Text
63 | *.tmlanguage.cache
64 | *.tmPreferences.cache
65 | *.stTheme.cache
66 |
67 | # Workspace files are user-specific
68 | *.sublime-workspace
69 |
70 | # Project files should be checked into the repository, unless a significant
71 | # proportion of contributors will probably not be using Sublime Text
72 | # *.sublime-project
73 |
74 | # SFTP configuration file
75 | sftp-config.json
76 |
77 | # Package control specific files
78 | Package Control.last-run
79 | Package Control.ca-list
80 | Package Control.ca-bundle
81 | Package Control.system-ca-bundle
82 | Package Control.cache/
83 | Package Control.ca-certs/
84 | Package Control.merged-ca-bundle
85 | Package Control.user-ca-bundle
86 | oscrypto-ca-bundle.crt
87 | bh_unicode_properties.cache
88 |
89 | # Sublime-github package stores a github token in this file
90 | # https://packagecontrol.io/packages/sublime-github
91 | GitHub.sublime-settings
92 | ### Windows template
93 | # Windows thumbnail cache files
94 | Thumbs.db
95 | ehthumbs.db
96 | ehthumbs_vista.db
97 |
98 | # Dump file
99 | *.stackdump
100 |
101 | # Folder config file
102 | [Dd]esktop.ini
103 |
104 | # Recycle Bin used on file shares
105 | $RECYCLE.BIN/
106 |
107 | # Windows Installer files
108 | *.cab
109 | *.msi
110 | *.msix
111 | *.msm
112 | *.msp
113 |
114 | # Windows shortcuts
115 | *.lnk
116 | ### Linux template
117 | *~
118 |
119 | # temporary files which can be created if a process still has a handle open of a deleted file
120 | .fuse_hidden*
121 |
122 | # KDE directory preferences
123 | .directory
124 |
125 | # Linux trash folder which might appear on any partition or disk
126 | .Trash-*
127 |
128 | # .nfs files are created when an open file is removed but is still being accessed
129 | .nfs*
130 | ### Java template
131 | # Compiled class file
132 | *.class
133 |
134 | # Log file
135 | *.log
136 |
137 | # BlueJ files
138 | *.ctxt
139 |
140 | # Mobile Tools for Java (J2ME)
141 | .mtj.tmp/
142 |
143 | # Package Files #
144 | *.jar
145 | *.war
146 | *.nar
147 | *.ear
148 | *.zip
149 | *.tar.gz
150 | *.rar
151 |
152 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
153 | hs_err_pid*
154 | ### Python template
155 | # Byte-compiled / optimized / DLL files
156 | __pycache__/
157 | *.py[cod]
158 | *$py.class
159 |
160 | # C extensions
161 | *.so
162 |
163 | # Distribution / packaging
164 | .Python
165 | build/
166 | develop-eggs/
167 | dist/
168 | downloads/
169 | eggs/
170 | .eggs/
171 | lib/
172 | lib64/
173 | parts/
174 | sdist/
175 | var/
176 | wheels/
177 | *.egg-info/
178 | .installed.cfg
179 | *.egg
180 | MANIFEST
181 |
182 | # PyInstaller
183 | # Usually these files are written by a python script from a template
184 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
185 | *.manifest
186 | *.spec
187 |
188 | # Installer logs
189 | pip-log.txt
190 | pip-delete-this-directory.txt
191 |
192 | # Unit test_linux_elf / coverage reports
193 | htmlcov/
194 | .tox/
195 | .coverage
196 | .coverage.*
197 | .cache
198 | nosetests.xml
199 | coverage.xml
200 | *.cover
201 | .hypothesis/
202 | .pytest_cache/
203 |
204 | # Translations
205 | *.mo
206 | *.pot
207 |
208 | # Django stuff:
209 | *.log
210 | local_settings.py
211 | db.sqlite3
212 |
213 | # Flask stuff:
214 | instance/
215 | .webassets-cache
216 |
217 | # Scrapy stuff:
218 | .scrapy
219 |
220 | # Sphinx documentation
221 | docs/_build/
222 |
223 | # PyBuilder
224 | target/
225 |
226 | # Jupyter Notebook
227 | .ipynb_checkpoints
228 |
229 | # pyenv
230 | .python-version
231 |
232 | # celery beat schedule file
233 | celerybeat-schedule
234 |
235 | # SageMath parsed files
236 | *.sage.py
237 |
238 | # Environments
239 | .env
240 | .venv
241 | env/
242 | venv/
243 | ENV/
244 | env.bak/
245 | venv.bak/
246 |
247 | # Spyder project settings
248 | .spyderproject
249 | .spyproject
250 |
251 | # Rope project settings
252 | .ropeproject
253 |
254 | # mkdocs documentation
255 | /site
256 |
257 | # mypy
258 | .mypy_cache/
259 | ### macOS template
260 | # General
261 | .DS_Store
262 | .AppleDouble
263 | .LSOverride
264 |
265 | # Icon must end with two \r
266 | Icon
267 |
268 | # Thumbnails
269 | ._*
270 |
271 | # Files that might appear in the root of a volume
272 | .DocumentRevisions-V100
273 | .fseventsd
274 | .Spotlight-V100
275 | .TemporaryItems
276 | .Trashes
277 | .VolumeIcon.icns
278 | .com.apple.timemachine.donotpresent
279 |
280 | # Directories potentially created on remote AFP share
281 | .AppleDB
282 | .AppleDesktop
283 | Network Trash Folder
284 | Temporary Items
285 | .apdisk
286 |
287 | *.i64
288 | *.idb
289 | *.id1
290 | *.id2
291 | *.id0
292 | *.nam
293 | *.til
294 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/re_scripts.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2019 MSWorkers
2 |
3 | Anti 996 License Version 1.0 (Draft)
4 |
5 | Permission is hereby granted to any individual or legal entity obtaining a copy
6 | of this licensed work (including the source code, documentation and/or related
7 | items, hereinafter collectively referred to as the "licensed work"), free of
8 | charge, to deal with the licensed work for any purpose, including without
9 | limitation, the rights to use, reproduce, modify, prepare derivative works of,
10 | publish, distribute and sublicense the licensed work, subject to the following
11 | conditions:
12 |
13 | 1. The individual or the legal entity must conspicuously display, without
14 | modification, this License on each redistributed or derivative copy of the
15 | Licensed Work.
16 |
17 | 2. The individual or the legal entity must strictly comply with all applicable
18 | laws, regulations, rules and standards of the jurisdiction relating to
19 | labor and employment where the individual is physically located or where
20 | the individual was born or naturalized; or where the legal entity is
21 | registered or is operating (whichever is stricter). In case that the
22 | jurisdiction has no such laws, regulations, rules and standards or its
23 | laws, regulations, rules and standards are unenforceable, the individual
24 | or the legal entity are required to comply with Core International Labor
25 | Standards.
26 |
27 | 3. The individual or the legal entity shall not induce or force its
28 | employee(s), whether full-time or part-time, or its independent
29 | contractor(s), in any methods, to agree in oral or written form,
30 | to directly or indirectly restrict, weaken or relinquish his or
31 | her rights or remedies under such laws, regulations, rules and
32 | standards relating to labor and employment as mentioned above,
33 | no matter whether such written or oral agreement are enforceable
34 | under the laws of the said jurisdiction, nor shall such individual
35 | or the legal entity limit, in any methods, the rights of its employee(s)
36 | or independent contractor(s) from reporting or complaining to the copyright
37 | holder or relevant authorities monitoring the compliance of the license
38 | about its violation(s) of the said license.
39 |
40 | THE LICENSED WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
42 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
43 | HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
44 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ANY WAY CONNECTION
45 | WITH THE LICENSED WORK OR THE USE OR OTHER DEALINGS IN THE LICENSED WORK.
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # re_scripts
2 | Some reverse-engineering scripts
3 |
4 | ## jeb
5 |
6 | 1. [FridaCodeGenerator.py](./jeb/FridaCodeGenerator.py) A jeb plugin generate frida hook code
7 |
8 | ## ida
9 |
10 | 1. [Armariris_string_obfuscation_bypass.py](./ida/Armariris_string_obfuscation_bypass.py) An ida plugin bypass Armariris string obfuscation
11 |
--------------------------------------------------------------------------------
/ida/Armariris_flow_flattening_bypass.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | # @Author: smartdone
5 | # @Date: 2019-07-01 11:00
6 |
7 | from Simulator import *
8 | from capstone import *
9 |
10 | ret_addr = None
11 | start_addr = 0
12 |
13 |
14 | def hook_mem_access(uc, type, address, size, value, userdata):
15 | pc = uc.reg_read(UC_ARM64_REG_PC)
16 | print 'pc:%x type:%d addr:%x size:%x' % (pc, type, address, size)
17 | # uc.emu_stop()
18 | return False
19 |
20 | md = Cs(CS_ARCH_ARM64,CS_MODE_ARM)
21 | md.detail = True #enable detail analyise
22 |
23 | def reg_ctou(regname):#
24 | # This function covert capstone reg name to unicorn reg const.
25 | type1 = regname[0]
26 | if type1 == 'w' or type1 =='x':
27 | idx = int(regname[1:])
28 | if type1 == 'w':
29 | return idx + UC_ARM64_REG_W0
30 | else:
31 | if idx == 29:
32 | return 1
33 | elif idx == 30:
34 | return 2
35 | else:
36 | return idx + UC_ARM64_REG_X0
37 | elif regname=='sp':
38 | return 4
39 | return None
40 |
41 | branch_control = 1
42 |
43 | def hook_code(uc, address, size, user_data):
44 | global ret_addr
45 | global start_addr
46 | global branch_control
47 | # print user_data
48 | instruction = uc.mem_read(address, size)
49 | # print ["0x%x" % item for item in block_starts]
50 | idc.set_color(address, idc.CIC_ITEM, 0xFFB6C1)
51 | # print ["0x%x" % item for item in instruction]
52 | _code = idc.GetDisasm(address)
53 |
54 | # print(type(_code), _code.startswith("CMP"), _code)
55 | # print("0x%016x \t%s" % (address, _code))
56 |
57 | if address in user_data and address != start_addr:
58 | print("address 0x%x" % address)
59 | uc.emu_stop()
60 | ret_addr = address
61 | return
62 |
63 | if "RET" == _code:
64 | ret_addr = -1
65 | uc.emu_stop()
66 |
67 | for ins in md.disasm(instruction, address):
68 | if ins.mnemonic == 'csel':
69 | print("csel 0x%x:\t%s\t%s" % (ins.address, ins.mnemonic, ins.op_str))
70 | regs = [reg_ctou(x) for x in ins.op_str.split(', ')]
71 | assert len(regs) == 4
72 | v1 = uc.reg_read(regs[1])
73 | v2 = uc.reg_read(regs[2])
74 | if branch_control == 1:
75 | uc.reg_write(regs[0], v1)
76 | else:
77 | uc.reg_write(regs[0], v2)
78 | uc.reg_write(UC_ARM64_REG_PC, address + size)
79 |
80 |
81 | class FLASimulator(Simulator):
82 | def __init__(self, basic_blocks):
83 | super(FLASimulator, self).__init__()
84 | self.context = None
85 | self.basic_blocks = basic_blocks
86 |
87 | def emu_start(self, func_start, func_end):
88 | global ret_addr
89 | if self.arch == UC_ARCH_ARM:
90 | if self.is_thumb_ea(func_start):
91 | print("thumb mode")
92 | self.mode = UC_MODE_THUMB
93 | mu = Uc(self.arch, self.mode)
94 |
95 | for item in self.mem_map:
96 | Simulator.map_memory(mu, item['start'], item['length'])
97 |
98 | # 给栈分配内存
99 | Simulator.map_memory(mu, self.stack_base, self.stack_length)
100 |
101 | # 写入数据
102 | for item in self.segments:
103 | Simulator.write_memory(mu, item['start'], item['data'])
104 |
105 | # 配置寄存器
106 | mu.reg_write(self.sp, self.stack_base + 1024 * 1024)
107 |
108 | mu.hook_add(htype=UC_HOOK_CODE, callback=hook_code, user_data=self.basic_blocks)
109 | mu.hook_add(UC_HOOK_MEM_UNMAPPED, hook_mem_access)
110 | mu.hook_add(UC_ERR_WRITE_UNMAPPED, hook_mem_access)
111 |
112 | self.set_context(mu, self.context)
113 |
114 | try:
115 | # 开始执行
116 | if self.mode == UC_MODE_THUMB:
117 | mu.emu_start(func_start + 1, func_end)
118 | else:
119 | mu.emu_start(func_start, func_end)
120 | except Exception as e:
121 | print("Err: %s. Execution function failed.(The function address is 0x%x)" % (e, func_start))
122 |
123 | self.get_context(mu)
124 |
125 | # 读取数据
126 | for item in self.segments:
127 | _data = Simulator.read_memory(mu, item['start'], item['end'])
128 | self.replace_data(item['start'], _data)
129 |
130 | # unmap memory
131 | for item in self.mem_map:
132 | Simulator.unmap_memory(mu, item['start'], item['length'])
133 |
134 | Simulator.unmap_memory(mu, self.stack_base, self.stack_length)
135 |
136 | if ret_addr != 0:
137 | tmp_addr = ret_addr
138 | ret_addr = None
139 | return tmp_addr
140 |
141 | return None
142 |
143 | def get_context(self, mu=None):
144 | if not mu:
145 | return self.context
146 | else:
147 | regs = []
148 | if self.arch == UC_ARCH_ARM:
149 | print("arm")
150 | elif self.arch == UC_ARCH_ARM64:
151 | print("arm64")
152 | for idx in range(UC_ARM64_REG_X0, UC_ARM64_REG_X28 + 1):
153 | regs.append((idx, mu.reg_read(idx)))
154 | elif self.arch == UC_ARCH_X86:
155 | print("x86")
156 |
157 | self.context = regs
158 | # print "regs", regs
159 |
160 | return self.context
161 |
162 | def set_context(self, mu=None, _context=None):
163 | if not mu:
164 | self.context = _context
165 | else:
166 | if _context:
167 | self.context = _context
168 | for item in self.context:
169 | mu.reg_write(item[0], item[1])
170 |
171 |
172 | for func in idautils.Functions():
173 | global start_addr
174 | func_name = idc.GetFunctionName(func)
175 | func_data = idaapi.get_func(func)
176 | start = func_data.start_ea
177 | end = func_data.end_ea
178 | # print func_name, hex(start), hex(end)
179 | if func_name == "JNI_OnLoad":
180 | print func_name, hex(start), hex(end)
181 | f = idaapi.FlowChart(idaapi.get_func(start))
182 | basic_blocs = []
183 | for block in f:
184 | print("%x - %x [%d]:" % (block.start_ea, block.end_ea, block.id))
185 | block_start = block.start_ea
186 | block_end = block.end_ea
187 | basic_blocs.append(block_start)
188 | sim = FLASimulator(basic_blocs)
189 | sim.sp = UC_ARM64_REG_SP
190 | sim.arch = UC_ARCH_ARM64
191 | sim.mode = UC_MODE_ARM
192 |
193 | flow = {}
194 | start_addr = start
195 | queue = [(start, None)]
196 |
197 | while len(queue) > 0:
198 | env = queue.pop()
199 | pc = env[0]
200 | context = env[1]
201 |
202 | sim.set_context(context)
203 |
204 | if pc in flow.keys():
205 | print("0x%x in flow" % pc)
206 | continue
207 |
208 | flow[pc] = []
209 | start_addr = pc
210 |
211 | p = sim.emu_start(pc, end)
212 |
213 | if p:
214 | print("0x%x --> 0x%x" % (pc, p))
215 | flow[pc].append(p)
216 | queue.append((p, sim.get_context()))
217 |
218 |
219 |
220 |
221 |
--------------------------------------------------------------------------------
/ida/Armariris_string_obfuscation_bypass.md:
--------------------------------------------------------------------------------
1 | ## 编译Armariris
2 |
3 | ```
4 | git clone git@github.com:gossip-sjtu/Armariris.git
5 | ```
6 |
7 | 编译
8 |
9 | ```shell
10 | cd Armariris
11 | mkdir build
12 | cd build
13 | cmake ../ -DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD="ARM;X86;AArch64"
14 | make -j8
15 | ```
16 |
17 | 测试文件内容如下:
18 |
19 | ```c
20 | #include
21 |
22 | void fun(){
23 | printf("test 3333\n");
24 | }
25 |
26 | int main(int argc, char *argv[]) {
27 | printf("test 1111\n");
28 | printf("test 2222\n");
29 | fun();
30 | return 0;
31 | }
32 |
33 | ```
34 |
35 | 使用编译好的llvm编译这个测试的文件
36 |
37 | ```shell
38 | clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -mllvm -sobf test.c -o test
39 | ```
40 |
41 | 使用isysroot指定sdk,然后使用`-mllvm -sobf`开启字符串混淆
42 |
43 | ## Armariris是如何进行字符串混淆的
44 |
45 | 我们直接看使用ida反汇编出来的代码
46 |
47 | ```c++
48 | int __cdecl main(int argc, const char **argv, const char **envp)
49 | {
50 | printf(aRcur7777, argv, envp);
51 | printf(&byte_100001036);
52 | fun();
53 | return 0;
54 | }
55 | ```
56 |
57 | 可以看到有两个printf函数打印了一些数据出来,我们点第一个打印的字符串,双击`aRcur7777`跳转到
58 | 字符串定义位置,这个字符串在data段
59 |
60 | 
61 |
62 | 这个字符串我们本来输出的是`test 1111`这里显然不是,我们查看`aRcur7777`的交叉引用,发现两处,
63 | 其中一处是main函数中的printf,另一处应该就是还原这个字符串的位置了
64 |
65 | 
66 |
67 | 所以`__datadiv_decode14953400483976599729`这个函数就是还原这个字符的函数,我们看他是如何做的还原
68 | 。跳转过去按F5反编译,得到的结果如下:
69 |
70 | ```cpp
71 | __int64 datadiv_decode14953400483976599729()
72 | {
73 | bool v0; // ST23_1
74 | bool v1; // ST17_1
75 | __int64 result; // rax
76 | bool v3; // ST0B_1
77 | unsigned int v4; // [rsp+8h] [rbp-1Ch]
78 | unsigned int v5; // [rsp+14h] [rbp-10h]
79 | unsigned int v6; // [rsp+20h] [rbp-4h]
80 |
81 | v6 = 0;
82 | do
83 | {
84 | aLKl[v6] ^= 0x38u;
85 | v0 = v6++ < 0xA;
86 | }
87 | while ( v0 );
88 | v5 = 0;
89 | do
90 | {
91 | aRcur7777[v5] ^= 6u;
92 | v1 = v5++ < 0xA;
93 | }
94 | while ( v1 );
95 | v4 = 0;
96 | do
97 | {
98 | byte_100001036[v4] ^= 0x71u;
99 | result = v4 - 10;
100 | v3 = v4++ < 0xA;
101 | }
102 | while ( v3 );
103 | return result;
104 | }
105 | ```
106 |
107 | 我们可以看到`aRcur7777`的还原是和6做了异或操作,那我们来验证一下是否是我们看到的这样。
108 |
109 | `aRcur7777`的原始数据是`[0x72, 0x63, 0x75, 0x72, 0x26, 0x37, 0x37, 0x37, 0x37]`
110 |
111 | 每一位和6异或之后的结果是`[0x74, 0x65, 0x73, 0x74, 0x20, 0x31, 0x31, 0x31, 0x31]`
112 |
113 | 对于的ascii字符串就是`test 1111`
114 |
115 | 他这里做字符串混淆用的是一个很简单的原理,一个数字两次异或同一个值,得到的结果是本身的值。也就是
116 | 第一次异或就给字符串混淆了,再异或一次就把数据还原了。
117 |
118 | ## 源码分析
119 |
120 | 字符串混淆的源文件在`lib/Transforms/Obfuscation/StringObfuscation.cpp`这个位置,
121 | 实现字符串混淆的是一个`ModulePass`,关于`ModulePass`可以参考[http://llvm.org/doxygen/classllvm_1_1ModulePass.html#details](http://llvm.org/doxygen/classllvm_1_1ModulePass.html#details)
122 | 。在这个pass里面会遍历字符串,然后把字符串和生成的key异或,并替换原始的值,关键代码如下:
123 |
124 | ```cpp
125 |
126 | // Duplicate global variable
127 | GlobalVariable *dynGV = new GlobalVariable(M,
128 | gv->getType()->getElementType(),
129 | !(gv->isConstant()), gv->getLinkage(),
130 | (Constant*) 0, gv->getName(),
131 | (GlobalVariable*) 0,
132 | gv->getThreadLocalMode(),
133 | gv->getType()->getAddressSpace());
134 | // dynGV->copyAttributesFrom(gv);
135 | dynGV->setInitializer(gv->getInitializer());
136 |
137 | std::string tmp=gv->getName().str();
138 | // errs()<<"GV: "<<*gv<<"\n";
139 |
140 | Constant *initializer = gv->getInitializer();
141 | ConstantDataSequential *cdata = dyn_cast(initializer);
142 | if (cdata) {
143 | const char *orig = cdata->getRawDataValues().data();
144 | unsigned len = cdata->getNumElements()*cdata->getElementByteSize();
145 |
146 | encVar *cur = new encVar();
147 | cur->var = dynGV;
148 | cur->key = llvm::cryptoutils->get_uint8_t();
149 | // casting away const is undef. behavior in C++
150 | // TODO a clean implementation would retrieve the data, generate a new constant
151 | // set the correct type, and copy the data over.
152 | //char *encr = new char[len];
153 | //Constant *initnew = ConstantDataArray::getString(M.getContext(), encr, true);
154 | char *encr = const_cast(orig);
155 | // Simple xor encoding
156 | for (unsigned i = 0; i != len; ++i) {
157 | encr[i] = orig[i]^cur->key;
158 | }
159 |
160 | // FIXME Second part of the unclean hack.
161 | dynGV->setInitializer(initializer);
162 |
163 | // Prepare to add decode function for this variable
164 | encGlob.push_back(cur);
165 | } else {
166 | // just copying default initializer for now
167 | dynGV->setInitializer(initializer);
168 | }
169 |
170 | // redirect references to new GV and remove old one
171 | gv->replaceAllUsesWith(dynGV);
172 | toDelConstGlob.push_back(gv);
173 |
174 | ```
175 |
176 | 在替换了之后为了保证程序可以正常运行,还得加一个函数输还原字符串,还原字符串的
177 | 函数生成代码在`addDecodeFunction`中。在这里添加了`.datadiv_decode`开始的函数
178 | 加上一串随机字符串,里面进行了异或操作,将数据还原。然后将这个函数加入到了`entry`,这个在
179 | elf文件的话,就会被加入到`.init_array`,在mach-o文件中就会被加入到`__mod_init_func`。
180 | 代码也比较简单,可以参照源码看一下。
181 |
182 |
183 | ## 还原字符串
184 |
185 | 前面讲了原理其实很简单,那么怎么还原字符串呢,其实也有很多方式,第一种是内存dump,因为他会在
186 | 初始化程序的时候就把原始字符串还原回去。但是有时候我就行静态分析,不想执行之后去dump。如果只
187 | 静态分析,也可以去人工还原字符串。但是如果字符串很多,人工还原工作量很大。其实我们还可以使用
188 | unicorn之类的工具,模拟去执行他的指令,把字符串进行还原。
189 |
190 | ### 还原混淆字符串的思路
191 |
192 | 1. 找到所有`.datadiv_decode`开始的函数
193 | 2. unicorn分配内存,将程序的.text段和.data段映射到unicorn分配的内存中
194 | 3. 模拟执行所有`.datadiv_decode`开始的函数
195 | 4. 最后将unicorn中分配的data读出来,patch到程序中
196 |
197 | ### 使用的工具
198 |
199 | 因为不同操作系统可执行文件格式不一样。为了简单点,我们直接写一个ida插件。所以需要以下工具:
200 |
201 | 1. ida
202 | 2. python2 (因为ida里面内置的python是python2)
203 | 3. python2安装unicorn和keystone库
204 |
205 | ### 找到所有的`.datadiv_decode`开始的函数
206 |
207 | `idautils.Functions()`可以遍历函数,遍历匹配含有`datadiv_decode`的函数,保存他们
208 | 的起始地址,代码很简单,如下:
209 |
210 | ```python
211 | import idaapi
212 | import idc
213 | import idautils
214 |
215 | for func in idautils.Functions():
216 | func_name = idc.GetFunctionName(func)
217 | if "datadiv_decode" in func_name:
218 | func_data = idaapi.get_func(func)
219 | start = func_data.start_ea
220 | end = func_data.end_ea
221 | ```
222 |
223 | ### unicorn分配内存
224 |
225 | 我这里分配内存的想法是直接用ida的api获取data段和text段的内容,以及起始地址,然后在
226 | unicorn里面对于起始分配内存,将data段和text段写进去。
227 |
228 | unicorn分配内存还是有些坑,不能直接在任意地址分配,必须得整除1024的才可以,所以需
229 | 要稍微计算一下分配的地址。这里对基地址减去对(1024 * 1024)求余的结果作为新的基地址,
230 | 然后分配内存的长度增加(1024 * 1024),实现的代码如下
231 |
232 | ```python
233 |
234 | def get_base_and_len(base, length):
235 | _base = base - (base % (1024 * 1024))
236 | _length = (length / (1024 * 1024) + 1) * 1024 * 1024
237 | return _base, _length
238 |
239 | ```
240 |
241 | 算出起始地址之后使用unicorn的`mem_map`方法分配内存即可
242 |
243 | ### 模拟执行,patch程序
244 |
245 | 模拟执行这里也比较简单,直接调用unicorn的`emu_start`方法,然后传入函数的起始地址即可开始
246 | 模拟执行,模拟执行完成之后将data段读出来,模拟执行下一个函数的时候使用这个data加载到内存中。
247 | 这样所有的`.datadiv_decode`函数执行之后data段就被还原了。将还原的data段用ida的patch去
248 | 修改掉原始的data,这个时候你看到的字符串就是原始的字符串了。
249 |
250 | 运行脚本前效果如下:
251 |
252 | 
253 |
254 | 运行脚本之后效果如下:
255 |
256 | 
257 |
258 | 
259 |
260 | 现在脚本已经可以自动重新解析data段,并且会给string的所有引用处加上注释,效果大概可以参考[http://iosre.com/t/armariris-ida/15071](http://iosre.com/t/armariris-ida/15071)
261 |
262 | 
263 |
264 | 
265 |
266 |
267 | ### 完整代码以及示例二进制文件
268 |
269 | 代码以及二进制文件存放在[https://github.com/smartdone/re_scripts/tree/master/ida](https://github.com/smartdone/re_scripts/tree/master/ida)
270 | 其中Armariris_string_obfuscation_bypass.py是ida用来还原Armariris混淆过的字符串的插件。sample里面的test_linux_x86_64和test_macos_x86_64是示例二进制文件。
271 |
272 | > 如果本文说的有错误的地方,请及时指正,谢谢。
273 |
274 |
--------------------------------------------------------------------------------
/ida/Armariris_string_obfuscation_bypass.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python2
2 | # -*- coding: utf-8 -*-
3 |
4 | # @Author: smartdone
5 | # @Date: 2019-07-01 11:00
6 |
7 | import idaapi
8 | import idautils
9 | import idc
10 |
11 | from Simulator import Simulator
12 |
13 | sim = Simulator()
14 | for func in idautils.Functions():
15 | func_name = idc.GetFunctionName(func)
16 | func_data = idaapi.get_func(func)
17 | start = func_data.start_ea
18 | end = func_data.end_ea
19 | # print(func_name, hex(start), hex(end))
20 | if "datadiv_decode" in func_name and not ("j_.datadiv_decode" in func_name):
21 | sim.emu_start(start, end)
22 |
23 | sim.patch_segment('data')
24 |
25 | for seg in sim.segments:
26 | if "data" in seg['name']:
27 | # 把data段全部undefined
28 | print("MakeUnknown %s" % seg['name'])
29 | idc.MakeUnknown(seg['start'], seg['end'] - seg['start'], idaapi.DELIT_DELNAMES)
30 | # 调用ida重新解析data段
31 | print("analyze area: 0x%x - 0x%x" % (seg['start'], seg['end']))
32 | idaapi.analyze_area(seg['start'], seg['end'])
33 | # idaapi.clear_strlist()
34 | # idaapi.build_strlist()
35 |
36 | # 查询string的交叉引用,在引用位置添加备注
37 | s = idautils.Strings(False)
38 | s.setup()
39 | for i, str_info in enumerate(s):
40 | if str_info:
41 | # print("%x: len=%d index=%d-> '%s'" % (str_info.ea, str_info.length, i, str(str_info)))
42 | str_cont = str(str_info)
43 | refs = idautils.DataRefsTo(str_info.ea)
44 | for ref in refs:
45 | idc.MakeComm(ref, str_cont)
46 |
--------------------------------------------------------------------------------
/ida/Simulator.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python2
2 | # -*- coding: utf-8 -*-
3 |
4 | # @Author: smartdone
5 | # @Date: 2019-07-01 11:00
6 |
7 | import idaapi
8 | import idc
9 | import idautils
10 | import sys
11 |
12 | sys.path.append('/usr/local/lib/python2.7/site-packages/')
13 |
14 | from unicorn import *
15 | from unicorn.x86_const import *
16 | from unicorn.arm_const import *
17 | from unicorn.arm64_const import *
18 |
19 | IMAGE_BASE = idaapi.get_imagebase()
20 | DEBUG = True
21 |
22 |
23 | def hook_code(uc, address, size, user_data):
24 | instruction = uc.mem_read(address, size)
25 | if instruction == b'\xc3':
26 | uc.emu_stop()
27 |
28 | if address == 0:
29 | uc.emu_stop()
30 |
31 | if address != 0 and address != IMAGE_BASE:
32 | idc.set_color(address, idc.CIC_ITEM, 0xFFB6C1)
33 |
34 | if DEBUG:
35 | _code = idc.GetDisasm(address)
36 | print("0x%016x \t%s" % (address, _code))
37 |
38 |
39 | class Simulator(object):
40 | def __init__(self):
41 | self.segments = []
42 | self.mem_map = []
43 |
44 | self.ph_flag = None
45 | self.ph_id = None
46 |
47 | self.arch = None
48 | self.mode = None
49 |
50 | self.sp = None
51 |
52 | self.stack_base = 0
53 | self.stack_length = 1024 * 1024 * 2
54 |
55 | self.get_segments()
56 | self.get_arch()
57 | self.get_unicorn_mem_pages()
58 |
59 | def get_segments(self):
60 | if len(self.segments) == 0:
61 | for seg in idautils.Segments():
62 | name = idc.SegName(seg)
63 | start = idc.SegStart(seg)
64 | end = idc.SegEnd(seg)
65 | d = idc.GetManyBytes(start, end - start)
66 | d = [ord(item) for item in list(d)]
67 | seg_data = {"name": name, "start": start, "end": end, "data": d}
68 | self.segments.append(seg_data)
69 | return self.segments
70 |
71 | def get_arch(self):
72 | self.ph_id = idaapi.ph.id
73 | self.ph_flag = idaapi.ph.flag
74 |
75 | if self.ph_id == idaapi.PLFM_386 and self.ph_flag & idaapi.PR_USE64:
76 | self.arch = UC_ARCH_X86
77 | self.mode = UC_MODE_64
78 | self.sp = UC_X86_REG_RSP
79 | elif self.ph_id == idaapi.PLFM_386 and self.ph_flag & idaapi.PR_USE32:
80 | self.arch = UC_ARCH_X86
81 | self.mode = UC_MODE_32
82 | self.sp = UC_X86_REG_RSP
83 | elif self.ph_id == idaapi.PLFM_ARM and self.ph_flag & idaapi.PR_USE32:
84 | self.arch = UC_ARCH_ARM
85 | self.mode = UC_MODE_ARM
86 | self.sp = UC_ARM_REG_SP
87 | elif self.ph_id == idaapi.PLFM_ARM and self.ph_flag & idaapi.PR_USE64:
88 | self.arch = UC_ARCH_ARM64
89 | self.mode = UC_MODE_ARM
90 | self.sp = UC_ARM64_REG_SP
91 |
92 | def is_thumb_ea(self, ea):
93 | if self.ph_id == idaapi.PLFM_ARM and not self.ph_flag & idaapi.PR_USE64:
94 | if idaapi.IDA_SDK_VERSION >= 700:
95 | try:
96 | t = idaapi.get_sreg(ea, 20)
97 | except:
98 | t = idaapi.get_sreg(ea, "T")
99 | else:
100 | t = idaapi.get_segreg(ea, 20)
101 | return t is not idaapi.BADSEL and t is not 0
102 | else:
103 | return False
104 |
105 | def emu_start(self, func_start, func_end):
106 | if self.arch == UC_ARCH_ARM:
107 | if self.is_thumb_ea(func_start):
108 | print("thumb mode")
109 | self.mode = UC_MODE_THUMB
110 | mu = Uc(self.arch, self.mode)
111 |
112 | for item in self.mem_map:
113 | Simulator.map_memory(mu, item['start'], item['length'])
114 |
115 | # 给栈分配内存
116 | Simulator.map_memory(mu, self.stack_base, self.stack_length)
117 |
118 | # 写入数据
119 | for item in self.segments:
120 | Simulator.write_memory(mu, item['start'], item['data'])
121 |
122 | # 配置寄存器
123 | mu.reg_write(self.sp, self.stack_base + 1024 * 1024)
124 |
125 | mu.hook_add(UC_HOOK_CODE, hook_code)
126 |
127 | try:
128 | # 开始执行
129 | if self.mode == UC_MODE_THUMB:
130 | mu.emu_start(func_start + 1, func_end)
131 | else:
132 | mu.emu_start(func_start, func_end)
133 | except Exception as e:
134 | print("Err: %s. Execution function failed.(The function address is 0x%x)" % (e, func_start))
135 |
136 | # 读取数据
137 | for item in self.segments:
138 | _data = Simulator.read_memory(mu, item['start'], item['end'])
139 | self.replace_data(item['start'], _data)
140 |
141 | # unmap memory
142 | for item in self.mem_map:
143 | Simulator.unmap_memory(mu, item['start'], item['length'])
144 |
145 | Simulator.unmap_memory(mu, self.stack_base, self.stack_length)
146 |
147 | def replace_data(self, start, data):
148 | for i in range(len(self.segments)):
149 | if self.segments[i]['start'] == start:
150 | self.segments[i]['data'] = data
151 |
152 | @staticmethod
153 | def write_memory(mu, start, data):
154 | if isinstance(data, list):
155 | data = bytearray(data)
156 | mu.mem_write(start, bytes(data))
157 |
158 | @staticmethod
159 | def read_memory(mu, start, end):
160 | _length = end - start
161 | _data = mu.mem_read(start, _length)
162 | return bytearray(_data)
163 |
164 | @staticmethod
165 | def map_memory(mu, start, _length):
166 | mu.mem_map(start, _length)
167 | print("map memory: offset 0x%x, size: 0x%x" % (start, _length))
168 |
169 | @staticmethod
170 | def unmap_memory(mu, start, _length):
171 | mu.mem_unmap(start, _length)
172 | print("unmap memory: offset 0x%x, size: 0x%x" % (start, _length))
173 |
174 | @staticmethod
175 | def get_base_and_len(base, length):
176 | _base = base - (base % (1024 * 1024))
177 | _length = (length / (1024 * 1024) + 1) * 1024 * 1024
178 | return _base, _length
179 |
180 | def get_unicorn_mem_pages(self):
181 | if len(self.segments) == 0:
182 | return None
183 |
184 | if len(self.mem_map) == 0:
185 | seg = None
186 | pages = []
187 | for item in self.segments:
188 | if not seg:
189 | seg = {'start': item['start'], 'end': item['end']}
190 | else:
191 | if item['start'] - seg['end'] > (1024 * 1024 * 2):
192 | pages.append(seg)
193 | seg = {'start': item['start'], 'end': item['end']}
194 | else:
195 | seg['end'] = item['end']
196 | pages.append(seg)
197 |
198 | for item in pages:
199 | start, length = Simulator.get_base_and_len(item['start'], item['end'] - item['start'])
200 | self.mem_map.append({"start": start, "length": length})
201 |
202 | for item in self.mem_map:
203 | if self.stack_base < item['start'] + item['length']:
204 | self.stack_base = item['start'] + item['length']
205 |
206 | return self.mem_map
207 |
208 | def patch_segment(self, seg_name):
209 | for seg in self.segments:
210 | if seg_name in seg['name']:
211 | print("patch %s: 0x%x - 0x%x" %(seg['name'], seg['start'], seg['end']))
212 | for i in range(len(seg['data'])):
213 | idc.patch_byte(seg['start'] + i, seg['data'][i])
214 |
--------------------------------------------------------------------------------
/ida/img/after1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smartdone/re_scripts/275d21ea189cae5eac18a5f3b0c1875d4d0cf01b/ida/img/after1.png
--------------------------------------------------------------------------------
/ida/img/after2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smartdone/re_scripts/275d21ea189cae5eac18a5f3b0c1875d4d0cf01b/ida/img/after2.png
--------------------------------------------------------------------------------
/ida/img/after3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smartdone/re_scripts/275d21ea189cae5eac18a5f3b0c1875d4d0cf01b/ida/img/after3.png
--------------------------------------------------------------------------------
/ida/img/after4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smartdone/re_scripts/275d21ea189cae5eac18a5f3b0c1875d4d0cf01b/ida/img/after4.png
--------------------------------------------------------------------------------
/ida/img/before.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smartdone/re_scripts/275d21ea189cae5eac18a5f3b0c1875d4d0cf01b/ida/img/before.png
--------------------------------------------------------------------------------
/ida/img/data_orig.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smartdone/re_scripts/275d21ea189cae5eac18a5f3b0c1875d4d0cf01b/ida/img/data_orig.png
--------------------------------------------------------------------------------
/ida/img/xref.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smartdone/re_scripts/275d21ea189cae5eac18a5f3b0c1875d4d0cf01b/ida/img/xref.png
--------------------------------------------------------------------------------
/ida/sample/test.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | void fun(){
4 | printf("test 3333\n");
5 | }
6 |
7 | int main(int argc, char *argv[]) {
8 | printf("test 1111\n");
9 | printf("test 2222\n");
10 | fun();
11 | return 0;
12 | }
13 |
--------------------------------------------------------------------------------
/ida/sample/test_linux_x86_64:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smartdone/re_scripts/275d21ea189cae5eac18a5f3b0c1875d4d0cf01b/ida/sample/test_linux_x86_64
--------------------------------------------------------------------------------
/ida/sample/test_linux_x86_64_fla:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smartdone/re_scripts/275d21ea189cae5eac18a5f3b0c1875d4d0cf01b/ida/sample/test_linux_x86_64_fla
--------------------------------------------------------------------------------
/ida/sample/test_macos_x86_64:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smartdone/re_scripts/275d21ea189cae5eac18a5f3b0c1875d4d0cf01b/ida/sample/test_macos_x86_64
--------------------------------------------------------------------------------
/jeb/FridaCodeGenerator.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 | # @Author: smartdone
5 | # @Date: 2019-06-05 18:23
6 |
7 | # -*- coding: utf-8 -*-
8 |
9 | import sys
10 |
11 | reload(sys)
12 | sys.setdefaultencoding('utf8')
13 |
14 | from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext
15 | from com.pnfsoftware.jeb.core import RuntimeProjectUtil
16 | from com.pnfsoftware.jeb.core.units.code.android import IDexUnit
17 | from com.pnfsoftware.jeb.core.util import DecompilerHelper
18 | from com.pnfsoftware.jeb.client.api import IconType, ButtonGroupType
19 | import os
20 |
21 | # hook_template = """
22 | # if(Java.available) {{
23 | # Java.perform(function(){{
24 | # var application = Java.use("android.app.Application");
25 | #
26 | # application.attach.overload('android.content.Context').implementation = function(context) {{
27 | # var result = this.attach(context); // 先执行原来的attach方法
28 | # var classloader = context.getClassLoader(); // 获取classloader
29 | # Java.classFactory.loader = classloader;
30 | #
31 | # var {simple_class_name} = Java.classFactory.use("{full_class_name}");
32 | #
33 | # {simple_class_name}.{method_name}.overload({types}).implementation = function({args}) {{
34 | # {log_code}
35 | # }}
36 | #
37 | # return result;
38 | # }}
39 | # }});
40 | # }}
41 | # """
42 |
43 | hook_template = """
44 | if(Java.available) {{
45 | Java.perform(function(){{
46 |
47 | var {simple_class_name} = Java.use("{full_class_name}");
48 |
49 | {simple_class_name}.{method_name}.overload({types}).implementation = function({args}) {{
50 | {log_code}
51 | }}
52 |
53 | }});
54 | }}
55 | """
56 |
57 |
58 | def generate_type_code(types):
59 | types = ["'{0}'".format(t) for t in types]
60 | return ", ".join(types)
61 |
62 |
63 | def generate_args_code(args):
64 | return ", ".join(args)
65 |
66 |
67 | def generate_log_code(types, retval, method_name, class_name, args):
68 | log_code = ""
69 | i = 0
70 | for _type in types:
71 | log_code += ' console.log("{class_name}->{method_name} (argType: {_type}): " + {arg});\n'.format(
72 | class_name=class_name, method_name=method_name, _type=_type, arg=args[i])
73 | i += 1
74 |
75 | if retval != "void":
76 | log_code += ' var retval = this.{method_name}({args})\n'.format(method_name=method_name,
77 | args=", ".join(args))
78 | log_code += ' console.log("{class_name}->{method_name} (retType: {_type}): " + retval)\n'.format(
79 | class_name=class_name, method_name=method_name, _type=retval)
80 | log_code += ' return retval;\n'
81 | else:
82 | log_code += ' this.{method_name}({args});\n'.format(method_name=method_name, args=", ".join(args))
83 |
84 | return log_code
85 |
86 |
87 | class JavaMethod(object):
88 | def __init__(self):
89 | self.class_name = None
90 | self.name = None
91 | self.arg = []
92 | self.retType = None
93 |
94 | def get_parameters(self):
95 | return self.arg
96 |
97 | def get_return_type(self):
98 | return self.retType
99 |
100 | def get_name(self):
101 | return self.name
102 |
103 | def get_class_name(self):
104 | return self.class_name
105 |
106 | def __str__(self):
107 | return "name: %s, args: %s, return type: %s" % (self.name, self.arg, self.retType)
108 |
109 |
110 | class FridaCodeGenerator(IScript):
111 |
112 | @staticmethod
113 | def to_canonical_name(dalvik_name):
114 | dalvik_name = dalvik_name.replace('/', '.')
115 |
116 | type_name = {
117 | 'C': "char",
118 | 'I': "int",
119 | 'B': "byte",
120 | 'Z': "boolean",
121 | 'F': "float",
122 | 'D': "double",
123 | 'S': "short",
124 | 'J': "long",
125 | 'V': "void",
126 | 'L': dalvik_name[1:-1],
127 | '[': dalvik_name
128 | }
129 |
130 | return type_name[dalvik_name[0]]
131 |
132 | def run(self, ctx):
133 | self.keys = {}
134 |
135 | engctx = ctx.getEnginesContext()
136 | if not engctx:
137 | print('Back-end engines not initialized')
138 | return
139 |
140 | projects = engctx.getProjects()
141 | if not projects:
142 | print('There is no opened project')
143 | return
144 |
145 | project = projects[0] # Get current project(IRuntimeProject)
146 | print('Decompiling code units of %s...' % project)
147 |
148 | self.dexunit = RuntimeProjectUtil.findUnitsByType(project, IDexUnit, False)[
149 | 0] # Get dex context, needs >=V2.2.1
150 | try:
151 | self.current_unit = ctx.getFocusedView().getActiveFragment().getUnit() # Get current Source Tab in Focus
152 | # java_class = self.current_unit.getClassElement() # needs >V2.1.4
153 | current_addr = ctx.getFocusedView().getActiveFragment().getActiveAddress() # needs 2.1.4
154 | if "(" in current_addr:
155 | current_addr = current_addr.split("+")[0]
156 | print("current function: " + current_addr)
157 | m = FridaCodeGenerator.get_decompiled_method(self.dexunit, current_addr)
158 |
159 | method_name = m.get_name()
160 | class_name = FridaCodeGenerator.to_canonical_name(m.get_class_name())
161 |
162 | return_type = FridaCodeGenerator.to_canonical_name(str(m.get_return_type()))
163 |
164 | if method_name == "":
165 | return
166 |
167 | args = []
168 | for item in range(len(m.get_parameters())):
169 | # print(item.getIdentifier().getName())
170 | args.append(str("arg_%d" % item))
171 |
172 | types = [FridaCodeGenerator.to_canonical_name(param) for param in m.get_parameters()]
173 |
174 | simple_class_name = class_name.split('.')[-1].replace("$", "_")
175 |
176 | if method_name == "": method_name = "$init"
177 |
178 | type_code = generate_type_code(types)
179 | args_code = generate_args_code(args)
180 | log_code = generate_log_code(types, return_type, method_name, simple_class_name,
181 | args)
182 |
183 | hook_code = hook_template.format(simple_class_name=simple_class_name,
184 | full_class_name=class_name,
185 | method_name=method_name,
186 | types=type_code,
187 | args=args_code,
188 | log_code=log_code)
189 | print(hook_code)
190 |
191 | if not isinstance(ctx, IGraphicalClientContext):
192 | print('This script must be run within a graphical client')
193 | return
194 | file_name = 'hook_{class_name}.js'.format(class_name=simple_class_name)
195 | file_path = os.path.join(os.environ['HOME'], file_name)
196 |
197 | value = ctx.displayQuestionBox('Input',
198 | 'Enter the hook script save path(Save to directory {file_path} by default)'
199 | .format(file_path=file_path), file_path)
200 | # print(value)
201 | # with open(value)
202 | if value:
203 | file_path = value
204 | try:
205 | with open(file_path, "w+") as f:
206 | f.write(hook_code)
207 | f.flush()
208 | ctx.displayMessageBox('Information', 'Frida script save to \n{}'.format(file_path),
209 | IconType.INFORMATION,
210 | ButtonGroupType.OK)
211 | except Exception as e:
212 | print(e)
213 |
214 | else:
215 | print("Place the cursor in the function you want to generate the Frida code, then run the script")
216 | except Exception as e:
217 | print(e)
218 | print("Place the cursor in the function you want to generate the Frida code, then run the script")
219 |
220 | @staticmethod
221 | def get_decompiled_method(dex, msig):
222 | method_info = JavaMethod()
223 | infos = str(msig).split("->")
224 | if len(infos) == 2:
225 | method_info.class_name = infos[0]
226 | if len(infos[1].split("(")) == 2:
227 | method_info.name = infos[1].split("(")[0]
228 | if len(infos[1].split(")")) == 2:
229 | method_info.retType = infos[1].split(")")[1]
230 | if len(infos[1].split("(")) == 2 and len(infos[1].split(")")) == 2:
231 | args = infos[1].split("(")[-1].split(")")[0]
232 | while args:
233 | if args[0] in ['C', 'I', 'B', 'Z', 'F', 'D', 'S', 'J', 'V']:
234 | method_info.arg.append(str(args[0]))
235 | args = args[1:]
236 | elif args[0] == '[':
237 | if args[1] == 'L':
238 | offset = args.find(";")
239 | method_info.arg.append(str(args[0:offset + 1]))
240 | args = args[offset + 1:]
241 | else:
242 | method_info.arg.append(str(args[0:2]))
243 | args = args[2:]
244 | elif args[0] == 'L':
245 | offset = args.find(";")
246 | method_info.arg.append(str(args[0:offset + 1]))
247 | args = args[offset + 1:]
248 | print(method_info)
249 |
250 | return method_info
251 |
--------------------------------------------------------------------------------