动态库注入app以及在非越狱手机使用

1. 动态库编写

动态库编写有多种方式,可以使用Xcode创建动态库,也可以通过tweak生成动态库

  • 对于越狱手机,可以直接编写tweak,将tweak打包成动态库
  • 对于非越狱手机,可以使用Xcode创建动态库,在Xcode中编写hook代码,生成动态库

第一种,在越狱手机上编写tweak,打包成动态库

1. 安装Xcode,这个就不多说了,在苹果官网下载安装即可

2. tweak环境的安装及编写请看这个

第一个tweak生成后,我们来hook一下之前新建的一个demo, 可以在我的github上面下载到。

@implementation FirstTweakViewController- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view.self.view.backgroundColor = [UIColor whiteColor];self.title = @"FirstTweakDemo";
}- (void)viewDidAppear:(BOOL)animated {[super viewDidAppear:animated];dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{[self showAlert];});
}- (void)showAlert {UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"提醒" message:@"这是原始弹窗" preferredStyle:(UIAlertControllerStyleAlert)];UIAlertAction * action = [UIAlertAction actionWithTitle:@"确定" style:(UIAlertActionStyleDefault) handler:nil];[alert addAction:action];[self presentViewController:alert animated:YES completion:nil];
}@end

demo的主要内容是在控制器的ViewDidAppear方法中延时两秒弹出一个弹窗,我们现在来一下这个逻辑,hook住弹出弹窗的方法,然后弹出一个actionSheet.

hook前的效果

3. 开始编写hook代码

回到我们生成的Tweak目录,发现四个文件

  • control
  • FirstTweak.plist
  • Makefile
  • Tweak.xm

hook的代码在Tweak.xm中编写,在xcode中打开Tweak.xm,logos语法参见Wiki. 编写如下代码

#import <UIKit/UIKit.h>@interface FirstTweakViewController: UIViewController- (void)showAlert;@end
%hook FirstTweakViewController- (void)showAlert {UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"提醒" message:@"这是hook后的提醒" preferredStyle:(UIAlertControllerStyleActionSheet)];UIAlertAction * cancel = [UIAlertAction actionWithTitle:@"Cancel" style:(UIAlertActionStyleCancel) handler:nil];UIAlertAction * confirm = [UIAlertAction actionWithTitle:@"Confirm" style:(UIAlertActionStyleDestructive) handler:nil];[alert addAction:cancel];[alert addAction:confirm];[self presentViewController:alert animated:YES completion:nil];
}%end

检查无误后进行配置,打开FirstTweak.plist, 替换bundleId为我们想要hook的App的bundleId
NH.FirstTweakDemo


4. 安装tweak到手机

然后我们将22端口转发到22222

iproxy 22222 22

回到终端,cd到tweak目录, 连接我们的越狱手机

$ make package install

如果报错,缺少THEOS_DEVICE_IP,在终端输入如下

export THEOS_DEVICE_IP=localhost:22222

再次执行make package install
结果如下:

$ make package install
xcrun: error: sh -c '/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -sdk /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.2.sdk -find llvm-dsymutil 2> /dev/null' failed with exit code 17664: (null) (errno=No such file or directory)
xcrun: error: unable to find utility "llvm-dsymutil", not a developer tool or in PATH
> Making all for tweak FirstTweak…
xcrun: error: sh -c '/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -sdk /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.2.sdk -find llvm-dsymutil 2> /dev/null' failed with exit code 17664: (null) (errno=No such file or directory)
xcrun: error: unable to find utility "llvm-dsymutil", not a developer tool or in PATH
xcrun: error: sh -c '/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -sdk /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.2.sdk -find llvm-dsymutil 2> /dev/null' failed with exit code 17664: (null) (errno=No such file or directory)
xcrun: error: unable to find utility "llvm-dsymutil", not a developer tool or in PATH
make[2]: Nothing to be done for `internal-library-compile'.
> Making stage for tweak FirstTweak…
xcrun: error: sh -c '/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -sdk /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.2.sdk -find llvm-dsymutil 2> /dev/null' failed with exit code 17664: (null) (errno=No such file or directory)
xcrun: error: unable to find utility "llvm-dsymutil", not a developer tool or in PATH
dm.pl: building package `com.yourcompany.firsttweak:iphoneos-arm' in `./packages/com.yourcompany.firsttweak_0.0.1-3+debug_iphoneos-arm.deb'
==> Installing…
root@localhost's password:
Selecting previously unselected package com.yourcompany.firsttweak.
(Reading database ... 2140 files and directories currently installed.)
Preparing to unpack /tmp/_theos_install.deb ...
Unpacking com.yourcompany.firsttweak (0.0.1-3+debug) ...
Setting up com.yourcompany.firsttweak (0.0.1-3+debug) ...
install.exec "killall -9 SpringBoard"
root@localhost's password:

输入两次密码后,成功安装到手机。 SpringBoard重启后打开FirstTweakDemo,点击Push,进入FirstTweakViewController控制器,等待两秒后可以看到。

成功的hook住了!!!

那么问题来了,这是给越狱设备使用的,如何给未越狱设备使用呢?

5. 将tweak生成的动态库注入app中,实现非越狱设备安装

FirstTweak.dylib依赖替换

回到tweak目录,可以看到,有个.theos目录,找到.theos/_/Library/MobileSubstrate/DynamicLibraries/FirstTweak.dylib 文件,这是tweak打包后生成的动态库

用otool查看动态库的依赖情况

$ otool -l FirstTweak.dylib | grep namename /Library/MobileSubstrate/DynamicLibraries/FirstTweak.dylib (offset 24)name /usr/lib/libobjc.A.dylib (offset 24)name /System/Library/Frameworks/Foundation.framework/Foundation (offset 24)name /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation (offset 24)name /Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate (offset 24)name /usr/lib/libc++.1.dylib (offset 24)name /usr/lib/libSystem.B.dylib (offset 24)name /System/Library/Frameworks/UIKit.framework/UIKit (offset 24)

动态库依赖了CydiaSubstrate,这个是属于越狱设备的,非越狱设备上没有,需要修改依赖

这是依赖库 name /Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate

至于这个依赖 name /Library/MobileSubstrate/DynamicLibraries/FirstTweak.dylib 不用管

修改如下

install_name_tool -change /Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate @executable_path/Frameworks/libsubstrate.dylib FirstTweak.dylib

再次查看依赖,发现已经修改成功

name /Library/MobileSubstrate/DynamicLibraries/FirstTweak.dylib (offset 24)name /usr/lib/libobjc.A.dylib (offset 24)name /System/Library/Frameworks/Foundation.framework/Foundation (offset 24)name /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation (offset 24)name @executable_path/Frameworks/libsubstrate.dylib (offset 24)name /usr/lib/libc++.1.dylib (offset 24)name /usr/lib/libSystem.B.dylib (offset 24)name /System/Library/Frameworks/UIKit.framework/UIKit (offset 24)

FirstTweak.dylib与libsubstrate.dylib签名

下一步,需要将libsubstrate.dylibFirstTweak.dylib签名

我们先来查看下电脑上有哪些可用的证书

$ security find-identity -v -p codesigning
$ 1) DFCF477C3F593AFEF3AB261E9FD641873600EAAD "Apple Development: daye  (12CD56GH78)"

找到之前用于签名FirstTweakDemo的证书,开始签名

$ codesign -fs 'Apple Development: daye  (12CD56GH78)' libsubstrate.dylib
$ codesign -fs 'Apple Development: daye  (12CD56GH78)' FirstTweak.dylib

签名成功后,将libsubstrate.dylibFirstTweak.dylib复制到FirstTweakDemo.app/Frameworks/

然后将FirstTweak.dylib注入到FirstTweakDemo

$ ./optool install -c load -p @executable_path/Frameworks/FirstTweak.dylib -t FirstTweakDemo.app/FirstTweakDemo
Found thin header...
Inserting a LC_LOAD_DYLIB command for architecture: arm64
Successfully inserted a LC_LOAD_DYLIB command for arm64
Writing executable to FiirstTweakDemo.app/FirstTweakDemo...

签名FirstTweak.app

生成entitlements.plist文件
将FiirstTweakDemo.app/下的 embedded.mobileprovision 拷贝一份

查看描述文件内容,寻找Entitlements节点,将Entitlements节点下的内容复制到根节点下

