├── .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 | --------------------------------------------------------------------------------