0%

前段时间,因为一年前篡改了一个sdk的包,导致了一系列不可预知的损失,这也充分说明了技术的正确性,需要决策的正确性来支撑的。作为技术人员,不仅仅要考虑技术的可行性,也要考虑实施后的风险和结果的预期假设。 从工程角度,简单记录一下篡改sdk包的技术。

  1. 解压jar或者apk的包
  2. JD-GUI 反编译查看class的相关代码,找到修改点
  3. 自己编写相应的方法或者类,使用 javaassist 注入到class文件
  4. 重新打包

javassist 使用方法参考: https://jboss-javassist.github.io/javassist/tutorial/tutorial.html 附上测试代码,修改一个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static void main(String[] args) {
try {
javassist.tools.reflect.Loader cl = new javassist.tools.reflect.Loader();
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath("/tmp/aa/");
CtClass cc = pool.get("com.facebook.ads.internal.d.g");
CtMethod ctmethod = cc.getDeclaredMethod("c");
cc.getDeclaredField("c");
// getCode()即为需要替换的的代码
ctmethod.setBody(getCode());
cc.writeFile("/tmp/aa/");
} catch (CannotCompileException e) {
e.printStackTrace();
} catch (NotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

最近的论文用到了RSA相关的东西,做一个整理。

流程图

  • 密钥生成过程: RSA
  • 加密解密过程: RSA

选取2个质数p、q

\(RSA\)算法的主要就是基于一个十分简单的数论事实:将两个大素数相乘十分容易,但是想要对其乘积进行因式分解却极其困难。 同时为了增强强度,\(p-1\)和\(q-1\)的最大公因子要小 质数的选取方法:

  • 随机搜索法随机产生一个奇数 \(p_1\) 进行素数测试,若是素数,则结束;否则,重新随机产生一个奇数 \(p_2\) 进行素性测试,直至找到一个素数 \(p_t\)。
  • 随机递增搜索法随机产生一个奇数,对以该数为起点的奇数依次进行测试,直至找到一个素数。这种方法相对于随机搜索法,在速度上有一定的提高,但是并没有本质上的区别
阅读全文 »

最近在看《深度探索c++对象模型》对于对象模型有了一点了解,做一个总结。以下的一些结论的实验见:https://github.com/xtestw/CPPObjModelTest

单个对象模型

对于一个单个对象而言,对象的内部结构是类似于一张表结构,依次存储着c++的对象内部数据,我们都知道,一个c++的类内部一般会包含下面的几个部分:

  • 非静态成员变量
  • 非静态成员函数
  • 静态成员变量
  • 静态成员函数
  • 虚函数
  • 友元函数
  • 构造函数、析构函数、拷贝构造函数

对于一个简单的对象,将设我们定义类如下:

1
2
3
4
5
6
7
8
9
10
class base{
public:
base();
virtual ~base();
int a,b;
void f();
static int c,d;
static void f2();
virtual void f3();
};

不考虑继承的话,他们的存储结构会是这样的一个结构,如下图: 其中,非静态数据成员,被放在对象的内部,而虚函数会放在对象的一个虚表中,对象在编译的时候会形成一个vptr的指针置于对象的内部,其指针指向这张虚表(考虑类的继承和多态,指向这张虚表的vptr的设置和重置会在构造函数、析构函数和拷贝函数中自动完成)。 对象在图对象表中的位置,是按照对象的申明顺序来排列的。比如a,b的申明,因为a先申明的,那么a就被先压栈,占据高地址。 而如图所见,不论虚函数是多少个,对象内部只有一个指针指向它,所以始终占一个指针大小的空间(32位机器下是4byte). 而对这个vptr在对象中的位置,不同的编译器的处理是不一样的,vc为代表的是将其放在了头部,而gcc等则是将其放在了对象的尾部,放在尾部是为了综合考虑与C的struct的兼容问题,而在头部,则是考虑继承之后的子类在调用vptr的方便性。(我个人更偏向于放在头部,因为这样在继承的时候更好理解也更方便自然),本文中的代码和模型是基于g++编译器做的实验,都是放在头部的。 还有一个问题,《深度探索c++对象模型》中,对这个1byte的类的type_info位置,他是说放在虚表的第一个位置,而其实g++中,下面这个实验,并不是放在虚表中的:

阅读全文 »

转自:http://www.ibm.com/developerworks/cn/linux/l-cn-nohup/ 我们经常会碰到这样的问题,用 telnet/ssh 登录了远程的 Linux 服务器,运行了一些耗时较长的任务, 结果却由于网络的不稳定导致任务中途失败。如何让命令提交后不受本地关闭终端窗口/网络断开连接的干扰呢?下面举了一些例子, 您可以针对不同的场景选择不同的方式来处理这个问题。

nohup/setsid/&

场景:

如果只是临时有一个命令需要长时间运行,什么方法能最简便的保证它在后台稳定运行呢?

hangup 名称的来由

在 Unix 的早期版本中,每个终端都会通过 modem 和系统通讯。当用户 logout 时,modem 就会挂断(hang up)电话。 同理,当 modem 断开连接时,就会给终端发送 hangup 信号来通知其关闭所有子进程。

阅读全文 »

http://xtestw.site/?p=29

##x&(-x)取x的最后一个1的证明##
明天要给新队员讲树状数组,避不开的一个证明,之前教主讲过,当时没听明白给忘了- -,只有自己想了一种证明方法。

#####证明#####
大家都知道,计算机是用补码来存储一个数的,在这种编码情况下,整数是自然状态编码,假设我们是一个5位的机器(只是假设),那么1,编码就为00001, 而-1的编码则是11111(并不是10001),在这种情况下,我们会有下面的结论:

             -x=11111 – x +1 (x 假设为正数二进制)  -----------------结论一
              x=00000 + x (x 假设为正数二进制) ----------------------结论二

我们还会有以下2个结论:

结论三:11111 - x 不会出现借位的情况(二进制下 ,对应位只会是两种情况 1-0 和 1-1)
结论四:00000 + x 不会出现进位的情况(二进制下,对应位只会是 0+1,0+0)

假设 x的二进制编码为 x1 x2 x3 x4 x5

那么我们就会发现 对于任何一位(以x1为例) 0+x1 和 1-x1 两个 必然是一个为1 一个为0,因为:

X1的值 0+x1 1-x1
0 0 1
1 1 0
阅读全文 »

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class base{
public:
int i,j;
base(){
i=j=0;
}

base(int a,int b){
i=a;j=b;
}
};

class derived:public base{
public:
derived(int a,int b){
base(a,b);
}
};

int main(){
  derived d(3,1);
  cout<<d.i<<" "<<d.j<<endl;
  return 0;
}

上面的这段代码会输出什么呢?!正确的答案是输出了”0 0”. 我的理解是,虽然其中调用了base(a,b) 但是其实操作的a和b不是当前d的a,b,其实是定义了一个新的base的对象。正确的写法应该是下面这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class base{
public:
int i,j;
base(){
i=j=0;
}
base(int a,int b){
i=a;j=b;
}
};



class dervied:public base{

public:
derived(int a,int b):base(a,b){
}
};

int main(){
  derived d(3,1);
  cout<<d.i<<" "<<d.j<<endl;
  return 0;
}

关于c++构造函数两个误解

  1. 任何class只要没有定义构造函数,就会被编译器自动合成一个。 有些构造函数在实际中有和没有是没有任何用处的,所以无用的构造函数是不会被构建出来

  2. 编译器合成的构造函数,会为每个成员变量设定初始值 在我看来,编译器合成的构造函数,是为了调用成员类或者父类的构造函数,而这个成员类的构造函数的调用顺序则是按照申明的顺序来调用。

1、assigned

主键由外部程序负责生成,在 save() 之前必须指定一个。Hibernate不负责维护主键生成。与Hibernate和底层数据库都无关,可以跨数据库。在存储对象前,必须要使用主键的setter方法给主键赋值,至于这个值怎么生成,完全由自己决定,这种方法应该尽量避免。

“ud”是自定义的策略名,人为起的名字,后面均用“ud”表示。

特点:可以跨数据库,人为控制主键生成,应尽量避免。

2、increment

由Hibernate从数据库中取出主键的最大值(每个session只取1次),以该值为基础,每次增量为1,在内存中生成主键,不依赖于底层的数据库,因此可以跨数据库。

Hibernate调用org.hibernate.id.IncrementGenerator类里面的generate()方法,使用select max(idColumnName) from tableName语句获取主键最大值。该方法被声明成了synchronized,所以在一个独立的Java虚拟机内部是没有问题的,然而,在多个JVM同时并发访问数据库select max时就可能取出相同的值,再insert就会发生Dumplicate entry的错误。所以只能有一个Hibernate应用进程访问数据库,否则就可能产生主键冲突,所以不适合多进程并发更新数据库,适合单一进程访问数据库,不能用于群集环境。

阅读全文 »

1.常见的错误返回类型

VOID/BOOL/HANDLE/PVOID/LONG/DWORD VOID 不可能失败 BOOL 错误返回false 编码的时候最好测试是否不为FALSE HANDLE 错误 NULL/INVALID_HANDLE_VALUE PVOID  失败返回NULL,成功返回一个数据块的地址 LONG/DWORD 0或-1,不绝对 2.GetLastError函数   DWORD GetLastError(); 3.WinError.h 头文件定义了若干的Mircosoft定义的代码列表 4.

IOCP是windows下的一种异步IO通信模式(linux下面对应epoll,二者有区别—>http://www.cnblogs.com/uzhang/archive/2012/02/24/2365980.html)

一样的socket的绑定通信的过程,不同的是并不对每一个用户请求单独开设一个线程去处理用户请求,而是在后面一线程池的方式,开固定的线程,轮流处理用户请求,减少线程的上下文切换损耗,同时利用重叠IO,减低内存等资源的消耗。 关于具体的一些内容,见:http://blog.csdn.net/neicole/article/details/7549497

×