security cms -D -i embedded.mobileprovision > entitlements.plist

最终的entitlements.plist文件内容如下

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict><key>com.apple.developer.team-identifier</key><string>12CD56GH78</string><key>get-task-allow</key><true/><key>keychain-access-groups</key><array><string>12CD56GH78.*</string><string>com.apple.token</string></array><key>application-identifier</key><string>12CD56GH78.*</string>
</dict>
</plist>

接着对FirstTweakDemo.app签名

codesign -fs 'Apple Development: daye  (12CD56GH78)' --no-strict --entitlements=entitlements.plist FirstTweakDemo.app

打包ipa包

zip -ry FirstTweakDemo.ipa Payload

现在就可以安装到未越狱的手机啦~~~

详细的信息参见install.sh

第二种,直接在Xcode中编写动态库

前面的tweak生成动态库方式,编写tweak稍显麻烦,我们直接在Xcode中编写代码。

利用Xcode,将目标APP拷贝到当前APP的目录替换现有APP,注入动态库到目标APP,重新签名,运行到设备。

1. 新建一个普通的iOS 工程, 名为InsertDemo

2. 新建一个target,选择dylib,名为InsertDemoDylib

3. 删除InsertDemoDylib类,新建InsertDemoDylib.xm与InsertDemoDylib.mm文件,工程结构如下

4. 在InsertDemoDylib.xm文件中编写hook代码,还是logos语法, 还是hook前面的FirstTweakDemo

#import <UIKit/UIKit.h>@interface FirstTweakViewController: UIViewController- (void)showAlert;@end
%hook FirstTweakViewController- (void)showAlert {UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"提醒" message:@"这是hook后的提醒" preferredStyle:(UIAlertControllerStyleActionSheet)];UIAlertAction * cancel = [UIAlertAction actionWithTitle:@"Cancel" style:(UIAlertActionStyleCancel) handler:nil];UIAlertAction * confirm = [UIAlertAction actionWithTitle:@"Confirm" style:(UIAlertActionStyleDestructive) handler:nil];[alert addAction:cancel];[alert addAction:confirm];[self presentViewController:alert animated:YES completion:nil];
}%end

5. 使用theos生成InsertDemoDylib.mm的代码,这样才能让Xcode识别并编译InsertDemoDylib.mm

/opt/theos/bin/logos.pl InsertDemoDylib.xm > InsertDemoDylib.mm

InsertDemoDylib.mm 的内容如下

#line 1 "InsertDemoDylib/InsertDemoDylib.xm"#if TARGET_OS_SIMULATOR
#error Do not support the simulator, please use the real iPhone Device.
#endif#import <UIKit/UIKit.h>@interface FirstTweakViewController: UIViewController- (void)showAlert;@end#include <substrate.h>
#if defined(__clang__)
#if __has_feature(objc_arc)
#define _LOGOS_SELF_TYPE_NORMAL __unsafe_unretained
#define _LOGOS_SELF_TYPE_INIT __attribute__((ns_consumed))
#define _LOGOS_SELF_CONST const
#define _LOGOS_RETURN_RETAINED __attribute__((ns_returns_retained))
#else
#define _LOGOS_SELF_TYPE_NORMAL
#define _LOGOS_SELF_TYPE_INIT
#define _LOGOS_SELF_CONST
#define _LOGOS_RETURN_RETAINED
#endif
#else
#define _LOGOS_SELF_TYPE_NORMAL
#define _LOGOS_SELF_TYPE_INIT
#define _LOGOS_SELF_CONST
#define _LOGOS_RETURN_RETAINED
#endif@class FirstTweakViewController;
static void (*_logos_orig$_ungrouped$FirstTweakViewController$showAlert)(_LOGOS_SELF_TYPE_NORMAL FirstTweakViewController* _LOGOS_SELF_CONST, SEL); static void _logos_method$_ungrouped$FirstTweakViewController$showAlert(_LOGOS_SELF_TYPE_NORMAL FirstTweakViewController* _LOGOS_SELF_CONST, SEL); #line 15 "InsertDemoDylib/InsertDemoDylib.xm"static void _logos_method$_ungrouped$FirstTweakViewController$showAlert(_LOGOS_SELF_TYPE_NORMAL FirstTweakViewController* _LOGOS_SELF_CONST __unused self, SEL __unused _cmd) {UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"提醒" message:@"这是hook后的提醒" preferredStyle:(UIAlertControllerStyleActionSheet)];UIAlertAction * cancel = [UIAlertAction actionWithTitle:@"Cancel" style:(UIAlertActionStyleCancel) handler:nil];UIAlertAction * confirm = [UIAlertAction actionWithTitle:@"Confirm" style:(UIAlertActionStyleDestructive) handler:nil];[alert addAction:cancel];[alert addAction:confirm];[self presentViewController:alert animated:YES completion:nil];
}static __attribute__((constructor)) void _logosLocalInit() {
{Class _logos_class$_ungrouped$FirstTweakViewController = objc_getClass("FirstTweakViewController"); MSHookMessageEx(_logos_class$_ungrouped$FirstTweakViewController, @selector(showAlert), (IMP)&_logos_method$_ungrouped$FirstTweakViewController$showAlert, (IMP*)&_logos_orig$_ungrouped$FirstTweakViewController$showAlert);} }
#line 27 "InsertDemoDylib/InsertDemoDylib.xm"

