find_basic
没保存过程数据,以后找时间再补吧
用 IDA 查看,能够发现反编译结果很奇怪,看看反汇编就能够发现存在很多 pusha 和 call 之类的指令,不如对着汇编动调
动调后因为不好找核心逻辑的位置,所以在输入处下个硬件断点,发现取出了输入的第一位内容,然后进行乘法和累加,类似下面这段代码:
ADD EAX,0x11
MOVZX EAX,byte ptr [EAX]
MOVZX EAX,AL
IMUL EAX,EAX,-0x35cf6
主逻辑在0x750a9的位置,继续跟踪一下逻辑,整体相当于n个虚拟机,结构大概如下:
father_func@0x750a9:
MOV CL, 0x??
CALL son_func
son_func:
CMP CL, 0x??
JMP ???
CMP CL, 0x??
JMP ???
每个子函数的开头和结尾都有一些类似话指令的逻辑,但动调发现好像不影响程序的逻辑,所以就忽略了
跟踪了几个验证用的逻辑后,发现主要是乘法和累加,说明应该是一个矩阵运算,考虑到工作量很大,那就需要想办法进行自动提取了
使用了 idapython 进行自动调试,并记录过程中运行的指令:
from binascii import hexlify
ea = get_reg_value('eip')
end = ea + 0x1806
main_func = get_func_name(ea)
fp = open('all_asm.txt','w')
while True:
ea = get_reg_value('eip')
next_ea = next_head(ea)
fp.write(hexlify(get_bytes(ea, next_ea - ea)).decode())
fp.write('\t' + GetDisasm(ea)+'\n')
if ea >= end:
break
if main_func == get_func_name(ea):
step_into()
else:
step_over()
wait_for_next_event(WFNE_SUSP, -1)
需要先动调到要提取的函数的第一条指令
这段代码把所有执行的指令都提取了出来,这样也能够验证一下之前的分析结果是否正确
观察提取结果发现,可以根据核心逻辑的上下指令进行定位,位于 jnz
之后,jmp
之前:
codes = open('all_asm.txt', 'r').read().split('\n')
c = open('asm.txt', 'w')
for i in range(len(codes)):
if codes[i].find('jnz') != -1 and codes[i+2].find('jnz') == -1:
j = i+3
while j < len(codes) and codes[j].find('jmp') == -1:
c.write(codes[j]+'\n')
j += 1
codes = open('asm.txt', 'r').read().split('\n')
codes = [i[:i.find('\t')] for i in codes]
datas = b''
for i in codes:
datas += unhexlify(i)
datas += '\xc3'
print(datas)
open('mch1', 'wb').write(datas)
之前提取的时候同时打印了指令的二进制代码,主要原因是提取的指令无法使用 nasm 编译,用 pwntools 速度又太慢,不如直接提取
提取到的内容直接反编译就好了,得到一个大长串的式子
提示说是需要对每个式子取绝对值,那就按照每次减常数来切割就行,最后的代码如下:
from z3 import *
a1 = [BitVec('x%d' % i, 32)for i in range(28)]
con1 = 40085 * a1[3] - 222506 * a1[2] + 54507 * a1[4] + 88056 * a1[1] + 212571 * a1[5] - 160722 * a1[0] - 434973
con2 = 49300 * a1[3] + 259229 * a1[0] + 278066 * a1[2] - 127937 * a1[1] - 295169 * a1[4] - 8368677
con3 = 42214 * a1[1] - 108025 * a1[3] + 205972 * a1[0] + 27559 * a1[2] - 17114904
con4 = - 151496 * a1[1] + 204740 * a1[0] + 80143 * a1[2] - 12295783
con5 = 241935 * a1[1] + 124128 * a1[0] - 38790036
con6 = 273221 * a1[0] - 27868542
con7 = -279656 * a1[2] - 199574 * a1[1] - 258130 * a1[8] - 200399 * a1[3] - 173903 * a1[7] + 175816 * a1[0] - 234569 * a1[6] - 108273 * a1[4] - 222957 * a1[5] + 128244179
con8 = - 81541 * a1[1] - 268763 * a1[0] + 219073 * a1[3] + 34782 * a1[6] + 21153 * a1[5] + 173005 * a1[7] + 76285 * a1[4] + 32825 * a1[2] - 13874925
con9 = 85214 * a1[2] - 268299 * a1[3] - 230981 * a1[1] + 290772 * a1[5] - 74394 * a1[4] + 28044 * a1[6] - 242995 * a1[0] + 50871139
con10 = -279656 * a1[2] - 199574 * a1[1] - 258130 * a1[8] - 200399 * a1[3] - 173903 * a1[7] + 175816 * a1[0] - 234569 * a1[6] - 108273 * a1[4] - 222957 * a1[5] + 128244179
con11 = - 81541 * a1[1] - 268763 * a1[0] + 219073 * a1[3] + 34782 * a1[6] + 21153 * a1[5] + 173005 * a1[7] + 76285 * a1[4] + 32825 * a1[2] - 13874925
con12 = 85214 * a1[2] - 268299 * a1[3] - 230981 * a1[1] + 290772 * a1[5] - 74394 * a1[4] + 28044 * a1[6] - 242995 * a1[0] + 50871139
con13 = -208564 * a1[0] + 81934 * a1[9] - 106641 * a1[7] + 198477 * a1[2] + 154505 * a1[1] + 48440 * a1[5] - 149004 * a1[3] - 108909 * a1[4] - 51714 * a1[10] - 296420 * a1[8] + 263021 * a1[6] + 688726
con14 = - 131130 * a1[2] + 224265 * a1[3] + 230702 * a1[0] - 176285 * a1[7] - 274778 * a1[4] + 103848 * a1[8] - 136039 * a1[9] - 241151 * a1[5] + 15542 * a1[6] - 17521 * a1[1] + 41644083
con15 = 195056 * a1[4] - 15717 * a1[9] - 180214 * a1[6] - 114427 * a1[5] + 277782 * a1[7] + 261379 * a1[8] - 225266 * a1[2] + 107609 * a1[0] + 259792 * a1[3] + 270563 * a1[11] + 205124 * a1[1] + 138334 * a1[10] + 103474 * a1[12] - 117027475
con16 = 189573 * a1[8] + 64393 * a1[6] + 231137 * a1[1] + 145315 * a1[4] - 53938 * a1[10] - 291345 * a1[5] + 216413 * a1[3] - 204681 * a1[0] - 65519 * a1[9] - 262826 * a1[2] + 187002 * a1[7] + 271732 * a1[11] - 38663722
con17 = 15645 * a1[13] + 276267 * a1[12] + 31190 * a1[5] - 244002 * a1[2] + 81415 * a1[3] - 22940 * a1[10] - 126076 * a1[7] + 8932 * a1[8] + 112153 * a1[4] + 194218 * a1[11] + 197656 * a1[9] - 204463 * a1[0] - 219500 * a1[1] + 19777 * a1[6] - 24531260
con18 = 279969 * a1[8] - 123977 * a1[4] + 162094 * a1[0] - 215769 * a1[1] - 18878 * a1[14] - 80292 * a1[11] - 237675 * a1[5] - 222121 * a1[6] + 269381 * a1[12] + 153934 * a1[13] - 165380 * a1[10] - 157137 * a1[2] - 186748 * a1[3] + 170756 * a1[7] - 186932 * a1[9] + 87264470
con19 = -87190 * a1[2] - 74836 * a1[1] + 16892 * a1[9] - 185781 * a1[8] - 12726 * a1[7] + 85022 * a1[12] + 232989 * a1[10] + 68516 * a1[0] - 120254 * a1[6] - 204892 * a1[5] - 65901 * a1[4] - 201087 * a1[13] + 158612 * a1[11] - 49445 * a1[3] - 181860 * a1[14] - 111015 * a1[15] + 43646834
con20 = -170184 * a1[3] - 137671 * a1[4] - 85374 * a1[9] - 73658 * a1[11] + 230891 * a1[13] + 54346 * a1[15] - 280694 * a1[0] + 60411 * a1[2] + 27171 * a1[7] - 50618 * a1[6] + 11843 * a1[10] + 131778 * a1[5] + 13956 * a1[8] - 42562 * a1[12] - 19972 * a1[1] - 145797 * a1[14] - 58717 * a1[16] + 74613584
con21 = 242475 * a1[16] - 234385 * a1[0] + 124653 * a1[2] - 287929 * a1[13] - 190916 * a1[12] - 277578 * a1[11] + 39 * a1[8] - 41625 * a1[6] + 67262 * a1[5] - 250144 * a1[9] - 70886 * a1[10] - 223492 * a1[15] - 179651 * a1[7] + 206538 * a1[17] + 161965 * a1[3] - 146258 * a1[4] + 167068 * a1[1] + 196330 * a1[14] + 76353817
con22 = 29700 * a1[18] - 60542 * a1[5] + 274107 * a1[11] + 154914 * a1[13] - 143185 * a1[12] + 167424 * a1[2] + 137439 * a1[8] - 186151 * a1[10] - 77157 * a1[9] - 233090 * a1[6] - 27400 * a1[7] - 76557 * a1[15] - 108002 * a1[17] + 103161 * a1[14] - 133956 * a1[1] - 219502 * a1[4] - 202897 * a1[0] - 250957 * a1[3] - 119297 * a1[16] + 100812197
con23 = -171971 * a1[9] + 38740 * a1[4] - 31661 * a1[10] - 194653 * a1[18] - 295910 * a1[16] + 136489 * a1[12] + 212619 * a1[17] + 165592 * a1[11] + 211791 * a1[1] + 156909 * a1[2] - 232187 * a1[8] - 73709 * a1[7] + 79735 * a1[14] + 184882 * a1[13] + 111105 * a1[6] + 148840 * a1[3] - 35774 * a1[19] - 275711 * a1[0] + 135265 * a1[5] - 141221 * a1[15] - 39117122
con24 = -186514 * a1[17] - 7791 * a1[2] + 276755 * a1[11] - 294815 * a1[14] - 238763 * a1[15] - 146099 * a1[5] + 184977 * a1[16] + 178413 * a1[1] + 287303 * a1[3] - 71946 * a1[10] - 73771 * a1[9] - 129032 * a1[18] + 200202 * a1[20] - 150509 * a1[6] - 156625 * a1[13] + 14093 * a1[7] + 192584 * a1[12] - 122770 * a1[0] - 255494 * a1[8] + 65 * a1[4] - 108479 * a1[19] + 13521895
con25 = 210978 * a1[7] + 300336 * a1[10] + 207254 * a1[15] + 216206 * a1[5] - 63529 * a1[0] - 274903 * a1[11] - 10750 * a1[14] + 25008 * a1[4] - 100942 * a1[19] - 104857 * a1[2] + 266501 * a1[8] + 229070 * a1[17] - 234559 * a1[16] + 298459 * a1[3] - 172052 * a1[6] - 98938 * a1[12] + 66155 * a1[13] - 84761 * a1[1] - 283508 * a1[18] + 288577 * a1[21] - 75407 * a1[20] - 204447 * a1[9] + 4351595
con26 = -201846 * a1[14] + 272550 * a1[20] + 60398 * a1[6] + 45580 * a1[7] + 195108 * a1[11] + 38596 * a1[0] + 220445 * a1[18] - 190873 * a1[15] + 103477 * a1[9] + 118842 * a1[19] + 206336 * a1[10] - 249940 * a1[17] - 48084 * a1[21] + 104901 * a1[5] - 48576 * a1[4] + 287104 * a1[16] - 286686 * a1[1] - 30253 * a1[22] + 121183 * a1[3] + 90967 * a1[2] - 195519 * a1[12] - 129304 * a1[8] + 141188 * a1[13] - 56642147
con27 = 110609 * a1[4] + 5913 * a1[21] - 197578 * a1[7] + 45127 * a1[18] + 282426 * a1[13] - 71019 * a1[16] - 6980 * a1[11] + 208216 * a1[15] - 13544 * a1[20] + 17852 * a1[8] + 167833 * a1[12] + 145568 * a1[17] + 3610 * a1[19] + 91985 * a1[1] - 267402 * a1[5] - 32355 * a1[14] - 197823 * a1[23] + 135525 * a1[2] - 229424 * a1[22] + 38093 * a1[10] + 50167 * a1[6] + 118713 * a1[9] + 123874 * a1[0] - 89499 * a1[3] - 43090537
con28 = -164755 * a1[9] + 175470 * a1[8] - 28660 * a1[1] + 7217 * a1[11] - 295102 * a1[4] - 28531 * a1[19] - 106265 * a1[25] - 92750 * a1[10] + 16738 * a1[21] - 231714 * a1[6] + 172042 * a1[24] - 215890 * a1[17] + 199697 * a1[12] - 84235 * a1[7] + 44614 * a1[13] + 75104 * a1[5] - 195843 * a1[0] - 15784 * a1[14] - 131950 * a1[15] - 268167 * a1[16] - 197565 * a1[20] + 24340 * a1[23] + 105130 * a1[2] - 79750 * a1[22] - 264668 * a1[3] + 50329 * a1[18] + 137774797
con28 = 62119 * a1[17] - 17215 * a1[24] + 289621 * a1[18] + 53006 * a1[20] + 95969 * a1[11] + 202404 * a1[0] + 247060 * a1[21] + 144211 * a1[19] + 280106 * a1[7] - 126431 * a1[10] - 226837 * a1[12] + 10463 * a1[23] + 121257 * a1[13] - 84190 * a1[9] + 88917 * a1[1] + 15453 * a1[14] + 271442 * a1[4] + 110851 * a1[3] - 231422 * a1[5] + 176741 * a1[22] + 266134 * a1[2] - 197327 * a1[6] - 55225 * a1[8] - 265465 * a1[15] + 119612 * a1[16] - 98514358
con29 = 62119 * a1[17] - 17215 * a1[24] + 289621 * a1[18] + 53006 * a1[20] + 95969 * a1[11] + 202404 * a1[0] + 247060 * a1[21] + 144211 * a1[19] + 280106 * a1[7] - 126431 * a1[10] - 226837 * a1[12] + 10463 * a1[23] + 121257 * a1[13] - 84190 * a1[9] + 88917 * a1[1] + 15453 * a1[14] + 271442 * a1[4] + 110851 * a1[3] - 231422 * a1[5] + 176741 * a1[22] + 266134 * a1[2] - 197327 * a1[6] - 55225 * a1[8] - 265465 * a1[15] + 119612 * a1[16] - 98514358
con30 = 151924 * a1[25] - 265311 * a1[6] + 107604 * a1[11] - 47851 * a1[24] + 227178 * a1[13] - 162699 * a1[2] + 2171 * a1[20] + 211070 * a1[23] + 94815 * a1[22] + 124760 * a1[16] + 41462 * a1[19] - 277022 * a1[15] - 62501 * a1[26] - 17727 * a1[14] - 257908 * a1[4] - 175112 * a1[21] + 8972 * a1[10] - 71801 * a1[8] - 114724 * a1[5] - 252898 * a1[9] + 161457 * a1[1] - 64461 * a1[0] - 111493 * a1[18] + 200145 * a1[17] - 290075 * a1[3] + 158466 * a1[12] - 275262 * a1[7] + 86899519
con31 = 142850 * a1[18] - 166704 * a1[1] + 284852 * a1[22] + 248972 * a1[7] - 76200 * a1[17] + 261708 * a1[19] + 91911 * a1[24] + 22347 * a1[3] + 76006 * a1[21] + 256511 * a1[6] - 100052 * a1[14] - 115830 * a1[2] - 93202 * a1[23] + 248858 * a1[12] - 262669 * a1[10] + 67895 * a1[5] - 111771 * a1[8] - 132193 * a1[11] - 141512 * a1[13] + 139406 * a1[27] + 109646 * a1[16] - 286309 * a1[9] + 175476 * a1[15] + 138067 * a1[20] + 192825 * a1[25] + 199577 * a1[0] - 63091 * a1[4] - 285207 * a1[26] - 58820340
s = Solver()
for i in range(28):
s.add(a1[i] > 0x20)
s.add(a1[i] < 0x7f)
s.add(a1[0] == ord('f'))
s.add(a1[1] == ord('l'))
s.add(a1[2] == ord('a'))
s.add(a1[3] == ord('g'))
s.add(a1[4] == ord('{'))
s.add(a1[27] == ord('}'))
s.add(con1 == 0)
s.add(con2 == 0)
s.add(con3 == 0)
s.add(con4 == 0)
s.add(con5 == 0)
s.add(con6 == 0)
s.add(con7 == 0)
s.add(con8 == 0)
s.add(con9 == 0)
s.add(con10 == 0)
s.add(con11 == 0)
s.add(con12 == 0)
s.add(con13 == 0)
s.add(con14 == 0)
s.add(con15 == 0)
s.add(con16 == 0)
s.add(con17 == 0)
s.add(con18 == 0)
s.add(con19 == 0)
s.add(con20 == 0)
s.add(con21 == 0)
s.add(con22 == 0)
s.add(con23 == 0)
s.add(con24 == 0)
s.add(con25 == 0)
s.add(con26 == 0)
s.add(con27 == 0)
s.add(con28 == 0)
s.add(con29 == 0)
s.add(con30 == 0)
s.add(con31 == 0)
if sat == s.check():
m = s.model()
flag = ''
for i in a1:
flag += chr(m[i].as_long())
import hashlib
hl = hashlib.md5()
hl.update(flag.encode())
print (hl.hexdigest() == '042905954c2c27f21bd74489ea0d151f')
print (flag)
GameMaster
.net逆向
一个21点游戏,逆向发现存在作弊码
if (memcmp1(input, "MF3K", 4)) {
try {
game.Player.Bet -= 22m;
for (int i = 0; i < memory.Length; i++) {
memory[i] ^= 34;
}
Environment.SetEnvironmentVariable("AchivePoint1", game.Player.Balance.ToString());
return;
}
}
if (memcmp1(input, "EEPW", 4)) {
try {
game.Player.Balance += 175m;
byte[] key = new byte[16]
{
66, 114, 97, 105, 110, 115, 116, 111, 114, 109,
105, 110, 103, 33, 33, 33
};
RijndaelManaged rijndaelManaged = new RijndaelManaged();
rijndaelManaged.Key = key;
rijndaelManaged.Mode = CipherMode.ECB;
rijndaelManaged.Padding = PaddingMode.Zeros;
ICryptoTransform cryptoTransform = rijndaelManaged.CreateDecryptor();
m = cryptoTransform.TransformFinalBlock(memory, 0, memory.Length);
Environment.SetEnvironmentVariable("AchivePoint2", game.Player.Balance.ToString());
return;
}
}
if (memcmp1(input, "6VD6", 4)) {
try {
game.Player.Balance -= 27m;
Environment.SetEnvironmentVariable("AchivePoint3", game.Player.Balance.ToString());
BinaryFormatter binaryFormatter = new BinaryFormatter();
MemoryStream serializationStream = new MemoryStream(m);
binaryFormatter.Deserialize(serializationStream);
return;
}
}
依次触发三个作弊码,就会对文件内容进行解密并反序列化
解密后发现序列化内容中包含了一个dll文件:
private static void Check1(ulong x, ulong y, ulong z, byte[] KeyStream)
{
int num = -1;
for (int i = 0; i < 320; i++)
{
x = (((x >> 29 ^ x >> 28 ^ x >> 25 ^ x >> 23) & 1UL) | x << 1);
y = (((y >> 30 ^ y >> 27) & 1UL) | y << 1);
z = (((z >> 31 ^ z >> 30 ^ z >> 29 ^ z >> 28 ^ z >> 26 ^ z >> 24) & 1UL) | z << 1);
bool flag = i % 8 == 0;
if (flag)
{
num++;
}
KeyStream[num] = (byte)((long)((long)KeyStream[num] << 1) | (long)((ulong)((uint)((z >> 32 & 1UL & (x >> 30 & 1UL)) ^ (((z >> 32 & 1UL) ^ 1UL) & (y >> 31 & 1UL))))));
}
}
private static void ParseKey(ulong[] L, byte[] Key)
{
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
{
Key[i * 4 + j] = (byte)(L[i] >> j * 8 & 255UL);
}
}
}
public T1()
{
try
{
string environmentVariable = Environment.GetEnvironmentVariable("AchivePoint1");
string environmentVariable2 = Environment.GetEnvironmentVariable("AchivePoint2");
string environmentVariable3 = Environment.GetEnvironmentVariable("AchivePoint3");
bool flag = environmentVariable == null || environmentVariable2 == null || environmentVariable3 == null;
if (!flag)
{
ulong num = ulong.Parse(environmentVariable);
ulong num2 = ulong.Parse(environmentVariable2);
ulong num3 = ulong.Parse(environmentVariable3);
ulong[] array = new ulong[3];
byte[] array2 = new byte[40];
byte[] array3 = new byte[40];
byte[] array4 = new byte[12];
byte[] first = new byte[] {
101, 5, 80, 213, 163, 26, 59, 38,
19, 6, 173, 189, 198, 166, 140, 183,
42, 247, 223, 24, 106, 20, 145, 37,
24, 7, 22, 191, 110, 179, 227, 5,
62, 9, 13, 17, 65, 22, 37, 5
};
byte[] array5 = new byte[] {
60, 100, 36, 86, 51, 251, 167, 108,
116, 245, 207, 223, 40, 103, 34, 62,
22, 251, 227
};
array[0] = num;
array[1] = num2;
array[2] = num3;
T1.Check1(array[0], array[1], array[2], array2);
bool flag2 = first.SequenceEqual(array2);
if (flag2)
{
T1.ParseKey(array, array4);
for (int i = 0; i < array5.Length; i++)
{
array5[i] ^= array4[i % array4.Length];
}
MessageBox.Show("flag{" + Encoding.Default.GetString(array5) + "}", "Congratulations!", MessageBoxButtons.OK);
}
}
}
catch (Exception)
{
}
}
使用 z3 求解三个环境变量
from z3 import *
x = BitVec('x', 64)
y = BitVec('y', 64)
z = BitVec('z', 64)
flag1 = [
101, 5, 80, 213, 163, 26, 59, 38, 19, 6, 173,
189, 198, 166, 140, 183, 42, 247, 223, 24, 106,
20, 145, 37, 24, 7, 22, 191, 110, 179, 227, 5,
62, 9, 13, 17, 65, 22, 37, 5
]
s = Solver()
for i in range(320):
x = (((x >> 29 ^ x >> 28 ^ x >> 25 ^ x >> 23) & 1) | x << 1)
y = (((y >> 30 ^ y >> 27) & 1) | y << 1)
z = (((z >> 31 ^ z >> 30 ^ z >> 29 ^ z >> 28 ^ z >> 26 ^ z >> 24) & 1) | z << 1)
cur = ((z >> 32) & 1 & (x >> 30 & 1)) ^ ((((z >> 32) & 1) ^ 1) & ((y >> 31) & 1))
s.add(cur == (flag1[i // 8] >> (7 - i % 8)) & 1)
if s.check() == sat:
m = s.model()
print (m)
else:
print ('no res')
求解,或动调改内存
array = [156324965, 868387187, 3131229747]
Key = [0] * 12
for i in range(3):
for j in range(4):
Key[i * 4 + j] = (array[i] >> (j * 8)) & 0xff
print (Key)
cipher = [60, 100, 36, 86, 51, 251, 167, 108, 116, 245, 207, 223, 40, 103, 34, 62, 22, 251, 227]
for i in range(len(cipher)):
print (chr(cipher[i] ^ Key[i % len(Key)]), end='')
Easyapk
逻辑在 so 层,有大量的垃圾指令,实际逻辑是先进行 rot13,再进行 tea 加密 解密脚本如下:
#include <stdio.h>
#include <stdint.h>
//解密函数
void decrypt (uint32_t* v, uint32_t* k) {
uint32_t v0=v[0], v1=v[1], sum=0xC6EF3720, i; /* set up */
uint32_t delta=0x9e3779b9; /* a key schedule constant */
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
for (i=0; i<32; i++) { /* basic cycle start */
v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
sum -= delta;
} /* end cycle */
v[0]=v0; v[1]=v1;
}
int main()
{
uint32_t v[]={1570024068u, 351937696u, 727056912u, 3063668041u, 2867849940u, 1267528902u, 159365321u, 3052163538u},k[4]={0x33323130, 0x37363534, 0x62613938, 0x66656463};
// v为要加密的数据是两个32位无符号整数
// k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
printf("加密后的数据:%u %u\n",v[0],v[1]);
decrypt(v, k);
decrypt(v + 2, k);
decrypt(v + 4, k);
decrypt(v + 6, k);
unsigned char *x = (unsigned char *)v;
for (int i = 0; i < 32; i++) {
printf("%x", x[i]);
}
return 0;
}
// synt{Vg_Vf_A0g_guNg_zHpu_unEqre}
结果进行rot13即可
flag{It_Is_N0t_thAt_mUch_haRder}
deeprev
这道题一开始找不到逻辑,后来在 check 下断点,断在了 ld 中,看一下各个段的大小,发现relo部分很大,说明逻辑应该藏在了链接过程中
后来发现是仿照 google ctf eldar 那题的,直接借用别人的脚本
import lief
from collections import namedtuple
from dataclasses import dataclass
b = lief.ELF.parse('./deeprev')
def to_sym(name):
assert len(name) == 1
return ord(name[0])
Rel = namedtuple('REL', ['dst', 'val', 'ridx'])
Copy = namedtuple('CPY', ['dst', 'symbol', 'ridx'])
R64 = namedtuple('R64', ['dst', 'symbol', 'addend', 'ridx'])
R32 = namedtuple('R32', ['dst', 'symbol', 'addend', 'ridx'])
@dataclass
class Symbol(object):
idx: int
def __repr__(self):
return f's{self.idx}'
@dataclass
class Reloc(object):
idx: int
def __repr__(self):
return f'r{self.idx}'
@dataclass
class Ref(object):
def __repr__(self):
return f'&{self.val}'
@dataclass
class SymAddr(object):
sym: Symbol
field: str
def __repr__(self):
return f'{self.sym}.{self.field}'
@dataclass
class RelocAddr(object):
reloc: Reloc
field: str
def __repr__(self):
return f'{self.reloc}.{self.field}'
def vaddr(self):
off = 0
print(self.field)
# match self.field:
# case 'r_address': off = 0
# case 'r_info': off = 8
# case 'r_addend': off = 16
return (self.reloc.idx * 24) + off + rela.virtual_address
@dataclass
class FlagAddr(object):
idx: int
def __repr__(self):
return f'flag[{self.idx}]'
BaseAddr = namedtuple('baseaddr', [])
CheckAddr = namedtuple('check', [])
rela = [x for x in b.sections if x.name == '.rela.dyn'][0]
dynsym = [x for x in b.sections if x.name == '.dynsym'][0]
def format_addr(addr: int):
if (addr >= rela.virtual_address and addr < rela.virtual_address + rela.size):
offset = addr - rela.virtual_address
r_offset = (offset // 24)
r_rem = offset % 24
if r_rem == 0:
return RelocAddr(Reloc(r_offset), 'r_address')
elif r_rem == 8:
return RelocAddr(Reloc(r_offset), 'r_info')
elif r_rem == 16:
return RelocAddr(Reloc(r_offset), 'r_addend')
else:
return RelocAddr(Reloc(r_offset), r_rem)
elif (addr > dynsym.virtual_address
and addr < dynsym.virtual_address + dynsym.size
):
offset = addr - dynsym.virtual_address
r_offset = (offset // 24)
r_rem = offset % 24
if r_rem == 0:
return SymAddr(Symbol(r_offset), 'st_name')
elif r_rem == 8:
return Symbol(r_offset)
elif r_rem == 16:
return SymAddr(Symbol(r_offset), 'st_size')
else:
return SymAddr(Symbol(r_offset), r_rem)
elif addr >= 0x404040 and addr < 0x404040+0x21:
off = addr-0x404040
return FlagAddr(off)
elif addr == 0x804000:
return BaseAddr()
elif addr == 0x404064:
return CheckAddr()
else:
return addr
def parse(b) -> list:
print('[*] Loading relocations...')
relocs = list(b.relocations)
print('[*] Parsing...')
instructions = []
for i in range(3, len(relocs)):
r = relocs[i]
if r.type == 1:
instructions.append(
R64(format_addr(r.address), to_sym(r.symbol.name), format_addr(r.addend), i))
elif r.type == 5: # CPY
instructions.append(
Copy(format_addr(r.address), to_sym(r.symbol.name), i))
elif r.type == 8: # REL
instructions.append(
Rel(format_addr(r.address), format_addr(r.addend), i))
elif r.type == 7: # R32
# instructions.append(
# R32(r.address, to_sym(r.symbol.name), r.addend, i))
instructions.append(R32(1, 1, 1, 1))
return instructions
def dump(instructions):
for op in instructions:
if type(op).__name__ == 'REL':
print(f'[{op.ridx:04d}] :: rel {op.dst}, {op.val}')
elif type(op).__name__ == 'CPY':
print(f'[{op.ridx:04d}] :: copy {op.dst}, s{op.symbol}')
elif type(op).__name__ == 'R64':
print(f'[{op.ridx:04d}] :: r64 {op.dst}, {op.symbol} + {op.addend}') # 其实是 s{symbol} + {addend}
elif type(op).__name__ == 'R32':
print('7')
if(op.ridx == 1266):
break
instructions = parse(b)
dump(instructions)
提取后发现主要逻辑为加载 shellcode 并执行
具体过程是使用了 rel
指令将 shellcode 移动到某个地址,随后使用 r64
指令调用该地址
[0005] :: rel s2, flag[0]
[0006] :: rel s2.st_size, 1
[0007] :: copy s4, s2
[0009] :: rel r8.r_address, 0x16008040cc253480
[0010] :: rel r8.r_info, 0xc3
[0011] :: rel s3, r8.r_address
[0012] :: rel s3.st_name, 0x1000a0000001a
[0013] :: r64 r8.r_address, 3 + 0 ; 这里相当于 call s3
[0014] :: rel s2, r101002.r_address
[0015] :: rel s2.st_size, 0x18
[0016] :: copy r8.r_address, s2
[0018] :: rel r17.r_address, 0x8040cc250480
[0019] :: rel r17.r_info, 0xc3
[0020] :: rel s3, r17.r_address
[0021] :: rel s3.st_name, 0x1000a0000001a
[0022] :: r64 r17.r_address, 3 + 0
[0023] :: copy r17.r_address, s2
[0024] :: rel s2, s4
[0025] :: rel s2.st_size, 1
[0026] :: copy r3.r_address, s2
[0597] :: rel s2, r3.r_address
[0598] :: rel s2.st_size, 8
[0599] :: copy s6, s2
[0601] :: rel r600.r_address, 0x70008040fc253480
[0602] :: rel r600.r_info, 0xc3
[0603] :: rel s3, r600.r_address
[0604] :: rel s3.st_name, 0x1000a0000001a
[0605] :: r64 r600.r_address, 3 + 0
[0606] :: rel s2, r101002.r_address
[0607] :: rel s2.st_size, 0x18
[0608] :: copy r600.r_address, s2
[0609] :: rel s2, s6
[0610] :: rel s2.st_size, 8
[0611] :: copy r612.r_addend, s2
[0612] :: r64 s5, 5 + 0
[0613] :: rel r612.r_addend, 0
[0614] :: rel s6, 0
上述代码对 flag 第一位执行了两处 shellcode,使用 Capstone 分析发现 shellcode 中的逻辑为 f ^ 0x16 + 0
随后对该值 xor 0x70,最后值为 0 表示通过
前28位flag均是该逻辑的重复
xor = [0x16, 0x17, 0x10, 0x12, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x24, 0x2c, 0x26, 0x1e, 0x1f, 0x20, 0x20, 0x21, 0x23, 0x27, 0x24, 0x25, 0x26, 0x27]
add = [0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b]
xor2 = [0x70, 0x7c, 0x73, 0x78, 0x6f, 0x27, 0x2a, 0x2c, 0x7f, 0x35, 0x2d, 0x32, 0x37, 0x3b, 0x22, 0x59, 0x53, 0x8e, 0x3d, 0x2a, 0x59, 0x27, 0x2d, 0x29, 0x34, 0x2d, 0x61, 0x32]
for x1, a, x2 in zip(xor, add, xor2):
print (chr((x2 - a) ^ x), end='')
# flag{366c950370fec47e34581a0
对最后几位 flag 进行的计算有个小 smc 的操作:
[1074] :: rel s2, flag[28]
[1075] :: copy s6, s2
[1076] :: rel s2, flag[29]
[1077] :: copy s7, s2
[1078] :: rel s8, 0
[1079] :: rel s2, s8
[1080] :: copy r1081.r_addend, s2
[1081] :: r64 s8, 8 + 0 ; 上一个指令将此处修改为 r64 s8, s8 + s2, 相当于 add s8, s2
[1082] :: rel r1081.r_addend, 0
[1083] :: rel s2, s6
[1084] :: copy r1085.r_addend, s2
[1085] :: r64 s8, 8 + 0
[1086] :: rel r1085.r_addend, 0
[1087] :: rel s9, 0
[1088] :: rel s2, s9
[1089] :: copy r1090.r_addend, s2
[1090] :: r64 s9, 9 + 0
[1091] :: rel r1090.r_addend, 0
[1092] :: rel s2, s7
[1093] :: copy r1094.r_addend, s2
[1094] :: r64 s9, 9 + 0
[1095] :: rel r1094.r_addend, 0
[1096] :: rel s2, s9
[1097] :: copy r1098.r_addend, s2
[1098] :: r64 s10, 8 + 0
[1099] :: rel r1098.r_addend, 0
[1101] :: rel r1100.r_address, 0x6c0080415c253480
[1102] :: rel r1100.r_info, 0xc3
[1103] :: rel s3, r1100.r_address
[1104] :: rel s3.st_name, 0x1000a0000001a
[1105] :: r64 r1100.r_address, 3 + 0
[1106] :: rel s2, r101002.r_address
[1107] :: rel s2.st_size, 0x18
[1108] :: copy r1100.r_address, s2
[1109] :: rel s2, s10
[1110] :: rel s2.st_size, 8
[1111] :: copy r1112.r_addend, s2
[1112] :: r64 s5, 5 + 0
[1113] :: rel r1112.r_addend, 0
[1114] :: rel s6, 0
[1115] :: rel s7, 0
[1116] :: rel s8, 0
[1117] :: rel s9, 0
[1118] :: rel s10, 0
总结一下:
(flag[28] + flag[29]) ^ 0x6c = 0
(flag[28] * 2 + flag[29]) ^ 0xa1 = 0
(flag[30] + flag[31]) ^ 0xb1 = 0
(flag[30] * 2 + flag[31]) ^ 0xe5 = 0
计算得到flag最后四位