What is JNI

JNI是Java Native Interface的缩写,主要是提供了一系列API,让你能在其它语言中写Java。

What JNI can bring us

JNI最大的好处就是,额,Java你懂的,跑在JVM里面,虽然有着一处编译,到处运行的优势(,方便啊),但是它的效率。。。至少相对于c和C艹来说,比较低下,而且,正是由于这个能一处编译,到处运行的原因,Java极容易被反编译。Java中一般用的加密方式就是混淆了,然而其实并没有太大的作用。你还是开源吧。。。因为不开源也会被反编译的。。。
PS:并没有贬低Java的意思,个人还是挺喜欢用Java的

然后,相反的,JNI由于是用C或者C艹写,效率很高,可以用来处理一些底层的东西,比如解码或者TCP/IP有关的。编译过后跟C(艹)编译的结果是一样的,在Android里面是.so文件。然后,因为是C(艹),所以需要针对不同的平台,不同的处理器进行编译。所以,使用JNI,你需要在编译的时候生成许多个平台的版本,否则,Java跨平台这个优点相当于直接被废了。还有就是JNI的调试会非常蛋疼。

How to use JNI

Hello World

我用的Android Studio,有各种一键生成(x),要看手撸的话,网上应该能搜到,本文主要是介绍那些遇到的坑。

AS生成的main.cpp长这样:

#include <jni.h>
#include <string>

