安卓APP破解利器之FRIDA

本文讲的是安卓APP破解利器之FRIDA在我去年参加RadareCon大会的时候,我了解到了一个动态的二进制插桩框架——Frida。起初我觉得它似乎只有一丁点趣味,后来经过实践才发现它原来是如此的有趣。记得游戏里的上帝模式吗?这就是Frida操作本机应用程序的感觉。这是一篇关于专门使用Frida把玩Android应用程序的博客文章。而且,因为我们是在阐述这一点,所以我们也将在这篇文章的第二部分中进行一点Android APP的破解实战。

什么是动态二进制插桩?

动态二进制插桩(DBI)意味着将外部代码注入到现有的(正在运行的)二进制文件中,使它们能够做一些以前没有做过的事情。这个过程不是利用了漏洞,因为代码注入并不是通过你之前必须搞清楚的一些漏洞所导致的。它也不是调试,因为你没有将调试器附加到二进制文件上,尽管你可以做类似调试的一些事情。你能用DBI做些什么呢?这是一个很酷的东西:)

访问进程的内存
在应用程序运行时覆盖一些功能
从导入的类中调用函数
在堆上查找对象实例并使用这些对象实例
Hook,跟踪和拦截函数等等

当然,你也可以使用调试器完成所有这些操作,不过使用调试器会带来各种麻烦。例如在Android中,你必须反汇编并重新编译应用程序以使其可用于调试。一些应用程序会检测并尝试阻止调试器的调试过程,你需要摆脱这些反调试逻辑。可能你会成功,但是整个过程非常麻烦。使用Frida的DBI可以让你使用黑盒进程快速启动Android应用程序。

FRIDA

安卓APP破解利器之FRIDA

Frida可以“将你自己的JavaScript代码片段或代码库注入到Windows,MACOS,Linux, iOS,Android和QNX 的本地应用中”。这是第一款基于谷歌的V8 JavaScript引擎运行的应用程序,在Frida的第九个版本中使用的是Duktape,但它仍然允许你切换回V8引擎,如果你需要这么做的话。Frida有很多可以与二进制文件进行交互(包括在无root权限的设备上对应用程序进行插桩的可能性)的操作模式,不过我们在本文这里会使用最常见的用户操作,并且现在我们不需要关心其内部实现。

在开始本文的破解教程之前,你需要做以下几个事情:

1. Frida(我在本教程中使用的9.1.16版本)

2. frida-server可以二进制发布页面下载到( 在本文发表时为frida-server-9.1.16-android-arm.xz,frida-serve的版本应该与Frida版本一致。)

3. Android模拟器或已ROOT的安卓设备。Frida已经开发了Android 4.4 ARM对应的版本,但它应该适用于更高的版本。我在本教程中成功使用了Android 7.1.1 ARM。对于第二部分的破解,我们需要用到比Android 4.4版本更高的一些东西。

我使用linux系统作为宿主机的操作系统,如果你使用的是Windows或Mac,那你可能需要调整一些命令。

如果你想在解决OWASP Unbreakable Crackme Level 1中的问题,你可以在本系列教程的第二部分中找到破解方法,同时,你也应该下载下面的几个程序:

1. OWASP Unbreakable Crackme Level 1APK

2. BytecodeViewer

3. dex2jar

Frida提供各种API和开始破解的方法。你可以使用命令行界面或frida-trace跟踪低级功能的工具(如libc.so中的“open”函数的调用)进行快速运行。你可以使用C,NodeJS或Python绑定更复杂的东西。在Frida内部,会更多的使用Javascript工作,你也将使用这种语言完成大部分插桩工作。所以如果你和我一样,不喜欢使用Javascript(除了XSS功能之外),那Frida或许是让你熟悉JavaScript的另一个原因。

如果没有,请安装Frida(请参阅README以获得其他的安装方式):

pip install frida
npm install frida

启动你的模拟器或连接你的设备,并确保adb正在运行并列出了你的设备:

michael@sixtyseven:~$ adb devices
List of devices attached
emulator-5556   device

然后安装frida-server。解压文档并将二进制文件push到设备上:

