├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── airfps.go
├── bindata.go
├── fps.c
├── inject.c
├── inject.h
├── jni
├── Android.mk
├── Application.mk
└── myfps.c
├── make.bash
├── run.bat
├── study-diary
├── ELF.md
├── Makefile
├── README.md
├── RESOURCES.md
├── bar.c
├── counter.c
├── fifo.c
├── foo.c
├── foo.h
├── hookfoo.c
├── hookfoo.h
├── inject.c
├── inject.h
├── main.c
├── max.s
├── myinject
│ ├── Makefile
│ ├── dump.c
│ ├── dump.h
│ ├── run.js
│ └── trap_counter.c
├── myreadelf.c
└── push_file.sh
├── utils.c
└── utils.h
/.gitignore:
--------------------------------------------------------------------------------
1 | # Object files
2 | *.o
3 | *.ko
4 | *.obj
5 | *.elf
6 |
7 | # Precompiled Headers
8 | *.gch
9 | *.pch
10 |
11 | # Libraries
12 | *.lib
13 | *.a
14 | *.la
15 | *.lo
16 |
17 | # Shared objects (inc. Windows DLLs)
18 | *.dll
19 | #*.so
20 | *.so.*
21 | *.dylib
22 |
23 | # Executables
24 | *.exe
25 | *.out
26 | *.app
27 | *.i*86
28 | *.x86_64
29 | *.hex
30 | *.so
31 | obj
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 | {description}
294 | Copyright (C) {year} {fullname}
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
341 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | #
2 | # Makefile
3 | # hzsunshx, 2015-02-10 09:45
4 | #
5 |
6 | OBJS = libfps.so airfps
7 |
8 | all: $(OBJS)
9 | @echo "Makefile needs your attention"
10 |
11 |
12 | libfps.so: fps.c utils.c
13 | gcc -Werror -fpic -c -llog -landroid -lEGL -lGLESv2 fps.c
14 | gcc -shared -o $@ fps.o
15 | adb push libfps.so /data/local/tmp/
16 |
17 | airfps: airfps.go utils.c inject.c
18 | go build
19 | adb push airfps /data/local/tmp
20 | adb shell chmod 777 /data/local/tmp/airfps
21 |
22 | install: clean all
23 |
24 | clean:
25 | rm -f $(OBJS)
26 |
27 | # vim:ft=make
28 | #
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## airfps
2 | Get FPS(frame per second) on android. Root privileges required.
3 |
4 | ## Still developing
5 | Not working for now.
6 |
7 | 目前遇到了点瓶颈。只hook函数eglSwapBuffers看来还不够。(忽略了点细节,只要在adb开发选项中把硬件刷新关了就可以了)
8 |
9 | 需要从基础工作开始研究了。
10 |
11 | 1. [ELF格式解析](study-diary/ELF.md)
12 | 2. [ptrace学习](study-diary/ptrace.md)
13 | 3. [inject函数的编写](study-diary/inject.md)
14 | 4. [注入1+1 != 2](study-diary/hook.md)
15 |
16 | ## 编译指南
17 | 代码中用了Golang,Android-NDK. [Golang-mobile](https://github.com/golang/mobile)的官网推荐用docker
18 |
19 | docker pull golang/mobile
20 | docker run -v $GOPATH/src:/src golang/mobile /bin/bash -c 'cd /src/your/project && ./make.bash'
21 |
22 | 不过手工搭建个环境也不复杂,参考着[Dockerfile](https://github.com/golang/mobile/blob/master/Dockerfile)中的描述一点点的来就可以。
23 | Golang需要1.4+的版本,直接下载个就好
24 |
25 | 不要忘了把CGO打开,我有时就会忘了`export CGO_ENABLED=1`
26 |
27 | ## 感谢
28 | * [未完成的项目realfps](https://github.com/cuitteam/RealFPS)
29 | * [fpsmeter反编译分析](http://blog.csdn.net/freshui/article/details/9245511#comments)
30 | * [jinzhuojun的CSDN-如何写出一个显示fps的东西](http://blog.csdn.net/jinzhuojun/article/details/10428435)
31 | * [hook的原理](http://bbs.pediy.com/showthread.php?t=157419)
32 |
33 | ## Demo
34 |
35 | function `__android_log_print` should not appear in `hook_entry` function.
36 |
37 | cd study-diary
38 | make install
39 | cd ..
40 | make
41 |
42 | adb shell
43 | cd /data/local/tmp/
44 | export LD_LIBRARY_PATH=$PWD
45 | ./bar
46 |
47 | adb shell
48 | ./airfps -l libfps.so -pname ./bar # hack
49 | ./airfps -l libfps.so -pname ./bar # unhack
50 | ./airfps -l libfps.so -pname ./bar # hack
51 |
52 | su
53 | cd /data/local/tmp
54 | ./airfps -l libfps.so -rf eglSwapBuffers -rl /system/lib/libsurfaceflinger.so
55 |
56 | adb shell
57 | tail -f /data/local/tmp/log.txt
58 |
59 | ## LICENSE
60 | Under [GPL v2](LICENSE)
61 |
--------------------------------------------------------------------------------
/airfps.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | // #cgo CFLAGS: -DGOLANG=1
4 | // #cgo LDFLAGS: -lm -llog -landroid -lEGL -lGLESv2
5 | // #include "inject.h"
6 | // #include "utils.h"
7 | import "C"
8 | import (
9 | "flag"
10 | "fmt"
11 | "log"
12 | "os"
13 | "os/signal"
14 | "path/filepath"
15 | "unsafe"
16 | )
17 |
18 | var (
19 | pid = flag.Int("p", 0, "pid of process to inject")
20 | pname = flag.String("pname", "/system/bin/surfaceflinger", "process name(ignore if pid set)")
21 | libpath = flag.String("l", "", "libpath to inject")
22 | hookfunc = flag.String("n", "hook_entry", "hook init function")
23 |
24 | repllib = flag.String("rl", "/data/local/tmp/libfoo.so", "hooked library")
25 | replfunc = flag.String("rf", "eglSwapBuffers", "hooked function name")
26 | )
27 |
28 | func SurfacePid() int {
29 | if *pid > 0 {
30 | return *pid
31 | } else {
32 | sfpid := int(C.find_pid_of(C.CString(*pname)))
33 | if sfpid == -1 {
34 | log.Fatalf("No such process: %s", *pname)
35 | }
36 | return sfpid
37 | }
38 | }
39 |
40 | const (
41 | LIBMYFPS = "libs/armeabi/libmyfps.so"
42 | LIBSF = "/system/lib/libsurfaceflinger.so"
43 | )
44 |
45 | func TrapSignal(sigs ...os.Signal) {
46 | ch := make(chan os.Signal, 1)
47 | for _, sig := range sigs {
48 | signal.Notify(ch, sig)
49 | }
50 | go func() {
51 | for sig := range ch {
52 | fmt.Println("Receive signal: ", sig)
53 | }
54 | }()
55 | }
56 |
57 | func main() {
58 | flag.Parse()
59 | sfpid := SurfacePid()
60 | fmt.Printf("[%s] pid: %d\n", *pname, sfpid)
61 |
62 | dir := filepath.Join(filepath.Dir(os.Args[0]), "_tmp")
63 | dir, _ = filepath.Abs(dir)
64 | var err error
65 |
66 | if *libpath == "" {
67 | if err = RestoreAsset(dir, LIBMYFPS); err != nil {
68 | log.Fatal(err)
69 | }
70 | *libpath = filepath.Join(dir, LIBMYFPS)
71 | } else {
72 | *libpath, _ = filepath.Abs(*libpath)
73 | }
74 |
75 | mbase := C.get_module_base(C.pid_t(sfpid), C.CString(*repllib))
76 | if mbase == C.long(0) {
77 | log.Fatal("maybe you need root previlage")
78 | }
79 | log.Printf("module base: 0x%x\n", mbase)
80 |
81 | func_addr := C.find_got_entry_address(C.pid_t(sfpid),
82 | C.CString(*repllib), C.CString(*replfunc))
83 | log.Printf("func address: 0x%x\n", func_addr)
84 | arg := fmt.Sprintf("0x%x\n", func_addr)
85 | //arg := "abcdefg"
86 |
87 | C.inject_remote_process(C.pid_t(sfpid), C.CString(*libpath),
88 | C.CString(*hookfunc), unsafe.Pointer(C.CString(arg)), C.size_t(len(arg)))
89 | }
90 |
--------------------------------------------------------------------------------
/bindata.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "compress/gzip"
6 | "fmt"
7 | "io"
8 | "strings"
9 | "os"
10 | "time"
11 | "io/ioutil"
12 | "path"
13 | "path/filepath"
14 | )
15 |
16 | func bindata_read(data []byte, name string) ([]byte, error) {
17 | gz, err := gzip.NewReader(bytes.NewBuffer(data))
18 | if err != nil {
19 | return nil, fmt.Errorf("Read %q: %v", name, err)
20 | }
21 |
22 | var buf bytes.Buffer
23 | _, err = io.Copy(&buf, gz)
24 | gz.Close()
25 |
26 | if err != nil {
27 | return nil, fmt.Errorf("Read %q: %v", name, err)
28 | }
29 |
30 | return buf.Bytes(), nil
31 | }
32 |
33 | type asset struct {
34 | bytes []byte
35 | info os.FileInfo
36 | }
37 |
38 | type bindata_file_info struct {
39 | name string
40 | size int64
41 | mode os.FileMode
42 | modTime time.Time
43 | }
44 |
45 | func (fi bindata_file_info) Name() string {
46 | return fi.name
47 | }
48 | func (fi bindata_file_info) Size() int64 {
49 | return fi.size
50 | }
51 | func (fi bindata_file_info) Mode() os.FileMode {
52 | return fi.mode
53 | }
54 | func (fi bindata_file_info) ModTime() time.Time {
55 | return fi.modTime
56 | }
57 | func (fi bindata_file_info) IsDir() bool {
58 | return false
59 | }
60 | func (fi bindata_file_info) Sys() interface{} {
61 | return nil
62 | }
63 |
64 | var _libs_armeabi_libmyfps_so = []byte("\x1f\x8b\x08\x00\x00\x09\x6e\x88\x00\xff\xec\xbc\x7d\x7c\x54\xd5\xb5\x37\xbe\xcf\x99\x99\x64\x12\x86\x64\x26\x09\x10\x63\xaa\x27\x24\x4a\x44\x88\x93\xf0\x52\x04\xd4\x01\x82\xbc\x05\x1d\x08\x62\x4a\xbd\x26\x43\x32\x79\xa9\x21\x19\x67\x26\x0a\x5c\x2d\x93\x04\xc4\x4b\x09\x0c\x04\x6f\x51\xa4\x1e\x14\x2d\xb6\xfc\x6e\x83\xd5\xde\x7e\xfa\x83\x7b\x03\x5a\x6b\xbd\xb6\x4f\xa8\xf6\x63\x5f\xbc\xcf\x3d\xe7\x0c\xd3\x46\x90\x18\xaf\x5a\xb4\xbe\xcc\xf3\x5d\xe7\xec\x49\x4e\xc6\x60\xfb\xdc\x3f\x9e\xbf\x1c\x5d\xac\xfd\xdd\xaf\x6b\xaf\xbd\xd6\xda\x7b\x9f\xcc\x99\x6d\x4b\xab\x6e\x15\x04\x81\x25\x3f\x16\x56\xca\x92\x68\x36\xd1\x1a\x3d\x69\x9b\xcd\x24\x96\x8e\xb2\x49\x2c\x8f\xa5\x25\xcb\x38\x29\x9c\xac\x9c\x46\x7b\x33\x3e\x8e\xa5\x06\xd9\x08\x38\x8d\xf2\x92\xdb\x40\x77\x1b\x74\x8e\x32\x44\xa6\xf7\x4b\xe5\x48\x32\x37\xca\xdd\x77\x1b\x94\x2f\x18\x94\xc6\xfb\x5f\x13\x0f\x37\xb0\x71\x3e\x69\x9c\xa3\x6a\xc0\xb9\x18\x5d\x71\xb2\x0b\x06\x25\xe5\x5b\x8b\xf6\xe6\xf1\xdf\x12\x0c\x4a\xf6\x9f\xfa\x31\xcf\x27\xc7\x10\x71\x0c\xfe\x1a\xb5\xbb\x8e\x41\x43\x84\xd3\x19\x92\xac\x0f\xff\x1c\x14\x0c\x3c\x17\x78\xf9\xf5\x8c\xd5\x71\xbc\x04\xb8\x75\x1a\x63\x51\x8e\xbd\xc0\xa7\xae\xc0\x5c\x79\xfb\x7f\x00\x1f\x9c\x03\x99\x39\x0e\x83\x47\x67\x32\x68\xde\xc0\xdd\xe0\x8f\x17\x31\xe6\xe1\x78\x2f\xb8\x17\xb8\x84\xe3\xc7\x09\x4f\xc5\x18\xa2\x81\x9f\x05\x3e\x89\x42\x2b\xc7\x3f\x01\xee\xbf\x96\x31\x99\xd7\x3f\x03\xfe\x1e\xc6\x5f\xce\xe5\xf9\x6d\xca\xfc\xb4\x14\xfc\x17\xd2\x41\x9d\xa1\x2b\x17\xb4\xf1\x19\x78\x61\x9d\x21\x2f\x61\xd2\x75\x9e\x09\xe7\x00\x3b\x4c\xf5\x0b\x68\x3d\x4c\xf8\x2a\x5a\x9b\x11\x9c\xcd\xca\x85\xb1\xe3\x79\x80\x6f\xbc\x12\x32\x73\x79\xbd\x29\xe5\xdf\x48\xc1\xb5\x29\xb8\x31\x05\x6f\x4a\xc1\x1d\x29\xf8\x1f\x53\x70\x57\x0a\xee\x35\x61\x17\xe8\x69\x60\x85\xaf\xd7\x54\xc8\x77\x12\xf8\x63\xc8\xfb\x0e\xd7\xe7\x40\x4a\xfb\x37\x53\xf0\x5b\x29\x58\x4d\xc1\x7f\x4e\xc1\x43\x29\xf8\xc3\x14\xfc\x79\x0a\xb6\x88\x63\x71\x86\x38\x56\xfe\x49\xc0\x71\x18\x71\x03\x33\xe4\x95\x80\x57\x5c\xc5\x74\xbb\x24\x3c\x13\xf8\xe0\x55\xc9\xf6\xe9\x6c\x01\xb0\x0c\xdc\xc7\xed\xa9\x32\x05\xaf\x01\x3f\x0a\x7c\x9c\xe3\x0d\x29\xb8\x49\x1c\x6b\xaf\x1d\xc0\x61\xe0\x52\x8e\x3b\x53\x70\x8f\x38\xd6\xde\x0f\x01\xbf\x05\x1c\xe6\xf8\xd9\x14\xfc\x13\xe0\x61\xe0\x3a\x8e\xcf\xa4\xe0\x5f\x8b\x63\xfd\xe3\x4d\x71\xac\x7f\x28\xe2\x58\xff\xb8\x08\x5c\x07\x7f\x7d\x9c\xaf\xe7\x27\xc0\xcd\xc0\x07\x38\xb6\x5a\xc6\x62\x97\x65\xac\x7f\x17\x00\x7f\x5a\x3a\x3a\x9f\xd2\x14\x3c\xc7\x32\x36\x7e\x2c\x02\x2e\x01\xde\xcd\xf1\xed\x29\xf8\x2e\xe0\xd9\xc0\x0f\x70\xdc\x9c\x82\xc3\x96\xb1\xf1\xe7\x41\xcb\xd8\xf8\xf3\x10\xf0\xb1\x19\xa3\xfa\xda\x93\x82\xff\x99\xda\x03\xb7\x72\xfc\xbd\x14\xfc\x0c\xf0\x4f\x67\x8c\xda\xcb\x8f\x52\xf0\xbf\xa6\xe0\x7f\x07\x2e\x9c\x39\xba\xde\x03\xc0\x33\x4c\xf1\x4c\x49\xc1\xc3\xc0\x1e\x13\x4e\x00\xaf\x33\xe1\x2c\x04\x8a\x66\x13\xfe\x1a\xf0\x03\x26\x3c\xc3\x14\xc4\xb1\xcc\xac\x12\xf8\x77\x26\xff\xfc\x26\xf0\xd1\x05\xa3\xf6\x7c\x1f\xf0\x01\x13\xde\x0b\xfc\xbc\x09\xff\x18\xf8\x25\x13\xfe\x35\x70\xd5\xc2\x51\xfc\xb6\x75\xac\x7f\x0d\x03\x47\x16\x8c\xca\x23\xda\xc6\x96\xdb\x81\x9b\xcb\x21\x17\x2f\xcf\x07\x7e\xc3\x84\x4b\x4d\xf5\xc9\x26\x16\xa6\xe0\xd5\xc0\x91\x9b\x20\xb3\xc5\xa8\xbf\x31\xa5\x7c\x33\xf0\x30\xe6\x5b\xc3\xcb\x1f\x06\x3e\x6e\x92\x5f\x06\x7e\xdc\x84\x7f\x02\x7c\xd2\x84\xdf\x04\xb6\x9b\xe6\xf7\x2e\x70\x9d\x09\xa7\x63\xa3\x5c\x7e\xe3\xe8\xfe\x34\x19\xd8\x0b\x7c\x94\xe3\x0a\xe0\x0f\x80\x0b\x39\xae\x02\x76\xce\x1f\xb5\x9f\x3a\xe0\x07\xe6\x8f\xda\xfb\x36\xe0\xc7\xe7\x1b\xf1\x9f\x70\x14\x58\x36\x95\x1f\x03\xee\x9f\x3f\x1a\xff\xff\x3d\x6d\xac\x7e\x5f\x07\x0e\x2f\x1c\xb5\xaf\x77\x52\xb0\x25\x1d\xbe\x60\xc2\x79\x29\xb8\x18\xf8\xa4\x09\xdf\x92\x82\xbf\x01\xac\x98\x70\x20\x05\xef\x00\xb6\xde\x34\x8a\x1f\x4d\xc1\x3f\x04\xce\x5b\x34\x1a\xbf\xfe\x0d\x78\x1d\xf0\x0c\x8e\x7f\x0b\x1c\x01\x9e\xc7\xf1\x87\xc0\x3f\x5d\x34\xaa\x5f\x07\x12\xfd\x26\x5c\x6c\x37\xf6\x4f\xfa\x38\xd9\x7b\x89\xe9\x29\x78\x2e\x70\x89\x09\xb3\xda\xda\xfa\xcd\xbe\xda\xc6\x96\x36\x5f\x6b\xcb\x56\x3f\x87\xbe\xb0\x7f\x73\x4b\x18\xc0\xe7\xf7\x6d\x6c\xa9\x6d\xa9\x68\x1c\x49\x37\x6e\xea\x68\x1d\x05\x0d\x2d\xf7\x8d\x02\x5f\x43\x03\x0b\xb7\x6c\xf2\xd7\xfa\x5b\x7d\x81\x90\xbf\x61\xa4\xa4\xa3\xed\xfe\x96\xb6\x86\xda\xfa\x40\xa0\x36\x10\x74\x8f\x36\xa8\xdf\x14\x68\x1a\x1d\xa5\xb1\xc2\xd4\xa2\xc5\x04\x1a\xcc\x43\x36\x98\x87\x6c\x80\x60\x6d\xfe\xfb\x6b\xfd\x4d\xad\xd5\xf7\xfb\x02\x8b\x3b\x1a\x1b\xfd\xc1\x10\x6b\xf2\x87\x49\x8e\xf6\xc6\x06\xdf\x16\xaa\xdb\xd6\x10\x6c\x6f\x69\xa8\x6d\x6d\x6f\xc2\xf8\x2d\x6d\x61\xd6\x18\xf4\x6d\xf2\x87\x58\x28\xec\x0b\x86\x6b\xa9\x2a\xab\xef\x08\x06\x8d\x54\x52\xf8\xc6\x40\x88\xb5\xb7\x36\xa4\xf6\xfd\xc5\xa1\x6a\x37\xb5\x37\x74\xb4\xfa\x6b\x37\xfa\x42\x7e\x16\x6a\xd3\x47\x68\x64\x8d\xed\x01\x7f\x1b\x46\x08\xe2\x7f\x62\xe1\xf6\x7b\x0c\x86\xb9\x34\xa2\x55\x88\x35\xd6\xb7\xb6\x87\x48\xe3\x10\xa3\xfe\x9e\xda\xfa\xe6\x7b\xa0\xc2\x96\xd6\x31\x19\x4d\x1d\xbe\xe0\x65\xf4\x58\xce\x9a\xdb\xdb\xef\x19\x67\xea\x81\x96\x06\xa6\x0f\x1e\xf4\xfb\x1a\x58\x6b\xc8\xef\xbf\x87\x6d\xf2\xb5\xb6\xb6\xd7\x93\x00\xd0\x39\xdb\x14\x08\xb6\x87\xfd\xf5\xa4\x07\x3f\xa6\xce\xc5\x08\xf8\x9a\xfc\xb5\x21\x32\x02\xa3\xe3\xb6\x70\x70\x8b\x91\x0c\xfa\x43\xe1\xf6\xa0\x7f\x54\xed\xc1\x50\xc7\x46\x92\xb3\x63\x63\x43\xe3\xac\xd1\x6c\x23\x17\x66\x30\x26\x97\xcc\xa2\xb6\xb6\xb1\xb5\xdd\x17\xee\x68\x0b\xb5\x34\x34\x26\x11\x4f\x27\x6d\x8c\x6a\xf9\x37\x87\xfd\x6d\x0d\xa1\xc6\x86\xc6\x8a\xd1\x36\x0d\xe6\x7a\x1d\xad\x15\x23\xdd\x8d\x29\x30\xf2\x61\x2a\xc6\xe0\x30\x13\x23\x11\x0e\x76\xb4\xd5\x37\x34\x86\xf4\x1e\xb9\xa5\x8d\xca\x1f\x32\x49\xda\x38\x22\xbf\x91\x3b\x22\x72\xa8\xd1\x6c\x98\x26\xf9\x43\x8d\x66\x29\x43\x63\xa4\x6c\x1c\x95\x32\x64\x96\xb2\xd1\x90\x32\x94\x94\xd2\x48\x34\x85\x0d\x01\x9b\xfc\x06\x6f\xe5\xb8\x95\x63\x2c\x9b\x91\x68\xe3\x19\xfe\x7b\xcd\x33\xaa\x6f\xa4\x85\x6d\xf5\x9b\x32\x80\xfd\xf7\x8e\xc5\xa6\xf2\x94\x62\xbd\x34\x3c\x16\x8e\xad\xdc\x44\xb0\xa9\xad\xa3\xf6\x0e\xc3\x08\x6f\xa5\x7f\x10\x25\x1a\x36\x8f\x6f\x9e\x15\x63\xab\xaf\x35\x4c\xa8\x76\xfd\xad\xde\xda\xca\xcb\x16\x7d\x49\x9b\xda\xf2\xb9\xb5\xe1\xf6\xda\x59\xe5\xe3\xd7\xb9\x73\xf5\xea\x9a\xcb\xf4\x4b\x45\x4b\xd8\x26\xff\xa6\xfa\xc0\x16\xc6\x4d\xb9\xb6\x9e\xfe\x09\xfa\x9b\x42\xcc\xb7\xb1\x3d\x88\x99\xf3\x46\xeb\xd7\x56\xd7\x2e\xf3\x8f\xc5\xd5\x84\xf5\xb8\xb8\xd1\xdf\xd4\xd2\x56\x5b\xdf\xea\xf7\xb5\x75\x04\x78\x5e\x78\x4b\xc0\x5f\xbb\xc9\x17\xae\x6f\xe6\xc3\x73\x35\xf8\x37\xfb\xeb\x3b\xc2\xc9\x88\x5a\x0f\x07\x44\x89\x7f\x73\x00\x7e\x47\xa1\xd1\xd4\xbf\xb7\x3d\x30\x56\xf2\x6a\xdf\x7d\xe3\x6a\x2a\x99\x7f\xb9\xda\x97\xd1\x91\x5e\x61\x1c\x05\x8d\xe4\x2f\x19\x11\x07\x53\x5f\x72\xeb\xa2\x14\x3d\xfa\x5a\x42\xfe\xa5\x9b\xeb\xfd\x81\x70\x4b\x7b\x5b\x8a\x15\xb4\x07\xeb\xfd\x0d\x06\xf8\x82\xf6\x3b\x36\xf9\xc7\xcb\xab\x6d\x0f\x22\x15\x6e\x0e\xb6\xdf\x3f\x32\xee\x92\x76\x32\x38\xd2\x16\xcf\xa8\xf4\x13\xbc\xcc\xa8\x8b\x11\x1e\xc3\x41\x5f\x3d\x75\xff\xc5\x05\xad\xad\xbd\x9c\xe4\x97\x9b\x50\xaa\xcc\xa9\x53\xf8\xdb\xf2\x8f\x53\x52\x3b\xbe\x8e\xc6\x57\xdc\x38\x33\x1b\x67\xae\x26\xeb\xd2\x77\x30\xf3\xb2\xad\x85\x69\xb6\xb7\x55\xd3\x8e\x66\xce\xae\xf2\xb5\x61\x0f\x69\xf2\x57\xc3\xee\x5a\x1a\x5b\xea\x2b\x7d\x61\x9f\xb9\x9c\xf0\x5a\x7f\xeb\x62\xda\xbb\x4c\xd9\xeb\x10\x88\x47\xb2\xfd\x0d\x7a\xa3\xda\x8d\xa1\x50\x6d\xc8\x18\x00\x41\x9a\xb5\xb6\x6c\xc4\x8e\x5a\x16\x6a\xa7\x14\xdf\x63\x39\x5a\xba\xac\x8a\xa7\x96\x55\x2d\xad\xbe\xaf\x82\x83\x50\xb8\xa1\xfe\xfa\xeb\x39\xd8\xc4\x79\x3d\xe7\x0d\xad\xc9\x82\x2d\xd8\x7a\x29\x4d\xcf\x32\x7a\xf8\xd9\x35\x9f\xf3\xbb\x46\x8f\xb3\xfa\xb3\x95\x74\x9e\xfe\x47\x90\x8f\xa7\xaf\x31\xd5\xb9\x87\xf3\x9d\xa0\xfb\x41\x5b\x38\xbe\x81\xf3\x0a\x50\xc4\x54\x9f\xce\x59\x74\xd7\xf9\x27\x36\xf6\x53\xc2\xf9\x9d\x9c\x3f\x08\x72\xf0\xf4\x32\xce\x67\x81\x36\x80\x1e\x06\xdd\xc4\xf3\xbe\x6d\xea\x63\x25\x33\xee\x99\x41\xd0\x0a\x9e\xf7\x75\x66\x3c\xfb\xda\x0a\xfa\x86\xa9\xee\xcd\xa0\x6f\x81\x1e\x02\xad\x05\xd1\x11\x7b\x3d\x28\x97\x97\x2f\xe2\x1c\x57\x37\x56\x0b\xa2\xe7\x42\x7e\x10\x5d\xc5\xef\xe0\x65\x55\x9c\x37\x82\xa6\x83\x3a\x41\x35\xa0\xef\x80\x76\xa5\xcc\x8f\x74\xbd\x1c\xb4\x0d\x74\xad\x29\x3f\x0b\xd4\xc5\xd3\xb8\x26\xeb\xe7\xf6\x26\x8e\xe9\x51\xc1\x66\xd0\x4c\x53\x7d\x7a\x9e\xd5\xce\xbe\xf8\x69\xe5\x9c\xee\xa1\x93\x41\xf7\x81\x76\xf0\xbc\x7b\x41\x01\x66\xdc\x79\x93\x1f\xba\xcf\xde\x06\xba\xda\x94\xe7\x19\xa7\xdf\xbf\xf7\x63\x7e\x4e\x37\xf1\xef\xa8\xef\xe2\x3c\xf5\x99\xdf\xa4\x14\x3c\x81\xf3\x2b\xc6\xe9\xc3\xfe\x77\x8c\x43\x1f\x4b\x0a\xce\xf9\x3b\xda\xd0\xf3\xc0\x3c\x13\xbe\x05\xb4\x98\x8d\x3e\xe7\x4c\x7e\xe8\x8e\x5b\x06\x2a\x00\xdd\x98\x52\xe6\x4c\xc1\xb7\x72\x3e\x0d\x44\x8f\x76\xe9\x59\x64\x25\xcf\x9b\x02\x2a\x06\xd1\x55\x10\xd7\x51\x96\x09\xba\xdb\xd4\x76\x1d\xe7\xe3\x3d\x7c\xcd\x06\xb5\x80\x70\x4d\x63\x9b\x78\xde\x6a\x66\x3c\x43\xaa\x07\x6d\x64\xc6\xb3\x62\xba\xf3\x7c\x13\x74\x1d\xaf\xd3\x01\xaa\x66\xc6\x5d\x6f\x95\xa9\xbf\x36\xd0\x95\xa0\xa5\xa0\x10\x28\xc3\x54\xb6\x1d\x34\x87\xa7\x9b\x41\xb7\x33\xe3\x39\x2e\xc9\xbf\xbc\xd6\xe0\x55\x9c\x7b\x39\x5f\xc7\x79\x0d\xe7\x77\x71\x5e\xc7\x79\x98\xf3\xcd\x9c\x3f\xc0\x79\x84\xf3\xed\x9c\x37\x80\x4f\x42\xa0\x6a\x26\x4e\xcf\x90\x88\x43\xf9\x01\xe2\x08\x5c\x0f\x13\x47\x40\x3a\x08\x3e\x99\x9e\x5b\x11\x87\x61\xca\xc4\xb1\x18\x47\x89\xc3\xf0\x8e\x11\xc7\x82\x1d\x27\x8e\x89\xf6\x11\x87\xe3\x3d\x4f\x1c\x41\xef\xa7\xc4\xe1\xe8\x27\x89\xc3\x41\xfa\x89\x43\x49\x2f\x11\x87\x93\xbe\x42\x1c\x41\xf0\x35\xe2\x70\xe6\x01\xe2\x58\xd4\x37\x88\xc3\xc9\x7e\x47\x1c\x4a\x7e\x8b\x38\x02\x83\x42\x1c\x81\xe4\x1c\x71\x2c\xc2\x20\x71\x38\xf5\x3b\xc4\x21\xf7\x30\x71\x2c\xd4\x07\xc4\x11\x64\x3e\x22\x0e\xe5\x7e\x4a\x1c\x0b\x6b\x55\x66\xc6\xad\xca\x13\xf1\x2c\x65\xaf\x62\x1f\xfe\xb7\xf8\x3a\x0a\x88\x2f\xef\xd5\xac\xaf\xee\xd6\xd6\x5d\x3a\x19\x4f\xa6\xab\x4c\xe9\x4a\x53\x7a\xa1\x29\x3d\xdb\x94\x9e\x61\x4a\x97\x98\xd2\x85\xa6\x74\x9e\x29\xed\x30\xa5\xad\xa6\xf4\xa7\x7f\x19\x4d\x7f\x60\x4a\xbf\x63\x4a\x9f\x33\xa5\xdf\x32\xa5\xdf\x30\xa5\x5f\x33\xa5\x5f\x32\xa5\x4f\x9a\xd2\xcf\x9b\xd2\xc7\x4d\xe9\xa3\xa6\xf4\xe3\xa6\xf4\x01\x53\x7a\x37\xa5\xf5\x8f\x37\x66\xf7\xcc\x7c\xdb\x1e\x39\x65\x77\x27\x6e\x50\xc1\x07\x99\x53\x56\x1d\xd2\x13\x71\x07\x7b\x22\x2e\x4a\x7b\x15\xc6\xf6\x2a\x4f\x27\x12\xe7\xeb\xe0\xb4\x6f\x24\x12\x09\x2b\xf2\x29\xef\x29\xe4\x2d\x47\xde\xb6\x17\x9c\x05\x85\x85\x13\x0b\xd9\xf0\xf7\xde\x4d\x5f\xc1\x86\xff\xfb\xdd\xb4\xc2\xba\x2b\xd9\xf0\xc1\x77\xad\x2b\x84\xe1\x9f\x0c\x0a\x85\x6e\x94\xbd\x34\x64\xed\x0b\x9c\xc2\xee\xa3\xbb\xf9\xf0\x0b\xb7\xdc\x76\xcb\xaa\x9e\xbe\x09\xfb\xbe\x5d\x99\x19\xbd\x54\x73\xd3\xaa\x1b\x9b\x3f\xac\x61\xd7\x65\x9c\x2d\x2d\x4c\x5c\x9a\xfa\xee\xfc\x15\x0b\x56\x5a\xa5\x2d\x95\x5b\x2b\x09\x2d\x68\x66\x45\xc2\xac\x05\x75\x5f\x5f\xf5\x41\x8d\x84\xf2\x9c\x77\x1d\xaf\x59\x7e\x95\xb8\x74\x26\x31\x6b\xd5\xec\x15\xd6\xc2\xf7\x6b\xf2\xeb\x84\xe1\x67\xde\x66\xa5\xd7\x0f\xcc\x6f\x96\x0a\xdd\xab\xd3\xf7\xb3\xe1\xa3\x17\xec\xd1\x8c\x7d\xe9\x07\xd9\xf0\xe6\x0b\x53\x57\x15\xad\x64\xc3\x1f\x5c\xb0\x3f\x96\x71\x88\x0d\x5f\x7b\x11\x34\x34\x6d\x55\x69\xd5\x83\x95\xef\xd7\x3c\x50\x99\x5f\xc7\x86\x77\x5f\x48\x3f\xc4\xa2\xc2\x3e\xb1\xd7\x2a\x51\xad\xa2\x42\xeb\x7e\x5b\xef\x74\x8c\xa6\x0d\xb1\xe2\x05\x75\x57\x61\xec\xe2\x66\x61\x56\xda\xd9\xc2\x15\x5f\x1b\x91\xed\x8d\x21\xa9\xb9\x60\xd5\x95\x2b\xae\x5c\xf9\x7e\x0d\x8b\x5e\xb9\x7c\x6b\xe5\x96\x4a\x77\x8d\x10\xb5\x4a\x89\x4b\x2f\x0f\x4d\x78\xb4\xb8\x39\xf3\xe0\xc1\x65\x13\xfb\x86\x4f\xf5\xdf\x4a\xff\xe9\x2a\xf7\xec\xf5\xec\x41\x40\x39\x09\x5d\xf6\x83\x3e\x87\x9f\x88\xf0\x91\x97\x90\x7e\x05\x44\x3a\x7a\x01\x79\xaf\x21\x7d\x10\x7c\x00\x7c\x23\xf8\x91\x52\x43\xff\x2b\xc1\x0f\x90\x6f\x21\x3d\xfc\xc2\xb4\xdb\xa6\x55\x65\x17\x7e\xbb\x72\x6e\xf3\xd3\x95\x89\x67\x2b\x9a\x4b\xcb\x2d\x85\x99\x75\xac\x54\xfc\xc3\xd4\x95\x5b\x2b\x05\x85\xfe\x95\x8a\x84\x63\x89\x4b\xdb\x87\xa4\x15\xc2\xb1\x2d\x95\x94\xb2\x5d\x35\xa3\x90\xcd\x2c\x1e\x98\xac\x64\x1c\xbb\x11\x73\xec\x1a\x62\xa5\xce\x81\x2b\x56\x64\xe8\xe5\x91\x21\xe7\x54\x56\x44\xb9\x93\x56\x5a\x0b\xbb\xf3\x37\x5c\xd5\xbb\xe8\x9b\x8b\x0b\x3d\x76\x25\x52\x94\x71\xec\x97\x8c\xb4\xb2\x15\x2d\x94\xb3\xd6\xc2\x52\x3d\x9d\xf8\x61\xe9\xac\x82\xe6\x59\xd0\xfd\x63\x8b\x85\x81\xc4\xa5\xcd\x43\xf6\x55\x87\x2b\x87\x4f\x95\x60\xb5\xdf\xf8\x24\x91\x28\x81\xbc\x65\x9c\x9c\xb4\xf9\x46\x12\x09\x7a\x3e\x3a\xfc\x42\xd3\x6d\x4d\xab\x9e\xee\xcb\x8c\x7e\xbb\xf2\x52\x4d\x63\x55\xe3\xca\x05\xcd\x8d\xab\x1f\xa8\x2c\xee\x6d\x5c\xb5\xb5\x92\x56\xc7\x2a\x59\x7a\x1b\x56\x59\x1e\x7d\xbf\x66\x42\x5d\x11\xc6\x2a\x18\x4a\x5c\xba\x6d\xe8\x3a\xa4\x3a\x13\xb5\x2b\xd3\xa3\x45\x85\x5b\x2b\xd3\x0f\x91\xbe\xb3\x86\x4a\x0b\x49\xea\x5b\x87\x26\x7c\xd7\x56\xb8\xb8\x30\xfd\x6c\x0d\xd6\x89\xca\x13\x97\x18\xca\xf6\x2a\x93\x8f\x17\x15\xce\x9e\x9a\xb8\x34\x7f\xa8\x78\xcf\xb4\x6f\x95\x16\x66\xf4\xaa\x3d\xb6\x7d\xbe\x3d\xb6\xc7\x58\xc9\x9a\x25\x6c\xea\x2f\xaf\x48\x5c\xaa\x18\x72\x3c\x6b\x7b\x8c\x66\x55\x3a\xe4\x3a\x98\xb8\xe4\x1e\x72\x3e\x9a\x16\x65\x53\x29\xa7\x64\xc8\xf5\x58\xda\xa3\xa5\xba\x14\x37\x16\x2e\xba\x1d\xe3\x4d\xa5\xf4\xb6\xca\x1a\xa5\xb4\x90\xda\x25\x2e\x39\x86\x26\x1e\x12\xae\xbf\xfd\x6c\xda\xa3\x8e\x43\xff\x91\x9f\x5f\x78\x63\xa1\xd8\x9b\xb8\x94\x07\x6d\xa5\x0d\xcc\x5d\x21\x1e\x24\xfd\x3a\x80\x6e\x3e\x9b\xfe\x58\xf6\xa1\x8a\x15\x56\xe9\xf5\x7c\xb1\xb7\x7c\xa5\xeb\x10\x59\x94\xbd\x97\xf5\x8a\x87\x12\x97\x8e\x5d\xb4\x3c\xca\x4a\x32\x9b\x4b\x15\xf1\xd0\x15\x85\xd6\xde\x2b\x9b\xad\xe5\xe2\xbe\xde\xc5\xb9\x67\x4b\x57\x94\x8e\x58\xdf\x81\x8b\xd7\xae\xb2\x3e\xf6\x7e\xcd\x15\xcd\xcb\x17\x3b\x3d\x16\xc8\xf2\xa7\x8b\x99\x87\xac\x8f\x4e\xa8\xcb\x55\x8a\x56\xdc\x5b\xf3\x9d\xc5\xe9\x67\xa5\x15\xd2\x48\xfd\x07\x2e\x5a\x14\xeb\x6c\xfb\xa1\xc7\x17\xbf\xfe\xba\x30\x3b\xe3\xd1\xdd\x8b\x9f\xfa\x63\x1a\xe6\x38\x70\x91\xe6\xf4\xc6\x45\xd8\xeb\x63\x13\x9a\x8d\x15\xfc\xe7\x8b\xd7\xc0\x6a\x1f\xc2\xc6\xfc\x16\xec\x2c\x0d\x31\xfd\x61\xd0\xf7\xa7\x19\xf6\x48\xb6\xf7\x24\xd2\x1f\x23\xe6\xaf\x04\x9f\x06\x72\x22\xed\x04\x3f\x87\xfd\x60\x02\xb8\x82\x3a\x64\xaf\xcf\x03\x27\x50\x16\x78\x21\x67\x75\xce\xca\xb4\xc2\x07\x2b\xaf\xc3\x9a\xd0\x8a\xcd\xbe\x98\x5d\xe5\x5c\x79\x5d\xa1\x55\xda\x5a\xf9\x00\xe4\x9b\x71\xb1\xa4\x79\x22\x70\x31\x2f\x2f\xb9\x98\x09\x34\x8b\xa3\xab\x2e\x92\xef\xff\xe9\x73\x26\x05\x4e\x95\xa2\xcf\x87\xd1\xe7\x06\x1c\x4e\xf7\x82\x3f\x02\xfa\x3e\x68\xde\x0b\x59\xab\xb3\x30\x5f\x63\x8c\xc4\x25\xeb\x45\x47\x95\xc3\x34\xc2\xa7\xef\x94\x34\x67\x98\x46\xf8\xe0\x1d\xea\xf3\x24\xfa\x9c\x77\xea\x25\x1c\xbc\x4b\xd0\xc7\xa7\x1e\xec\xc3\xe0\x5b\x41\xa2\xab\x88\xfe\xec\x78\x5e\x2c\x2f\xd6\xdc\x88\x93\x9d\x1e\x59\xed\xf6\xca\xaa\x8d\xcd\x56\x45\xe6\x16\x58\xff\x01\x97\xd8\xff\x88\xeb\xa5\x57\x87\x5d\x3f\x07\xe1\xcc\x9f\x79\x74\xa5\xac\x3e\xbd\x21\xac\x30\xaf\xff\x39\x9c\x91\xfe\x60\xf3\x6c\x57\x44\x49\x52\x2c\xee\x22\xc4\xd2\xa9\x8a\xe0\x2c\x1e\xc1\x73\xd9\x1d\x31\x77\xe4\xd4\xc3\xa2\xe0\x8a\x09\x93\x65\x55\x78\x59\x8e\x15\x4d\xde\x4d\x7f\x7a\xc3\xb1\x27\xa0\x31\xa7\xaa\x89\x42\x6e\xcc\x32\x57\x56\x8b\xe7\xf2\x7c\x29\xa8\x31\x77\x4c\x23\x19\x70\x47\xc8\x14\x3c\x95\x9a\xa4\x74\x00\xb3\x9f\xe5\xfc\x5a\x56\x2b\x6c\x51\x85\x39\x8f\x68\xb9\x59\x51\xa5\x7a\xd2\x09\x05\x32\x9c\x97\xbc\x4b\x51\x67\x8f\x26\xb0\xb5\xb1\x5c\xd4\x11\xfb\x77\x17\x57\xa3\xde\x8b\x57\x9f\x50\xc4\x35\x82\x26\xd2\x75\xa2\xff\x01\xcc\x75\x58\x1f\x53\x48\x5b\x13\xc3\x79\x7c\xbe\x08\x8e\xf6\xf3\x8f\x38\xfb\xd4\x3a\xd6\xa7\xb6\xf6\x43\x46\xcf\x76\x6d\xfb\x74\x70\x5b\x30\x86\xf3\xff\x74\x51\xb8\x2b\x26\xf7\xf7\x41\xb4\x3e\xcd\x5a\x70\x44\xb1\x39\x3b\x55\xcc\x69\x70\x77\x7f\x9f\x8a\x3c\xcc\xf7\x88\x22\xa4\x61\x7e\x90\xf3\xfd\x44\x02\xa7\x8d\xf2\x18\xf6\x18\x3a\x8d\x58\x5c\xb7\xb4\xab\x92\xbb\x5b\x9c\xe0\x5e\xa2\x49\x52\xb5\x86\x3b\xd6\x0f\x1c\xd2\x7e\x0d\xa1\xe1\x0f\x79\xfd\x5d\x9a\x43\xda\xa8\xb9\x1c\xb2\x5a\x9e\x23\xab\xc8\x3b\x9f\x27\x75\x69\x52\xff\xc6\xdf\xb8\x80\xdd\x85\x9d\x67\x9d\xa2\x7c\xd6\xe2\x59\xa7\x58\x0b\x3a\x65\x8c\x7b\x04\xe3\x1e\xb3\x7a\x14\xf5\x6a\xcf\x3a\x6d\x22\xf5\xe5\x39\x40\x7a\x79\x35\x0f\x32\x4b\x52\x83\xe6\xb6\xca\xaa\x4b\x8c\xa8\xe5\x79\x3b\x74\x19\x1d\x9e\x06\x23\x5f\x44\xbe\x35\xa2\xda\xb0\xf7\x51\x7e\x39\xea\x25\xd3\x38\x31\xc6\x84\xb9\xc5\x9a\x30\xb9\x48\xc4\xdc\x45\xc1\xbb\x34\xe7\x08\xf6\x3e\xac\xb9\x4a\x6b\x8e\x63\x58\x66\xd2\x1e\x2c\xb4\x3e\xfd\x07\x54\x0b\xe6\x27\x32\x59\xa0\xf6\x16\x56\xae\x62\xbe\xb8\x4e\xc9\xb9\xc0\xf9\x47\x5f\xed\x53\x51\xaf\x20\x02\x7d\x0a\x4e\xe8\xdf\xd5\x59\x4c\xf5\x84\xa5\x07\x30\x46\x67\x05\xd2\xf3\x68\x4d\xb6\xe5\xed\x88\x65\x4f\xee\x8c\xa1\x1d\xd9\xc8\x20\x8d\x87\x7e\x5d\xe8\x97\x6c\x4d\x10\xdc\xb2\x8b\x49\xb2\x4b\xb8\x35\xaa\x5a\xd6\xef\xc7\xd8\xe5\x82\x38\xa5\x33\xd7\x90\xd9\x4d\x3a\xb6\x5c\x95\xb8\x41\x20\xbb\x15\x56\xc9\xb1\x0a\xe8\x80\x79\x65\xca\x8f\x1d\x86\xfc\x97\xab\x23\xae\x71\xc2\x0e\xea\x16\x53\xbd\x5e\xd4\x8b\x48\x7d\xea\x19\x97\xac\xfa\xa0\x0f\x31\x4b\x56\x13\xb3\x73\x72\x12\xd6\x59\xb9\x59\xb9\x45\x39\x68\x8b\x53\x64\x45\x8c\x70\xb2\x9f\xac\xd5\x72\x4c\xb7\x29\xd7\xe9\x58\x37\xda\x0b\x52\x54\x4d\x96\xd1\xf8\x64\x8f\xe6\x3c\x71\x8d\x0b\x76\x49\xd7\x59\xc3\xde\x93\x72\x14\xfd\x47\x9f\x7a\x57\x22\x91\x69\x91\xe4\xd8\xf1\xd3\xd0\x99\xd4\x95\x33\xc2\x8b\xba\x14\xc9\xbd\x51\x73\xbe\x88\x35\xa2\xb5\xb3\x44\x74\x1b\x11\xe1\x6b\xb7\x63\xcc\x00\xfa\x4d\xf4\xcb\xb1\xf4\xd7\x76\xc7\x8a\x56\x16\x2a\xc5\x1b\x0a\x9d\x0e\x36\xdb\xe5\x60\x73\x5c\xb8\x33\x4e\x20\x9f\xb4\xd4\x15\x29\xbb\x0b\x4e\xab\xbb\xe7\xbf\xa8\x0a\xeb\x75\x1d\xba\xa0\x7f\xac\x75\x77\x0c\x47\xe0\xcc\xe8\x99\x3d\x0a\xc9\xbb\x4f\x7b\x5a\x11\x8b\xd2\xb4\x68\xec\x69\x85\xd5\x01\x57\x3f\x03\x3f\x5e\x18\x13\x94\x3d\xb9\x09\xd8\xb4\xe8\x58\x1f\xb3\x54\xbd\x44\x3e\x34\x7d\x8f\xd2\xa7\xda\xbc\x2f\x28\x69\x75\xcf\x28\x0f\x4d\xea\x52\x9f\x2e\xe8\x54\x77\xd8\x64\xf5\x5f\x32\x23\xea\x9e\xb8\xac\x7e\xd6\xbf\x4e\x4b\x77\xdc\xd5\x8d\x7b\xca\x2e\x51\xb8\x3b\x26\x2b\xa3\x7e\x13\xc0\xba\x89\xbe\x34\x4d\x70\x3e\x04\x5b\x8a\x60\xad\x8b\x94\xe3\xb0\x6d\x87\x37\xdc\x0f\xfb\x3e\x8d\xf2\x57\x48\x3e\xa6\xc8\xf0\xa5\x75\x1a\xee\xd7\xaf\xce\x65\x61\xb2\x8f\xd7\x45\x97\xf0\x1b\x94\xff\x8e\x91\x3d\xa3\x2c\x9f\xdb\xfe\x84\xa4\xed\x7b\x1b\x34\xe7\x1c\xe8\x8a\xec\xdf\x16\x51\xc5\x22\x63\x7d\x9e\xca\x8e\x2a\xe5\x79\x4f\x2a\xdd\xca\xf7\xd4\xa7\xb2\xfb\x85\x40\xd2\x27\xa8\xfe\x6c\xd4\xb7\x19\x7e\x81\xfe\x35\xaa\x4b\x71\xc5\x5c\x57\xaf\x17\xdf\xa3\xba\x51\xd7\x35\xa7\x5b\x25\xbf\xa1\xba\xe5\xd6\x7e\xd4\x89\x8c\xe9\x97\xfc\x08\xf7\xd4\x02\xd1\x27\x68\x86\xed\x8f\xc4\x05\xf1\xaf\x58\xe7\x34\xc4\x0d\xdc\xa8\x62\x64\x53\xa2\xcf\xa2\x75\xc1\xee\x2c\xee\xa7\x50\x27\x37\x06\x9f\x33\xea\xb8\xbb\xc9\x6e\x54\xac\xa5\x5a\xbc\xc1\xa1\xd0\x7a\xc2\x9e\x32\x3b\xeb\xa2\x6a\x77\xdd\x7e\x57\x1c\xf1\x85\xf4\x46\x32\x90\xdf\xd0\xb8\x54\x46\xbe\x48\x3e\x49\x75\xa0\xfb\x4c\x6a\x0f\xc5\x17\x08\x8d\x51\x15\x3a\x2a\x40\x3f\xba\x4f\x5a\x1a\xf7\x53\x5d\x17\xf9\x99\x8d\x30\xef\x6b\x5b\x5e\xe7\x88\x2f\x52\x9f\x84\x6f\x9e\xd2\xa9\xa7\xbf\xcc\xd6\xee\xd2\x6d\xad\x52\xb1\x28\x45\x0a\xec\x4b\xa5\x18\x8f\xfb\x65\x26\xc5\x73\x61\xad\x1c\x2b\xae\xd8\xa1\x4e\x5d\xd8\xad\x8a\x25\xb2\x5a\xb4\x76\x87\x2a\xdd\xb5\x43\x65\x0d\xa4\xc3\x2c\xcd\xc2\xee\x80\x2c\xeb\x85\xcf\x3c\x47\x35\x4b\xd5\x76\xf2\x91\xe9\x4f\xb9\xfb\xd4\x8d\x12\xca\xeb\xd6\x2b\x16\xef\xcf\x95\x24\x16\xd2\xe0\x73\xbf\x00\x29\xc8\x57\x7e\xab\x88\x75\xb7\x4a\x59\x5e\xb9\xc8\xc1\x22\x45\x23\x6d\x52\xca\x8e\x7f\x49\xd9\x0c\xe1\x4b\xda\xa1\x2c\x4d\x79\x44\x9d\x48\x71\x6f\xad\xac\x5e\x7b\xf7\x0e\x35\x6d\x23\xf6\xc5\x72\x59\x7d\xf2\xe6\x6e\xb5\xab\x48\x56\x67\x9c\xe9\x53\xff\x88\xb5\xa0\xf5\x4d\xa3\x47\x02\xfa\xda\x42\xbe\xd3\x72\xec\xb7\x14\x13\x90\x8f\x3c\x7d\x0f\x48\xfa\xc4\xca\x44\x62\x97\xa5\xff\x0e\x45\xec\x5f\xcf\xcc\xfb\x89\xee\x17\x6a\x96\x56\x34\x79\x8f\x7a\xdc\x73\xe0\xb2\xfe\x30\x1f\xfd\xa6\x29\x3b\xd4\x79\xe0\x86\x6d\xcc\x86\x9d\xcc\x11\x10\xc3\xf4\xf5\x26\x39\x28\x56\xee\x85\x5c\xb4\xde\x3b\xc1\xb1\xd6\x14\x53\xd5\x5d\x68\x43\x79\x56\x5a\x73\xac\xd3\x26\xb4\x49\x96\x51\x0c\x4b\xda\xd7\x6a\xb4\xe9\xf4\x44\xd5\xcd\xe0\xdd\xde\xfd\x6a\x3d\xf8\x3f\x52\x39\xf4\x94\x5e\xb1\x56\x13\x5f\xae\x9e\xba\xed\xe7\x0f\x4c\xc5\x58\x8f\x89\xa7\x61\xe7\xd0\x85\x9c\xb5\x5b\x15\x85\xb5\x31\x8b\x20\x2b\x38\x30\x50\xbc\x55\x05\xec\xf7\xb0\xc3\x82\xb2\x6b\x7b\xb1\xbf\x0b\xcf\x21\xef\x04\xcd\xe5\x49\xec\xab\xf9\xd8\xf3\xb0\xc7\x69\xce\x85\x7d\x7a\x6c\x13\x58\x24\xb7\x13\xbe\xfb\x14\x28\xd7\x11\x51\x67\x55\x40\x26\xb7\xac\xfe\x09\xe3\x9e\x99\x3f\xac\xdb\xad\x30\x37\xaa\x6e\xb3\xca\xb9\x16\x5b\x24\x57\xf7\x1f\x01\x36\x6b\x8d\xc4\x44\x5b\x84\xfc\x09\xe3\x4b\xc6\x39\x06\xe7\x19\x8a\xe7\x9d\xee\x3e\x97\x85\x55\xb8\x16\xbf\x36\xec\x5a\x02\x5a\x88\xb5\x9c\x3a\x03\x67\x8c\x85\x41\x05\xf1\xb5\x5f\x70\x4a\xfd\x8c\x15\x19\xdc\x5d\xff\xdc\x15\xac\x9a\xfa\xe9\x14\x05\xa7\xde\x67\xc2\xda\x0f\x5f\xa8\xcb\xa1\x73\x8b\x38\xa9\x33\x96\xc8\x3b\x8d\xf5\xf4\xe5\xa0\x4f\xb5\x98\xce\x24\xd2\x62\x6d\x8d\x25\x8a\x18\x5d\xaf\xb9\x72\x61\x43\xe5\xcc\x88\xf1\xce\x7b\x21\x87\xa2\x89\x36\x6f\x0c\x7e\x37\x5f\xb0\x7a\x63\xd0\xf9\x7c\x19\xf1\x80\xf6\x17\x01\xfb\xfb\xe7\x38\xa3\xcc\xd4\xcf\x15\x6b\x62\x5d\x13\x0c\x9d\x59\x10\x0f\x69\x1e\x9d\x4e\x3a\x53\xc8\x8a\x68\x73\xc6\x68\x8c\x0f\xa1\x7f\xe7\xff\x6a\x57\xed\xfd\x55\x38\x2b\xac\x55\x9c\x38\x2b\x74\x4d\x88\xc8\x4c\xda\xf8\x1c\xda\x1c\x71\x8b\xdd\x27\xa8\x1d\xed\x51\xe2\xa4\x22\x8c\x2b\x41\x15\x5d\xa2\xe0\x5e\x92\x43\x76\xd8\x09\x3d\x42\x07\x2a\xe9\x20\x97\xfc\x12\xf2\x8b\xfa\xd9\xab\x22\x26\x20\x66\xe8\x6b\xc5\xdc\x34\xa6\xae\xd7\x84\x35\x27\x26\xf2\xbd\x5b\x14\x22\xc5\xba\x6e\x4b\xf6\xa3\xdf\x48\x05\xd2\xe5\x34\xcf\x6d\xd6\xee\x11\xbd\x2f\x9e\x31\x8c\xf6\xb2\x6b\xc9\xc2\x61\x01\xfb\xa9\x2b\x52\x02\xdd\xcf\xee\x13\xd0\xa7\x20\x18\x6b\xa5\x32\x37\xec\x57\x3f\x63\xf2\x7d\x17\xe7\x26\xda\x1b\x57\xcd\xee\xa6\xfd\x59\xf7\x9b\x6c\xd3\x9e\x49\xf5\x6d\x26\x2c\x96\xa7\xec\x9f\x74\xee\xea\x97\x85\xe4\x19\xeb\x9b\xe8\x47\xa8\x58\x22\x8a\x73\x96\x68\x85\x37\x40\x57\xd2\x5a\xad\x6b\xc1\x12\x3a\x17\xfe\xcc\x55\xd1\xad\x38\xcf\xc8\xaa\x04\x9b\xa3\x73\x9c\x5b\x7c\x6a\xc4\x46\x25\xac\x85\x8b\x97\xed\x66\x51\xb5\x1c\x65\xc7\x11\xcb\xa9\x8c\xe2\x9d\x7c\x7d\xa1\x72\x64\x01\xc5\xba\x0a\xc4\xba\x59\xae\x9b\x49\x7f\x52\x97\x22\xf4\x4b\x4a\xc4\x8a\x79\xe6\xf5\xb9\x9c\xc8\x13\x67\xa1\xae\xd8\xad\x1e\xc9\xe9\x56\xa3\xe5\x9d\x8a\x28\x38\xa0\xaf\x35\xb1\x4e\xa7\x5c\xfe\xd4\xd5\x9d\xe5\xb0\xe5\x72\xd8\xb8\xba\x4d\x3a\xa3\x7d\x86\xb5\x37\xf6\xc6\xea\x91\xb5\x37\xd6\xcf\x8d\xd8\xe1\xd0\x3a\xf3\x64\x41\xb6\xee\x56\x8f\x58\xa9\xfe\x5a\x2d\xe1\x0e\x9e\xc1\x7a\x9f\x46\x9d\xd3\xa4\x73\x5d\x97\xc8\x9f\x83\x7d\xef\x0a\x16\x8c\x89\x02\xfb\x0d\xca\xce\xc2\x1e\xb4\x08\x6c\x87\xce\x08\x34\x1f\x9a\xb3\x8f\xe9\xb6\xa4\xed\x76\xf7\xaa\xc9\x79\xe9\x76\x72\x9a\x61\x7f\xc2\xf9\xcd\xe6\xb4\xc0\xc6\xc4\x4f\xf4\xf8\x11\x41\xd9\xac\x18\xf9\x74\x27\x9d\xed\x6c\x2e\x0b\x6c\xc8\x28\x43\x6c\xc3\x3d\xfe\xfc\x91\x05\x14\x73\x2a\x54\xd2\x85\x8d\xe6\x7d\x7a\x00\xf5\xcf\xe6\xbe\x4d\x71\x90\x49\x98\x37\xd3\x46\xe6\x22\xb8\x73\xc9\xbe\x70\x16\xce\x15\x85\xf2\xdc\x89\xfa\x9e\x54\xa1\xef\x49\x64\x23\x19\xfa\x9e\x34\x8b\x6c\xb1\x00\xf6\xa2\xdb\x10\xc5\xa6\x64\x3f\x66\xdf\xa6\xb4\x85\xa7\xc7\x5b\x97\xf9\xfa\xba\x2c\xd6\xd7\x05\x6b\xa2\x46\xb0\x47\xd3\xf9\x46\xa8\xc0\x5d\x03\x6b\x22\x55\x60\xff\xc1\x9a\x08\xac\x9a\xe2\x4a\xf9\x83\xd2\x93\x9a\xf8\x73\xb2\xc9\x6a\x15\x73\x94\x68\x0f\x39\x82\xf4\x11\xa4\x69\xcf\x28\x12\xaa\xd5\xa2\xf2\x25\x12\xed\x11\x47\x90\x3e\x82\x34\xed\x09\x96\x8a\x3e\x8a\xfb\xae\x61\xcc\x97\xd6\x11\x67\xcc\x5d\xd4\x87\x79\x1d\x69\x0d\x69\xfd\xb6\xe1\x8c\x7f\xb9\xb5\xdb\xab\x9f\x4d\xff\x67\xeb\xf0\x92\x69\x1d\x6c\x5c\x97\x3f\xd1\xe3\xfd\x2c\xf5\x47\xfa\x3a\xc8\xea\x73\x7a\xac\x9f\xa5\xc7\x4d\xd2\xed\x77\xd1\x96\xf2\x8f\x23\x3f\xb9\x66\xff\x9b\xf6\x86\xa2\x81\xd8\x93\xc4\xcb\xcf\xc6\xf6\x80\x1f\x25\xb9\xfa\x15\xf2\xff\xf3\x02\xf4\xcc\x38\xb7\xf6\xcf\x8c\x47\x10\xff\xcd\xb1\x04\xf7\x98\x4c\xeb\x40\x8f\xf6\x54\xff\x7e\x8a\x1f\x88\xb5\x6b\xbd\xa7\xb3\xe5\xce\xd3\xd9\x0a\xd9\xba\xee\xf7\x54\x57\x5f\xef\x97\x8c\xfb\x00\xda\xa9\xef\xd1\xfe\x02\xfc\x2e\x64\xb2\xb2\xc3\xe7\x74\x7b\xa1\x3b\x17\xe4\x73\x20\x16\xe0\x3a\x7d\x3e\x1b\xe7\x0d\xdc\xad\x2f\xd0\xb3\x3b\xf8\xfc\x92\x6c\xec\x8f\x76\x65\x66\xfc\x23\xe4\xa1\x9e\x85\x62\x95\x7d\xf8\xf0\x39\xca\x7b\xdf\xc8\x9b\x85\xbc\xe2\x64\xde\x90\x91\xd7\x8b\xbc\xee\x64\x5e\x7c\x9c\x3c\x25\xa5\x2d\x73\x47\xe3\x02\xee\xa2\x62\x79\x77\xae\x58\xfe\x22\xc5\x64\x85\xe4\x1b\xbe\x7d\xe6\xdb\xc7\xd7\x3c\x11\x3f\x5e\xfe\x44\xdc\xe6\xdd\x8b\x7b\xed\x23\x7f\xce\x1b\x58\x8d\x58\x56\x1d\x63\x1e\x3a\xab\x2c\xa2\x3b\x58\x66\x9a\x3e\x87\x1e\xed\xec\x5f\x21\x7b\xd4\x4b\x77\xbd\x82\xf5\xd0\xe1\x66\xb4\xdb\x5c\xf4\x44\x9c\xda\x89\xd1\x47\xfe\x6c\x71\xb7\x28\x2f\xe2\x2c\xe1\x70\xf7\xc4\x1d\xee\xc3\x71\xea\xa7\x88\xec\x15\x77\x44\x4b\x1f\xe2\xa1\x0c\x9d\xa3\x6c\x42\x60\x97\xf2\xfd\x6d\x3b\x95\x5f\x04\x64\x75\x67\xe7\x3f\x29\x76\xf4\x8f\x98\x7e\xc1\x2a\x1d\x8e\x8b\xec\x4e\x8a\xab\xb0\x15\xc5\x42\x63\x0b\x6c\xa7\x16\x11\xfe\x49\x61\x68\xf7\x1a\xe9\x0e\xfd\x0a\xcc\x83\x33\xd6\x7a\xd2\xfd\xf4\x4c\xd4\xb7\x53\xbd\xbe\x65\xda\xa0\x6e\x7f\xeb\xc9\xf7\x76\x31\x56\xa3\x9f\x5f\x28\x3e\x0b\xf2\x4e\x4d\xa5\x32\xd8\xa9\xd3\xbd\x3d\x9e\x81\xb8\x7c\x3d\xf2\x69\xdc\x7f\xa5\x71\xdd\x07\xd1\x67\x35\xd9\xb1\x05\xe5\xb6\xe5\x6c\x7b\xdc\x86\xf5\x98\xaa\xef\x27\xd5\xd8\xe7\x1e\xd6\xaa\xd8\xf6\x17\x70\x6f\xfb\x31\xfa\xfe\xd9\x0f\x74\x39\xe4\x18\xf2\xe2\x55\xec\x40\xdc\x8b\x3e\x49\xc7\x54\x37\x07\xe7\xf7\xe2\x85\xb2\x9a\xed\xce\xd5\xea\xa4\x27\x6c\x16\xf2\x03\xe9\x91\x74\xa7\xb4\xdd\x96\xa5\xcf\xa7\x5a\x3f\x8b\x2e\x77\x3f\x61\xa3\xfd\x4a\x04\x26\x1b\x5a\xe8\x36\xf4\x48\xf2\xd1\x7d\xd9\x89\xbe\xdf\x80\xcc\xf0\xa9\x0b\x94\xa6\x36\x79\xb0\xcb\xe1\xbd\xa7\x06\x4f\x96\x33\x76\x0e\x06\x85\xf9\x26\xde\xa1\x67\x89\xa0\x0f\x40\x1f\xd1\x33\x48\xc8\xe1\xa4\x3b\x21\xcb\xd5\xd7\x90\x7c\x49\x44\x7a\x39\x8b\xd0\x5e\x93\xf9\x89\xc8\x2e\x90\xfd\x7f\x48\xdc\x7d\x00\x63\xe5\xd2\x1e\x56\x30\xc0\xb6\x6b\x1f\xf1\x3c\x3b\xcf\xbb\x32\x6b\xbb\x76\x89\xe7\x39\x91\xe7\xc4\x1d\xb8\x18\x79\x4e\xcf\xa9\xc1\x5c\x0b\x3b\x3f\xbc\x6c\xe6\xdb\x56\xa5\x53\x73\x44\xa2\xf1\xfc\x68\x34\x9e\xf3\x9f\xb0\x1d\xb2\x1b\x2f\xe6\xcf\xfe\x6d\xd0\x0e\x5b\x72\xf4\xf7\x68\xd9\xec\xf8\x20\xe1\xb1\xfc\x7b\x83\x2c\x20\xc7\xec\x75\x54\xbe\x7b\xd0\x1e\xe8\x81\x2c\xb2\xba\xdc\x79\x38\x5e\xa3\xfb\xc8\x1d\xb1\x4c\x59\xce\xcd\x90\xe1\x17\x5e\xaf\xe6\x94\x77\xe5\x7a\xdc\x87\x27\x39\x20\xd7\x72\xd8\x40\x1e\xf4\x94\x06\xbf\xca\x98\x29\xc7\xf6\x67\xf7\x68\x33\x61\x9b\x4e\xc8\x99\x89\x3e\xac\xc8\xdf\x7f\x43\x8f\x36\x0b\x36\x1e\xa9\x80\x1d\xc2\xce\xaa\x60\x3f\x1e\xb1\x67\x10\x7e\x11\xcb\x44\xb9\x15\x7e\x6f\x85\x9d\xcd\xd3\xe3\xba\x7e\xa6\x29\xd0\xc7\x04\x27\x7b\xdf\x7f\x75\x8f\x46\x7d\x4f\x41\xbf\x76\x76\x67\xec\x4d\xf8\x75\x3a\x38\xe9\xc5\xca\x1e\xd2\x64\xe8\x25\xc3\x4b\xfb\xb9\xac\x3e\x8c\x75\x11\xfe\x88\xb5\xd9\x79\x6a\x70\x9e\x71\x57\x56\x05\x5d\xf7\xb2\xea\x71\x1e\x88\x97\x70\x9f\xa7\xbe\x3d\x90\x31\xcf\xbd\x23\x4e\x72\x38\xdd\x8f\xc4\x6d\x5c\x16\x92\xd5\x8e\x3a\xef\xe9\xe3\x50\x7c\xc0\x5a\x7d\x0c\xdb\xc4\xba\xec\xd0\xd7\x6c\x0d\xd9\x09\x62\xcb\x1a\xc4\x31\xb9\x9b\x7c\x9e\x7c\x38\x1b\xf1\x1a\x76\xd7\x8b\x33\x0d\x63\xd2\x61\x9b\xd5\x19\xb5\x91\x1d\xc3\x56\x73\x99\xb3\xcb\xb0\x3f\xe3\x0c\xa4\x5e\x4d\x76\x21\xe9\xfe\xa9\xd1\x33\x02\xf2\x29\xd4\x53\xff\x0c\xf9\x1c\x0c\x3a\xc5\x3c\x98\x7e\xdf\xfb\xf2\xb1\x98\xf3\xb0\x4d\x1f\x6f\x64\x9c\x7d\x18\x37\x62\xa3\x71\x2c\xfc\xec\xa5\x8f\x85\xbe\x9d\x88\x1b\x56\xa9\x28\xce\x9c\x3d\x71\x1a\xd3\x42\x77\x1e\xe4\x53\xdc\x4a\x8e\x47\x71\x88\xf4\x55\x15\x80\xae\x60\x33\x62\x9d\xac\xda\x9d\x0c\xed\xbe\x7b\x8e\x7c\x0a\xb1\x27\x6e\x89\x30\x2d\x9f\x6c\x04\x32\xeb\x36\x8d\x7b\xd7\xd7\xdc\xbf\x82\xcf\xc0\x2f\xa4\x9e\x78\x21\xd2\x88\x4b\xe7\xa7\xce\x43\xac\x75\x5b\x34\xb1\x54\x56\x29\xdf\x72\xef\x4e\x45\x94\xe4\x18\xd5\x2d\x94\x7e\x15\xf7\x46\x0f\x20\xc6\xd4\xc4\xe6\x05\x0e\xd8\x84\xe8\x15\x1a\x8b\xca\xb9\x47\x31\xd7\x63\x68\x4b\xf1\x67\xb9\x9b\xca\xd7\xc6\xac\x7d\xdf\xb5\x3d\xd7\xf7\xa6\xcb\xee\xdd\x29\xf6\xc9\x6f\xba\xac\xde\x9d\x39\x64\x17\x42\xe0\x3f\x62\xd9\x34\x8f\xc0\x4e\x45\x8f\x03\x14\xaf\xa0\x23\xf8\xec\x2e\xc1\xfd\xaa\x9e\x8f\xf5\xa3\xbc\x62\x8a\xc1\x42\xdf\x04\x4d\x90\x33\xb5\xef\xc8\xbb\xb0\x17\x6c\x88\x4d\xd2\x7d\x9e\xcd\x17\x91\x3e\x82\x79\xcc\xc5\xb8\x14\xab\xe8\xbc\xa7\x7f\xbd\x1a\x6b\x6f\x0d\xec\xd0\x3a\xf5\x67\x53\xb2\xba\xf7\xf3\xc4\x85\x79\x01\xf8\xbf\x97\x9e\xa3\xc9\xea\x96\x8f\x0d\x5b\x82\x2c\x2e\xc8\x91\x6b\x93\x64\xd7\x5e\xd4\xfd\x0e\xef\x67\xb2\x6e\xc7\xde\xd8\x55\xfa\xb9\xe4\x91\x41\x91\x85\x62\x3b\x91\xb6\x3b\x29\x6e\x86\x62\x58\x0f\xc4\x3f\x23\xae\x59\xdc\x3b\x9c\x56\x67\xef\x24\x3b\xf6\x63\xea\xfb\xc9\x1b\xb0\x5e\xb0\x8d\x7a\x3e\x86\x93\xf5\xd8\x9c\x3c\x8e\xa3\x41\xe6\x0c\x8c\x41\x3a\x98\x88\xf9\x4b\x58\xab\x6f\xf1\xf9\x53\xfc\x2a\x85\xde\xe8\xde\x0b\x99\x05\x0f\x8d\x87\x39\x78\x49\xa6\xc0\x23\x71\x92\x4b\xbc\xf7\x17\xb1\x6b\xb8\x6c\x8b\x49\x36\x89\xfa\x25\x7b\xba\x33\xd7\xd6\x27\x0b\x76\x59\x16\x30\xf9\xcc\x05\xb4\x0e\xce\xff\x3f\x6e\xd7\xc7\x5d\x13\x13\xfa\x77\x69\x13\x9c\xfb\xa6\x40\x2e\x3d\x3e\x30\x89\xfc\x06\x76\xd3\xdf\x13\xbf\x91\xe4\xec\xa7\x7a\x5e\xda\xdb\xe1\x37\x1b\xd4\xeb\xf5\x78\xc7\xce\x3b\x64\xdc\xeb\x80\x2f\x60\x5f\x4e\xca\x7c\x33\xc5\x52\xf4\x5b\x82\xd8\x51\xea\xdd\x1e\x97\x10\x57\x51\x16\xf3\x98\xe7\x25\x1f\x88\xcf\xd6\xe7\xb5\x41\xcd\x37\xcd\x6b\x12\xf9\xbd\xdb\x78\x06\xe7\x46\x7b\xe8\x4e\x29\xc5\xba\xcc\x70\x6e\x8f\xcf\xd6\xe3\xff\x23\xb4\x87\xa8\xd0\xd3\xcf\x68\xcf\x62\xc2\x0e\x45\xc2\xda\x91\xad\x40\x66\x95\xfa\xbf\x11\xfd\x12\xa7\x3e\xc8\xe7\xaf\xa2\xbd\x29\x4a\x7e\x88\xb6\x81\x9d\xfa\xbe\x6b\xf5\xee\x78\x2e\xfd\xde\x1d\x0a\xed\x5b\x35\xd2\x76\xcd\x0e\x1b\x66\xf2\x77\x31\xc7\x0d\xb1\xf5\x7c\x2e\x79\xf0\x29\xa6\xc7\x04\x5a\x23\xd2\xe9\x37\x62\xd8\xcb\x2c\xb4\xbf\xd0\x78\x54\xc7\x82\xb1\xb2\x4c\x63\x17\x00\x4b\xd2\x13\x34\x5f\x95\xea\x88\xd2\x13\x7f\x9e\x84\xbc\x74\x66\x9c\x87\x68\x4f\x2c\xe1\xfb\xcb\x2b\xd7\x31\x26\x62\xae\xf9\x74\xdf\x07\x9f\xcc\xf7\xce\x3c\xf0\xe1\xa5\x88\x6f\xf0\xd5\xa6\xff\x82\x9f\xea\x36\x89\x35\x7a\x6f\x6f\xf4\x55\xf4\x41\x77\x9b\x49\x34\x27\xd0\x6e\xd0\x71\x4a\x23\xc6\xbf\xa4\xdb\x66\x75\xec\x34\xad\xcd\x66\xc4\x5c\x29\x1a\x9f\x06\x2e\x40\x0e\x97\x65\x8a\x62\x11\xb6\x3b\xad\xfd\xfb\xf3\x04\x77\xb7\x66\xed\x8f\x4c\x72\xa2\xee\x47\x74\x7e\xdb\x3a\x45\x9b\x87\x7d\xf3\x79\xcc\xab\x8f\xfa\x77\x56\x6b\x68\x93\x4b\x7b\xa8\x8d\x55\xe7\xfe\x7f\xd4\x5f\x0d\x9d\xb1\xaa\x63\x53\x9b\x65\xf5\x1a\xa4\xd3\x24\xe8\x1d\x71\xc8\xa9\x9f\xa7\xd6\xaa\x47\xc9\x2f\xd9\x12\xc4\x19\xdc\xb1\x22\xc7\x94\x6c\xb6\x3e\x86\x73\x48\xaf\x20\xe3\xbc\xc4\x0a\xd4\xc7\x75\xfd\x6d\x88\xd9\x64\xd9\x45\xb1\xdd\x09\x9b\x74\xca\x8b\xbb\x58\xe0\x31\x0d\xb2\x63\xac\x29\x2a\x0e\x57\x05\xd4\x2e\x8b\xce\x15\xd2\x01\xac\x73\x0e\xed\x17\x99\x82\xf3\x4c\x8c\x6c\x12\xb2\xc4\x96\x3b\x23\xe7\xc8\x0f\x44\x67\x27\x6c\x64\x7b\x1c\xce\x7f\x81\xec\xd0\x22\x9d\xc1\x1a\x6c\x8f\x5f\x29\xd0\x7a\xdd\x49\x67\x92\xcc\xe4\xbe\xab\xc7\x58\xf7\x8b\x7a\x1f\x03\xee\xc8\xb9\x02\xbd\x4e\x0d\xdd\xab\x33\x5d\x59\x3d\x5a\x16\x6f\x43\xcf\xde\x40\xe7\x49\x06\xc8\xb8\x8b\xca\x26\xf0\x32\xb2\x7b\xf8\xa9\x8e\x9d\xde\x46\x6d\x1e\xf6\x1b\xda\xc7\x28\xc6\xc2\xcf\xff\xe0\x2a\xeb\xd1\x76\x60\x9d\x1e\x12\xba\x14\x1b\xc5\x5e\xf2\xaf\xfe\x7d\xe7\xa0\xe7\x73\x74\x66\xc7\xf9\x7b\xba\xad\xbc\x5b\xa1\xbe\xc8\x7e\x1a\xb1\xef\x39\x31\x0e\xec\xa4\x08\x3a\x9c\x15\x11\x3a\x95\x5d\xf0\xa5\xae\x6c\x9c\xb9\xb0\x57\x65\x5e\xa6\x8f\x4c\xbd\x0f\xc8\xee\xee\xce\x21\x3e\x0f\x3e\x21\xf2\x79\xfc\x81\xb1\x0b\x9e\x14\xf9\x7f\xcf\x0c\xf9\x6f\xe2\xf2\x13\xfe\x3a\xe9\x0b\xba\xfc\x3a\x5f\xd7\x91\x35\x85\xaf\x91\x4d\x94\x93\xfe\x71\xbe\xa1\xf3\x09\xf5\x6d\x87\xee\xfa\xca\x23\xe7\xfe\x0b\x6d\xa3\xe8\xe3\x3f\xc1\xe7\x41\xb7\x94\xa6\x39\xd3\x5e\xf3\x90\x2f\xa2\xd8\xb0\xff\x8b\x7a\x5c\xe9\x3d\x27\x04\x96\x69\x56\xe7\x43\xe7\x68\xec\xbf\xc2\xc6\x6c\x6b\xba\x94\x79\x88\x05\xd4\xe6\x24\xda\x5f\xa1\x9f\xbd\x8c\x7d\x8e\x7c\x5e\x3f\x4f\xf1\xf1\x9c\x18\xcf\x5d\x11\x39\x47\xb2\x4e\xc4\x9a\xbd\x49\xe3\xc1\x1e\xf4\xfb\x8f\x6e\xcb\x93\x14\x2b\xdb\x9f\x67\x11\x7a\xa6\xe8\xb6\x0c\x99\x3f\xc6\x18\xb0\xe1\x38\xd5\x7f\x99\xe6\x1c\x30\xfc\x4d\x04\x87\xef\xc1\x8f\xe0\x73\x3b\x4e\x0d\x56\xb2\x68\x5c\x3f\x93\x63\xdf\x5c\xe8\xde\x17\xa7\x73\x95\xc7\xdd\x19\x4f\x9e\x25\xe8\xdc\xb4\x0f\x3e\x67\xfd\x92\xf3\x54\xc6\x26\xe3\x2c\xc5\xdc\x4a\xcc\x53\x71\x6d\xdc\x38\x7f\x1c\x8e\x5b\x3f\x33\xe2\xf9\x44\xb2\x6f\x7e\xe6\x98\x68\x3a\x73\xd0\xf9\x08\xf7\x8d\x4c\x92\xb1\xed\x33\x8a\x51\xba\x5d\x15\xd8\xf8\x59\xea\x97\xc8\xa3\xf8\xb0\x8f\x64\xc5\x7e\x9d\x2f\x45\xe2\x0b\xa5\xde\xb8\xc3\x19\x89\x7b\xa4\xee\x38\xd9\x05\xc5\xe5\xad\x9f\x25\xce\xd3\xf9\x27\x0f\x3a\x23\xd9\x1d\x74\x26\xf6\xd0\x59\xbe\x3a\x26\x78\xf5\x35\xc8\xc4\xf9\x23\x16\x40\x7f\x59\xa4\x67\x94\x53\xff\x38\x37\xc4\x6c\x5c\x9e\x74\x1e\xcb\x49\x2e\x1b\x3f\xbf\x5b\xd9\x0e\xad\x12\xba\xd3\x7d\x0d\xf5\x9f\x46\xfb\xe7\x2f\xe1\xcc\x22\x45\xf9\x1e\xc2\x0a\xfa\x10\x9b\x16\x4a\xfb\x20\x0f\x74\x86\x31\xee\x84\x2c\xa4\x4f\xfd\x3b\x12\x24\x87\xd3\x90\x83\xbe\x2f\x41\x67\xaf\x59\xfc\x3b\x13\x01\x5d\xdf\xfb\xa1\xef\x2e\xe8\xbb\x6b\x8c\xbe\x9f\x81\xbe\xef\xfa\x12\x7d\x27\x75\x5d\x83\xbd\x89\x74\x77\x10\xba\x7e\xf6\x53\x43\xd7\x70\x82\x82\x1a\xd8\x14\xee\x7e\x31\xd2\xf5\xcf\x20\x33\x61\x9a\xef\x1c\x7e\xe6\xcc\xd0\xfd\x1a\xe5\xc8\xab\x91\xe0\x77\xd8\x9b\x48\xae\x0c\x7d\xee\xde\x5c\x86\x73\xd0\xa0\xfe\x77\x37\xc4\xe7\x3a\x39\x46\xed\xd3\x3e\x33\xf6\x90\x67\xb0\x16\xf4\x7c\x75\xb6\x33\xa2\xcd\x63\xfb\x06\xe7\xb1\x99\x6f\x27\xb2\xa3\x83\x4c\x39\x3c\x58\x34\x21\xfa\x0e\xcd\xbd\x68\x42\x44\xe7\x12\xc7\xd2\x08\x1e\xe0\xbc\xff\x1d\xe3\x19\xe3\xf0\x3b\xa2\x0b\x54\x04\x2a\x07\x2d\x02\xad\x01\xf9\x40\xf7\x82\x3a\x41\xfb\x40\x47\x40\x27\x40\xa7\x41\x67\x41\x2a\xe8\xbd\x61\xde\x87\x82\x3e\x40\x45\xa0\x72\xd0\x22\xd0\x1a\x90\x0f\x74\x2f\xa8\x13\xb4\x0f\x74\x04\x74\x02\x74\x1a\x74\x16\xa4\x82\xde\x53\xf4\x3e\x84\xce\xbe\x4f\x85\x7d\xa0\x23\xa0\x13\x7d\x9f\x1a\x79\x32\xf2\x40\x47\x40\x27\x64\x3d\x8f\x29\x98\xeb\xd5\xd8\x87\xe0\x77\x0e\xcc\xdb\x8a\xfd\xd0\x8d\xbd\xcc\xa3\xe0\xfc\x86\x3d\xec\x72\x75\xee\xfc\x3b\xea\xb4\xfc\xad\x3a\x38\x7b\xdc\xfc\xb7\xea\x60\x2d\x9b\x53\xea\xd8\xa5\x81\xb8\x71\xaf\x5d\x4b\xcf\x3f\x73\x28\xa6\x67\x50\x1e\xf0\x04\xfe\xdc\x35\x43\xea\x8f\x5b\x25\xf2\x89\xfd\xe7\xd2\x58\x64\x90\xfe\x7e\x66\x97\x5e\x84\x3d\xf7\xc6\xa7\x3a\x64\xfd\x6f\x0d\x0c\xfe\x46\xfd\xf5\xc1\x6e\xf4\xe7\x46\xcb\xf8\x7d\x03\xb6\x42\xb1\xc5\x21\xf7\x68\x89\xef\x19\x67\x00\xdc\x79\x2f\xf4\x91\x7f\xc3\x9e\x5d\xfa\x3e\x77\x27\x9d\x33\x73\xdf\xa0\x67\x54\x9e\x1e\x2d\x1d\x76\x97\x85\x31\xd2\xdd\xc6\x5d\x85\x79\x7a\xe2\xe7\x60\xbf\x94\xa6\xb2\x6c\x5e\x46\xf9\x1f\x7f\xaa\x9f\x15\xd4\x9f\xc3\x16\x23\x4e\xa7\x46\xe7\x0e\xda\x63\x58\xa7\x1e\x4b\xf4\x7b\x50\x22\x62\xd7\x98\xdc\x13\x7f\x1d\x75\xe9\xfe\x6f\x8d\x3c\xac\x79\x58\x5e\xcc\x12\x69\x76\xda\x23\xdd\xcc\x11\xa1\xb3\x4c\x8f\x1e\x97\xe8\x0c\x54\x82\xbe\x86\xdd\xf4\xac\xa8\x3a\xa6\xcb\x57\x69\x9c\x55\x7e\x4c\x77\x72\x4f\x44\x15\x33\xd6\xc5\x8e\xd1\x7e\x49\xe7\xa5\xe9\x86\x8c\x53\x4b\x8d\xfb\xc3\x5b\xf0\x03\x6b\x97\xac\xfb\xd1\x13\xb4\x5f\x64\xe4\xc7\x68\xbf\xa6\xe7\x79\x51\xde\x5f\xb6\xc4\xb4\x89\xfa\x73\xc9\xb5\xb9\x2c\x82\xbd\x3c\x22\x5b\x0e\xe8\x67\x61\xa3\x2f\x3b\xc9\x0f\x79\xbe\xcf\xe7\x4c\xf9\x84\x69\x3e\x34\xc6\x49\xe4\xd3\xb3\x25\x99\xe2\x06\xc9\x27\x29\x6a\xba\x24\x6a\xdf\x98\x8a\xba\xcc\x19\x4b\x94\x89\x9a\x70\x5d\x57\x6e\x72\x3e\xff\x80\xf9\xf4\xa1\x2e\xce\xb0\x05\x27\x20\x57\x3a\xbf\x4b\x6e\xa3\xf3\xa4\xe4\xd5\x3a\x30\xf6\xb0\x33\x47\x0b\xf1\xd8\xba\x06\xf5\x9f\x43\xbd\x7c\xfd\xef\x2c\x7a\x9c\xd0\xc7\xd5\xfb\x83\x1c\x9d\x9f\x1a\xbe\xde\x8e\xf6\xa2\x47\x8e\xe9\xcf\x05\xac\xdd\x8a\x83\xf5\xe8\xe7\xb8\x74\xcf\x76\xdd\xce\x68\x3d\x48\xdf\xdb\x18\xd3\xfe\x42\xcf\xa1\x6e\xe9\xd6\xa8\x1e\xf5\x41\x75\xa9\xbf\x5f\x62\x1e\x3f\x36\xc9\xb4\x81\x9e\x11\x61\xcc\x6c\xe8\x7f\x18\x7a\xa2\xfd\x8a\xea\x4d\xc1\x18\x9f\x22\xef\x79\xba\x1f\x78\x98\x98\xee\xb1\xe6\x08\xd2\x76\x11\x94\x43\x67\x7b\xd1\x89\x7b\xca\xb4\x2e\x0b\xd2\x2e\x3a\x8b\x89\xd3\xba\x72\xaf\x46\x9b\x7e\x63\x7f\x2e\x78\x99\xbe\x97\xc6\xc7\x58\x84\x31\x2c\xe3\x8c\x21\x5e\xdb\xad\x22\x3e\x9e\xff\x85\xa9\xee\x5c\xae\xa3\x19\xa4\x23\x77\x8e\x36\x7d\x74\xff\xd1\xed\xe4\x23\xf4\x41\x63\xd0\x33\x9d\x6c\x0f\xfa\xc2\xd9\xd8\x66\xfc\x9d\x27\x96\x59\xda\xa5\xeb\x0d\x06\x7e\xfe\x15\xa6\xff\x5d\x42\xef\xb3\x08\x7d\xd2\xb8\x4e\xa9\x8b\xbe\x97\x70\xfe\x97\x28\xcb\xe3\x65\x05\xbc\x8c\x64\x4b\xca\x45\xfa\x20\xd9\xd2\xf9\x78\x03\x7c\xdd\xd3\x4d\xe3\xe9\x75\xa6\x75\xc5\x6c\xf0\xf5\x05\x7c\x5f\x2d\xc6\xde\x49\xfb\xa3\x73\xc0\x78\x06\x40\x77\xdf\x2a\x7a\xe6\x46\xf2\xc3\x77\x29\xae\x90\xef\x22\x2e\xc4\xed\x52\xb7\x7e\xa6\x27\xdf\x76\xe0\x3e\x9a\xee\x7e\x3d\x3e\x11\x77\x53\x87\xe9\x3e\x4c\xed\xc9\x1e\xf4\x7b\x3a\x3d\x4b\xe3\x76\xf1\xd9\x27\x86\x5f\x2d\x67\xbd\xf1\xbf\x55\xf7\xbf\x79\xdd\x2a\x77\x6f\x3c\x5d\x7a\x3d\x2e\xe2\x7c\x66\x67\xdd\x5a\xb2\x1d\xed\x8b\xb1\x0f\x13\x17\x88\xab\xe0\xbb\x9e\x43\xf4\xed\xfb\xa1\xfe\x15\xe8\x23\x7a\xfa\x5f\xf4\xf4\x2d\xd7\x52\xba\x96\xbe\x32\xcb\xec\x27\x28\xcd\xbe\x34\x7d\xfc\xcd\xc4\xb6\x1f\x64\x9f\x88\xfc\x0e\xbc\xef\x87\x8e\x48\xfe\xef\x13\xdb\x8e\x26\x12\xdb\x06\x7e\x4f\xb8\x24\xf2\xfc\x1f\xc1\xfb\x7e\x10\x71\xbe\x95\xd8\xf6\xac\xfd\x44\xa4\x06\x9c\x06\xba\xeb\x02\xe5\xf7\x45\x5a\x89\xff\xc8\x1a\x99\x77\x91\xf0\xb1\xc8\xe3\xe0\x01\xb4\x3f\x36\x64\xd4\xff\x68\xc8\xa8\x37\xfb\x5d\xe4\x61\x9c\x75\xef\x1a\x38\xca\xf1\xf3\x84\x7f\x64\x8f\x78\xde\x37\xf2\xbd\xe0\x0b\xd1\x5e\xfe\x8b\x81\x89\xff\xd0\xf9\x5c\x64\xe1\x25\x5e\x7e\xc9\xe8\xf7\x79\x8e\x07\xc0\x71\xef\xd9\x36\x08\xfe\x83\x6b\x9e\x8b\xc8\x1f\x19\xf2\x79\xfe\xca\xc7\x01\xff\x17\x8c\x53\xf8\xb9\x31\xde\x4f\xc1\x3f\x00\x11\xff\x88\x73\xaa\x5f\xb9\x74\xf1\x1d\xcb\xd8\x8a\xdb\x56\x2e\x5d\xb2\x4e\x0a\xfa\x43\xfe\xb0\x54\xdf\xda\x5e\x7f\x4f\x32\xab\x31\x10\xba\xe9\x9a\xb2\x8a\x46\xc9\x78\xcd\xf0\xa6\x6b\x1a\x24\xfe\x52\xa1\x9e\x9d\xc9\xfc\xc1\x60\x7b\x30\x33\x59\xbd\xcd\x7f\xff\x4d\xd7\x04\x66\x48\xed\xad\x28\x0f\x48\xf5\x1d\x41\xb0\x4c\x76\x43\x20\xd8\x5e\x7f\x43\xc8\xdf\xda\x78\xc3\x26\x34\xe6\xf8\x9a\x06\x03\x05\xd9\xcc\x64\xfb\xdb\x83\x2d\x4d\xd2\xd8\xd7\x00\xa5\x9b\x24\xbd\x8b\xd0\x96\x50\xd8\xbf\xe9\x86\xd6\x96\x8d\x44\xa1\x8e\x60\xa3\xaf\xde\xdf\xd8\xda\xd2\xd6\xe4\x0f\xd2\x1b\x16\xbc\x87\xf1\xca\x24\x5f\x43\x03\xe6\x96\xec\xaa\xac\xa9\x3d\x5c\x46\xaf\x88\x51\x22\xd9\xae\xbd\x23\x4c\x6f\xcb\x05\xa9\x4e\xeb\xe6\x19\x3a\xa6\x17\x08\x0d\x3c\x32\xc1\x5b\xdb\x3b\xda\x1a\x52\x25\x6c\x69\x93\xd0\xd3\x48\x9d\x45\xad\xf4\xb6\xe2\x16\x89\xde\x37\xf4\x37\x8c\x64\xaf\xde\xb2\x1c\x19\x52\xa8\xa3\xbe\x1e\xb2\x8c\x64\xeb\xaf\xb9\xe8\x75\x21\xae\x59\x91\xb5\xe3\xea\x81\x97\x07\x7c\x58\x0f\xe9\x9a\xd4\x6e\xf8\x1b\x44\x23\xb9\xe3\x76\xf1\xd5\xe7\xab\xcf\x57\x9f\xaf\x3e\x5f\x7d\xbe\xfa\x7c\xf5\xf9\xea\xf3\xd5\xc7\xf8\x1c\x37\xbd\xf8\x48\xef\x31\xee\xae\x35\xde\x0b\x1d\x60\xfc\x9d\xba\x29\xc6\x7b\x8a\x2e\x4e\xaf\x4c\x36\xde\x6f\x8c\x32\xe3\x5d\x56\x7a\x47\xf2\xaf\x89\x44\x3b\xbd\x93\x49\xef\x2e\xe6\x09\xc6\x3b\x95\xf4\x6e\x22\xbd\x17\x7b\xce\xf8\x83\x16\x7b\xdc\x6e\xbc\x8b\x19\xc9\x31\xde\xe5\x5c\x60\x37\xf8\x52\xce\xab\x39\xff\x07\xce\xbf\xc5\xf9\xfd\x9c\x6f\xe3\x7c\xa7\xdd\x78\xa7\x35\x6a\x37\xde\x89\xa4\x77\x03\x0b\xb9\x1c\x34\x95\x19\x77\x1b\xef\x16\x5a\xd9\xe8\xfb\x91\xf4\x4e\x2d\xc9\xf0\x09\xe4\x14\xfe\x07\x3a\xa2\xdf\x43\xa3\xdf\x40\xa3\xdf\x3d\xa3\xdf\x3a\xd3\x7f\xdf\xac\xce\xf8\x5d\xb5\xd4\x4f\xfe\x12\xe3\x77\xe9\xe8\x37\xde\xe8\x77\xc3\x7e\x37\xe7\x8b\x75\x4a\xf3\xff\xdf\x10\x7d\x12\xf4\xbd\x83\x65\x4b\x96\xcc\x97\x4a\x97\xdd\x76\xc7\x75\xd2\xec\xb2\xb9\x52\x85\xbb\x1c\xff\xbb\xe7\x4a\xa5\x81\xa0\x3f\xe8\x6f\xf5\xfb\x42\xfe\xeb\xb8\xce\x32\x38\x47\x65\xd6\x84\x1b\x8d\x54\x5e\x56\x4e\xef\xaa\x2e\xa2\x97\x3b\xf5\x9f\x89\x60\x02\xbd\x93\x6a\x9b\xb3\x6e\x29\x4b\xb3\xda\x85\x0c\x21\x53\xcc\xb1\xe6\x09\x93\x84\x29\x96\x7c\xa1\x40\xbc\x4a\x9c\x21\xb0\xb2\x50\x33\xfd\x56\x8a\x6f\x23\x2b\x6b\xd8\xd2\x16\xda\xb2\xc9\xe0\xe1\x20\x2b\x6b\xf6\x85\x9a\x59\x19\x46\xa5\x1c\x23\xa1\x5f\x4b\xc2\xfe\xcd\xf8\x77\xd1\xda\xd5\x65\x48\x50\x43\x23\x49\xbf\x4f\x51\x16\x6c\xd7\xdf\x60\x2f\x6b\x6c\x69\x6b\xa9\xf5\x05\x83\xbe\x2d\xac\x0c\xc9\x70\x32\x8d\x9e\x7c\x9b\x5a\xea\x8d\xbb\x4d\x99\x51\x77\x63\x28\xc4\xca\xea\xdb\x37\x6d\xf2\xb7\x21\xaf\xad\x3d\xec\x2f\x6b\x6a\xeb\x28\xa3\x39\xcd\xbc\x0f\xf7\x02\xfa\xe5\x00\x7d\x0c\x5f\x38\x1c\x6c\xd9\xd8\x11\xf6\x87\xfe\x6f\x6c\x62\x02\x27\xf2\x15\xb2\x79\xa2\x01\xbb\x81\x05\x36\x6a\x7f\xe4\x23\x16\x5e\x8f\x7c\x81\xe8\x71\xd3\x8b\xc5\x49\x7b\x24\x9b\xb5\xf1\x7a\xe4\x23\x44\xcb\xad\xa3\xe5\xc9\xdf\x37\x2c\xe2\x6b\x44\xf5\xc8\x17\x89\xa2\x6c\x6c\x3d\xea\x7e\xba\xa9\x1e\xf9\x30\xd1\x00\xaf\x97\x66\xaa\x57\x66\xca\x4b\xda\x4d\x95\xc9\x49\x92\xef\x4f\xcf\x32\xd5\xdb\x7c\x85\x41\x5e\xd3\x0b\xdf\xc9\x29\xdd\xc8\xeb\xe9\xf2\x2d\x32\x68\x39\xfb\x62\x7f\x95\x46\xbd\x00\xbd\x9b\x6e\xfe\x2d\xc7\xf4\x94\x79\xdc\xce\xfb\xa3\xf7\xfc\xc9\xb7\x88\x3e\x10\xc6\xea\x8f\x88\xde\xeb\xcf\xe6\xba\xd6\x7f\xfb\xf1\xb6\xb1\xef\x6f\x27\xc7\xa5\xf7\x95\xb3\x78\x3d\x8a\x15\x33\x6e\x1b\xfb\x8e\x78\xb2\xeb\x76\x3e\x57\xaa\xa7\xff\x56\xe5\x6d\xc6\xef\x54\x8a\x29\xf2\x6d\xe6\x6d\xa8\x1e\xbd\x9f\xbc\x1c\x02\xff\x74\x9c\x71\x1f\x34\xd5\xd3\x7f\x44\xc0\x3b\xbe\x7c\xdd\xbc\x5f\xaa\x47\x71\xc6\xee\x35\xe2\x5b\x6a\xbd\x87\x79\x7f\x6e\x8e\xa9\xde\xb5\xec\x8b\x7a\xd9\xc7\x46\x75\x4a\x1f\xf7\x65\xfa\x3b\x66\x8c\x19\x48\xe6\x57\x79\xc7\xfe\x2e\x80\x60\xe2\xe6\xf7\xdd\xb7\x78\xc7\xce\x37\x59\xef\xff\x04\x00\x00\xff\xff\xdf\x64\xa5\x36\x7c\x54\x00\x00")
65 |
66 | func libs_armeabi_libmyfps_so_bytes() ([]byte, error) {
67 | return bindata_read(
68 | _libs_armeabi_libmyfps_so,
69 | "libs/armeabi/libmyfps.so",
70 | )
71 | }
72 |
73 | func libs_armeabi_libmyfps_so() (*asset, error) {
74 | bytes, err := libs_armeabi_libmyfps_so_bytes()
75 | if err != nil {
76 | return nil, err
77 | }
78 |
79 | info := bindata_file_info{name: "libs/armeabi/libmyfps.so", size: 21628, mode: os.FileMode(493), modTime: time.Unix(1423542890, 0)}
80 | a := &asset{bytes: bytes, info: info}
81 | return a, nil
82 | }
83 |
84 | // Asset loads and returns the asset for the given name.
85 | // It returns an error if the asset could not be found or
86 | // could not be loaded.
87 | func Asset(name string) ([]byte, error) {
88 | cannonicalName := strings.Replace(name, "\\", "/", -1)
89 | if f, ok := _bindata[cannonicalName]; ok {
90 | a, err := f()
91 | if err != nil {
92 | return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
93 | }
94 | return a.bytes, nil
95 | }
96 | return nil, fmt.Errorf("Asset %s not found", name)
97 | }
98 |
99 | // AssetInfo loads and returns the asset info for the given name.
100 | // It returns an error if the asset could not be found or
101 | // could not be loaded.
102 | func AssetInfo(name string) (os.FileInfo, error) {
103 | cannonicalName := strings.Replace(name, "\\", "/", -1)
104 | if f, ok := _bindata[cannonicalName]; ok {
105 | a, err := f()
106 | if err != nil {
107 | return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
108 | }
109 | return a.info, nil
110 | }
111 | return nil, fmt.Errorf("AssetInfo %s not found", name)
112 | }
113 |
114 | // AssetNames returns the names of the assets.
115 | func AssetNames() []string {
116 | names := make([]string, 0, len(_bindata))
117 | for name := range _bindata {
118 | names = append(names, name)
119 | }
120 | return names
121 | }
122 |
123 | // _bindata is a table, holding each asset generator, mapped to its name.
124 | var _bindata = map[string]func() (*asset, error){
125 | "libs/armeabi/libmyfps.so": libs_armeabi_libmyfps_so,
126 | }
127 |
128 | // AssetDir returns the file names below a certain
129 | // directory embedded in the file by go-bindata.
130 | // For example if you run go-bindata on data/... and data contains the
131 | // following hierarchy:
132 | // data/
133 | // foo.txt
134 | // img/
135 | // a.png
136 | // b.png
137 | // then AssetDir("data") would return []string{"foo.txt", "img"}
138 | // AssetDir("data/img") would return []string{"a.png", "b.png"}
139 | // AssetDir("foo.txt") and AssetDir("notexist") would return an error
140 | // AssetDir("") will return []string{"data"}.
141 | func AssetDir(name string) ([]string, error) {
142 | node := _bintree
143 | if len(name) != 0 {
144 | cannonicalName := strings.Replace(name, "\\", "/", -1)
145 | pathList := strings.Split(cannonicalName, "/")
146 | for _, p := range pathList {
147 | node = node.Children[p]
148 | if node == nil {
149 | return nil, fmt.Errorf("Asset %s not found", name)
150 | }
151 | }
152 | }
153 | if node.Func != nil {
154 | return nil, fmt.Errorf("Asset %s not found", name)
155 | }
156 | rv := make([]string, 0, len(node.Children))
157 | for name := range node.Children {
158 | rv = append(rv, name)
159 | }
160 | return rv, nil
161 | }
162 |
163 | type _bintree_t struct {
164 | Func func() (*asset, error)
165 | Children map[string]*_bintree_t
166 | }
167 | var _bintree = &_bintree_t{nil, map[string]*_bintree_t{
168 | "libs": &_bintree_t{nil, map[string]*_bintree_t{
169 | "armeabi": &_bintree_t{nil, map[string]*_bintree_t{
170 | "libmyfps.so": &_bintree_t{libs_armeabi_libmyfps_so, map[string]*_bintree_t{
171 | }},
172 | }},
173 | }},
174 | }}
175 |
176 | // Restore an asset under the given directory
177 | func RestoreAsset(dir, name string) error {
178 | data, err := Asset(name)
179 | if err != nil {
180 | return err
181 | }
182 | info, err := AssetInfo(name)
183 | if err != nil {
184 | return err
185 | }
186 | err = os.MkdirAll(_filePath(dir, path.Dir(name)), os.FileMode(0755))
187 | if err != nil {
188 | return err
189 | }
190 | err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
191 | if err != nil {
192 | return err
193 | }
194 | err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
195 | if err != nil {
196 | return err
197 | }
198 | return nil
199 | }
200 |
201 | // Restore assets under the given directory recursively
202 | func RestoreAssets(dir, name string) error {
203 | children, err := AssetDir(name)
204 | if err != nil { // File
205 | return RestoreAsset(dir, name)
206 | } else { // Dir
207 | for _, child := range children {
208 | err = RestoreAssets(dir, path.Join(name, child))
209 | if err != nil {
210 | return err
211 | }
212 | }
213 | }
214 | return nil
215 | }
216 |
217 | func _filePath(dir, name string) string {
218 | cannonicalName := strings.Replace(name, "\\", "/", -1)
219 | return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
220 | }
221 |
222 |
--------------------------------------------------------------------------------
/fps.c:
--------------------------------------------------------------------------------
1 | /*
2 | * foo.c
3 | * Copyright (C) 2015 hzsunshx
4 | *
5 | * Distributed under terms of the MIT license.
6 | */
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 | #define PAGE_START(addr, size) ~((size) - 1) & (addr)
18 |
19 | EGLBoolean (*old_eglSwapBuffers)(EGLDisplay dpy, EGLSurface surf) = NULL;
20 |
21 | float time_elapsed(struct timeval t0, struct timeval t1){
22 | return (t1.tv_sec - t0.tv_sec) * 1000.0f + (t1.tv_usec - t0.tv_usec) / 1000.0f;
23 | }
24 |
25 | FILE * logfp;
26 | #define LOG(fmt, args...) fprintf(logfp, fmt, ##args)
27 |
28 | unsigned int frames = 0;
29 | struct timeval start_time, curr_time;
30 | float elapsed = 0.0;
31 | float fps = 0.0;
32 |
33 | EGLBoolean new_eglSwapBuffers(EGLDisplay dpy, EGLSurface surface)
34 | {
35 | if (frames == 0){
36 | gettimeofday(&start_time, NULL);
37 | LOG("INJECT reset clock");
38 | }
39 | frames++;
40 | gettimeofday(&curr_time, NULL);
41 | elapsed = time_elapsed(start_time, curr_time);
42 | if (elapsed > 1000.0) { // > 1s
43 | fps = frames * 1000.0 / elapsed;
44 | LOG("INJECT fps=%.2f frames=%d elapsed=%.2f\n", fps, frames, elapsed);
45 | frames = 0;
46 | }
47 |
48 | if (old_eglSwapBuffers == NULL){
49 | LOG("error\n");
50 | }
51 | return old_eglSwapBuffers(dpy, surface);
52 | }
53 |
54 | int new_puts(const char *a){
55 | printf("hacked, This is new shard lib\n");
56 | }
57 |
58 | int func_replace(long func_addr, long new_func, long old_func){
59 | long page_size = getpagesize();
60 | long entry_page_start = (func_addr) & (~(page_size - 1));
61 | LOG("page start: %p\n", entry_page_start);
62 | int ret = mprotect((long *)entry_page_start, page_size, PROT_READ | PROT_WRITE);
63 | LOG("mprotect return: %d\n", ret);
64 | if (ret == 0){
65 | long *target = (long*)(func_addr);
66 | LOG("cur : %p\n", *target);
67 | LOG("new : %p\n", new_func);
68 | LOG("old : %p\n", puts);
69 | if (*target == (long)old_func){
70 | *target = new_func;
71 | } else {
72 | *target = (long)old_func;
73 | }
74 | }
75 | }
76 |
77 | int hook_entry(char*a){
78 | logfp = fopen("/data/local/tmp/log.txt", "a+");
79 | if (logfp == NULL){
80 | fprintf(logfp, "create log file failed\n");
81 | }
82 |
83 | LOG("arguments: %s\n", a);
84 | long addr = 0;
85 | sscanf(a, "%p", &addr);
86 | LOG("addr = %p\n", addr);
87 | //func_replace(addr, (long)new_puts, (long)puts);
88 | func_replace(addr, (long)new_eglSwapBuffers, (long)eglSwapBuffers);
89 | //int ret = mkfifo("/data/local/tmp/my_fifo", 0777);
90 | //printf("ret: %d\n", ret);
91 | LOG("Hook foo success\n");
92 |
93 | fclose(logfp);
94 | return 0;
95 | }
96 |
--------------------------------------------------------------------------------
/inject.c:
--------------------------------------------------------------------------------
1 | /*
2 | * inject.c
3 | * Use dlopen dlsym and ptrace to inject a lib into a running process
4 | */
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 | // FIXME(ssx): alse define here
18 | #include
19 | #include // `struct pt_regs` defined here
20 |
21 | #include "utils.h"
22 |
23 | #if defined(__i386__)
24 | #define pt_regs user_regs_struct
25 | #endif
26 |
27 |
28 | const char *libc_path = "/system/lib/libc.so";
29 | const char *linker_path = "/system/bin/linker";
30 |
31 | int ptrace_readdata(pid_t pid, uint8_t *src, uint8_t *buf, size_t size)
32 | {
33 | uint32_t i, j, remain;
34 | uint8_t *laddr;
35 |
36 | union u {
37 | long val;
38 | char chars[sizeof(long)];
39 | } d;
40 |
41 | j = size / 4;
42 | remain = size % 4;
43 |
44 | laddr = buf;
45 |
46 | for (i = 0; i < j; i ++) {
47 | d.val = ptrace(PTRACE_PEEKTEXT, pid, src, 0);
48 | memcpy(laddr, d.chars, 4);
49 | src += 4;
50 | laddr += 4;
51 | }
52 |
53 | if (remain > 0) {
54 | d.val = ptrace(PTRACE_PEEKTEXT, pid, src, 0);
55 | memcpy(laddr, d.chars, remain);
56 | }
57 |
58 | return 0;
59 | }
60 |
61 | int ptrace_writedata(pid_t pid, uint8_t *dest, uint8_t *data, size_t size)
62 | {
63 | uint32_t i, j, remain;
64 | uint8_t *laddr;
65 |
66 | union u {
67 | long val;
68 | char chars[sizeof(long)];
69 | } d;
70 |
71 | j = size / 4;
72 | remain = size % 4;
73 |
74 | laddr = data;
75 |
76 | for (i = 0; i < j; i ++) {
77 | memcpy(d.chars, laddr, 4);
78 | ptrace(PTRACE_POKETEXT, pid, dest, d.val);
79 |
80 | dest += 4;
81 | laddr += 4;
82 | }
83 |
84 | if (remain > 0) {
85 | d.val = ptrace(PTRACE_PEEKTEXT, pid, dest, 0);
86 | for (i = 0; i < remain; i ++) {
87 | d.chars[i] = *laddr ++;
88 | }
89 |
90 | ptrace(PTRACE_POKETEXT, pid, dest, d.val);
91 | }
92 |
93 | return 0;
94 | }
95 |
96 | #if defined(__arm__)
97 | int ptrace_call(pid_t pid, uint32_t addr, long *params, uint32_t num_params, struct pt_regs* regs)
98 | {
99 | uint32_t i;
100 | for (i = 0; i < num_params && i < 4; i ++) {
101 | regs->uregs[i] = params[i];
102 | }
103 |
104 | //
105 | // push remained params onto stack
106 | //
107 | if (i < num_params) {
108 | regs->ARM_sp -= (num_params - i) * sizeof(long) ;
109 | ptrace_writedata(pid, (void *)regs->ARM_sp, (uint8_t *)¶ms[i], (num_params - i) * sizeof(long));
110 | }
111 |
112 | regs->ARM_pc = addr;
113 | if (regs->ARM_pc & 1) {
114 | /* thumb */
115 | regs->ARM_pc &= (~1u);
116 | regs->ARM_cpsr |= CPSR_T_MASK;
117 | } else {
118 | /* arm */
119 | regs->ARM_cpsr &= ~CPSR_T_MASK;
120 | }
121 |
122 | regs->ARM_lr = 0;
123 |
124 | if (ptrace_setregs(pid, regs) == -1
125 | || ptrace_continue(pid) == -1) {
126 | printf("error\n");
127 | return -1;
128 | }
129 |
130 | int stat = 0;
131 | waitpid(pid, &stat, WUNTRACED);
132 | while (stat != 0xb7f) {
133 | if (ptrace_continue(pid) == -1) {
134 | printf("error\n");
135 | return -1;
136 | }
137 | waitpid(pid, &stat, WUNTRACED);
138 | }
139 |
140 | return 0;
141 | }
142 |
143 | #elif defined(__i386__)
144 | long ptrace_call(pid_t pid, uint32_t addr, long *params, uint32_t num_params, struct pt_regs * regs)
145 | {
146 | regs->esp -= (num_params) * sizeof(long) ;
147 | ptrace_writedata(pid, (void *)regs->esp, (uint8_t *)params, (num_params) * sizeof(long));
148 |
149 | long tmp_addr = 0x00;
150 | regs->esp -= sizeof(long);
151 | ptrace_writedata(pid, regs->esp, (char *)&tmp_addr, sizeof(tmp_addr));
152 |
153 | regs->eip = addr;
154 |
155 | if (ptrace_setregs(pid, regs) == -1
156 | || ptrace_continue( pid) == -1) {
157 | printf("error\n");
158 | return -1;
159 | }
160 |
161 | int stat = 0;
162 | waitpid(pid, &stat, WUNTRACED);
163 | while (stat != 0xb7f) {
164 | if (ptrace_continue(pid) == -1) {
165 | printf("error\n");
166 | return -1;
167 | }
168 | waitpid(pid, &stat, WUNTRACED);
169 | }
170 |
171 | return 0;
172 | }
173 | #else
174 | #error "Not supported"
175 | #endif
176 |
177 | int ptrace_getregs(pid_t pid, struct pt_regs * regs)
178 | {
179 | if (ptrace(PTRACE_GETREGS, pid, NULL, regs) < 0) {
180 | perror("ptrace_getregs: Can not get register values");
181 | return -1;
182 | }
183 |
184 | return 0;
185 | }
186 |
187 | int ptrace_setregs(pid_t pid, struct pt_regs * regs)
188 | {
189 | if (ptrace(PTRACE_SETREGS, pid, NULL, regs) < 0) {
190 | perror("ptrace_setregs: Can not set register values");
191 | return -1;
192 | }
193 |
194 | return 0;
195 | }
196 |
197 | int ptrace_continue(pid_t pid)
198 | {
199 | if (ptrace(PTRACE_CONT, pid, NULL, 0) < 0) {
200 | perror("ptrace_cont");
201 | return -1;
202 | }
203 |
204 | return 0;
205 | }
206 |
207 | int ptrace_attach(pid_t pid)
208 | {
209 | if (ptrace(PTRACE_ATTACH, pid, NULL, 0) < 0) {
210 | perror("ptrace_attach");
211 | return -1;
212 | }
213 |
214 | int status = 0;
215 | waitpid(pid, &status , WUNTRACED);
216 |
217 | return 0;
218 | }
219 |
220 | int ptrace_detach(pid_t pid)
221 | {
222 | if (ptrace(PTRACE_DETACH, pid, NULL, 0) < 0) {
223 | perror("ptrace_detach");
224 | return -1;
225 | }
226 |
227 | return 0;
228 | }
229 |
230 | void* get_remote_addr(pid_t target_pid, const char* module_name, void* local_addr)
231 | {
232 | void* local_handle, *remote_handle;
233 |
234 | local_handle = (void*)get_module_base(-1, module_name);
235 | remote_handle = (void*)get_module_base(target_pid, module_name);
236 |
237 | DEBUG_PRINT("[+] get_remote_addr: local[%x], remote[%x]\n", local_handle, remote_handle);
238 |
239 | void * ret_addr = (void *)((uint32_t)local_addr + (uint32_t)remote_handle - (uint32_t)local_handle);
240 |
241 | #if defined(__i386__)
242 | if (!strcmp(module_name, libc_path)) {
243 | ret_addr += 2;
244 | }
245 | #endif
246 | return ret_addr;
247 | }
248 |
249 | int find_pid_of(const char *process_name)
250 | {
251 | int id;
252 | pid_t pid = -1;
253 | DIR* dir;
254 | FILE *fp;
255 | char filename[32];
256 | char cmdline[256];
257 |
258 | struct dirent * entry;
259 |
260 | if (process_name == NULL)
261 | return -1;
262 |
263 | dir = opendir("/proc");
264 | if (dir == NULL)
265 | return -1;
266 |
267 | while((entry = readdir(dir)) != NULL) {
268 | id = atoi(entry->d_name);
269 | if (id != 0) {
270 | sprintf(filename, "/proc/%d/cmdline", id);
271 | fp = fopen(filename, "r");
272 | if (fp) {
273 | fgets(cmdline, sizeof(cmdline), fp);
274 | fclose(fp);
275 |
276 | if (strcmp(process_name, cmdline) == 0) {
277 | /* process found */
278 | pid = id;
279 | break;
280 | }
281 | }
282 | }
283 | }
284 |
285 | closedir(dir);
286 | return pid;
287 | }
288 |
289 | long ptrace_retval(struct pt_regs * regs)
290 | {
291 | #if defined(__arm__)
292 | return regs->ARM_r0;
293 | #elif defined(__i386__)
294 | return regs->eax;
295 | #else
296 | #error "Not supported"
297 | #endif
298 | }
299 |
300 | long ptrace_ip(struct pt_regs * regs)
301 | {
302 | #if defined(__arm__)
303 | return regs->ARM_pc;
304 | #elif defined(__i386__)
305 | return regs->eip;
306 | #else
307 | #error "Not supported"
308 | #endif
309 | }
310 |
311 | int ptrace_call_wrapper(pid_t target_pid, const char * func_name, void * func_addr, long * parameters, int param_num, struct pt_regs * regs)
312 | {
313 | DEBUG_PRINT("[+] Calling %s in target process.\n", func_name);
314 | if (ptrace_call(target_pid, (uint32_t)func_addr, parameters, param_num, regs) == -1)
315 | return -1;
316 |
317 | if (ptrace_getregs(target_pid, regs) == -1)
318 | return -1;
319 | DEBUG_PRINT("[+] Target process returned from %s, return value=%x, pc=%x \n",
320 | func_name, ptrace_retval(regs), ptrace_ip(regs));
321 | return 0;
322 | }
323 |
324 | int inject_remote_process(pid_t target_pid, const char *library_path, const char *function_name, const char *param, size_t param_size)
325 | {
326 | int ret = -1;
327 | void *mmap_addr, *dlopen_addr, *dlsym_addr, *dlclose_addr, *dlerror_addr;
328 | void *local_handle, *remote_handle, *dlhandle;
329 | uint8_t *map_base = 0;
330 | uint8_t *dlopen_param1_ptr, *dlsym_param2_ptr, *saved_r0_pc_ptr, *inject_param_ptr, *remote_code_ptr, *local_code_ptr;
331 |
332 | struct pt_regs regs, original_regs;
333 | extern uint32_t _dlopen_addr_s, _dlopen_param1_s, _dlopen_param2_s, _dlsym_addr_s, \
334 | _dlsym_param2_s, _dlclose_addr_s, _inject_start_s, _inject_end_s, _inject_function_param_s, \
335 | _saved_cpsr_s, _saved_r0_pc_s;
336 |
337 | uint32_t code_length;
338 | long parameters[10];
339 |
340 | DEBUG_PRINT("[+] Injecting process: %d\n", target_pid);
341 |
342 | if (ptrace_attach(target_pid) == -1)
343 | goto exit;
344 |
345 | if (ptrace_getregs(target_pid, ®s) == -1)
346 | goto exit;
347 |
348 | /* save original registers */
349 | memcpy(&original_regs, ®s, sizeof(regs));
350 |
351 | mmap_addr = get_remote_addr(target_pid, libc_path, (void *)mmap);
352 | DEBUG_PRINT("[+] Remote mmap address: %x\n", mmap_addr);
353 |
354 | /* call mmap */
355 | parameters[0] = 0; // addr
356 | parameters[1] = 0x4000; // size
357 | parameters[2] = PROT_READ | PROT_WRITE | PROT_EXEC; // prot
358 | parameters[3] = MAP_ANONYMOUS | MAP_PRIVATE; // flags
359 | parameters[4] = 0; //fd
360 | parameters[5] = 0; //offset
361 |
362 | if (ptrace_call_wrapper(target_pid, "mmap", mmap_addr, parameters, 6, ®s) == -1)
363 | goto exit;
364 |
365 | map_base = ptrace_retval(®s);
366 | DEBUG_PRINT("[+] map_base %p\n", map_base);
367 |
368 | dlopen_addr = get_remote_addr( target_pid, linker_path, (void *)dlopen );
369 | dlsym_addr = get_remote_addr( target_pid, linker_path, (void *)dlsym );
370 | dlclose_addr = get_remote_addr( target_pid, linker_path, (void *)dlclose );
371 | dlerror_addr = get_remote_addr( target_pid, linker_path, (void *)dlerror );
372 |
373 | DEBUG_PRINT("[+] Get imports: dlopen: %x, dlsym: %x, dlclose: %x, dlerror: %x\n",
374 | dlopen_addr, dlsym_addr, dlclose_addr, dlerror_addr);
375 |
376 | printf("library path = %s\n", library_path);
377 | ptrace_writedata(target_pid, map_base, library_path, strlen(library_path) + 1);
378 |
379 | parameters[0] = map_base;
380 | parameters[1] = RTLD_NOW| RTLD_GLOBAL;
381 |
382 | if (ptrace_call_wrapper(target_pid, "dlopen", dlopen_addr, parameters, 2, ®s) == -1)
383 | goto exit;
384 |
385 | void * sohandle = ptrace_retval(®s);
386 |
387 | #define FUNCTION_NAME_ADDR_OFFSET 0x100
388 | ptrace_writedata(target_pid, map_base + FUNCTION_NAME_ADDR_OFFSET, function_name, strlen(function_name) + 1);
389 | parameters[0] = sohandle;
390 | parameters[1] = map_base + FUNCTION_NAME_ADDR_OFFSET;
391 |
392 | if (ptrace_call_wrapper(target_pid, "dlsym", dlsym_addr, parameters, 2, ®s) == -1)
393 | goto exit;
394 |
395 | void * hook_entry_addr = ptrace_retval(®s);
396 | DEBUG_PRINT("hook_entry_addr = %p\n", hook_entry_addr);
397 |
398 | #define FUNCTION_PARAM_ADDR_OFFSET 0x200
399 | ptrace_writedata(target_pid, map_base + FUNCTION_PARAM_ADDR_OFFSET, param, strlen(param) + 1);
400 | parameters[0] = map_base + FUNCTION_PARAM_ADDR_OFFSET;
401 |
402 | if (ptrace_call_wrapper(target_pid, "hook_entry", hook_entry_addr, parameters, 1, ®s) == -1)
403 | goto exit;
404 |
405 | //printf("Press enter to dlclose and detach\n");
406 | //getchar();
407 | parameters[0] = sohandle;
408 |
409 | if (ptrace_call_wrapper(target_pid, "dlclose", dlclose, parameters, 1, ®s) == -1)
410 | goto exit;
411 |
412 | /* restore */
413 | printf("detach\n");
414 | ptrace_setregs(target_pid, &original_regs);
415 | ptrace_detach(target_pid);
416 | ret = 0;
417 |
418 | exit:
419 | return ret;
420 | }
421 |
422 | void print_usage( const char *pname, int exit_code )
423 | {
424 | printf( "Usage: %s -p pid -l libpath\n", pname );
425 | printf( " -h --help Display this usage information.\n"
426 | " -p --pid PID of target process.\n"
427 | " -l --libpath Absolute path of the shared library that will be injected.\n" );
428 |
429 | exit( exit_code );
430 | }
431 |
432 | #ifndef GOLANG
433 | int main( int argc, char** argv )
434 | {
435 | int target_pid;
436 | char *libpath;
437 |
438 | const char *pname = strrchr( argv[0], '/' ) + 1;
439 | if (argc < 2)
440 | print_usage(pname, 1);
441 |
442 | int next_opt;
443 | const char *short_opts = "hp:l:";
444 | const struct option long_opts[] = {
445 | {"help", 0, NULL, 'h'},
446 | {"pid", 1, NULL, 'p'},
447 | {"libpath", 1, NULL, 'l'},
448 | {NULL, 0, NULL, 0 }
449 | };
450 |
451 | do
452 | {
453 | next_opt = getopt_long( argc, argv, short_opts, long_opts, NULL );
454 | switch ( next_opt )
455 | {
456 | case 'h':
457 | print_usage( pname, 0 );
458 | case 'p':
459 | target_pid = atoi( optarg );
460 | break;
461 | case 'l':
462 | libpath = optarg;
463 | break;
464 | case '?':
465 | printf("\n");
466 | print_usage( pname, 1 );
467 | case -1:
468 | break;
469 | default:
470 | ;
471 | }
472 | } while ( next_opt != -1 );
473 |
474 | char *param = "";
475 |
476 | inject_remote_process( target_pid, libpath, "hook_entry", param, strlen(param) );
477 |
478 | return 0;
479 | }
480 | #endif
481 |
--------------------------------------------------------------------------------
/inject.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #ifdef __cplusplus
6 | extern "C"
7 | {
8 | #endif
9 |
10 | int inject_remote_process(pid_t pid, const char *lib_path, const char *func_name, void *param, size_t param_size);
11 |
12 | int find_pid_of(const char *process_name);
13 |
14 | //void* get_module_base(pid_t pid, const char* module_name);
15 |
16 | #ifdef __cplusplus
17 | }
18 | #endif
19 |
20 |
21 | struct inject_param_t
22 | {
23 | pid_t from_pid;
24 | } ;
25 |
--------------------------------------------------------------------------------
/jni/Android.mk:
--------------------------------------------------------------------------------
1 | #module inject
2 | LOCAL_PATH := $(call my-dir)
3 |
4 | include $(CLEAR_VARS)
5 |
6 | LOCAL_MODULE := myfps
7 | LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv2
8 | LOCAL_SRC_FILES := myfps.c
9 |
10 | include $(BUILD_SHARED_LIBRARY)
11 |
--------------------------------------------------------------------------------
/jni/Application.mk:
--------------------------------------------------------------------------------
1 | APP_ADI := armeabi
2 | APP_PLATFORM := android-9
3 |
--------------------------------------------------------------------------------
/jni/myfps.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | #define LOG_TAG "DEBUG"
13 | #define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, fmt, ##args)
14 | #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args)
15 | #define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, fmt, ##args)
16 |
17 | EGLBoolean (*old_eglSwapBuffers)(EGLDisplay dpy, EGLSurface surf) = -1;
18 |
19 | unsigned int frames = 0;
20 | struct timeval start_time, curr_time;
21 | float elapsed = 0.0;
22 | float fps = 0.0;
23 |
24 | // return millisecond
25 | float time_elapsed(struct timeval t0, struct timeval t1){
26 | return (t1.tv_sec - t0.tv_sec) * 1000.0f + (t1.tv_usec - t0.tv_usec) / 1000.0f;
27 | }
28 |
29 | EGLBoolean new_eglSwapBuffers(EGLDisplay dpy, EGLSurface surface)
30 | {
31 | if (frames == 0){
32 | gettimeofday(&start_time, NULL);
33 | LOGI("INJECT reset clock");
34 | }
35 | frames++;
36 | gettimeofday(&curr_time, NULL);
37 | elapsed = time_elapsed(start_time, curr_time);
38 | if (elapsed > 1000.0) { // > 1s
39 | fps = frames * 1000.0 / elapsed;
40 | LOGI("INJECT fps=%.2f frames=%d elapsed=%.2f\n", fps, frames, elapsed);
41 | frames = 0;
42 | }
43 |
44 | if (old_eglSwapBuffers == -1)
45 | LOGI("error\n");
46 | LOGI("INJECT new=%p, old=%p cur=%p\n", new_eglSwapBuffers, old_eglSwapBuffers, eglSwapBuffers);
47 | return old_eglSwapBuffers(dpy, surface);
48 | }
49 |
50 | typedef EGLBoolean eglfunc(EGLDisplay dpy, EGLSurface surface);
51 |
52 | void* get_module_base(pid_t pid, const char* module_name)
53 | {
54 | FILE *fp;
55 | long addr = 0;
56 | char *pch;
57 | char filename[32];
58 | char line[1024];
59 |
60 | if (pid < 0) {
61 | /* self process */
62 | snprintf(filename, sizeof(filename), "/proc/self/maps", pid);
63 | } else {
64 | snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
65 | }
66 |
67 | fp = fopen(filename, "r");
68 |
69 | if (fp != NULL) {
70 | while (fgets(line, sizeof(line), fp)) {
71 | if (strstr(line, module_name)) {
72 | pch = strtok( line, "-" );
73 | addr = strtoul( pch, NULL, 16 );
74 |
75 | if (addr == 0x8000)
76 | addr = 0;
77 |
78 | break;
79 | }
80 | }
81 |
82 | fclose(fp) ;
83 | }
84 |
85 | return (void *)addr;
86 | }
87 |
88 | #define LIBSF_PATH "/system/lib/libsurfaceflinger.so"
89 | int hook_eglSwapBuffers(eglfunc newfunc)
90 | {
91 | old_eglSwapBuffers = eglSwapBuffers;
92 | LOGI("INJECT Orig eglSwapBuffers = %p\n", old_eglSwapBuffers);
93 | void * base_addr = get_module_base(getpid(), LIBSF_PATH);
94 | LOGI("INJECT libsurfaceflinger.so address = %p\n", base_addr);
95 |
96 | int fd;
97 | fd = open(LIBSF_PATH, O_RDONLY);
98 | if (-1 == fd) {
99 | LOGI("error\n");
100 | return -1;
101 | }
102 |
103 | Elf32_Ehdr ehdr;
104 | read(fd, &ehdr, sizeof(Elf32_Ehdr));
105 |
106 | unsigned long shdr_addr = ehdr.e_shoff;
107 | int shnum = ehdr.e_shnum;
108 | int shent_size = ehdr.e_shentsize;
109 | unsigned long stridx = ehdr.e_shstrndx;
110 |
111 | Elf32_Shdr shdr;
112 | lseek(fd, shdr_addr + stridx * shent_size, SEEK_SET);
113 | read(fd, &shdr, shent_size);
114 |
115 | char * string_table = (char *)malloc(shdr.sh_size);
116 | lseek(fd, shdr.sh_offset, SEEK_SET);
117 | read(fd, string_table, shdr.sh_size);
118 | lseek(fd, shdr_addr, SEEK_SET);
119 |
120 | int i;
121 | uint32_t out_addr = 0;
122 | uint32_t out_size = 0;
123 | uint32_t got_item = 0;
124 | int32_t got_found = 0;
125 |
126 | for (i = 0; i < shnum; i++) {
127 | read(fd, &shdr, shent_size);
128 | if (shdr.sh_type == SHT_PROGBITS) {
129 | int name_idx = shdr.sh_name;
130 | if (strcmp(&(string_table[name_idx]), ".got.plt") == 0
131 | || strcmp(&(string_table[name_idx]), ".got") == 0) {
132 | out_addr = base_addr + shdr.sh_addr;
133 | out_size = shdr.sh_size;
134 | LOGI("INJECT out_addr = %lx, out_size = %lx\n", out_addr, out_size);
135 |
136 | for (i = 0; i < out_size; i += 4) {
137 | got_item = *(uint32_t *)(out_addr + i);
138 | if (got_item == old_eglSwapBuffers) {
139 | LOGI("INJECT Found eglSwapBuffers in got\n");
140 | got_found = 1;
141 |
142 | uint32_t page_size = getpagesize();
143 | uint32_t entry_page_start = (out_addr + i) & (~(page_size - 1));
144 | mprotect((uint32_t *)entry_page_start, page_size, PROT_READ | PROT_WRITE);
145 | *(uint32_t *)(out_addr + i) = newfunc; //new_eglSwapBuffers;
146 |
147 | break;
148 | } else if (got_item == new_eglSwapBuffers) {
149 | LOGI("INJECT Already hooked\n");
150 | break;
151 | }
152 | }
153 | if (got_found)
154 | break;
155 | }
156 | }
157 | }
158 |
159 | free(string_table);
160 | close(fd);
161 | }
162 |
163 | int hook_entry(char * a){
164 | LOGI("INJECT MyHook success\n");
165 | LOGI("INJECT Start hooking\n");
166 | LOGI("INJECT new_eglSwapBuffers = %p\n", new_eglSwapBuffers);
167 | LOGI("INJECT param %s\n", a);
168 | /*
169 | FILE *fp;
170 | if ((fp = fopen(a, "wb")) == NULL){
171 | printf("Cannot open file\n");
172 | exit(1);
173 | }
174 | fprintf(fp, "Hello\n");
175 | fclose(fp);
176 | */
177 |
178 | hook_eglSwapBuffers(new_eglSwapBuffers);
179 |
180 |
181 | return 0;
182 | }
183 |
184 | int hook_restore(char * a){
185 | LOGI("INJECT MyHook success\n");
186 | LOGI("INJECT Start restore\n");
187 | LOGI("INJECT eglSwapBuffers = %p\n", eglSwapBuffers);
188 | hook_eglSwapBuffers(eglSwapBuffers);
189 | return 0;
190 | }
191 |
--------------------------------------------------------------------------------
/make.bash:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -eu
3 | cd $(dirname $0)
4 |
5 | export NDK_PROJECT_PATH=$PWD
6 |
7 | ndk-build -B NDK_APPLICATION_MK=jni/Application.mk 2>&1 | grep -v warning
8 | go-bindata libs/armeabi/...
9 | go build 2>&1 | grep -v warning
10 | cp -v airfps study-diary
11 |
--------------------------------------------------------------------------------
/run.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | SET TMP=/data/local/tmp
3 |
4 | echo "Push ..."
5 | adb push airfps %TMP%
6 | adb shell chmod 777 %TMP%/airfps
7 | adb shell %TMP%/airfps
8 |
--------------------------------------------------------------------------------
/study-diary/ELF.md:
--------------------------------------------------------------------------------
1 | ELF学习笔记
2 | ================
3 |
4 | 多谢
5 |
6 | ELF分为3中类型
7 |
8 | 1. Relocatable 下文的max.o就是这种类型
9 | 2. Executable 可执行文件
10 | 3. ShareObject 共享文件
11 |
12 | 先准备点汇编代码 [max.s]
13 |
14 | # purpose: this program finds the maximum number of a set of data items.
15 | #
16 | # variables: the registers have the following uses:
17 | # %eax-current data item
18 | # %ebx-largest data item found
19 | # %edi-holds the index of the data item being examined
20 | #
21 | # the following memory locations are used:
22 | # data_item-contains the data item. a 0 is used to terminate the data.
23 | .section .data
24 | data_items: # these are the data_items
25 | .long 3, 67, 34, 222, 45, 75, 54, 34, 44, 33, 22, 11, 66, 0
26 |
27 | .section .text
28 | .globl _start
29 | _start:
30 | movl $0, %edi # move 0 into the index
31 | movl data_items(, %edi, 4), %eax
32 | movl %eax, %ebx
33 | start_loop:
34 | cmpl $0, %eax # check to see if we are hit the end.
35 | je loop_exit
36 | incl %edi
37 | movl data_items(, %edi, 4), %eax
38 | cmpl %ebx, %eax
39 | jle start_loop
40 | movl %eax, %ebx
41 | jmp start_loop
42 | loop_exit:
43 | movl $1, %eax # 1 is the _exit() syscall
44 | int $0x80
45 |
46 | 一个求最大值的汇编程序,代码段,数据段区分的很清晰
47 |
48 | as -o max.o max.s
49 | ld -o max max.o
50 | ./max
51 | echo $? # 结果应该是222
52 |
53 | ELF文件的结构是
54 |
55 | +----------------------+
56 | | elf header |
57 | +----------------------+
58 | | .... +
59 | +----------------------+
60 | | program header table |
61 | +----------------------+
62 | |......... |
63 | +----------------------+
64 | | section header table |
65 | +----------------------+
66 | |... |
67 |
68 | program header table和section header table的位置由elf header中描述的确定
69 |
70 | 用`readelf`这个工具先解析下 `readelf --file-header max.o`
71 |
72 | ELF Header:
73 | Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
74 | Class: ELF64
75 | Data: 2nd complement, little endian
76 | Version: 1 (current)
77 | OS/ABI: UNIX - System V
78 | ABI Version: 0
79 | Type: REL (Relocatable file)
80 | Machine: Advanced Micro Devices X86-64
81 | Version: 0x1
82 | Entry point address: 0x0
83 | Start of program headers: 0 (bytes into file)
84 | Start of section headers: 224 (bytes into file)
85 | Flags: 0x0
86 | Size of this header: 64 (bytes)
87 | Size of program headers: 0 (bytes)
88 | Number of program headers: 0
89 | Size of section headers: 64 (bytes)
90 | Number of section headers: 8
91 | Section header string table index: 5
92 |
93 | 1. 还有个Magic,不知道有什么用。算了,无所谓
94 | 2. Program header - start: 0x0, size: 0x0
95 | 3. Section header - start: 224(0x14), size: 64(0x40)
96 |
97 | 知道这些应该就差不多了,program header是空的,忽略掉
98 |
99 | 然后看看Section有什么 `readelf --sections --wide max.o`
100 |
101 | There are 8 section headers, starting at offset 0xe0:
102 |
103 | Section Headers:
104 | [Nr] Name Type Address Off Size ES Flg Lk Inf Al
105 | [ 0] NULL 0000000000000000 000000 000000 00 0 0 0
106 | [ 1] .text PROGBITS 0000000000000000 000040 00002d 00 AX 0 0 4
107 | [ 2] .rela.text RELA 0000000000000000 0003c8 000030 18 6 1 8
108 | [ 3] .data PROGBITS 0000000000000000 000070 000038 00 WA 0 0 4
109 | [ 4] .bss NOBITS 0000000000000000 0000a8 000000 00 WA 0 0 4
110 | [ 5] .shstrtab STRTAB 0000000000000000 0000a8 000031 00 0 0 1
111 | [ 6] .symtab SYMTAB 0000000000000000 0002e0 0000c0 18 7 7 8
112 | [ 7] .strtab STRTAB 0000000000000000 0003a0 000028 00 0 0 1
113 | Key to Flags:
114 | W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
115 | I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
116 | O (extra OS processing required) o (OS specific), p (processor specific)
117 |
118 | 除了.data和.text是代码里有的,其他的都是as这个汇编器自动生成的。
119 |
120 | Sections Headers[0] 竟然是空的,好奇怪,也许是历史遗留的问题.
121 |
122 | 用hexdump可以看看程序里面是个什么样子,头部大致贴出来,有部分被我省去
123 |
124 | 00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 |.ELF............|
125 | 00000010 01 00 3e 00 01 00 00 00 00 00 00 00 00 00 00 00 |..>.............|
126 | 00000020 00 00 00 00 00 00 00 00 e0 00 00 00 00 00 00 00 |................|
127 | 00000030 00 00 00 00 40 00 00 00 00 00 40 00 08 00 05 00 |....@.....@.....|
128 | 00000040 bf 00 00 00 00 67 8b 04 bd 00 00 00 00 89 c3 83 |.....g..........|
129 | 00000050 f8 00 74 12 ff c7 67 8b 04 bd 00 00 00 00 39 d8 |..t...g.......9.|
130 | 00000060 7e ed 89 c3 eb e9 b8 01 00 00 00 cd 80 00 00 00 |~...............|
131 | 00000070 03 00 00 00 43 00 00 00 22 00 00 00 de 00 00 00 |....C...".......|
132 | 00000080 2d 00 00 00 4b 00 00 00 36 00 00 00 22 00 00 00 |-...K...6..."...|
133 | 00000090 2c 00 00 00 21 00 00 00 16 00 00 00 0b 00 00 00 |,...!...........|
134 | 000000a0 42 00 00 00 00 00 00 00 00 2e 73 79 6d 74 61 62 |B.........symtab|
135 | 000000b0 00 2e 73 74 72 74 61 62 00 2e 73 68 73 74 72 74 |..strtab..shstrt|
136 | 000000c0 61 62 00 2e 72 65 6c 61 2e 74 65 78 74 00 2e 64 |ab..rela.text..d|
137 | 000000d0 61 74 61 00 2e 62 73 73 00 00 00 00 00 00 00 00 |ata..bss........|
138 | 000000e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
139 | *
140 | .......... 忽略部分
141 | 00000380 00 00 00 00 00 00 00 00 21 00 00 00 10 00 01 00 |........!.......|
142 | 00000390 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
143 | 000003a0 00 64 61 74 61 5f 69 74 65 6d 73 00 73 74 61 72 |.data_items.star|
144 | 000003b0 74 5f 6c 6f 6f 70 00 6c 6f 6f 70 5f 65 78 69 74 |t_loop.loop_exit|
145 | 000003c0 00 5f 73 74 61 72 74 00 09 00 00 00 00 00 00 00 |._start.........|
146 | 000003d0 0b 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 |................|
147 | 000003e0 1a 00 00 00 00 00 00 00 0b 00 00 00 02 00 00 00 |................|
148 | 000003f0 00 00 00 00 00 00 00 00 |........|
149 | 000003f8
150 |
151 | 有偏移地址(Off)和大小(Size)就可以直接看数据了。
152 |
153 | 比如这个.data段(start: 0x70, size: 56(0x38)):`hexdump -s 0x70 -n 56 -vC max.o`
154 |
155 | 00000070 03 00 00 00 43 00 00 00 22 00 00 00 de 00 00 00 |....C...".......|
156 | 00000080 2d 00 00 00 4b 00 00 00 36 00 00 00 22 00 00 00 |-...K...6..."...|
157 | 00000090 2c 00 00 00 21 00 00 00 16 00 00 00 0b 00 00 00 |,...!...........|
158 | 000000a0 42 00 00 00 00 00 00 00 |B.......|
159 | 000000a8
160 |
161 | 汇编的代码是这样的
162 |
163 | .section .data
164 | data_items: # these are the data_items
165 | .long 3, 67, 34, 222, 45, 75, 54, 34, 44, 33, 22, 11, 66, 0
166 |
167 | 因为是LittleEndian的嘛,所以其中0x00000003 对应 03 00 00 00
168 |
169 | 其他段的意思分别是
170 |
171 | 1. .text: 放代码的地方,一般都是只读
172 | 2. .bss: 用来存放全局未初始化的变量
173 | 3. .data: 存放全局初始化的变量
174 | 4. .stack: 栈区。 由编译器自动分配释放, 可以搞搞溢出攻击
175 | 5. .heap: 堆区, malloc, new之类的用到的
176 |
177 | .text可以用objdump这个工具反汇编 `objdump -d max.o`. 看代码跟原来的代码,很像
178 |
179 | tmp/max.o: file format elf64-x86-64
180 | Disassembly of section .text:
181 |
182 | 0000000000000000 <_start>:
183 | 0: bf 00 00 00 00 mov $0x0,%edi
184 | 5: 67 8b 04 bd 00 00 00 mov 0x0(,%edi,4),%eax
185 | c: 00
186 | d: 89 c3 mov %eax,%ebx
187 |
188 | 000000000000000f :
189 | f: 83 f8 00 cmp $0x0,%eax
190 | 12: 74 12 je 26
191 | 14: ff c7 inc %edi
192 | 16: 67 8b 04 bd 00 00 00 mov 0x0(,%edi,4),%eax
193 | 1d: 00
194 | 1e: 39 d8 cmp %ebx,%eax
195 | 20: 7e ed jle f
196 | 22: 89 c3 mov %eax,%ebx
197 | 24: eb e9 jmp f
198 |
199 | 0000000000000026 :
200 | 26: b8 01 00 00 00 mov $0x1,%eax
201 | 2b: cd 80 int $0x80
202 |
203 | 了解了大概的头部结构,然后用C语言写出一个读取elf header的代码
204 |
205 | #include
206 | #include
207 | #include
208 | #include
209 | #include
210 | #include
211 |
212 | #define LOGD(fmt, args...) {printf("[DEUBG] "); printf(fmt, ##args); printf("\n");}
213 | #define LOGE(fmt, args...) {printf("[ERROR] "); printf(fmt, ##args); printf("\n"); exit(1);}
214 |
215 | #define ELFHDR Elf64_Ehdr
216 |
217 | void dump(void* s, int len){
218 | int i;
219 | uint8_t *ent = (uint8_t*)s;
220 | for(i = 0; i < len; i++){
221 | if (i % 8 == 0){
222 | printf("\n");
223 | }
224 | printf("%02x ", ent[i]);
225 | }
226 | printf("\n");
227 | }
228 |
229 | int main(){
230 | FILE* fp;
231 | fp = fopen("max.o", "rb");
232 | if (fp == NULL){
233 | LOGE("file(max.o) open error");
234 | }
235 | ELFHDR ehdr;
236 | printf("size ehdr: %d\n", sizeof(ehdr));
237 | int n = fread(&ehdr, 1, sizeof(ehdr), fp);
238 | if (n != sizeof(ehdr)){
239 | LOGE("read error in %s at line %d", __FILE__, __LINE__);
240 | }
241 | fclose(fp);
242 | printf("section header offset: %d\n", ehdr.e_shoff);
243 | printf("section header string table index: %d\n", ehdr.e_shstrndx);
244 | printf("section entry size: %d\n", ehdr.e_shentsize);
245 | printf("flags: 0x%x\n", ehdr.e_flags);
246 | printf("version: %d\n", ehdr.e_version);
247 | printf("type: %d\n", ehdr.e_type);
248 | return 0;
249 | }
250 |
251 | 为了能兼容更多平台,ELFHDR可以这么定义
252 |
253 | #if defined(__amd64__)
254 | # define ELFHDR Elf64_Ehdr
255 | #elif defined(__i386__)
256 | # define ELFHDR Elf32_Ehdr
257 | #elif defined(__arm__)
258 | # define ELFHDR Elf32_Ehdr
259 | #else
260 | # error "Not supported"
261 | #endif
262 |
263 | 代码我放在了[myreadelf.c](study-diary/myreadelf.c)中
264 |
265 | shared lib学习,参考了
266 |
--------------------------------------------------------------------------------
/study-diary/Makefile:
--------------------------------------------------------------------------------
1 | #
2 | # Makefile
3 | # hzsunshx, 2015-01-23 17:23
4 | #
5 |
6 | OBJS = libfoo.so test libhookfoo.so counter bar inject libhookfoo2.so loadso
7 | all: $(OBJS)
8 | @echo "DONE"
9 |
10 | clean:
11 | rm $(OBJS)
12 |
13 | inject: inject.c
14 | gcc -o inject inject.c -llog -ldl
15 |
16 | libfoo.so: foo.c
17 | gcc -c -Wall -Werror -fpic foo.c
18 | gcc -shared -o libfoo.so foo.o
19 |
20 | libhookfoo.so: hookfoo.c
21 | gcc -fpic -c -llog -landroid -lEGL -lGLESv2 hookfoo.c
22 | gcc -shared -o $@ hookfoo.o
23 |
24 | libhookfoo2.so: hookfoo2.c
25 | gcc -fpic -c -llog -landroid -lEGL -lGLESv2 hookfoo2.c
26 | gcc -shared -o $@ hookfoo2.o
27 |
28 | test: main.c libfoo.so
29 | gcc -L$(PWD) -Wall -o test main.c -lfoo
30 |
31 | install: all
32 | sh push_file.sh airfps $(OBJS)
33 |
34 | # bin files
35 | counter: counter.c libfoo.so
36 | gcc -o counter counter.c
37 |
38 | bar: bar.c libfoo.so
39 | gcc -o bar -lfoo -L$(PWD) bar.c -llog
40 |
41 | loadso: loadso.c
42 | gcc -o $@ loadso.c -ldl
43 |
44 | # vim:ft=make
45 | #
46 |
--------------------------------------------------------------------------------
/study-diary/README.md:
--------------------------------------------------------------------------------
1 | steps
2 | ===============
3 |
4 | * x86-64寄存器介绍:
5 | * ptrace经典教学:
6 | * ptrace参数介绍:
7 | * X86-64调用约定:
8 | * 入栈顺序:
9 | * dlopen, dlsym的使用方法:
10 |
11 | ### trap counter
12 |
13 | ref:
14 |
15 | gcc trap_counter.c dump.c -o trap
16 | gcc counter.c -o counter
17 |
18 | # open a window
19 | ./counter
20 |
21 | ./trap
22 |
--------------------------------------------------------------------------------
/study-diary/RESOURCES.md:
--------------------------------------------------------------------------------
1 | * android下的hook技术:
2 |
--------------------------------------------------------------------------------
/study-diary/bar.c:
--------------------------------------------------------------------------------
1 | /*
2 | * counter.cc
3 | * Copyright (C) 2015 hzsunshx
4 | *
5 | * Distributed under terms of the MIT license.
6 | */
7 |
8 | #include
9 | #include
10 | #include
11 | //#include
12 |
13 | #include "foo.h"
14 |
15 | #include
16 | #define LOG_TAG "INJECT"
17 | #define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, fmt, ##args)
18 | #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args)
19 | #define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, fmt, ##args)
20 |
21 | int global = 1;
22 |
23 | int main(){
24 | printf("pid = %d\n", getpid());
25 | //printf("mkfifo addr = %p\n", mkfifo);
26 | int i;
27 | for (i = 0; i < 100; i++){
28 | printf("counter: %d, global: %d\n", i, global++);
29 | LOGI("bar print %d\n", i);
30 | foo();
31 | sleep(2);
32 | }
33 | return 0;
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/study-diary/counter.c:
--------------------------------------------------------------------------------
1 | /*
2 | * counter.cc
3 | * Copyright (C) 2015 hzsunshx
4 | *
5 | * Distributed under terms of the MIT license.
6 | */
7 |
8 | #include
9 | #include
10 |
11 | int global = 1;
12 |
13 | int main(){
14 | printf("pid = %d\n", getpid());
15 | int i;
16 | for (i = 0; i < 100; i++){
17 | printf("counter: %d, global: %d\n", i, global++);
18 | sleep(2);
19 | }
20 | return 0;
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/study-diary/fifo.c:
--------------------------------------------------------------------------------
1 | /*
2 | * fifo.c
3 | * Copyright (C) 2015 hzsunshx
4 | *
5 | * Distributed under terms of the MIT license.
6 | */
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | int main(){
14 | char* fifo_name = "/tmp/my_fifo";
15 | if (access(fifo_name, F_OK) == -1){
16 | int res = mkfifo(fifo_name, 0777);
17 | printf("FIFO created %s\n", res == 0 ? "success" : "failed");
18 | } else {
19 | printf("file exists\n");
20 | unlink(fifo_name);
21 | }
22 | return 0;
23 | }
24 |
25 |
26 |
--------------------------------------------------------------------------------
/study-diary/foo.c:
--------------------------------------------------------------------------------
1 | /*
2 | * foo.c
3 | * Copyright (C) 2015 hzsunshx
4 | *
5 | * Distributed under terms of the MIT license.
6 | */
7 |
8 | #include "foo.h"
9 |
10 | #include
11 |
12 | int HWC = 7;
13 |
14 | void foo(){
15 | //printf("This is a shared lib\n");
16 | puts("This is a shared lib");
17 | //puts("shared lib");
18 | }
19 |
20 | void hook_entry(char*a){
21 | puts("Hook foo success");
22 | }
23 |
--------------------------------------------------------------------------------
/study-diary/foo.h:
--------------------------------------------------------------------------------
1 | /*
2 | * foo.h
3 | * Copyright (C) 2015 hzsunshx
4 | *
5 | * Distributed under terms of the MIT license.
6 | */
7 |
8 | #ifndef FOO_H
9 | #define FOO_H
10 |
11 | extern void foo();
12 |
13 | #endif /* !FOO_H */
14 |
--------------------------------------------------------------------------------
/study-diary/hookfoo.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2009 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 |
24 | #include
25 | #include
26 | #include
27 | #include
28 |
29 |
30 | #define LOG_TAG "INJECT"
31 | #define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, fmt, ##args)
32 | #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args)
33 | #define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, fmt, ##args)
34 |
35 | #define PAGE_START(addr, size) ~((size) - 1) & (addr)
36 |
37 | void* get_module_base(pid_t pid, const char* module_name)
38 | {
39 | FILE *fp;
40 | long addr = 0;
41 | char *pch;
42 | char filename[32];
43 | char line[1024];
44 |
45 | if (pid < 0) {
46 | /* self process */
47 | snprintf(filename, sizeof(filename), "/proc/self/maps", pid);
48 | } else {
49 | snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
50 | }
51 |
52 | fp = fopen(filename, "r");
53 |
54 | if (fp != NULL) {
55 | while (fgets(line, sizeof(line), fp)) {
56 | if (strstr(line, module_name)) {
57 | pch = strtok( line, "-" );
58 | addr = strtoul( pch, NULL, 16 );
59 |
60 | if (addr == 0x8000)
61 | addr = 0;
62 | break;
63 | }
64 | }
65 | fclose(fp) ;
66 | }
67 | return (void *)addr;
68 | }
69 |
70 |
71 |
72 | /**
73 | * lookup symbol's GOT entry address
74 | *
75 | * module_path, absolute path of the module which imports symbol
76 | * symbol_name, name of the target symbol
77 | */
78 | uint32_t find_got_entry_address(const char *module_path, const char *symbol_name){
79 | void * module_base = get_module_base(-1, module_path);
80 |
81 | if ( module_base == 0 )
82 | {
83 | LOGE("[-] it seems that process %d does not dependent on %s", getpid(), module_path);
84 | return 0;
85 | }
86 |
87 | LOGI("[+] base address of %s: 0x%x", module_path, module_base);
88 |
89 | int fd = open(module_path, O_RDONLY);
90 | if ( fd == -1 )
91 | {
92 | LOGE("[-] open %s error!", module_path);
93 | return 0;
94 | }
95 |
96 | Elf32_Ehdr *elf_header = (Elf32_Ehdr *)malloc(sizeof(Elf32_Ehdr));
97 | if ( read(fd, elf_header, sizeof(Elf32_Ehdr)) != sizeof(Elf32_Ehdr) )
98 | {
99 | LOGE("[-] read %s error! in %s at line %d", module_path, __FILE__, __LINE__);
100 | return 0;
101 | }
102 |
103 | uint32_t sh_base = elf_header->e_shoff;
104 | uint32_t ndx = elf_header->e_shstrndx;
105 | uint32_t shstr_base = sh_base + ndx * sizeof(Elf32_Shdr);
106 | LOGI("[+] start of section headers: 0x%x", sh_base);
107 | LOGI("[+] section header string table index: %d", ndx);
108 | LOGI("[+] section header string table offset: 0x%x", shstr_base);
109 |
110 | lseek(fd, shstr_base, SEEK_SET);
111 | Elf32_Shdr *shstr_shdr = (Elf32_Shdr *)malloc(sizeof(Elf32_Shdr));
112 | if ( read(fd, shstr_shdr, sizeof(Elf32_Shdr)) != sizeof(Elf32_Shdr) )
113 | {
114 | LOGE("[-] read %s error! in %s at line %d", module_path, __FILE__, __LINE__);
115 | return 0;
116 | }
117 | LOGI("[+] section header string table offset: 0x%x", shstr_shdr->sh_offset);
118 |
119 | char *shstrtab = (char *)malloc(sizeof(char) * shstr_shdr->sh_size);
120 | lseek(fd, shstr_shdr->sh_offset, SEEK_SET);
121 | if ( read(fd, shstrtab, shstr_shdr->sh_size) != shstr_shdr->sh_size )
122 | {
123 | LOGE("[-] read %s error! in %s at line %d", module_path, __FILE__, __LINE__);
124 | return 0;
125 | }
126 |
127 | Elf32_Shdr *shdr = (Elf32_Shdr *)malloc(sizeof(Elf32_Shdr));
128 | Elf32_Shdr *relplt_shdr = (Elf32_Shdr *)malloc(sizeof(Elf32_Shdr));
129 | Elf32_Shdr *dynsym_shdr = (Elf32_Shdr *)malloc(sizeof(Elf32_Shdr));
130 | Elf32_Shdr *dynstr_shdr = (Elf32_Shdr *)malloc(sizeof(Elf32_Shdr));
131 |
132 | lseek(fd, sh_base, SEEK_SET);
133 | if ( read(fd, shdr, sizeof(Elf32_Shdr)) != sizeof(Elf32_Shdr) )
134 | {
135 | LOGE("[-] read %s error! in %s at line %d", module_path, __FILE__, __LINE__);
136 | perror("Error");
137 | return 0;
138 | }
139 | int i = 1;
140 | char *s = NULL;
141 | for ( ; i < elf_header->e_shnum; i++ )
142 | {
143 | s = shstrtab + shdr->sh_name;
144 | if ( strcmp(s, ".rel.plt") == 0 )
145 | memcpy(relplt_shdr, shdr, sizeof(Elf32_Shdr));
146 | else if ( strcmp(s, ".dynsym") == 0 )
147 | memcpy(dynsym_shdr, shdr, sizeof(Elf32_Shdr));
148 | else if ( strcmp(s, ".dynstr") == 0 )
149 | memcpy(dynstr_shdr, shdr, sizeof(Elf32_Shdr));
150 |
151 | if ( read(fd, shdr, sizeof(Elf32_Shdr)) != sizeof(Elf32_Shdr) )
152 | {
153 | LOGE("[-] read %s error! i = %d, in %s at line %d", module_path, i, __FILE__, __LINE__);
154 | return 0;
155 | }
156 | }
157 |
158 | LOGI("[+] offset of .rel.plt section: 0x%x", relplt_shdr->sh_offset);
159 | LOGI("[+] offset of .dynsym section: 0x%x", dynsym_shdr->sh_offset);
160 | LOGI("[+] offset of .dynstr section: 0x%x", dynstr_shdr->sh_offset);
161 |
162 | // read dynmaic symbol string table
163 | char *dynstr = (char *)malloc(sizeof(char) * dynstr_shdr->sh_size);
164 | lseek(fd, dynstr_shdr->sh_offset, SEEK_SET);
165 | if ( read(fd, dynstr, dynstr_shdr->sh_size) != dynstr_shdr->sh_size )
166 | {
167 | LOGE("[-] read %s error!", module_path);
168 | return 0;
169 | }
170 |
171 | // read dynamic symbol table
172 | Elf32_Sym *dynsymtab = (Elf32_Sym *)malloc(dynsym_shdr->sh_size);
173 | lseek(fd, dynsym_shdr->sh_offset, SEEK_SET);
174 | if ( read(fd, dynsymtab, dynsym_shdr->sh_size) != dynsym_shdr->sh_size )
175 | {
176 | LOGE("[-] read %s error!", module_path);
177 | return 0;
178 | }
179 |
180 | // read each entry of relocation table
181 | Elf32_Rel *rel_ent = (Elf32_Rel *)malloc(sizeof(Elf32_Rel));
182 | lseek(fd, relplt_shdr->sh_offset, SEEK_SET);
183 | if ( read(fd, rel_ent, sizeof(Elf32_Rel)) != sizeof(Elf32_Rel) )
184 | {
185 | LOGE("[-] read %s error!", module_path);
186 | return 0;
187 | }
188 | for ( i = 0; i < relplt_shdr->sh_size / sizeof(Elf32_Rel); i++ )
189 | {
190 | ndx = ELF32_R_SYM(rel_ent->r_info);
191 | if ( strcmp(dynstr + dynsymtab[ndx].st_name, symbol_name) == 0 )
192 | {
193 | LOGI("[+] got entry offset of %s: 0x%x", symbol_name, rel_ent->r_offset);
194 | break;
195 | }
196 | if ( read(fd, rel_ent, sizeof(Elf32_Rel)) != sizeof(Elf32_Rel) )
197 | {
198 | LOGE("[-] read %s error!", module_path);
199 | return 0;
200 | }
201 | }
202 |
203 | uint32_t offset = rel_ent->r_offset;
204 | Elf32_Half type = elf_header->e_type; // ET_EXEC or ET_DYN
205 |
206 | free(elf_header);
207 | free(shstr_shdr);
208 | free(shstrtab);
209 | free(shdr);
210 | free(relplt_shdr);
211 | free(dynsym_shdr);
212 | free(dynstr_shdr);
213 | free(dynstr);
214 | free(dynsymtab);
215 | free(rel_ent);
216 |
217 | // GOT entry offset is different between ELF executables and shared libraries
218 | if ( type == ET_EXEC )
219 | return offset;
220 | else if ( type == ET_DYN )
221 | return (uint32_t)(offset + module_base);
222 |
223 | return 0;
224 | }
225 |
226 | /**
227 | * replace GOT entry content of the function indicated by symbol name
228 | * with the address of hook_func
229 | *
230 | * return original addr
231 | */
232 | void* do_hook(const char *module_path, void* hook_func, const char *symbol_name){
233 | uint32_t entry_addr = find_got_entry_address(module_path, symbol_name);
234 |
235 | if ( entry_addr == 0 )
236 | return 0;
237 |
238 | void * original_addr = 0;
239 | // save original GOT entry content
240 | memcpy(&original_addr, (uint32_t *)entry_addr, sizeof(uint32_t));
241 |
242 | LOGD("[+] hook_fun addr: 0x%x", hook_func);
243 | LOGD("[+] got entry addr: 0x%x", entry_addr);
244 | LOGD("[+] original addr: 0x%x", original_addr);
245 |
246 | uint32_t page_size = getpagesize();
247 | uint32_t entry_page_start = PAGE_START(entry_addr, page_size);
248 | LOGD("[+] page size: 0x%x", page_size);
249 | LOGD("[+] entry page start: 0x%x", entry_page_start);
250 |
251 | // change the property of current page to writeable
252 | mprotect((uint32_t *)entry_page_start, page_size, PROT_READ | PROT_WRITE);
253 |
254 | // replace GOT entry content with hook_func's address
255 | memcpy((uint32_t *)entry_addr, &hook_func, sizeof(uint32_t));
256 |
257 | return original_addr;
258 | }
259 |
260 |
261 | unsigned int frames = 0;
262 | struct timeval start_time, curr_time;
263 | float elapsed = 0.0;
264 | float fps = 0.0;
265 |
266 | EGLBoolean (*orig_eglSwapBuffers)(EGLDisplay dpy, EGLSurface surf);
267 | float time_elapsed(struct timeval t0, struct timeval t1){
268 | // return millisecond
269 | return (t1.tv_sec - t0.tv_sec) * 1000.0f + (t1.tv_usec - t0.tv_usec) / 1000.0f;
270 | }
271 |
272 | EGLBoolean hooked_eglSwapBuffers(EGLDisplay dpy, EGLSurface surf)
273 | {
274 | if (frames == 0){
275 | gettimeofday(&start_time, NULL);
276 | LOGI("INJECT hook reset clock");
277 | }
278 | frames++;
279 | gettimeofday(&curr_time, NULL);
280 | elapsed = time_elapsed(start_time, curr_time);
281 | if (elapsed > 1000.0) { // > 1s
282 | fps = frames * 1000.0 / elapsed;
283 | LOGI("INJECT hook fps=%.2f frames=%d elapsed=%.2f\n", fps, frames, elapsed);
284 | frames = 0;
285 | }
286 |
287 | LOGI("eglSwapBuffers is invoked %p %p\n", orig_eglSwapBuffers, eglSwapBuffers);
288 |
289 | return (*orig_eglSwapBuffers)(dpy, surf);
290 | }
291 |
292 |
293 | void hook_entry(char* a)
294 | {
295 | LOGI("[INJECT] HOOK success, pid=%d\n", getpid());
296 | LOGI("[INJECT] Start hooking\n");
297 | /*
298 | //hook_eglSwapBuffers();
299 | char *sym = "eglSwapBuffers";
300 | char *module_path = "/system/lib/libsurfaceflinger.so";
301 |
302 | orig_eglSwapBuffers = do_hook(module_path, hooked_eglSwapBuffers, sym);
303 | if(0 == orig_eglSwapBuffers){
304 | LOGE("Hooked %s failed", sym);
305 | return ;
306 | }
307 | orig_eglCopyBuffers = do_hook(module_path, hooked_eglSwapBuffers, "eglCopyBuffers");
308 | orig_eglInitialize = do_hook(module_path, hooked_eglInitialize, "eglInitialize");
309 |
310 | LOGI("orignal eglSwapBufffers 0x%x", orig_eglSwapBuffers);
311 | */
312 | }
313 |
314 |
--------------------------------------------------------------------------------
/study-diary/hookfoo.h:
--------------------------------------------------------------------------------
1 | /*
2 | * hookfoo.h
3 | * Copyright (C) 2015 hzsunshx
4 | *
5 | * Distributed under terms of the MIT license.
6 | */
7 |
8 | #ifndef HOOKFOO_H
9 | #define HOOKFOO_H
10 |
11 | extern void foo(void);
12 |
13 | #endif /* !HOOKFOO_H */
14 |
--------------------------------------------------------------------------------
/study-diary/inject.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | // FIXME(ssx): add #cgo CFLAG in header
14 | //#define GOLANG 1
15 |
16 | #define CPSR_T_MASK ( 1u << 5 )
17 |
18 | #if defined(__amd64__)
19 | #include
20 | #include
21 | #include
22 | #define LOGD(fmt, args...) {printf("[D] "); printf(fmt, ##args); }
23 | #define LOGI(fmt, args...) {printf("[I] "); printf(fmt, ##args); }
24 | #define LOGE(fmt, args...) {printf("[E] "); printf(fmt, ##args); exit(1);}
25 | const char *libc_path = "/lib/x86_64-linux-gnu/libc-2.13.so";
26 | const char *linker_path = "/lib/x86_64-linux-gnu/ld-2.13.so";
27 | #elif defined(__i386__)
28 | #define pt_regs user_regs_struct
29 | #define LOGD(fmt, args...) {printf("[D] "); printf(fmt, ##args); printf("\n");}
30 | #define LOGI(fmt, args...) {printf("[I] "); printf(fmt, ##args); printf("\n");}
31 | #define LOGE(fmt, args...) {printf("[E] "); printf(fmt, ##args); printf("\n"); exit(1);}
32 | const char *libc_path = "/system/lib/libc.so";
33 | const char *linker_path = "/system/bin/linker";
34 | #elif defined(__arm__)
35 | #include
36 | #include
37 | #include
38 | #define LOG_TAG "INJECT"
39 | #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args)
40 | #define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, fmt, ##args)
41 | #define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, fmt, ##args)
42 | const char *libc_path = "/system/lib/libc.so";
43 | const char *linker_path = "/system/bin/linker";
44 | #else
45 | #error "Unsupported platform"
46 | #endif
47 |
48 | #define DEBUG_PRINT(format,args...) LOGD(format, ##args)
49 | typedef unsigned long ulong;
50 |
51 | int ptrace_readdata(pid_t pid, const uint8_t *src, uint8_t *buf, size_t size){
52 | uint32_t i, j, remain;
53 | uint8_t *laddr;
54 |
55 | union u {
56 | long val;
57 | char chars[sizeof(long)];
58 | } d;
59 |
60 | j = size / sizeof(ulong);
61 | remain = size % sizeof(ulong);
62 |
63 | laddr = buf;
64 |
65 | for (i = 0; i < j; i ++) {
66 | d.val = ptrace(PTRACE_PEEKTEXT, pid, src, 0);
67 | memcpy(laddr, d.chars, sizeof(ulong));
68 | src += sizeof(ulong);
69 | laddr += sizeof(ulong);
70 | }
71 |
72 | if (remain > 0) {
73 | d.val = ptrace(PTRACE_PEEKTEXT, pid, src, 0);
74 | memcpy(laddr, d.chars, remain);
75 | }
76 |
77 | return 0;
78 | }
79 |
80 | int ptrace_writedata(pid_t pid, uint8_t *dest, const uint8_t *data, size_t size)
81 | {
82 | uint32_t i, j, remain;
83 | const uint8_t *laddr;
84 |
85 | union u {
86 | long val;
87 | char chars[sizeof(long)];
88 | } d;
89 |
90 | j = size / sizeof(ulong);
91 | remain = size % sizeof(ulong);
92 |
93 | laddr = data;
94 |
95 | for (i = 0; i < j; i ++) {
96 | memcpy(d.chars, laddr, sizeof(ulong));
97 | ptrace(PTRACE_POKETEXT, pid, dest, d.val);
98 |
99 | dest += sizeof(ulong);
100 | laddr += sizeof(ulong);
101 | }
102 |
103 | if (remain > 0) {
104 | d.val = ptrace(PTRACE_PEEKTEXT, pid, dest, 0);
105 | for (i = 0; i < remain; i ++) {
106 | d.chars[i] = *laddr ++;
107 | }
108 |
109 | ptrace(PTRACE_POKETEXT, pid, dest, d.val);
110 | }
111 |
112 | return 0;
113 | }
114 |
115 | #if defined(__arm__)
116 | int ptrace_call(pid_t pid, ulong addr, long *params, uint32_t num_params, struct pt_regs* regs)
117 | {
118 | uint32_t i;
119 | for (i = 0; i < num_params && i < 4; i ++) {
120 | regs->uregs[i] = params[i];
121 | }
122 |
123 | //
124 | // push remained params onto stack
125 | //
126 | if (i < num_params) {
127 | regs->ARM_sp -= (num_params - i) * sizeof(long);
128 | ptrace_writedata(pid, (void *)regs->ARM_sp, (uint8_t *)¶ms[i], (num_params - i) * sizeof(long));
129 | }
130 |
131 | regs->ARM_pc = addr;
132 | if (regs->ARM_pc & 1) {
133 | /* thumb */
134 | regs->ARM_pc &= (~1u);
135 | regs->ARM_cpsr |= CPSR_T_MASK;
136 | } else {
137 | /* arm */
138 | regs->ARM_cpsr &= ~CPSR_T_MASK;
139 | }
140 |
141 | regs->ARM_lr = 0;
142 |
143 | if (ptrace_setregs(pid, regs) == -1
144 | || ptrace_continue(pid) == -1) {
145 | printf("error\n");
146 | return -1;
147 | }
148 |
149 | int stat = 0;
150 | waitpid(pid, &stat, WUNTRACED);
151 | while (stat != 0xb7f) {
152 | if (ptrace_continue(pid) == -1) {
153 | printf("error\n");
154 | return -1;
155 | }
156 | waitpid(pid, &stat, WUNTRACED);
157 | }
158 | return 0;
159 | }
160 |
161 | #elif defined(__i386__)
162 | long ptrace_call(pid_t pid, ulong addr, long *params, uint32_t num_params, struct pt_regs * regs)
163 | {
164 | regs->esp -= (num_params) * sizeof(long) ;
165 | ptrace_writedata(pid, (void *)regs->esp, (uint8_t *)params, (num_params) * sizeof(long));
166 |
167 | long tmp_addr = 0x00;
168 | regs->esp -= sizeof(long);
169 | ptrace_writedata(pid, regs->esp, (char *)&tmp_addr, sizeof(tmp_addr));
170 |
171 | regs->eip = addr;
172 |
173 | if (ptrace_setregs(pid, regs) == -1
174 | || ptrace_continue(pid) == -1) {
175 | printf("error\n");
176 | return -1;
177 | }
178 |
179 | int stat = 0;
180 | waitpid(pid, &stat, WUNTRACED);
181 | while (stat != 0xb7f) { // ?
182 | if (ptrace_continue(pid) == -1) {
183 | printf("error\n");
184 | return -1;
185 | }
186 | waitpid(pid, &stat, WUNTRACED);
187 | }
188 |
189 | return 0;
190 | }
191 | #elif defined(__amd64__)
192 | long ptrace_call(pid_t pid, ulong addr, long *params, uint32_t num_params, struct pt_regs * regs)
193 | {
194 | regs->rsp -= num_params * sizeof(long);
195 | ptrace_writedata(pid, (void*)regs->rsp, (uint8_t *)params, num_params * sizeof(long));
196 |
197 | ulong tmp_addr = 0x00;
198 | regs->rsp -= sizeof(ulong);
199 | ptrace_writedata(pid, (void*)regs->rsp, (char*)&tmp_addr, sizeof(tmp_addr));
200 |
201 | regs->rip = addr;
202 |
203 | if (ptrace_setregs(pid, regs) == -1
204 | || ptrace_continue( pid) == -1) {
205 | printf("error\n");
206 | return -1;
207 | }
208 |
209 | int stat = 0;
210 | waitpid(pid, &stat, WUNTRACED);
211 | while (stat != 0xb7f) {
212 | if (ptrace_continue(pid) == -1) {
213 | printf("error\n");
214 | return -1;
215 | }
216 | waitpid(pid, &stat, WUNTRACED);
217 | }
218 |
219 | return 0;
220 | }
221 | #else
222 | #error "Not supported"
223 | #endif
224 |
225 | int ptrace_getregs(pid_t pid, struct pt_regs * regs){
226 | if (ptrace(PTRACE_GETREGS, pid, NULL, regs) < 0) {
227 | perror("ptrace_getregs: Can not get register values");
228 | return -1;
229 | }
230 | return 0;
231 | }
232 |
233 | int ptrace_setregs(pid_t pid, struct pt_regs * regs){
234 | if (ptrace(PTRACE_SETREGS, pid, NULL, regs) < 0) {
235 | perror("ptrace_setregs: Can not set register values");
236 | return -1;
237 | }
238 | return 0;
239 | }
240 |
241 | int ptrace_continue(pid_t pid){
242 | if (ptrace(PTRACE_CONT, pid, NULL, 0) < 0) {
243 | perror("ptrace_cont");
244 | return -1;
245 | }
246 | return 0;
247 | }
248 |
249 | int ptrace_attach(pid_t pid){
250 | if (ptrace(PTRACE_ATTACH, pid, NULL, 0) < 0) {
251 | perror("ptrace_attach");
252 | return -1;
253 | }
254 |
255 | int status = 0;
256 | waitpid(pid, &status , WUNTRACED);
257 |
258 | return 0;
259 | }
260 |
261 | int ptrace_detach(pid_t pid){
262 | if (ptrace(PTRACE_DETACH, pid, NULL, 0) < 0) {
263 | perror("ptrace_detach");
264 | return -1;
265 | }
266 |
267 | return 0;
268 | }
269 |
270 | ulong get_module_base(pid_t pid, const char* module_name){
271 | FILE *fp;
272 | ulong addr = 0;
273 | char *pch;
274 | char filename[32];
275 | char line[1024];
276 |
277 | if (pid < 0) {
278 | /* self process */
279 | snprintf(filename, sizeof(filename), "/proc/self/maps", pid);
280 | } else {
281 | snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
282 | }
283 |
284 | fp = fopen(filename, "rb");
285 |
286 | if (fp != NULL) {
287 | while (fgets(line, sizeof(line), fp)) {
288 | if (strstr(line, module_name)) {
289 | pch = strtok(line, "-" );
290 | addr = strtoul(pch, NULL, 16);
291 |
292 | if (addr == 0x8000)
293 | addr = 0;
294 | break;
295 | }
296 | }
297 |
298 | fclose(fp) ;
299 | }
300 |
301 | return addr;
302 | }
303 |
304 | void* get_remote_addr(pid_t target_pid, const char* module_name, ulong local_addr){
305 | ulong local_handle, remote_handle;
306 |
307 | local_handle = get_module_base(-1, module_name);
308 | remote_handle = get_module_base(target_pid, module_name);
309 |
310 | DEBUG_PRINT("[+] [%s] get_remote_addr: local[%p], remote[%p]\n",
311 | module_name, local_handle, remote_handle);
312 | ulong ret_addr = local_addr + remote_handle - local_handle;
313 |
314 | #if defined(__i386__)
315 | if (!strcmp(module_name, libc_path)) {
316 | ret_addr += 2;
317 | }
318 | #endif
319 | return (void*)ret_addr;
320 | }
321 |
322 | int find_pid_of(const char *process_name){
323 | int id;
324 | pid_t pid = -1;
325 | DIR* dir;
326 | FILE *fp;
327 | char filename[32];
328 | char cmdline[256];
329 |
330 | struct dirent * entry;
331 |
332 | if (process_name == NULL)
333 | return -1;
334 |
335 | dir = opendir("/proc");
336 | if (dir == NULL)
337 | return -1;
338 |
339 | while((entry = readdir(dir)) != NULL) {
340 | id = atoi(entry->d_name);
341 | if (id != 0) {
342 | sprintf(filename, "/proc/%d/cmdline", id);
343 | fp = fopen(filename, "r");
344 | if (fp) {
345 | fgets(cmdline, sizeof(cmdline), fp);
346 | fclose(fp);
347 |
348 | if (strcmp(process_name, cmdline) == 0) {
349 | /* process found */
350 | pid = id;
351 | break;
352 | }
353 | }
354 | }
355 | }
356 |
357 | closedir(dir);
358 | return pid;
359 | }
360 |
361 | long ptrace_retval(struct pt_regs * regs)
362 | {
363 | #if defined(__arm__)
364 | return regs->ARM_r0;
365 | #elif defined(__i386__)
366 | return regs->eax;
367 | #elif defined(__amd64__)
368 | return regs->rax;
369 | #else
370 | #error "Not supported"
371 | #endif
372 | }
373 |
374 | long ptrace_ip(struct pt_regs * regs)
375 | {
376 | #if defined(__arm__)
377 | return regs->ARM_pc;
378 | #elif defined(__i386__)
379 | return regs->eip;
380 | #elif defined(__amd64__)
381 | return regs->rip;
382 | #else
383 | #error "Not supported"
384 | #endif
385 | }
386 |
387 | int ptrace_call_wrapper(pid_t target_pid, const char * func_name, void * func_addr, long * parameters, int param_num, struct pt_regs * regs)
388 | {
389 | DEBUG_PRINT("[+] Calling %s in target process.\n", func_name);
390 | if (ptrace_call(target_pid, (ulong)func_addr, parameters, param_num, regs) == -1)
391 | return -1;
392 |
393 | if (ptrace_getregs(target_pid, regs) == -1)
394 | return -1;
395 | DEBUG_PRINT("[+] Target process returned from %s, return value=%x, pc=%x \n",
396 | func_name, ptrace_retval(regs), ptrace_ip(regs));
397 | return 0;
398 | }
399 |
400 | int inject_remote_process(pid_t target_pid, const char *library_path,
401 | char *function_name, char *param, size_t param_size)
402 | {
403 | int ret = -1;
404 | void *mmap_addr, *dlopen_addr, *dlsym_addr, *dlclose_addr, *dlerror_addr;
405 | void *local_handle, *remote_handle, *dlhandle;
406 | uint8_t *map_base = 0;
407 | uint8_t *dlopen_param1_ptr, *dlsym_param2_ptr, *saved_r0_pc_ptr, *inject_param_ptr, *remote_code_ptr, *local_code_ptr;
408 |
409 | struct pt_regs regs, original_regs;
410 | extern ulong _dlopen_addr_s, _dlopen_param1_s, _dlopen_param2_s, _dlsym_addr_s, \
411 | _dlsym_param2_s, _dlclose_addr_s, _inject_start_s, _inject_end_s, _inject_function_param_s, \
412 | _saved_cpsr_s, _saved_r0_pc_s;
413 |
414 | uint32_t code_length;
415 | long parameters[10];
416 |
417 | DEBUG_PRINT("[+] Injecting process: %d\n", target_pid);
418 |
419 | if (ptrace_attach(target_pid) == -1)
420 | goto exit;
421 |
422 | if (ptrace_getregs(target_pid, ®s) == -1)
423 | goto exit;
424 |
425 | /* save original registers */
426 | memcpy(&original_regs, ®s, sizeof(regs));
427 |
428 | mmap_addr = get_remote_addr(target_pid, libc_path, (ulong)mmap);
429 | DEBUG_PRINT("[+] Remote mmap address: %x\n", mmap_addr);
430 |
431 | /* call mmap */
432 | parameters[0] = 0; // addr
433 | parameters[1] = 0x4000; // size
434 | parameters[2] = PROT_READ | PROT_WRITE | PROT_EXEC; // prot
435 | parameters[3] = MAP_ANONYMOUS | MAP_PRIVATE; // flags
436 | parameters[4] = 0; //fd
437 | parameters[5] = 0; //offset
438 |
439 | if (ptrace_call_wrapper(target_pid, "mmap", mmap_addr, parameters, 6, ®s) == -1)
440 | goto exit;
441 |
442 | map_base = (uint8_t*)ptrace_retval(®s);
443 |
444 | dlopen_addr = get_remote_addr(target_pid, linker_path, (ulong)dlopen );
445 | dlsym_addr = get_remote_addr(target_pid, linker_path, (ulong)dlsym );
446 | dlclose_addr = get_remote_addr(target_pid, linker_path, (ulong)dlclose );
447 | dlerror_addr = get_remote_addr(target_pid, linker_path, (ulong)dlerror );
448 |
449 | DEBUG_PRINT("[+] Get imports: \n\tdlopen: %p, \n\tdlsym: %p, \n\tdlclose: %p, \n\tdlerror: %p\n",
450 | dlopen_addr, dlsym_addr, dlclose_addr, dlerror_addr);
451 |
452 | printf("library path = %s\n", library_path);
453 | ptrace_writedata(target_pid, map_base, library_path, strlen(library_path) + 1);
454 |
455 | parameters[0] = (long)map_base;
456 | parameters[1] = RTLD_NOW| RTLD_GLOBAL;
457 |
458 | if (ptrace_call_wrapper(target_pid, "dlopen", dlopen_addr, parameters, 2, ®s) == -1)
459 | goto exit;
460 |
461 | void* sohandle = (void*)ptrace_retval(®s);
462 |
463 | #define FUNCTION_NAME_ADDR_OFFSET 0x100
464 | ptrace_writedata(target_pid, map_base + FUNCTION_NAME_ADDR_OFFSET, function_name, strlen(function_name) + 1);
465 | parameters[0] = (long)sohandle;
466 | parameters[1] = (long)(map_base + FUNCTION_NAME_ADDR_OFFSET);
467 |
468 | if (ptrace_call_wrapper(target_pid, "dlsym", dlsym_addr, parameters, 2, ®s) == -1)
469 | goto exit;
470 |
471 | void* hook_entry_addr = (void*)ptrace_retval(®s);
472 | DEBUG_PRINT("hook_entry_addr = %p\n", hook_entry_addr);
473 |
474 | #define FUNCTION_PARAM_ADDR_OFFSET 0x200
475 | ptrace_writedata(target_pid, map_base + FUNCTION_PARAM_ADDR_OFFSET, param, strlen(param) + 1);
476 | parameters[0] = (long)(map_base + FUNCTION_PARAM_ADDR_OFFSET);
477 |
478 | if (ptrace_call_wrapper(target_pid, "hook_entry", hook_entry_addr, parameters, 1, ®s) == -1)
479 | goto exit;
480 |
481 | //printf("Press enter to dlclose and detach\n");
482 | //getchar();
483 | parameters[0] = (long)sohandle;
484 |
485 | if (ptrace_call_wrapper(target_pid, "dlclose", dlclose, parameters, 1, ®s) == -1)
486 | goto exit;
487 |
488 | /* restore */
489 | printf("detach\n");
490 | ptrace_setregs(target_pid, &original_regs);
491 | ptrace_detach(target_pid);
492 | ret = 0;
493 |
494 | exit:
495 | return ret;
496 | }
497 |
498 | #ifndef GOLANG
499 | void print_usage( const char *pname, int exit_code ){
500 | printf( "Usage: %s -p pid -l libpath\n", pname );
501 | printf( " -h --help Display this usage information.\n"
502 | " -p --pid PID of target process.\n"
503 | " -l --libpath Absolute path of the shared library that will be injected.\n" );
504 |
505 | exit( exit_code );
506 | }
507 |
508 | int main( int argc, char** argv )
509 | {
510 | int target_pid;
511 | char *libpath;
512 |
513 | const char *pname = strrchr( argv[0], '/' ) + 1;
514 | if (argc < 2)
515 | print_usage(pname, 1);
516 |
517 | int next_opt;
518 | const char *short_opts = "hp:l:";
519 | const struct option long_opts[] = {
520 | {"help", 0, NULL, 'h'},
521 | {"pid", 1, NULL, 'p'},
522 | {"libpath", 1, NULL, 'l'},
523 | {NULL, 0, NULL, 0 }
524 | };
525 |
526 | do
527 | {
528 | next_opt = getopt_long( argc, argv, short_opts, long_opts, NULL );
529 | switch ( next_opt )
530 | {
531 | case 'h':
532 | print_usage( pname, 0 );
533 | case 'p':
534 | target_pid = atoi( optarg );
535 | break;
536 | case 'l':
537 | libpath = optarg;
538 | break;
539 | case '?':
540 | printf("\n");
541 | print_usage( pname, 1 );
542 | case -1:
543 | break;
544 | default:
545 | ;
546 | }
547 | } while ( next_opt != -1 );
548 |
549 | char *param = "";
550 |
551 | inject_remote_process(target_pid, libpath, "hook_entry", param, strlen(param));
552 |
553 | return 0;
554 | }
555 | #endif
556 |
--------------------------------------------------------------------------------
/study-diary/inject.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #ifdef __cplusplus
6 | extern "C"
7 | {
8 | #endif
9 |
10 | int inject_remote_process( pid_t target_pid, const char *library_path, const char *function_name, void *param, size_t param_size );
11 |
12 | int find_pid_of( const char *process_name );
13 |
14 | void* get_module_base( pid_t pid, const char* module_name );
15 |
16 | #ifdef __cplusplus
17 | }
18 | #endif
19 |
20 |
21 | struct inject_param_t
22 | {
23 | pid_t from_pid;
24 | } ;
25 |
--------------------------------------------------------------------------------
/study-diary/main.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include "foo.h"
3 | #include
4 |
5 | int main(void)
6 | {
7 | printf("pid = %d\n", getpid());
8 | puts("This is a shared library test...");
9 | for(;;){
10 | foo();
11 | sleep(1);
12 | }
13 | return 0;
14 | }
15 |
--------------------------------------------------------------------------------
/study-diary/max.s:
--------------------------------------------------------------------------------
1 | # purpose: this program finds the maximum number of a set of data items.
2 | #
3 | # variables: the registers have the following uses:
4 | # %eax-current data item
5 | # %ebx-largest data item found
6 | # %edi-holds the index of the data item being examined
7 | #
8 | # the following memory locations are used:
9 | # data_item-contains the data item. a 0 is used to terminate the data.
10 | .section .data
11 | data_items: # these are the data_items
12 | .long 3, 67, 34, 222, 45, 75, 54, 34, 44, 33, 22, 11, 66, 0
13 |
14 | .section .text
15 | .globl _start
16 | _start:
17 | movl $0, %edi # move 0 into the index
18 | movl data_items(, %edi, 4), %eax
19 | movl %eax, %ebx
20 | start_loop:
21 | cmpl $0, %eax # check to see if we're hit the end.
22 | je loop_exit
23 | incl %edi
24 | movl data_items(, %edi, 4), %eax
25 | cmpl %ebx, %eax
26 | jle start_loop
27 | movl %eax, %ebx
28 | jmp start_loop
29 | loop_exit:
30 | movl $1, %eax # 1 is the _exit() syscall
31 | int $0x80
32 |
--------------------------------------------------------------------------------
/study-diary/myinject/Makefile:
--------------------------------------------------------------------------------
1 | #
2 | # Makefile
3 | # hzsunshx, 2015-01-30 15:51
4 | #
5 |
6 | all:
7 | gcc dump.c trap_counter.c
8 |
9 |
10 | # vim:ft=make
11 | #
12 |
--------------------------------------------------------------------------------
/study-diary/myinject/dump.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | void hex_dump (char *desc, void *addr, int len) {
4 | int i;
5 | unsigned char buff[17];
6 | unsigned char *pc = (unsigned char*)addr;
7 |
8 | // Output description if given.
9 | if (desc != NULL)
10 | printf ("%s:\n", desc);
11 |
12 | // Process every byte in the data.
13 | for (i = 0; i < len; i++) {
14 | // Multiple of 16 means new line (with line offset).
15 |
16 | if ((i % 16) == 0) {
17 | // Just don't print ASCII for the zeroth line.
18 | if (i != 0)
19 | printf (" %s\n", buff);
20 |
21 | // Output the offset.
22 | printf (" %04x ", i);
23 | }
24 |
25 | // Now the hex code for the specific character.
26 | printf (" %02x", pc[i]);
27 |
28 | // And store a printable ASCII character for later.
29 | if ((pc[i] < 0x20) || (pc[i] > 0x7e))
30 | buff[i % 16] = '.';
31 | else
32 | buff[i % 16] = pc[i];
33 | buff[(i % 16) + 1] = '\0';
34 | }
35 |
36 | // Pad out last line if not exactly 16 characters.
37 | while ((i % 16) != 0) {
38 | printf (" ");
39 | i++;
40 | }
41 |
42 | // And print the final ASCII bit.
43 | printf (" %s\n", buff);
44 | }
45 |
--------------------------------------------------------------------------------
/study-diary/myinject/dump.h:
--------------------------------------------------------------------------------
1 | /*
2 | * dump.h
3 | * Copyright (C) 2015 hzsunshx
4 | *
5 | * Distributed under terms of the MIT license.
6 | */
7 |
8 | #ifndef DUMP_H
9 | #define DUMP_H
10 |
11 | void hex_dump (char *desc, void *addr, int len);
12 |
13 | #endif /* !DUMP_H */
14 |
--------------------------------------------------------------------------------
/study-diary/myinject/run.js:
--------------------------------------------------------------------------------
1 | var http = require('http');
2 | http.createServer(function (req, res) {
3 | res.writeHead(200, {'Content-Type': 'text/plain'});
4 | res.end('Hello World\n');
5 | }).listen(1337, '127.0.0.1');
6 | console.log('Server running at http://127.0.0.1:1337/');
7 |
--------------------------------------------------------------------------------
/study-diary/myinject/trap_counter.c:
--------------------------------------------------------------------------------
1 | /*
2 | * trace_counter.cc
3 | * Copyright (C) 2015 hzsunshx
4 | *
5 | * Distributed under terms of the MIT license.
6 | */
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 |
20 | #include "dump.h"
21 |
22 | #define pt_regs user_regs_struct
23 |
24 | #define LOGI(fmt, args...) {printf("[I] "); printf(fmt, ##args);}
25 | #define LOGE(fmt, args...) {printf("[E] "); printf(fmt, ##args);}
26 |
27 | const int long_size = sizeof(long);
28 |
29 | void getdata(pid_t child, long addr, char *str, int len){
30 | char *laddr;
31 | int i, j;
32 | union u{
33 | long val;
34 | char chars[long_size];
35 | } data;
36 |
37 | i = 0;
38 | j = len / long_size;
39 | laddr = str;
40 |
41 | while(i < j){
42 | data.val = ptrace(PTRACE_PEEKDATA, child, addr + i*long_size, NULL); // 1 or 0
43 | memcpy(laddr, data.chars, long_size);
44 | ++i;
45 | laddr += long_size;
46 | }
47 | j = len % long_size;
48 | if (j != 0){
49 | data.val = ptrace(PTRACE_PEEKDATA, child, addr + i*long_size, NULL);
50 | memcpy(laddr, data.chars, j);
51 | }
52 | }
53 |
54 | void putdata(pid_t child, long addr, char *str, int len){
55 | char *laddr;
56 | int i, j;
57 | union u {
58 | long val;
59 | char chars[long_size];
60 | } data;
61 |
62 | i = 0;
63 | j = len / long_size;
64 | laddr = str;
65 | while(i < j){
66 | memcpy(data.chars, laddr, long_size);
67 | ptrace(PTRACE_POKEDATA, child, addr + i * long_size, data.val);
68 | ++i;
69 | laddr += long_size;
70 | }
71 | j = len % long_size;
72 | if (j != 0) {
73 | data.val = ptrace(PTRACE_PEEKDATA, child, addr + i*long_size, NULL); // necessary
74 | memcpy(data.chars, laddr, j);
75 | ptrace(PTRACE_POKEDATA, child, addr + i * long_size, data.val);
76 | }
77 | }
78 |
79 | // ptrace function wrapper
80 | int ptrace_getregs(pid_t pid, struct pt_regs *regs){
81 | return ptrace(PTRACE_GETREGS, pid, NULL, regs);
82 | }
83 | int ptrace_setregs(pid_t pid, struct pt_regs *regs){
84 | return ptrace(PTRACE_SETREGS, pid, NULL, regs);
85 | }
86 | int ptrace_attach(pid_t pid){
87 | return ptrace(PTRACE_ATTACH, pid, NULL, 0);
88 | }
89 | int ptrace_detach(pid_t pid){
90 | return ptrace(PTRACE_DETACH, pid, NULL, 0);
91 | }
92 | int ptrace_continue(pid_t pid){
93 | return ptrace(PTRACE_CONT, pid, NULL, 0);
94 | }
95 | long ptrace_retval(struct pt_regs *regs){
96 | #if defined(__arm__)
97 | return regs->ARM_r0;
98 | #elif defined(__i386__)
99 | return regs->eax;
100 | #elif defined(__amd64__)
101 | return regs->rax;
102 | #else
103 | #error "retval not supported"
104 | #endif
105 | }
106 | long ptrace_ip(struct pt_regs *regs){
107 | #if defined(__arm__)
108 | return regs->ARM_pc;
109 | #elif defined(__i386__)
110 | return regs->eip;
111 | #elif defined(__amd64__)
112 | return regs->rip;
113 | #else
114 | #error "retval not supported"
115 | #endif
116 | }
117 |
118 |
119 | // function call using ptrace
120 | #if defined(__arm__)
121 | int ptrace_call(pid_t pid, long addr, long* args, int nargs, struct pt_regs regs){
122 | int i;
123 | for (i=0; i< nargs && i < 4; i++){
124 | regs->uregs[i] = args[i];
125 | }
126 |
127 | if (i < nargs) {
128 | regs->ARM_sp -= (nargs -i) * long_size;
129 | putdata(pid, regs->ARM_sp, (char*)&args[i], (nargs-i) * long_size);
130 | }
131 |
132 | regs->ARM_pc = addr;
133 | if (regs->ARM_pc & 1){
134 | /* thumb */
135 | regs->ARM_pc &= (~1u);
136 | regs->ARM_cpsr |= CPSR_T_MASK;
137 | } else {
138 | /* arm */
139 | regs->ARM_cpsr &= ~CPSR_T_MASK;
140 | }
141 |
142 | regs->ARM_lr = 0;
143 |
144 | if (ptrace_setregs(pid, ®s) != 0
145 | || ptrace_continue(pid) != 0){
146 | LOGE("error\n");
147 | return -1;
148 | }
149 |
150 | int stat = 0;
151 | waitpid(pid, &stat, WUNTRACED);
152 | while (stat != 0xb7f){
153 | if (ptrace_continue(pid) == -1){
154 | LOGE("error waitpid\n");
155 | return -1;
156 | }
157 | waitpid(pid, &stat, WUNTRACED);
158 | }
159 |
160 | return 0;
161 | }
162 | #elif defined(__amd64__)
163 | int ptrace_call(pid_t pid, long addr, long* args, int nargs, struct pt_regs regs){
164 | int i;
165 | long* urgs[6] = {®s.rdi, ®s.rsi, ®s.rdx, ®s.rcx, ®s.r8, ®s.r9};
166 | for (i=0; i< nargs && i < 6; i++){
167 | *urgs[i] = args[i];
168 | }
169 | //if (i < nargs) {
170 | // regs->ARM_sp -= (nargs -i) * long_size;
171 | // putdata(pid, regs->ARM_sp, (char*)&args[i], (nargs-i) * long_size);
172 | //}
173 | regs.rip = addr;
174 | if (ptrace_setregs(pid, ®s) != 0
175 | || ptrace_continue(pid) != 0){
176 | LOGE("error\n");
177 | return -1;
178 | }
179 | int stat = 0;
180 | waitpid(pid, &stat, WUNTRACED);
181 | int max = 10;
182 | while (stat != 0xb7f && max-- != 0){
183 | LOGI("stat: %p\n", stat);
184 | if (ptrace_continue(pid) == -1){
185 | LOGE("error waitpid\n");
186 | return -1;
187 | }
188 | waitpid(pid, &stat, WUNTRACED);
189 | }
190 | return 0;
191 | }
192 | #else
193 | #error "Not supported"
194 | #endif
195 |
196 | long get_mod_base(pid_t pid, char * mod_name){
197 | FILE *fp;
198 | long addr = 0;
199 | char *pch;
200 | char filename[32];
201 | char line[1024];
202 |
203 | if (pid < 0){
204 | snprintf(filename, sizeof(filename), "/proc/self/maps", pid);
205 | } else {
206 | snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
207 | }
208 |
209 | fp = fopen(filename, "r");
210 | if (fp != NULL){
211 | while (fgets(line, sizeof(line), fp)){
212 | if (strstr(line, mod_name)) {
213 | pch = strtok(line, "-");
214 | addr = strtoul(pch, NULL, 16);
215 | // ? check addr == 0x8000
216 | break;
217 | }
218 | }
219 | fclose(fp);
220 | }
221 | return addr;
222 | }
223 |
224 | int main(int argc, char *argv[]){
225 | if(argc != 2){
226 | printf("Usage: %s \n", argv[0]);
227 | exit(1);
228 | }
229 | //ptrace_call(
230 | return 0;
231 | }
232 | int _main(int argc, char *argv[]){
233 | pid_t traced_process;
234 | struct user_regs_struct regs, newregs;
235 | long ins;
236 | /* int 0x80, int3 */
237 | /* trap cmd */
238 | char code[] = {0xcd, 0x80, 0xcc};
239 | char backup[20];
240 | char backup2[20];
241 | if(argc != 2){
242 | printf("Usage: %s \n", argv[0]);
243 | exit(1);
244 | }
245 |
246 | traced_process = atoi(argv[1]);
247 | ins = ptrace(PTRACE_ATTACH, traced_process, NULL, NULL);
248 | printf("attrch: %d\n", ins);
249 | if (ins == -1){
250 | printf("traced failed\n");
251 | exit(1);
252 | }
253 | wait(NULL);
254 | ins = ptrace(PTRACE_GETREGS, traced_process, NULL, ®s);
255 | assert(ins == 0);
256 |
257 | // Copy instructions into a backup variable
258 | getdata(traced_process, regs.rip, backup, 16);
259 | hex_dump("backup", backup, 16);
260 | // breakpoint
261 | putdata(traced_process, regs.rip, code, 3);
262 |
263 | // debug
264 | getdata(traced_process, regs.rip, backup2, 16);
265 | hex_dump("backup2", backup2, 16);
266 |
267 | // execute the int 3 instruction
268 | ptrace(PTRACE_CONT, traced_process, NULL, NULL);
269 | wait(NULL);
270 | printf("Process stopped\n");
271 | printf("Press to continue: \n");
272 | getchar();
273 |
274 | // restore data
275 | putdata(traced_process, regs.rip, backup, 3);
276 | ptrace(PTRACE_SETREGS, traced_process, NULL, ®s);
277 |
278 | // debug
279 | getdata(traced_process, regs.rip, backup2, 8);
280 | hex_dump("backup2", backup2, 8);
281 |
282 | //printf("getregs: %d\n", ins);
283 | //ins = ptrace(PTRACE_PEEKTEXT, traced_process, regs.rip, ins);
284 | //printf("RIP: %lx Instruction executed: %lx\n", regs.rip, ins);
285 | ptrace(PTRACE_DETACH, traced_process, NULL, NULL);
286 | return 0;
287 | }
288 |
289 |
290 |
--------------------------------------------------------------------------------
/study-diary/myreadelf.c:
--------------------------------------------------------------------------------
1 | /*
2 | * myreadelf.c
3 | * dump section headers
4 | * Copyright (C) 2015 hzsunshx
5 | *
6 | * Distributed under terms of the MIT license.
7 | */
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 | #define LOGD(fmt, args...) {printf("[D] "); printf(fmt, ##args); printf("\n");}
18 | #define LOGI(fmt, args...) {printf("[I] "); printf(fmt, ##args); printf("\n");}
19 | #define LOGE(fmt, args...) {printf("[E] "); printf(fmt, ##args); printf("\n"); exit(1);}
20 |
21 | #if defined(__amd64__)
22 | # define ELF(item) Elf64_##item
23 | #elif defined(__i386__)
24 | # define ELF(item) Elf32_##item
25 | #elif defined(__arm__)
26 | # define ELF(item) Elf32_##item
27 | #else
28 | # error "Not supported"
29 | #endif
30 |
31 | void dump(void* s, int len){
32 | int i;
33 | uint8_t *ent = (uint8_t*)s;
34 | for(i = 0; i < len; i++){
35 | if (i % 8 == 0){
36 | printf("\n");
37 | }
38 | printf("%02x ", ent[i]);
39 | }
40 | printf("\n");
41 | }
42 |
43 | int main(int argc, char *argv[]){
44 | if(argc != 2){
45 | printf("Usage: %s \n", argv[0]);
46 | exit(1);
47 | }
48 | char* libpath = argv[1];
49 |
50 | FILE* fp;
51 | fp = fopen(libpath, "rb");
52 | if (fp == NULL){
53 | LOGE("file(%s) open error", libpath);
54 | }
55 | ELF(Ehdr) ehdr;
56 | printf("size ehdr: %d\n", sizeof(ehdr));
57 | int n = fread(&ehdr, 1, sizeof(ehdr), fp);
58 | if (n != sizeof(ehdr)){
59 | LOGE("read error in %s at line %d", __FILE__, __LINE__);
60 | }
61 | LOGI("section header offset: 0x%x", ehdr.e_shoff);
62 | LOGI("strtab index: %d", ehdr.e_shstrndx);
63 | //dump(&ehdr, sizeof(ehdr));
64 |
65 | long sh_base = ehdr.e_shoff; // section header start
66 | long strndx = ehdr.e_shstrndx;
67 |
68 | // section header string tab
69 | ELF(Shdr) shdr;
70 | fseek(fp, sh_base + strndx*sizeof(ELF(Shdr)), SEEK_SET);
71 | n = fread(&shdr, 1, sizeof(shdr), fp);
72 | assert(n != 0);
73 | LOGD(".shstrtab: offset: 0x%x, size: 0x%x", shdr.sh_offset, shdr.sh_size);
74 |
75 | // read shstrtab
76 | char *shstrtab = (char*)malloc(sizeof(char) * shdr.sh_size);
77 | fseek(fp, shdr.sh_offset, SEEK_SET);
78 | n = fread(shstrtab, 1, shdr.sh_size, fp);
79 | assert(n == shdr.sh_size);
80 |
81 | // read shdr one by one and get it's name
82 | fseek(fp, sh_base, SEEK_SET);
83 | int i = 0;
84 | for (; i < ehdr.e_shnum; i++){
85 | n = fread(&shdr, 1, sizeof(shdr), fp);
86 | assert(n == sizeof(shdr));
87 | char *name = shstrtab + shdr.sh_name;
88 | LOGI("%2d name: %-14s\toffset: 0x%06x size: 0x%04x", i, name, shdr.sh_offset, shdr.sh_size);
89 | }
90 |
91 | //ELF(Shdr) shdr_dynsym, shdr_synstr;
92 | //dump(shstrtab, n);
93 | fclose(fp);
94 | return 0;
95 | }
96 |
--------------------------------------------------------------------------------
/study-diary/push_file.sh:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 | #
3 | # push_file.sh
4 | # Copyright (C) 2015 hzsunshx
5 | #
6 | # Distributed under terms of the MIT license.
7 | #
8 |
9 |
10 | for name in "$@"
11 | do
12 | echo "push -- $name"
13 | adb push "$name" /data/local/tmp/
14 | adb shell chmod 777 /data/local/tmp/$name
15 | done
16 |
--------------------------------------------------------------------------------
/utils.c:
--------------------------------------------------------------------------------
1 | /*
2 | * utils.c
3 | * Copyright (C) 2015 hzsunshx
4 | *
5 | * Distributed under terms of the MIT license.
6 | */
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | #include "utils.h"
16 | #define LOG(fmt, args...) printf(fmt, ##args)
17 |
18 | long get_module_base(pid_t pid, char* module_name){
19 | FILE *fp;
20 | long addr = 0;
21 | char *pch;
22 | char filename[32];
23 | char line[1024];
24 |
25 | if (pid < 0) {
26 | /* self process */
27 | snprintf(filename, sizeof(filename), "/proc/self/maps", pid);
28 | } else {
29 | snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
30 | }
31 |
32 | fp = fopen(filename, "r");
33 |
34 | if (fp != NULL) {
35 | while (fgets(line, sizeof(line), fp)) {
36 | if (strstr(line, module_name)) {
37 | pch = strtok( line, "-" );
38 | addr = strtoul( pch, NULL, 16 );
39 |
40 | if (addr == 0x8000)
41 | addr = 0;
42 | break;
43 | }
44 | }
45 |
46 | fclose(fp) ;
47 | }
48 | return addr;
49 | }
50 | /**
51 | * lookup symbol's GOT entry address
52 | *
53 | * module_path, absolute path of the module which imports symbol
54 | * symbol_name, name of the target symbol
55 | */
56 | long find_got_entry_address(pid_t pid, char *module_path, char *symbol_name){
57 | long module_base = get_module_base(pid, module_path);
58 |
59 | if ( module_base == 0 ){
60 | LOG("[-] it seems that process %d does not dependent on %s\n", pid, module_path);
61 | return 0;
62 | }
63 |
64 | LOG("[+] base address of %s: 0x%x\n", module_path, module_base);
65 |
66 | int fd = open(module_path, O_RDONLY);
67 | if ( fd == -1 ) {
68 | LOG("[-] open %s error!", module_path);
69 | return 0;
70 | }
71 |
72 | Elf32_Ehdr *elf_header = (Elf32_Ehdr *)malloc(sizeof(Elf32_Ehdr));
73 | if ( read(fd, elf_header, sizeof(Elf32_Ehdr)) != sizeof(Elf32_Ehdr) ) {
74 | LOG("[-] read %s error! in %s at line %d\n", module_path, __FILE__, __LINE__);
75 | return 0;
76 | }
77 |
78 | uint32_t sh_base = elf_header->e_shoff;
79 | uint32_t ndx = elf_header->e_shstrndx;
80 | uint32_t shstr_base = sh_base + ndx * sizeof(Elf32_Shdr);
81 | LOG("[+] start of section headers: 0x%x\n", sh_base);
82 | LOG("[+] section header string table index: %d\n", ndx);
83 | LOG("[+] section header string table offset: 0x%x\n", shstr_base);
84 |
85 | lseek(fd, shstr_base, SEEK_SET);
86 | Elf32_Shdr *shstr_shdr = (Elf32_Shdr *)malloc(sizeof(Elf32_Shdr));
87 | if ( read(fd, shstr_shdr, sizeof(Elf32_Shdr)) != sizeof(Elf32_Shdr) )
88 | {
89 | LOG("[-] read %s error! in %s at line %d\n", module_path, __FILE__, __LINE__);
90 | return 0;
91 | }
92 | LOGI("[+] section header string table offset: 0x%x", shstr_shdr->sh_offset);
93 |
94 | char *shstrtab = (char *)malloc(sizeof(char) * shstr_shdr->sh_size);
95 | lseek(fd, shstr_shdr->sh_offset, SEEK_SET);
96 | if ( read(fd, shstrtab, shstr_shdr->sh_size) != shstr_shdr->sh_size ) {
97 | LOG("[-] read %s error! in %s at line %d\n", module_path, __FILE__, __LINE__);
98 | return 0;
99 | }
100 |
101 | Elf32_Shdr *shdr = (Elf32_Shdr *)malloc(sizeof(Elf32_Shdr));
102 | Elf32_Shdr *relplt_shdr = (Elf32_Shdr *)malloc(sizeof(Elf32_Shdr));
103 | Elf32_Shdr *dynsym_shdr = (Elf32_Shdr *)malloc(sizeof(Elf32_Shdr));
104 | Elf32_Shdr *dynstr_shdr = (Elf32_Shdr *)malloc(sizeof(Elf32_Shdr));
105 |
106 | lseek(fd, sh_base, SEEK_SET);
107 | if ( read(fd, shdr, sizeof(Elf32_Shdr)) != sizeof(Elf32_Shdr) ) {
108 | LOG("[-] read %s error! in %s at line %d\n", module_path, __FILE__, __LINE__);
109 | perror("Error");
110 | return 0;
111 | }
112 | int i = 1;
113 | char *s = NULL;
114 | for ( ; i < elf_header->e_shnum; i++ ) {
115 | s = shstrtab + shdr->sh_name;
116 | if ( strcmp(s, ".rel.plt") == 0 )
117 | memcpy(relplt_shdr, shdr, sizeof(Elf32_Shdr));
118 | else if ( strcmp(s, ".dynsym") == 0 )
119 | memcpy(dynsym_shdr, shdr, sizeof(Elf32_Shdr));
120 | else if ( strcmp(s, ".dynstr") == 0 )
121 | memcpy(dynstr_shdr, shdr, sizeof(Elf32_Shdr));
122 |
123 | if ( read(fd, shdr, sizeof(Elf32_Shdr)) != sizeof(Elf32_Shdr) ) {
124 | LOG("[-] read %s error! i = %d, in %s at line %d", module_path, i, __FILE__, __LINE__);
125 | return 0;
126 | }
127 | }
128 |
129 | LOG("[+] offset of .rel.plt section: 0x%x\n", relplt_shdr->sh_offset);
130 | LOG("[+] offset of .dynsym section: 0x%x\n", dynsym_shdr->sh_offset);
131 | LOG("[+] offset of .dynstr section: 0x%x\n", dynstr_shdr->sh_offset);
132 |
133 | // read dynmaic symbol string table
134 | char *dynstr = (char *)malloc(sizeof(char) * dynstr_shdr->sh_size);
135 | lseek(fd, dynstr_shdr->sh_offset, SEEK_SET);
136 | if ( read(fd, dynstr, dynstr_shdr->sh_size) != dynstr_shdr->sh_size ) {
137 | LOGE("[-] read %s error!", module_path);
138 | return 0;
139 | }
140 |
141 | // read dynamic symbol table
142 | Elf32_Sym *dynsymtab = (Elf32_Sym *)malloc(dynsym_shdr->sh_size);
143 | lseek(fd, dynsym_shdr->sh_offset, SEEK_SET);
144 | if ( read(fd, dynsymtab, dynsym_shdr->sh_size) != dynsym_shdr->sh_size ) {
145 | LOGE("[-] read %s error!", module_path);
146 | return 0;
147 | }
148 |
149 | // read each entry of relocation table
150 | Elf32_Rel *rel_ent = (Elf32_Rel *)malloc(sizeof(Elf32_Rel));
151 | lseek(fd, relplt_shdr->sh_offset, SEEK_SET);
152 | if ( read(fd, rel_ent, sizeof(Elf32_Rel)) != sizeof(Elf32_Rel) ) {
153 | LOG("[-] read %s error!\n", module_path);
154 | return 0;
155 | }
156 | int found = 0;
157 | for ( i = 0; i < relplt_shdr->sh_size / sizeof(Elf32_Rel); i++ ) {
158 | ndx = ELF32_R_SYM(rel_ent->r_info);
159 | LOG("name: %s\n", dynstr + dynsymtab[ndx].st_name);
160 |
161 | if ( strcmp(dynstr + dynsymtab[ndx].st_name, symbol_name) == 0 ) {
162 | LOG("[+] got entry offset of %s: 0x%x\n", symbol_name, rel_ent->r_offset);
163 | found = 1;
164 | break;
165 | }
166 | if ( read(fd, rel_ent, sizeof(Elf32_Rel)) != sizeof(Elf32_Rel) ) {
167 | LOG("[-] read %s error!\n", module_path);
168 | return 0;
169 | }
170 | }
171 | if (!found){
172 | LOG("[-] symbol not found\n");
173 | return 0;
174 | }
175 |
176 | uint32_t offset = rel_ent->r_offset;
177 | Elf32_Half type = elf_header->e_type; // ET_EXEC or ET_DYN
178 |
179 | free(elf_header);
180 | free(shstr_shdr);
181 | free(shstrtab);
182 | free(shdr);
183 | free(relplt_shdr);
184 | free(dynsym_shdr);
185 | free(dynstr_shdr);
186 | free(dynstr);
187 | free(dynsymtab);
188 | free(rel_ent);
189 |
190 | // GOT entry offset is different between ELF executables and shared libraries
191 | LOG("offset: %p, base: %p\n", offset, module_base);
192 | if ( type == ET_EXEC )
193 | return (long)offset;
194 | else if ( type == ET_DYN )
195 | return (long)(offset + module_base);
196 |
197 | return 0;
198 | }
199 |
--------------------------------------------------------------------------------
/utils.h:
--------------------------------------------------------------------------------
1 | /*
2 | * utils.h
3 | * Copyright (C) 2015 hzsunshx
4 | *
5 | * Distributed under terms of the MIT license.
6 | */
7 |
8 | #ifndef UTILS_H
9 | #define UTILS_H
10 |
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 | //const char * // point address not changed
17 | //char const * // content not changed
18 |
19 | #define LIBSF_PATH "/system/lib/libsurfaceflinger.so"
20 |
21 | long get_module_base(pid_t pid, char* module_name);
22 | long find_got_entry_address(pid_t pid, char *module_path, char *symbol_name);
23 |
24 | #define LOG_TAG "INJECT"
25 | #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args)
26 | #define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, fmt, ##args)
27 | #define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, fmt, ##args)
28 | #define DEBUG_PRINT(format,args...) LOGD(format, ##args)
29 |
30 | #define CPSR_T_MASK ( 1u << 5 )
31 |
32 | #endif /* !UTILS_H */
33 |
--------------------------------------------------------------------------------