extern "C"
jstring
Java_com_helloworld_jnidemo_MainActivity_stringFromJNI(JNIEnv *env, jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

分析一下:

  • 几个include,其中jni.h是JNI必需的,其他的可以添加C(艹)中的,比如stdio.h什么的
  • extern C,这个我也不是特别理解,自我修养里面说是声明为C语言,然而删掉过后就炸了
  • jstring,返回值类型
  • Java_com_helloworld_jnidemo_MainActivity_stringFromJNI,Java_包名_类名_方法名,这是函数声明的规范
  • JNIEnv *env, jobject /* this */,JNIEnv里面有巨量的函数,后面就知道了,jobject就是this
  • std::string hello = "Hello from C++";,C艹
  • env->NewStringUTF(hello.c_str()),这儿就出现了env的其中一个函数,这个函数会经常在后面用到,char*转String,没错,他们不一样!

然后我自己写了一个HelloWordl和求和的函数:

extern "C"
jstring
Java_com_helloworld_jnidemo_MainActivity_helloworld(JNIEnv *env, jobject /* this */) {
    return env->NewStringUTF("Hello World");
}

extern "C"
jint
Java_com_helloworld_jnidemo_MainActivity_sum(JNIEnv *env, jobject /* this */, jint a, jint b) {
    return a + b;
}

Java中该这样写:

static {
    System.loadLibrary("native-lib");
}

public native String stringFromJNI();

public native String helloworld();

其中,System.loadLibrary("native-lib");这句是加载库,static语句块中的内容只会被执行一次。native-lib为库的名称,声明方法时使用native关键字。

CMakeLists.txt:

add_library( 
            native-lib
            SHARED
            src/main/cpp/native-lib.cpp )
find_library( 
            log-lib
            log )
target_link_libraries(
                    native-lib
                    ${log-lib} )

其中,native-lib可以随便改,对应System.loadLibrary("native-lib");里面的。但是有个玄学问题,不能改成test。。。被坑了。。。
src/main/cpp/native-lib.cpp里面的文件名可以随便改,只要与你写的文件对应。

好的,JNI入门了的样子。

Learn More

写出来了Hello World,该继续深入研究了。在继续之前,我们还应该了解一下jstringjint这些是啥,这儿有个表,展示了JNI和Java里面的属性的关系:

  • jint –> int
  • jbyte –> byte
  • jshort –> short
  • jlong –> long
  • jfloat –> float
  • jdouble –> double
  • jchar –> char
  • jboolean –> boolean
  • jclass –> java.lang.Class
  • jstring –> java.lang.String
  • jarray –> Array
  • jxxxArray –> xxx[]
  • jobject –> Object

注意最后一个,一切皆为对象。

使用JNI,你应该实现Java的基本功能:

  • new对象
  • call方法
  • 获取属性

学会了以上三个操作,就可以用JNI代替Java中70%以上的操作了。让我们一个一个来看。

new对象 & Call方法

没错,new对象就是通过调用构造方法实现的。

extern "C"
jobject
Java_com_helloworld_asdf_MainActivity_newObject(JNIEnv *env, jobject /* this */) {
    jclass clazz = env->FindClass("java/lang/Object");
    jmethodID init = env->GetMethodID(clazz, "<init>", "()V");
    jobject result = env->NewObject(clazz, init);
    return result;
}

步骤:

  • 找到class,用/代替.,FindClass的参数为所在包名
  • 找到对应构造方法
  • 调用newObject,传入class和构造方法id。

再看看一般的方法调用:

extern "C"
jint
Java_com_helloworld_asdf_MainActivity_stringLen(JNIEnv *env, jobject /* this */, jstring str) {
    jclass clazz = env->GetObjectClass(str);
    jmethodID lenId = env->GetMethodID(clazz, "length", "()I");
    jint result = env->CallIntMethod(str, lenId);
    return result;
}

GetObjectClass可以直接从object中拿到class。

调用方法用CallxxxMethod,xxx为返回值类型。CallxxxMethod的第一个参数是jobject,不是jclass,这个与NewObject不同。前面有jxxxArray,然而并没有CallxxxArrayMethod哎,该怎么办呢?一切都是对象,用CallObjectMethod再强转就可以了。
比如:

extern "C"
jstring
Java_com_helloworld_asdf_MainActivity_toString(JNIEnv *env, jobject /* this */, jobject object) {
    jclass clazz = env->GetObjectClass(object);
    jmethodID lenId = env->GetMethodID(clazz, "toString", "()Ljava/lang/String;");
    jstring result = (jstring) env->CallObjectMethod(object, lenId);
    return result;
}

方法签名:
简直有毒,反人类

  • construction –>
  • void –> V
  • boolean –> Z
  • byte –> B
  • char –> C
  • short –> S
  • int –> I
  • long –> J
  • float –> F
  • double –> D
  • x[] –> [x
  • x[][] –> [[x
  • java.lang.String –> L/java/lang/String;

总结一下:

  • 每个基本类型都有对应的签名,基本法
  • 数组用[
  • 构造方法规定为
  • 其它类为L类;,注意:分号不能丢,分号不能丢,分号不能丢

获取Field

extern "C"
jint
Java_com_helloworld_asdf_MainActivity_getX(JNIEnv *env, jobject /* this */, jobject test) {
    jclass clazz = env->GetObjectClass(test);
    jfieldID lenId = env->GetFieldID(clazz, "x", "I");
    jint result = env->GetIntField(test, lenId);
    return result;
}

static

static的属性和方法与普通的有一些区别,例如CallStaticObjectMethod的第一个参数是jclass。这些在熟悉了上面的操作过后都没有太大的问题了。

多维数组的处理

  • 思路:一维一维处理
  • env->GetArrayLength()拿到第一维数组长度
  • for循环中用env->GetByteArrayRegion()将下一维的元素取出
  • 如果是多维,重复操作

分享一点经验

  • 一切都是object
  • Java里的String和C(艹)里的是不一样的,要记得NewStringUTF,被坑过
  • L/java/lang/String;
  • java/util/Listjava/util/ArrayList是不一样的。。。要看清方法的参数。。。

Read More

因为自己比较鶸,所以blog里面肯定会有很多error和warning,大佬们看到了希望能够提醒一下,在下不胜感激

lxc和lxd

lxc

lxc是指Linux Container,是一种轻量级的虚拟化,它与一般的虚拟机最大的不同就在于:

  • 容器中的系统与宿主机使用同一个内核,性能损耗小;
  • 不需要指令级模拟;
  • 不需要即时(Just-in-time)编译;
  • 容器可以在CPU核心的本地运行指令,不需要任何专门的解释机制;
  • 避免了准虚拟化和系统调用替换中的复杂性;
  • 轻量级隔离,在隔离的同时还提供共享机制,以实现容器与宿主机的资源共享。

可以看出来,lxc相对于传统虚拟机最大的优势就在于虚拟化程度低,运行效率高,同时实现了基本的虚拟机的功能,对服务器的备份和运维等非常有利。
而lxc的缺点也正是因为lxc使用了宿主机的内核。如果给予了容器特权,那么容器就有可能对宿主机进行破坏。

lxd

官方文档第一句介绍到:LXD is a container “hypervisor” and a new user experience for LXC.
然后,组成部分:
Specifically, it’s made of three components:

  • A system-wide daemon (lxd)
  • A command line client (lxc)
  • An OpenStack Nova plugin (nova-compute-lxd)

可以看出来,lxd是基于lxc的,用来对lxc中容器进行管理和维护的一个平台。

docker和lxc的区别

lxc内运行的容器基于一个干净的发行版镜像,可以支持长期运行。而docker关注点在临时的、无状态的、最小化容器上面,通常不会升级或重新配置,而是整个被替换掉。因此,docker的通常是一个容器运行一个软件或者服务,lxc容器内可以由用户自定义,灵活性更高。正因如此,lxc的镜像通常会特别大,一般大于1g,而docker的镜像一般500m以下。

本来还想写点docker的。。。看了下资料,发现docker的东西真不少。。。下次吧

如何配置lxc和lxd

1.初始化容器

在初始化之前,你需要装上一些dependence.

    apt update
    apt install zfs

然后

    lxd init

一路回车,按默认配置就行了。
然后就需要添加镜像,原镜像源是国外的,速度你懂的。(可选) 比如添加大清和重邮(内网有加成的哦)的:

    lxc remote add <name> <url> [--accept-certificate] [--password=PASSWORD] [--public] [--protocol=PROTOCOL] # usage 


    lxc remote add tsinghua-mirror https://mirrors.tuna.tsinghua.edu.cn/lxc-images --public
    lxc remote add cqupt-mirror https://mirrors.cqupt.edu.cn/lxc-images --public

然后启动时就可以通过国内的镜像源启动:

    lxc launch [remote:]<image> [remote:][<name>] [--ephemeral|-e] [--profile|-p <profile>...] [--config|-c <key=value>...]  # usage
    lxc launch cqupt-mirror:ubuntu16.04 test-container

配置容器:

    lxc config device set <[remote:]container> <name> <key> <value>   # usage
    lxc config device set test-container limits.memory 1024M # memory
    lxc config device set test-container limits.cpu 1 # cpu

运行/停止容器:

    lxc start test-container
    lxc stop test-container
    lxc restart test-container

在容器和宿主之前复制文件:

    lxc file pull <source> [<source>...] <target> # pull file from container
    lxc file push [--uid=UID] [--gid=GID] [--mode=MODE] <source> [<source>...] <target>  # push file into container

    lxc file pull test-container/root/.ssh/authorized_keys . # pull file /root/.ssh/authorized_keys in container to current dir
    lxc file push authorized_keys test-container/root/.ssh/ . # push file authorized_keys into container/root/.ssh/ 

注意push进容器后文件的权限,有些文件(比如.ssh/authorized_keys)权限不对是会炸的

配置profile,就不用每次都去单独配置每个容器了:

    lxc profile create test
    lxc profile edit test

在容器中执行命令:

    lxc exec test-container bash

这样你就直接在容器内的shell里了。

实战

在容器内搭建web服务器:

    lxc launch cqupt-mirror:ubuntu16.04 lnmp
    lxc config device set lnmp limits.memory 1024M 
    lxc config device set lnmp limits.cpu 1 
    lxc exec bash

然后现在就在容器内了
后面就是搭建web服务器的过程了,跳过。

如果你之前直接将虚拟网卡桥接到与主机同一局域网下,容器会分配到一个独立ip,直接ifconfig找到ip,访问这个ip就可以了。
如果你是直接建立的虚拟网桥,需要在宿主机上添加iptables规则,也就是NAT:

    iptables -t nat -A PREROUTING -d host_ip -p tcp --dport 80 -j DNAT --to-destination container_ip:80 # replace host_ip and aontainer_ip with yours

PS:lxc的所有配置都在这儿

Read More

作业大礼包

  • 复(预)习一下我上课讲的
  • 写一个文本搜索器
    • 新建线程,打开文件,(分段)读入内存,对关键字进行匹配,并打印出对应行和行号。
    • 要求能命令行下执行,也就是main函数参数,例如:
    • java Scanner Scanner.java public
  • 用网上的库实现mp3播放,并同步显示歌词库名:jlayer
    • 歌词处理:正则,队列
    • 多线程
    • 歌曲和歌词我限定了,一半一半 – 洛天依,发到群里,也可以在我的服务器下载,
    • 地址:
    • 同上,要求能命令行下执行,也就是main函数参数,例如:
    • java Player 一半一半.mp3 一半一半.lrc
  • 哲♂学家问题
    • 一圆桌前坐着5位哲♂学家,两个人中间有一只筷子,桌子中央有面条。哲♂学家思考问题,当饿了的时候拿起左右两只筷子吃饭,必须拿到两只筷子才能吃饭。上述问题会产生死锁的情况,当5个哲♂学家都拿起自己右手边的筷子,准备拿左手边的筷子时产生死锁现象。
      解决办法:
      • 1、添加一个服务生,只有当经过服务生同意之后才能拿筷子,服务生负责避免死锁发生。
      • 2、每个哲♂学家必须确定自己左右手的筷子都可用的时候,才能同时拿起两只筷子进餐,吃完之后同时放下两只筷子。
  • 该预习Android了哦(必做
    • 先自己装AS,搭环境,然后跑一个HelloWorld吧,HelloWorld都不会,怎么愉快的继续往下学习。
    • 了解一下那些控件
    • AS在这儿下,也可以去官网
    • 内网也有哦

从上往下做,做了几个不要紧,记得预习Android

  • 第一个和最后一个不交,其他的,你们尽力吧!

Read More

Java中的异常处理机制

0x00 异常

异常分为Error和Exception,我们通常说的处理异常,其实是处理Exception。而Error已经不是异常了,而是错误。一般是因为代码错误导致jvm崩溃。

用图说话 异常大类

0x01 Exception类和它的子类

从上面的图可以看到,Exception这个类下面有很多子类,他们都继承自Exception,我们也可以自己写一个异常类。例如:

public class MyException extends Exception {
}

然后就可以使用了,例如:

public static void throwException() throws MyException {
    throw new MyException();
}

原理会在下文讲到

这个继承于Exceptiion的类啥都没写,所以类里面的东西和Exception里面的是一样的。让我们来看看Exception里面有些什么。好吧。。。全是super.method,我们还是看它的父类Throwable吧。

private transient Object backtrace;
private String detailMessage;
private Throwable cause = this;

主要就是这三个。第一个,是在抛出Exception后的栈跟踪。第二个,是异常详情。第三个就是异常原因,就是异常本身。

异常的构造方法:

public Exception();
public Exception(String message);
public Exception(String message, Throwable cause);
public Exception(Throwable cause);

我就不具体写了。跟成员变量对应。

jvm通过栈来将异常一层一层往上抛(与一层一层的函数调用相反),直到他被处理(catch),否则,程序停止工作,jvm向用户报告错误。异常的抛出(栈)路径可以通过Exception.printStackTrace()方法查看
而异常被处理后,程序会回到抛出异常的地方继续执行。

0x02 如何抛出异常和进行异常处理

抛出异常

很简单,直接使用throw关键字,但是如果抛出的异常未在当前方法处理,需要在方法后面声明。需要在它所在的方法处声明throws MyException。意思是此方法将抛出错误。

注意:如果是RuntimeException或者它的子类的话,可以不附加声明,例如:

try {
    // some code
} catch (RemoteException e) { 
    throw new RuntimeException(e);
}

处理异常

使用try catch语句。用法:

public static void main(String[] args) {
    try {
        throwException();
    } catch (MyException e) {
        e.printStackTrace();
        //Do sth...
        //throw e;
    }
}

try可能会抛出异常的语句块,catch (MyException e)捕获异常MyException声明为引用e.一般来说都会跟上一个e.printStackTrace(),打印错误详情,方便debug。
当然你也可以再次将异常抛出,交给上层继续处理。
有人会发现,e.printStackTrace()输出的怎么是红的,因为你看看它的源码:

public void printStackTrace() {
    printStackTrace(System.err);
}

它默认用的输出流是System.err,而不是sout用的System.out哦。

多重异常处理

用于将不同类型的异常分开处理。

try {
    throwException();
    throw new NullPointerException();
} catch (MyException e) {
    e.printStackTrace();
} catch (NullPointerException e) {
    e.printStackTrace();
}

对了,如果程序因异常而退出,它的返回值就不为0,在IDEA中可以明显看到:Process finished with exit code xxx

0x03 finally关键字

你可能会遇到无论是否出现异常都需要进行某种操作的情况,这时候,你就需要用到finally了。比如:

try {
    throwException();
    throw new NullPointerException();
} catch (MyException e) {
    e.printStackTrace();
} catch (NullPointerException e) {
    e.printStackTrace();
}finally {
    System.out.println("An error occurred!");
}

这种方法经常用来在异常发生后关闭流。
其实可以把finally后面的语句看成擦屁股的

0x04 异常丢失

这是一种特殊情况,虽然不经常出现,但是还是提一下比较好。

最简单的触发方法:

public static void main(String[] args) {
    try {
        throwException();
        throw new NullPointerException();
    } catch (MyException e) {
        e.printStackTrace();
    } catch (NullPointerException e) {
        e.printStackTrace();
    }finally {
        System.out.println("An error occurred!");
        return;
    }
}

也就是在finally里面执行return。
IDEA里面会提示:MissingException
其实原理就是在finally里面的语句会在异常处理完成之前执行。如果在finnally里面return,就会发生异常丢失。

0x05 异常实例

private int take(int index, int last) {//取豆子
    int count;
    try {
        count = mPrisoners.get(index).take(index, last);
    } catch (Exception e) {//这些神经病抓出来单独死
        count = -1;
    }
    if (count > last || count < 0){//如果返回无效个数,和上面的神经病一样死
        count = -1;
    }
    System.out.println(mPrisoners.get(index).getName() + "取了" + count + "个");
    //保存每个人取的豆子数
    mTempHold.replace(mPrisoners.get(index), count);
    return count;
}

取豆子游戏,源码在这儿,Manager类。

这是异常处理的用法之一,你永远不知道熊孩子们会搞出什么异常炸掉你的程序,所以熊孩子必须抓出来单独死。

异常在Java中使用频率极高,多次使用对异常自然就熟悉了。

这blog的MarkDown解析有毒,觉得蛋疼可以去我的简书看。。。

感谢haruue帮我指出错误

Read More

引言

我们为什么需要规范命名?首先,容我举个栗子:

有这样一个求最大值的函数:

//C
int max(int a, int b);
int zuidazhi(int a, int b);

读第一行代码的过程:单词max->最大值 读第二行代码的过程:拼音zuidazhi->从拼音匹配对应汉字->最大值 可能你现在还觉得第二种命名也不是那么难理解。那么再看看下面的:

//C
int checkUsername(char *username);
int jianchayonghuming(char *yonghuming);

如果你还觉得第二种命名简单,不错,你的小学语文一定是满分,可是你写的程序其他人不一定能看懂,或者会花很多时间去看懂。然后,国外的人一定都看不懂(黑人问号)。

为什么要谈命名的艺术

  • 首先,每个文件,函数等都需要一个标签,为了我们能更快找到它们,命名就显得比较重要了。
  • 我们写的代码不光是给自己看的,你需要让你的老师,队友,甚至是上司都能看懂你的代码。一个人只能耍耍小聪明,写点小程序,而那些大型程序几乎没人能独自完成。团队合作时,命名就是看你是否坑队友的标准之一。
  • 其实命名规则也是一种习惯,我们都遵循这个的规则,世界才能更加和平(x)。

通用的命名规则

  • 不要用拼音!!!
  • 不要作死去用关键字/保留字。
  • 常量大写+下划线命名法。

例子:

//Java
public class class {//这行直接炸
      public static final int EXIT_SUCCESS = 0;//下面应该会有一个exit(EXIT_SUCCESS);吧,手动滑稽
      private int zonghe;//可以,这很拼音
}

Java命名规则

  • 据说Java支持中文变量,但是中文不要来。(据说有个叫e4a的就是这么干的)
  • 类名/文件名用帕斯卡命名法注:文件名必须和最外层public类类名相同
  • 方法名/成员变量名/形参等用驼峰命名法

例子: 文件名为TestClass.java

//Java
public class TestClass {//类名必须和文件名相同,帕斯卡命名法
    private int sum;
    public int getSum(){//驼峰
        return this.sum;
    }
}

更高级的姿势

命名要有意义

  • for循环里的i,j,k什么的就不要说了
  • 类名:要能代表这个类的功能和类型(ChatClient)
  • 方法名:同样要能表示方法的功能,还要简单易懂(getServerIP)
  • 属性1:一般是类名变驼峰命名,也可以将类名适当缩写(chatClient或client)
  • 属性2:java源码中经常以m开头来命名成员变量,虽然我不喜欢这么写(mChatClient或mClient)
  • 尽量不要使用重复命名,这样容易导致各种奇怪的问题。

其它命名规则(有点杂,就不列举多了)

  • 下划线命名法。(脚本语言使用较多)
  • 特殊前后缀标记(php的$,python的__)
 #python
class Test:
    def __init__(self):
        self.name = None
def is_empty(content)://下划线
    if content == "":
        return False
    else:
        return True

最后,再次强调:不要用拼音!!!

最后的最后,贴上朱大的地址,快去膜一波。

Read More

tips:<*> means required,[*] means optional

基本操作

创建仓库

git init

添加文件

git add <filename>

提交更改

git commit -m '<message>'

查看状态

git status

查看修改内容

git diff

查看日志

git log

回退到目标版本

注:

  • HEAD^为上一个版本,HEAD^^为上上个版本,依此类推
  • commit id类似3628164
git reset --hard <version>

撤销更改

  • 缓存区
git reset HEAD <filename>
  • 文件
git checkout -- <filename>

删除文件

git rm <filename>

远程仓库

添加远程仓库

git remote add origin <*.git>

推送到远程仓库

git push [-u] origin <repository-name>

克隆仓库

git clone <*.git>

分支

创建分支

git branch <branch-name>

切换分支

git checkout <branch-name>

合并某分支到当前分支

git checkout -b <branch-name>

创建+切换分支

git checkout -b <branch-name>

查看分支

git branch -a

删除分支

git branch -d <branch-name>

强行删除未合并分支

git branch -D <branch-name>

创建远程分支到本地

git checkout -b <branch-name> origin/<branch-name>

删除远程分支

git branch -r -d origin/<branch-name>
git push origin :<branch-name>

建立本地分支和远程分支的关联

git branch --set-upstream <branch-name> origin/<branch-name>

临时储存工作进度

储存

git stash

查看

git stash list

恢复

git stash apply [stash@{*}]

删除

git stash drop [stash@{*}]

恢复并删除

git stash pop [stash@{*}]

标签

创建标签

git tab <tag-name> [-m 'comment'] [commit-id]
 #eg.
git tag v0.9 6224937

删除标签

git tag -d <tag-name>

推送标签

git push origin <tag-name>

删除远程标签

先删除本地,然后再

git push origin :refs/tags/<tag-name>

忽略文件

创建:.gitignore,在里面写入过滤规则

*.class
test/

强行添加忽略的文件

git add -f <filename>

被忽略的文件的忽略规则

git check-ignore -v <filename>

自定义命令

(个人认为可以理解为C语言中的宏定义)

git config --global alias.<abbr> 'command'
 #e.g.
git config --global alias.fuck 'push origin'
git config --global alias.logd "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"

不小心push了密码到github上

没错,简直蠢哭了

BFG Repo-Cleaner 用法:

java -jar bfg.jar --replace-text replacements.txt my-repo.git

replacements.txt 这样写:

test@qq.com==>[email protected]

廖雪峰的原文看这里

git rebase

Read More

题目:我方截获了敌方的一款加密程序和一段密文(密文为:F2F5D39F18D762B7),尝试通过对此程序进行分析实现对密文的解密,解密后明文的32位小写MD5值作为flag提交。

1.先运行一发,运行截图:

图片1.png

2.扔进OD,搜索字符串,发现,咦,内有乾坤。

图片2.png

3.经过成吨的分析(本人鶸一只),发现题目给出的密文应该是经过打开程序时的默认加密方式加密的,那么,现在就要找到默认加密方式。

4.直接肛,强行跳到加密的地方

图片3.png

至于怎么跳,随便找个地方跳过去,我选择的改输入选项,把退出系统改成跳到自定义加密。 //反正我是来逆向的,程序boom了也没事。(雾

图片4.png

在这后面下断点,看它怎么跳的。

图片5.png

如果输入为2,push返回值0,call exit。 把参数干掉,call自定义加密解密:

图片6.png

图片7.png

图片8.png

跳过去了,然后下断点找到自定义加密是的函数。 Case 1 ,call了这个函数,跟过去。

图片9.png

加密函数就在这中间,在最后一次输入(输入密钥时)后面下断点仔细找找,找到了就跟进去。

图片10.png

这密密麻麻的运算,看来就是它了。

图片11.png

找到return的地方,往前下断点。

图片12.png

就是这儿,下断点,然后重新运行选择默认加密,随便加密一个就能看到密码是啥了。

图片13.png

这就是密码了。

图片14.png

再选择自定义解密。

图片15.png

直接在这儿看,或者看程序运行结果都可以。

图片16.png

然后拿去md5大法。就可以提交flag了。

Read More

主题介绍

Jekyll-Jacman 是为 Jekyll 设计的一款清新且具有响应式的主题,拥有更丰富的特性并支持了很多的国内服务。Jacman 始于 Jacman 移植而来。

配置指南

Jacman 主题提供了丰富的配置属性,可以实现您对主题的自定义。配置文件_config.yml位于主题根目录下。本次更新对配置文件进行了较大调整,如您之前就使用了 Jacman,也需要您根据以下指南进行相应的修改。

##### 菜单
menu:
  主页: /
  归档: /archives
  关于: /about

#### 控件
widgets: 
- category
- tag
- links
- rss

#### RSS 
rss: /atom.xml 

#### 图片相关
imglogo:
  enable: true               ## 是否显示网站 logo
  src: img/logo.png        
favicon: img/favicon.ico     ## 网站图标    
apple_icon: img/jacman.jpg   ## 苹果设备上的图标,背景不要透明
author_img: img/author.jpg   ## 网站底部的博主头像
banner_img: img/banner.jpg   ## 博客顶部的图片

close_aside: false      ##是否在文章页面自动关闭侧边栏

#### 首页相关
index:
  expand: true              ## 首页文章是否展开。默认为展开式,显示 Read More。
  excerpt_link: Read More    

#### 作者信息
author:
  name: ## 作者名
  intro_line1:  "Hello ,I'm Larry Page in Google."    ## 网站底部的个人介绍
  intro_line2:  "This is my blog,believe it or not."  
  weibo_verifier:  ## 微博秀的验证码
  tsina:           ## 用于微博秀和微博分享
  weibo:           ## 用于显示网站底部社交按钮,下同
  douban:         
  zhihu:  
  email:     
  twitter:   
  github:     
  facebook: 
  linkedin:   
  google_plus:   
  stackoverflow:  


#### 目录
toc:
  article: true   ## 是否在文章中显示目录
  aside: true     ## 是否在侧边栏显示目录

#### 友情链接
links:
  码农圈: https://coderq.com,一个面向程序员交流分享的新一代社区
  Jark's Blog: http://wuchong.me
  
#### 评论
duoshuo_shortname: 
disqus_shortname:  

#### 分享按钮
jiathis:
  enable: false   ## 默认使用主题内建分享
  id:    
  tsina: 
  
#### 网站统计
google_analytics:
  enable: false
  id:            ## google analytics ID.
  site:          ## 网站地址.
baidu_tongji:
  enable: false
  sitecode:      ## 百度统计站点特征码
cnzz_tongji:
  enable: false
  siteid:        ## CNZZ统计站点ID

#### 杂项
ShowCustomFont: true  
fancybox: true        
totop: true           

#### 自定义搜索
google_cse: 
  enable: false
  cx:  
baidu_search:    
  enable: false
  id:   
  site: http://zhannei.baidu.com/cse/search 
tinysou_search:     ## http://tinysou.com/
  enable: false
  id: "4ac092ad8d749fdc6293" 

属性功能

  • 菜单 menu 默认没有启用 /tags/categories页面,如果需要启用请在博客目录下的source文件夹中分别建立tagscategories文件夹每个文件夹中分别包含一个index.md文件。内容为:
layout: tags (或categories)
title: tags (或categories)
---

      因为主题中已经内置了这两个页面的模板,所以他们会被正确的解析出来。

  • 控件 widgets 提供了7种小工具。包括标签、分类、RSS、友情链接、微博秀。

友情链接:友情链接的网址添加可以在links属性下添加。

微博秀:需要注意的是,如果要启用微博秀,您必须填上author属性下tsinaweibo_verifier的值,前者是您微博ID,后者是您微博秀的验证码,访问 http://app.weibo.com/tool/weiboshow 在如下图位置,可以获得您的 verifier,如:我的是b3593ceb

如果要关闭侧边栏,将close_aside置为true,就会在博文页面自动关闭侧边栏。

  • 图片相关 Image 本主题可以设置网站相关图片,例如网站图标(favicon)、网站logo(imglogo)、作者头像(author_img)。建议启用网站logo,格式建议为.svg.png格式。同时建议提供配套的 favicon 以及在苹果设备上的图标apple_icon(背景不要透明)。

  • 首页显示模式 Index 目前首页的显示模式支持两种,一种是原先的卡片式(前往 Demo 预览),另一种是类似官方主题的文章展开式(本站即采用的这种)。两者各有优劣,前者首页加载速度更快,后者文章内容更能吸引读者。主题默认采用后一种展开式,如需开启第一种卡片式,请设置index属性下的expand: false

卡片式的文章摘要是截取文章内容的前140个字,也可以自己总结description并将其放在开头的front-matter中。展开式的文章摘要就是使用<!-- more -->截取了。

  • 作者信息 author 作者信息,建议尽量填写完整。其中tsina是你的新浪微博ID,不同于用户名或微博主页地址。启用这个属性后,其他用户在微博上分享你文章的同时会自动@你。同时它和weibo_verifier一起作用生成微博秀。intro_line1intro_line2是网站底部的个人介绍。weibotwitterfacebook等是用来显示网站右下角的社交按钮的,如下图所示。

  • 目录 toc 是否启用在文章中或侧边栏中的目录功能。二者可以都为true或都为false。同时,如果你希望在特定的某一篇文章中关闭目录功能你可以在文章文件开头中的front-matter中加上一行toc: false

  • 评论 comments 填写duoshuo_shortname多说的用户名,启用多说评论系统。在大陆地区更好用的评论系统。

填写disqus_shortnamedisqus 的用户名,启用 disqus 评论系统。国际上更广泛使用的评论系统。设置博客根目录下的_config.yml文件中的disqus_shortname同样也能开启该功能。

  • 加网分享 jiathis 加网分享系统。默认关闭,因为主题已经内置了原生的分享功能。

  • 网站统计 Analytics google_analytics:Google Analytics追踪代码。请注意:Google Analytics已经升级到了Universal Analytics。请先前往后台升级你的Google Analytics版本后再启用追踪代码,更多信息请点击这里了解。

baidu_tongji:百度统计功能。需要填写站点特征码sitecode,在官网注册并配置站点后,获取特征码。特征码可以在「网站中心」-> 「代码获取」中查看,如下图所示的e6d1f421bbc9962127a50488f9ed37d1,注意去掉前面的3F

cnzz_tongji:站长统计功能。需要填写站点IDsiteid,同理在站长官网注册并配置站点后获得。

  • 数学公式 mathjax 主题支持写 LaTex 数学公式。只需要在文章文件开头的front-matter中,加上一行mathjax: true,即可在文中写 LaTex 公式。

  • 图片浏览 fancybox 默认关闭,如果你经常发表 Gallery 类型的文章,那么请设置为true

  • 返回顶部 totop 右下角返回顶部按钮,默认开启。

  • 自定义搜索 Search baidu_search:如果开启百度站内搜索需要登录 百度站内搜索,配置好你的站点,并开启站内搜索获取搜索ID,另外site属性可以填默认值,也可以填自己做了CNAME的二级域名,更详细的可以阅读这篇博客了解。

google_cse:如果开启谷歌自定义搜索需要先登录 Google CSE,配置好你的站点,并获得此自定义搜索的ID。此外你需要在博客目录下的source文件夹中建立search文件夹并包含一个index.md文件。内容为:

 layout: search
 title: search
 ---

tiny_search: 如果要开启微搜索,需要先注册一个帐号,配置一个Engine,将Engine的Key填入配置文件中的id即可。

##常见问题

  • Q:图片默认都是居左的,我怎么设置能让图片居中呢?

    使用 <img src="" style="display:block;margin:auto"/>的HTML标签。

  • Q:如何建立一篇图片类文章(Gallery Post)?

    直接新建一个 Markdown 文件,将其front-matter修改为如下,即可看到主题为图片类文章提供的样式。 ``` — layout: photo title: Gallery Post photos:

  • http://i.minus.com/ibobbTlfxZgITW.jpg
  • http://i.minus.com/iedpg90Y0exFS.jpg

    ```

  • Q:我在配置文件中给某一项设置了值,但为什么总是看不到效果啊?

    _config.yml文件中的每个属性值前面必须留一个空格,建议在 Sublime/Notepad++ 中开启显示所有空格模式。另每篇文章的 front-matter 也要注意这个问题。

  • Q:怎么提意见和建议?

    主题还在不断完善中,欢迎 open issue 来提建议,参与讨论。

  • **Q:为什么我修改了配置文件/发表了博文,解析出来的却是乱码呢? **

    请将你的配置文件/markdown文件保存成 UTF-8 格式。

  • **Q:为什么开启了微博秀后,显示是空白的,没有内容展示? **

    每次修改参数都会这样,需要多刷新几次或者上传到服务器上就好了。

Read More