别忘了在工程中添加动态库依赖

别忘了把动态库的base SDK改为iOS,否则编译不通过

6. 编译InsertDemo,生成InsertDemoDylib.dylib

我们给工程的InsertDemoDylib target添加RunScript

dylib.sh的任务很简单,将InsertDemoDylib这个target下的所有 .xm后缀的文件生成对应的 .mm ,所用的工具即为 logos.pl

dylib.sh内容如下

TARGET_NAME=${PRODUCT_NAME}echo "TARGET_NAME:${TARGET_NAME}"function panic() # args: exitCode, message...
{local exitCode=$1set +eshift[[ "$@" == "" ]] || \echo "$@" >&2exit $exitCode
}
#预处理xm、x文件
function Processor()
{local logosProcessor="$1"local currentDirectory="$2"echo "currentDirectory:$currentDirectory"if [[ $currentDirectory =~ "Build/Products" ]] || [[ $currentDirectory =~ "Build/Intermediates" ]] || [[ $currentDirectory =~ "Index/DataStore" ]] || [[ $currentDirectory =~ "/LatestBuild/" ]]; thenecho "???????"returnfifor file in `ls "$currentDirectory"`;doecho "file:${file}"extension="${file#*.}"filename="${file##*/}"if [[ -d "$currentDirectory""$file" ]]; thenProcessor "$logosProcessor" "$currentDirectory""$file"elif [[ "$extension" == "xm" ]]; thenecho "XMFile:${file}"if [[ ! -f "$currentDirectory/${file%.*}.mm" ]] || [[ `ls -l "$currentDirectory/${file%.*}.mm" | awk '{ print $5 }'` < 10 ]] || [[ `stat -f %c "$currentDirectory/$file"` > `stat -f %c "$currentDirectory/${file%.*}.mm"` ]]; thenecho "Logos Processor: $filename -> ${filename%.*}.mm..."logosStdErr=$(("$logosProcessor" "$currentDirectory""$file" > "$currentDirectory""${file%.*}.mm") 2>&1) || \panic $? "Failed Logos Processor: $logosStdErr"fielif [[ "$extension" == "x" ]]; thenif [[ ! -f "$currentDirectory/${file%.*}.m" ]] || [[ `ls -l "$currentDirectory/${file%.*}.m" | awk '{ print $5 }'` < 10 ]] || [[ `stat -f %c "$currentDirectory/$file"` > `stat -f %c "$currentDirectory/${file%.*}.m"` ]]; thenecho "Logos Processor: $filename -> ${filename%.*}.m..."logosStdErr=$(("$logosProcessor" "$currentDirectory/$file" > "$currentDirectory/${file%.*}.m") 2>&1) || \panic $? "Failed Logos Processor: $logosStdErr"fifidone
}
logosProcessor="/opt/theos/bin/logos.pl"
echo "Start to genarate xxx.mm"
Processor "$logosProcessor" "$PROJECT_DIR/${TARGET_NAME}/"

给工程APP添加RunScript