adb push /home/michael/Downloads/frida-server-9.1.16-android-arm /data/local/tmp/frida-server

使用adb打开设备上的一个shell,切换到root用户并启动frida-server:

adb shell
su
cd /data/local/tmp
chmod 755 frida-server
./frida-server

(注1:如果frida-server没有启动,请确保你使用的是root用户,并且该文件已经正确push到设备中。我之前就遇到过文件传输损坏导致的各种奇怪的错误。注2:如果要启动frida-server作为后台进程,可以使用这个命令./frida-server &)

在另一个常规的OS shell终端中,检查Frida是否正在运行,并列出在Android上的进程:

frida-ps -U

参数-U代表USB,并让Frida检查USB设备,但它也可以与模拟器一起工作。你应该得到一个这样的进程列表:

michael@sixtyseven:~$ frida-ps -UPID  Name
----  --------------------------------------------------696  adbd
5828  android.ext.services
6188  android.process.acore
5210  audioserver
5211  cameraserver
8334  com.android.calendar
6685  com.android.chrome
6245  com.android.deskclock
5528  com.android.inputmethod.latin
6120  com.android.phone
6485  com.android.printspooler
8355  com.android.providers.calendar
5844  com.android.systemui
7944  com.google.android.apps.nexuslauncher
6416  com.google.android.gms
[...]

你可以看到进程标识(PID)和运行的进程(名称)。使用Frida你现在可以Hook这些进程,并且可以篡改进程。

例如,你可以跟踪Chrome完成的特定通话(如果Chrome未运行,请先启动模拟器里的Chrome):

frida-trace -i "open" -U com.android.chrome

命令执行结果如下:

michael@sixtyseven:~$ frida-trace -i open -U -f com.android.chrome
Instrumenting functions...                                             
open: Loaded handler at "/home/michael/__handlers__/libc.so/open.js"
Started tracing 1 function. Press Ctrl+C to stop.                      /* TID 0x2740 */282 ms  open(pathname=0xa843ffc9, flags=0x80002)/* TID 0x2755 */299 ms  open(pathname=0xa80d0c44, flags=0x2)/* TID 0x2756 */309 ms  open(pathname=0xa80d0c44, flags=0x2)/* TID 0x2740 */341 ms  open(pathname=0xa80d06f7, flags=0x2)592 ms  open(pathname=0xa77dd3bc, flags=0x0)596 ms  open(pathname=0xa80d06f7, flags=0x2)699 ms  open(pathname=0xa80d105e, flags=0x80000)717 ms  open(pathname=0x9aff0d70, flags=0x42)742 ms  open(pathname=0x9ceffda0, flags=0x0)758 ms  open(pathname=0xa63b04c0, flags=0x0)

frida-trace命令会生成一些JavaScript文件,Frida会将这些JavaScript文件注入到进程中并跟踪特定的调用。看看生成的open.js脚本文件(__handlers__/libc.so/open.js)。它Hook了libc.so中的“open”函数并输出一些参数。这在Frida中很容易就可以实现:

[...]
onEnter: function (log, args, state) {log("open(" + "pathname=" + args[0] + ", flags=" + args[1] + ")");
},
[...]

请注意,Frida可以访问到Chrome内部调用的open函数(args [0],args [1]等等)的一些调用参数。让我们修改一下这个脚本。如果我们明文输出打开的文件的路径,而不是存储这些路径的内存地址,这样不是更好吗?幸运的是,我们可以直接用Frida访问内存。看看Frida API和Memory对象。我们可以修改脚本,将内存地址所指向的内容输出为UTF8字符串,可以获得更清楚明了的输出。修改脚本后,如下所示:

onEnter: function (log, args, state) {
log("open(" + "pathname=" + Memory.readUtf8String(args[0])+ ", flags=" + args[1] + ")"); },

(我们刚刚添加了Memory.readUtf8String函数)我们得到如下输出结果:

michael@sixtyseven:~$ frida-trace -i open -U -f com.android.chrome
Instrumenting functions...                                             
open: Loaded handler at "/home/michael/__handlers__/libc.so/open.js"
Started tracing 1 function. Press Ctrl+C to stop.                      /* TID 0x29bf */240 ms  open(pathname=/dev/binder, flags=0x80002)/* TID 0x29d3 */259 ms  open(pathname=/dev/ashmem, flags=0x2)/* TID 0x29d4 */269 ms  open(pathname=/dev/ashmem, flags=0x2)/* TID 0x29bf */291 ms  open(pathname=/sys/qemu_trace/process_name, flags=0x2)453 ms  open(pathname=/dev/alarm, flags=0x0)456 ms  open(pathname=/sys/qemu_trace/process_name, flags=0x2)562 ms  open(pathname=/proc/self/cmdline, flags=0x80000)576 ms  open(pathname=/data/dalvik-cache/arm/system@app@Chrome@Chrome.apk@classes.dex.flock, flags=0x42)

Frida打印路径名很容易,不是吗?

另外需要注意的是,你可以先启动一个应用程序,然后再让Frida进行注入,或者使用-f选项,让Frida自动生成进程。

现在我们来看看Frida的命令行界面frida-cli:

frida -U -f com.android.chrome

这将启动Frida和Chrome应用。但是,它还没有启动Chrome的主进程。这意味着可以在应用程序的主进程启动之前注入Frida代码。不幸的是,在我自己的尝试中总是遇到一个问题——应用程序在启动2秒钟后自动卡死。这不是我们想要的。你可以使用这两秒来键入%resume,如cli输出的建议,让应用程序启动其主进程。或者你直接启动Frida,使用–no-pause参数选择不中断应用程序的启动,仍然将进程的产生留给Frida去做。

安卓APP破解利器之FRIDA

在这两种情况下,你都可以获得一个shell(不会被杀死),你现在可以使用其Javascript API向Frida写入命令。按TAB键可以查看可用的命令。shell还支持命令自动完成。

安卓APP破解利器之FRIDA

你想做的大多数事情在文档中都是有据可查的。对于Android,特别要检查Javascript-API 的Java部分(我将在此讨论一个“Java API”,尽管在技术上说,是一个用于访问Java对象的Javascript封装)。我们将重点关注Java API,因为这是使用Android应用程序更为方便的方式。我们可以直接使用Java函数和对象,而不是Hook libc中的函数。(注意:如果你对Frida的其他的Java API能做什么很感兴趣,那么你可以使用frida-trace Hook Android里面的更低级的C函数,并且查看文档里的函数部分,我不会按照文档所述那样在本文中进行描述。)

要开始Java API访问,只需从Frida的命令行界面显示正在运行的Android版本:

[USB::Android Emulator 5556::['com.android.chrome']]-> Java.androidVersion
"7.1.1"

或列出已加载的类(警告:此处会输出很多内容,后面我会解释代码的意思。):

[USB::Android Emulator 5556::['com.android.chrome']]-> Java.perform(function(){Java.enumerateLoadedClasses({"onMatch":function(className){ console.log(className) },"onComplete":function(){}})})
org.apache.http.HttpEntityEnclosingRequest
org.apache.http.ProtocolVersion
org.apache.http.HttpResponse
org.apache.http.impl.cookie.DateParseException
org.apache.http.HeaderIterator

我们在这里输入了相当长的命令,一些嵌套的函数代码的意思也很明确。请注意,我们输入的代码被封装在Java.perform(function(){ … }) 中,这些代码是调用Fridas Java API所需要的。

这是我们在Java.perform包装器中插入的函数的主体:

Java.enumerateLoadedClasses({"onMatch": function(className){console.log(className)},"onComplete":function(){}}
)

很简单:我们使用Java.enumerateLoadedClassesFridas API 枚举所有加载的类,并将每个匹配到的类使用console.log输出到控制台。这种回调对象的模式你会经常在Frida找到。你需要提供一个回调对象的模板。

{"onMatch":function(arg1, ...){ ... },"onComplete":function(){ ... },
}

一旦Frida匹配到你的请求,就会使用一个或多个参数调用onMatch并且当Frida完成迭代可能的匹配时,将会调用onComplete。

现在我们深入了解了Frida的魔法,并且使用Frida重写了一个函数。此外,我们还从外部脚本加载了代码,而不是将其输入到cli中,这样做更方便。将以下代码保存到脚本文件中,例如chrome.js:

Java.perform(function () {var Activity = Java.use("android.app.Activity");Activity.onResume.implementation = function () {console.log("[*] onResume() got called!");this.onResume();};
});

该代码重写了android.app.Activity类的onResume函数。它调用Java.use接收此类的包装对象并访问implementation的onResume函数的属性以提供新的实现。在新的函数主体内,它this.onResume()这样的方式调用原来的onResume的实现,所以应用程序可以继续正常运行。

打开你的模拟器,打开Chrome并用-l选项注入脚本:

frida -U -l chrome.js com.android.chrome

一旦你触发了onResume的执行—— 例如通过在模拟器中切换到另一个应用程序并返回到Chrome后你将得到下面的输出:

[*] onResume() got called!

很好用,不是吗?我们实际上重写了应用程序的一个函数。这给了我们控制目标应用程序行为的很多可能性。但是我们可以做更多的事情:我们也可以在堆上查找实例化的对象Java.choose。

在我们继续之前,我有一个警告:当你的模拟器变得有点慢的时候,Frida有时候会有时间超时的提示。为了防止这种情况,请将脚本包装在setImmediate函数中或将其导出为rpc。RPC在Frida中默认情况下不会超时( 感谢@oleavr的这些提示)。在你修改脚本文件后,setImmediate会自动重新运行Frida脚本,这样很方便。它也会在后台运行你的脚本。这意味着你可以立即得到一个cli,即使Frida仍在处理你的脚本。你只需要继续等待,不要离开cli,直到Frida向你显示了脚本的输出。

再次修改chrome.js:

setImmediate(function() {console.log("[*] Starting script");Java.perform(function () {Java.choose("android.view.View", {"onMatch":function(instance){console.log("[*] Instance found");},"onComplete":function() {console.log("[*] Finished heap search")}});});
});

使用frida -U -l chrome.js com.android.chrome运行它将产生以下输出:

[*] Starting script
[*] Instance found
[*] Instance found
[*] Instance found
[*] Instance found
[*] Finished heap search

所以我们在堆上发现了4个android.view.View对象的实例。让我们看看我们可以做些什么。也许我们可以调用这些对象实例的方法。我们这次只添加instance.toString()到我们的console.log进行输出(由于我们使用了setImmediate,所以我们现在可以修改我们的脚本,Frida将自动重新加载脚本文件):

setImmediate(function() {console.log("[*] Starting script");Java.perform(function () {Java.choose("android.view.View", {"onMatch":function(instance){console.log("[*] Instance found: " + instance.toString());},"onComplete":function() {console.log("[*] Finished heap search")}});});
});

执行脚本会后返回如下结果:

[*] Starting script
[*] Instance found: android.view.View{7ccea78 G.ED..... ......ID 0,0-0,0 #7f0c01fc app:id/action_bar_black_background}
[*] Instance found: android.view.View{2809551 V.ED..... ........ 0,1731-0,1731 #7f0c01ff app:id/menu_anchor_stub}
[*] Instance found: android.view.View{be471b6 G.ED..... ......I. 0,0-0,0 #7f0c01f5 app:id/location_bar_verbose_status_separator}
[*] Instance found: android.view.View{3ae0eb7 V.ED..... ........ 0,0-1080,63 #102002f android:id/statusBarBackground}
[*] Finished heap search

Frida实际上调用了android.view.View对象实例的toString方法。很酷哦。因此,通过Frida的帮助,我们可以读取进程内存,修改函数,查找实际的对象实例,并使用少量的几行代码。

现在你应该对Frida有了基本的了解了,并能够自己深入了解其文档和API。为了完成这篇文章,我想再谈两个主题,Frida的绑定和r2frida。但首先会有一点警告。

警告

当你尝试使用Frida时,你会注意到有一些不稳定。首先,将外部代码注入到另一个进程中容易导致崩溃,因为应用程序被以意想不到的方式触发运行。第二,Frida本身仍然让人感觉为一个实验品。有时候它的确在正常工作,但你经常需要尝试多种方式来获得你所需的结果。例如,当我尝试加载一个脚本并在命令行中执行一个命令生成一个进程时,Frida就会一直崩溃。相反,我必须首先启动该进程,然后让Frida去注入脚本。这就是为什么我向你展示了使用Frida的各种方法,并防止超时失败的提示出现。你可能需要弄清楚在你的实际情况下哪个才是最有效的方法。

Python绑定

一旦了解了Frida的工作原理之后,如果你想要使用Frida更自动化的完成你的工作,你应该查看易于使用的Python,C或NodeJS 绑定。例如从Python 中注入chrome.js脚本,你可以使用Frida的Python绑定并创建一个chrome.py脚本:

#!/usr/bin/python
import frida
# put your javascript-code here
jscode= """
console.log("[*] Starting script");
Java.perform(function() {var Activity = Java.use("android.app.Activity");Activity.onResume.implementation = function () {console.log("[*] onResume() got called!");this.onResume();};
});
"""
# startup frida and attach to com.android.chrome process on a usb device
session = frida.get_usb_device().attach("com.android.chrome")
# create a script for frida of jsccode
script = session.create_script(jscode)
# and load the script
script.load()

如果要结束Frida会话并销毁本次会话的脚本,可以调用session.detach()。

有关更多的例子,还是一如以往的请查看Frida的文档。

Frida和Radare2:r2frida

如果我们也可以使用像Radare2这样的反汇编框架来检查我们的应用程序的内存,那不是很好吗?在这里是r2frida。你可以使用r2frida将Radare2连接到Frida,并进行进程内存的静态分析和反汇编。我不会在这里详细介绍r2frida,因为它的使用本身就预先假定了使用者具备Radare2的知识(如果你没有一定的相关知识,那非常值得一看。),但是我仍然想给你一个使用简便的方式。

你可以使用Radare2的包管理器来安装r2frida(假设你已经安装了Radare2):

r2pm install r2frida

回到我们的frida跟踪示例,删除或重命名我们修改过的脚本,frida-trace会再次生成默认的脚本,并再次查看日志会有下面的输出:

michael@sixtyseven:~$ frida-trace -i open -U -f com.android.chrome
Instrumenting functions...                                             
open: Loaded handler at "/home/michael/__handlers__/libc.so/open.js"
Started tracing 1 function. Press Ctrl+C to stop.                      /* TID 0x2740 */282 ms  open(pathname=0xa843ffc9, flags=0x80002)/* TID 0x2755 */[...]

使用r2frida,你可以轻松的检查显示的内存地址并读取路径名(在这种情况下为/dev/binder):

root@sixtyseven:~# r2 frida://emulator-5556/com.android.chrome-- Enhance your graphs by increasing the size of the block and graph.depth eval variable.
[0x00000000]> s 0xa843ffc9
[0xa843ffc9]> px
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0xa843ffc9  2f64 6576 2f62 696e 6465 7200 4269 6e64  /dev/binder.Bind
0xa843ffd9  6572 2069 6f63 746c 2074 6f20 6f62 7461  er ioctl to obta
0xa843ffe9  696e 2076 6572 7369 6f6e 2066 6169 6c65  in version faile
0xa843fff9  643a 2025 7300 4269 6e64 6572 2064 7269  d: %s.Binder dri
[...]

访问进程并让r2frida执行注入的命令语句为

r2 frida://DEVICE-ID/PROCESS

还可以使用=!前缀检查可用的r2frida命令,以及哪些可以在内存区域快速搜索指定内容或写入任意内存地址的命令等等?

[0x00000000]> =!?
r2frida commands available via =!
?                          Show this help
?V                         Show target Frida version
/[x][j] <string|hexpairs>  Search hex/string pattern in memory ranges (see search.in=?)
/w[j] string               Search wide string
[...]

更多

如果这让你感到好奇,可以看看下面的内容:

 1. Frida的项目页面

 2. @oleavr在r2con的演讲视频 和David Weinstein对Frida简介的演讲。

 3. Frida的Twitter帐号@fridadotre

 4. Frida的Telegram频道

 5. AppMon——基于Frida的应用程序监视和注入的GUI工具(由@dpnishant提供)

在本教程的第二部分,我们将使用Frida来轻松解决一些小问题。




原文发布时间为:2017年4月14日
本文作者:丝绸之路 
本文来自云栖社区合作伙伴嘶吼,了解相关信息可以关注嘶吼网站。
原文链接

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/455005.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何获取option的下标和值_数智化时代下,如何获取企业增长密码?

信息化时代下&#xff0c;很多企业前前后后上线了各种信息化系统&#xff0c;ERP、OA、CRM…随着企业数字化的深入推进&#xff0c;“移动互联网、云计算、大数据、人工智能、物联网、区块链”等技术的革新&#xff0c;这些信息化系统难以满足企业对数智化转型的新需求&#xf…

基于51单片机的交通灯控制设计

课程设计任务书及成绩 课程名称 单片机课程设计 题目 交通灯控制设计 课程设计目标与任务、计划与进度安排: 实践教学要求与任务: 1、了解交通灯的基本工作原理&#xff1b; 2、用Proteus模拟实现交通灯控制&#xff1b; 3、用Keil C51编程实现上述功能&#xff1b; 4、…

福斯i6飞行模式设置_数据网络卡的时候,不妨试试“开关飞行模式”?上网速度明显变快...

相信大家都有过这种经历&#xff0c;手机数据网速很慢的时候&#xff0c;开一下飞行模式再关闭&#xff0c;上网速度会比之前快很多&#xff0c;这就有人有了疑问&#xff0c;为什么呢&#xff1f;开飞行模式再关掉飞行模式&#xff0c;其实等于是完成了一次手动的小区重选。移…

安装开源 ITIL 门户 iTOP

在 CentOS 7 上部署iTOP是一个简单的基于Web的开源IT服务管理工具。它有所有的ITIL功能&#xff0c;包括服务台、配置管理、事件管理、问题管理、变更管理和服务管理。iTOP依赖于Apache/IIS、MySQL和PHP&#xff0c;因此它可以运行在任何支持这些软件的操作系统中。因为iTOP是一…

基于FPGA 的8b10b编解码电路前端电路设计

基于FPGA 的8b10b编解码电路前端电路设计 摘 要 本设计是采用EDA技术设计的一种8B /10B 编解码电路,实现了在高速的串行数据传输中的直流平衡。该编解码电路设计大体上可以由五个模块构成&#xff0c;分别是默认编码模块、差异度计算模块、编码校正模块、并串转换模块、显示模…

day15(mysql 的多表查询,事务)

mysql之多表查询 1.合并结果集 作用:合并结果集就是把两个select语句查询的结果连接到一起&#xff01; /*创建表t1*/ CREATE TABLE t1(a INT PRIMARY KEY ,b VARCHAR(10) ) INSERT INTO t1 VALUES(1,a); INSERT INTO t1 VALUES(2,b); INSERT INTO t1 VALUES(3,c); /*创建t2*/…

vue router传参_新手使用vue-router传参时注意事项

1. 使用name和params组合传参this.$router.push({name: details, params: {id: 233}})路由配置import Vue from vueimport Router from vue-router Vue.use(Router) export default new Router({ mode: history, routes: [ { path: /details, name: details, component: resolv…

逻辑综合工具DesignCompiler使用教程

逻辑综合工具Design Compiler使用教程 图形界面design vision操作示例 逻辑综合主要是将HDL语言描述的电路转换为工艺库器件构成的网表的过程。综合工具目前比较主流的是synopsys公司Design Compiler&#xff0c;我们在设计实践过程中采用这一工具。Design compiler有两种工作…

遍历结构体_三菱ST语言编程(3)——结构体变量

上篇文章介绍了数组&#xff0c;是一组相同类型数据的列表&#xff0c;那么不同类型的数据能否组合到一起用一个标签表示呢&#xff1f;答案当然是可以的&#xff0c;而实现这个功能的就是结构体(struct)。建立结构体在三菱结构化编程的界面中左侧程序部件里可以找到结构体标签…

中的 隐藏鼠标菜单_Mac移动隐藏删除顶部菜单栏图标教程

苹果菜单栏贯穿 Mac 的屏幕顶部。左侧是苹果菜单和应用菜单&#xff0c;应用菜单一般显示你当前使用的Mac软件的所有功能菜单。右侧通常是以图标显示的状态菜单&#xff0c;帮助你快速查看Mac的状态以及快速访问某些Mac软件。移动图标位置若想要重新排列状态菜单栏的图标&#…

[hadoop] kettle spoon 基础使用 (txt 内容抽取到excel中)

spoon.bat 启动kettle。 测试数据 1. 新建转换 输入中选择文本文件输入 双击设置文本输入 字符集、分隔符设置 获取对应的字段&#xff0c;预览记录。 拖入 excel输出&#xff0c;设置转换关系 设置输出路径 获取字段 启动转换 导入的excel数据&#xff08;设置好格式,图中ID,A…

apache 支持.htaccess重写url

1. httpd.conf 添加&#xff1a; <Directory />Options Indexes FollowSymLinks MultiviewsAllowOverride allRequire all grantedRewriteEngine On</Directory> 开启&#xff1a; 在phpinfo里找到&#xff1a; 说明开启成功。 2.httpd-vhosts.conf &#xff08;开…

redis基本用法学习(C#调用FreeRedis操作redis)

FreeRedis属于常用的基于.net的redis客户端&#xff0c;EasyCaching中也提供适配FreeRedis的包。根据参考文献4中的说法&#xff0c;FreeRedis和CsRedis算是近亲&#xff08;都是GitHub中账号为2881099下的开源项目&#xff09;&#xff0c;因此其用法特别相似。FreeRedis的主要…

opencv:图像的基本变换

0.概述 图像变换的基本原理都是找到原图和目标图的像素位置的映射关系&#xff0c;这个可以用坐标系来思考&#xff0c;在opencv中&#xff0c; 图像的坐标系是从左上角开始(0,0)&#xff0c;向右是x增加方向(cols)&#xff0c;向下时y增加方向(rows)。 普通坐标关系&#xff1…

中通知设置响铃_主动切断干扰源——手机“通知”精细化管理

上周去参加我福福幼儿园的母亲节活动&#xff0c;内容是孩子和家长一起穿手链。期间我发现和我同桌的一个家长的手机不停在响&#xff0c;当然伴随着注意力被打断。不仅是这位家长自己&#xff0c;连我也受到了干扰。于是职业病又犯了&#xff0c;我悄悄的看了一眼这位家长的手…

python安装各种插件

http://www.lfd.uci.edu/~gohlke/pythonlibs/#pip 感受&#xff1a;如果编辑pip真的一直出问题&#xff0c;考虑降成32位的进行安装。毕竟合理搭配比木桶突出有用。转载于:https://www.cnblogs.com/osmondwang/p/7307678.html

编写数学公式的好工具

2019独角兽企业重金招聘Python工程师标准>>> http://private.codecogs.com/latex/eqneditor.php 转载于:https://my.oschina.net/yizhichao/blog/1542153

dev gridview 打印列数过多_R语言:如何将多张统计图绘制在一张上面

在使用R语言进行数据可视化的时候&#xff0c;常常需要将多张统计图表绘制在同一张图上面&#xff0c;从而更高效地传递信息&#xff0c;下面我们就来一起看看具体如何实现。一、使用R语言自带的函数绘制的图像R语言本身就已经内置了许多绘图函数&#xff0c;能够满足较为基本的…

[转]vue全面介绍--全家桶、项目实例

慢慢了解vue及其全家桶的过程 原文http://blog.csdn.net/zhenghao35791/article/details/67639415 简介 “简单却不失优雅&#xff0c;小巧而不乏大匠”。 2016年最火的前端框架当属Vue.js了&#xff0c;很多使用过vue的程序员这样评价它&#xff0c;“vue.js兼具angular.js和R…

TS 188字节流结构图

应该说真正了解TS&#xff0c;还是看了朋友推荐的《数字电视业务信息及其编码》一书之后&#xff0c;MPEG2 TS和数字电视是紧密不可分割的&#xff0c;值得总结一下其中的一些关系。 ISO/IEC&#xff0d;13818&#xff0d;1&#xff1a;系统部分&#xff1b; ISO/IEC&#xff…