目标APP替换现有APP的主要过程如下

将现有APP下的描述文件拷贝出来

if [ -f "${BUILD_APP_PATH}/../embedded.mobileprovision" ]; thenmv "${BUILD_APP_PATH}/../embedded.mobileprovision" "${BUILD_APP_PATH}"
fi

拷贝目标APP到当前APP的目录下做替换

cp -rf "${TARGET_APP_PATH}/" "${BUILD_APP_PATH}/"

拷贝描述文件到目标APP

if [ -f "${BUILD_APP_PATH}/../embedded.mobileprovision" ]; thenmv "${BUILD_APP_PATH}/../embedded.mobileprovision" "${BUILD_APP_PATH}"
fi

将依赖的动态库与当前公吃过生成的动态库拷贝到APP的Frameworks目录下

cp -rf "${BUILT_PRODUCTS_DIR}/lib""${TARGET_NAME}""Dylib.dylib" "${TARGET_APP_FRAMEWORKS_PATH}"
cp -rf "${DYLIBS_TO_INJECT_PATH}" "${TARGET_APP_FRAMEWORKS_PATH}"

将当前工程生成的动态库注入到APP的二进制文件中

APP_BINARY=`plutil -convert xml1 -o - ${BUILD_APP_PATH}/Info.plist | grep -A1 Exec | tail -n1 | cut -f2 -d\> | cut -f1 -d\<`
"$OPTOOL" install -c load -p "@executable_path/Frameworks/lib""${TARGET_NAME}""Dylib.dylib" -t "${BUILD_APP_PATH}/${APP_BINARY}"

对APP的Frameworks目录下的所有动态库签名

code_sign "${TARGET_APP_FRAMEWORKS_PATH}"

Xcode将APP安装到设备
具体脚本信息请参见install.sh

7. 修改项目配置,安装APP到设备

编译当前项目,发现报错,找不到substrate.h

InsertDemoDylib/InsertDemoDylib.xm:16:10: fatal error: 'substrate.h' file not found
#include <substrate.h>^~~~~~~~~~~~~
1 error generated.

这是因为我们没有引入substrate.h, 在header Search Path中引入下

再次编译,报错如下

Undefined symbols for architecture armv7:"_OBJC_CLASS_$_UIAlertAction", referenced from:objc-class-ref in InsertDemoDylib.o"_OBJC_CLASS_$_UIAlertController", referenced from:objc-class-ref in InsertDemoDylib.o"_MSHookMessageEx", referenced from:_logosLocalInit() in InsertDemoDylib.o
ld: symbol(s) not found for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)

这是因为我们的代码依赖UIKit库,但是动态库的target并没有引入,引入即可

再次编译,报错如下

Undefined symbols for architecture arm64:"_MSHookMessageEx", referenced from:_logosLocalInit() in InsertDemoDylib.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

这是libsubstrate库没有引入的原因

再次编译,报错如下

ld: -weak_library and -bitcode_bundle (Xcode setting ENABLE_BITCODE=YES) cannot be used together

关闭bitcode即可

再次编译,不报错了哟~~哈哈

跑个真机试试, 哟,一次成功了!!!

采用Xcode这种方式更简单,不用编写Tweak, 而且可以给Xcode添加一个template,以后就不用一步步配置啦,直接从Xcode新建项目菜单新建一个工程就行了。
剩下的就是把想要Hook的app拖入TargetAPP目录中就可以了。

动态库注入app以及在非越狱手机使用相关推荐

  1. iOS 逆向-非越狱手机Hook App

    引子 由于对iOS逆向分析很感兴趣,所以也花了很长一段时间学习了iOS逆向相关知识,并积累了一些相关经验, 这几天又到看雪论坛逛了一下,看看最近有没有什么大牛分享心得成果的!还真让我看到一篇有意思的文 ...

  2. ios动态库注入把越狱手机上自制的动态库安装到普通手机上

    文章目录 预备条件 导出越狱手机上的app包和自己注入的动态库 导出自己写的tweak动态库文件 查看依赖库 执行命令查看程序依赖的动态库名字 用machoview查看 安装insert_dylib ...

  3. iOS冰与火之歌番外篇 - 在非越狱手机上进行App Hook(转载)

    作者简介:郑旻(花名蒸米),阿里巴巴移动安全部门资深安全工程师,香港中文大学移动安全(Android & iOS)方向博士,曾在腾讯.百度以及硅谷的FireEye实习.在博士期间发表了多篇移动 ...

  4. iOS冰与火之歌番外篇 - 在非越狱手机上进行App Hook

    作者简介:郑旻(花名蒸米),阿里巴巴移动安全部门资深安全工程师,香港中文大学移动安全(Android & iOS)方向博士,曾在腾讯.百度以及硅谷的FireEye实习.在博士期间发表了多篇移动 ...

  5. 在非越狱手机上进行App Hook

    作者简介:郑旻(花名蒸米),阿里巴巴移动安全部门资深安全工程师,香港中文大学移动安全(Android & iOS)方向博士,曾在腾讯.百度以及硅谷的FireEye实习.在博士期间发表了多篇移动 ...

  6. iOS安全攻防(十九):基于脚本实现动态库注入

    基于脚本实现动态库注入 MobileSubstrate可以帮助我们加载自己的动态库,于是开发者们谨慎的采取了对MobileSubstrate的检索和防御措施. 那么,除了依靠MobileSubstra ...

  7. Android安全-SO动态库注入

    2019独角兽企业重金招聘Python工程师标准>>> 关于这方面技术,网上已经有大把的实现.在此,我只是记录下自己的学习过程. 0x1 原理 所谓的SO注入就是将代码拷贝到目标进程 ...

  8. WIN动态库注入(远线程注入)

    所谓动态库注入是指,将自己编写的动态库,通过自己的程序来注入到别的进程中去,然后运行. 原理: 在目标进程,开辟一段内存,然后写入要注入的动态库.dll .然后让目标运行加载动态库函数,将该动态库载入 ...

  9. 【错误记录】Android 应用配置第三方 so 动态库 ( /data/app/comxxx==/base.apk/lib/arm64-v8a]couldn‘t find “libx.so“ )

    文章目录 一.报错信息 二.修改方案 一.报错信息 应用运行时报错 : 2021-11-19 16:22:58.014 13244-13244/com.example E/AndroidRuntime ...

最新文章

  1. linux下tar.gz、tar、bz2、zip等解压缩、压缩命令小结
  2. android之android.intent.category.DEFAULT的用途和使用
  3. [NIOS] 如何Erase EPCS flash內容
  4. 计算机操作系统课设总结,计算机操作系统课程设计
  5. 如何读emmc里的引导程序_自制操作系统学习1 引导程序
  6. 揭秘熊猫TV HEVC直播
  7. github怎么隐藏自己的pr记录_记便签的软件哪个好?怎么及时记录自己的想法
  8. 翁恺老师C语言学习笔记(十一)字符串_字符串常量
  9. 若依如何防止请求重复提交?
  10. Nginx源码分析 - HTTP模块篇 - HTTP Request解析过程(22)
  11. Nginx源码分析 - 基础数据结构篇 - 单向链表结构 ngx_list.c(06)
  12. learning scala 变量
  13. java Relative Path and absolute
  14. matlab画柱状图_附带调色、字体
  15. 如何用计算机还原魔方,初学者使用的魔方口诀 7步教你快速复原三阶魔方(附图解)...
  16. 英语模板作文,词组,句子,句型,开头
  17. java 微信转账 ca_error_CA证书出错,请登录微信支付商户平台下载证书
  18. 音视频同步、网络抖动
  19. Android仿人人客户端(v5.7.1)——人人授权访问界面
  20. 智能硬件可能成为网络安全事件新的“爆发点”

热门文章

  1. C# 生成Word文档
  2. SECTION 22 面向对象编程(一)
  3. android手机照片导出来,华为手机相册怎么导出到电脑?华为手机相册批量导出电脑的三种方法...
  4. AJAX-异步的javascript和XHL
  5. DB2 Load的用法
  6. Spring Cloud Alibaba - 06 RestTemplate 实现自定义负载均衡算法
  7. python短期 培训班
  8. 标题党速成班——四步增加点击率
  9. Hive学习:数据仓库的建立
  10. odp 加固 mysql_C#使用ODP.NET连接oracle数据库